diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..f5e3093ffeb155f6a43fd72137e928783e65232d --- /dev/null +++ b/drivers/video/msm/Kconfig @@ -0,0 +1,921 @@ +config FB_MSM + tristate "MSM Framebuffer support" + depends on FB && ARCH_MSM + select FB_BACKLIGHT if FB_MSM_BACKLIGHT + select NEW_LEDS + select LEDS_CLASS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Support for MSM Framebuffer. + +if FB_MSM + +config FB_MSM_BACKLIGHT + bool "Support for backlight control" + default y + ---help--- + Say Y here if you want to control the backlight of your display. + +config FB_MSM_LOGO + bool "MSM Frame Buffer Logo" + default n + ---help--- + Show /initlogo.rle during boot. + +config FB_MSM_LCDC_HW + bool + default n + +config FB_MSM_TRIPLE_BUFFER + bool "Support for triple frame buffer" + default n + +config FB_MSM_MDP_HW + bool + default n + +choice + prompt "MDP HW version" + default FB_MSM_MDP22 + +config FB_MSM_MDP22 + select FB_MSM_MDP_HW + bool "MDP HW ver2.2" + ---help--- + Support for MSM MDP HW revision 2.2 + Say Y here if this is msm7201 variant platform. + +config FB_MSM_MDP30 + select FB_MSM_LCDC_HW + bool "MDP HW ver3.0" + ---help--- + Support for MSM MDP HW revision 3.0 + Say Y here if this is msm7x25 variant platform. + +config FB_MSM_MDP303 + depends on FB_MSM_MDP30 + select FB_MSM_MDP_HW + bool "MDP HW ver3.03" + default n + ---help--- + Support for MSM MDP HW revision 3.03. This is a new version of + MDP3.0 which has the required functionality to support the features + required for msm7x2xA platform. + Say Y here if this is msm7x2xA variant platform. + +config FB_MSM_MDP31 + select FB_MSM_LCDC_HW + select FB_MSM_MDP_HW + bool "MDP HW ver3.1" + ---help--- + Support for MSM MDP HW revision 3.1 + Say Y here if this is msm8x50 variant platform. + +config FB_MSM_MDP40 + select FB_MSM_LCDC_HW + select FB_MSM_MDP_HW + bool "MDP HW ver4.0" + ---help--- + Support for MSM MDP HW revision 4.0 + Say Y here if this is msm7x30 variant platform. + +config FB_MSM_MDP_NONE + bool "MDP HW None" + ---help--- + Say Y here if this is mdm platform. + +endchoice + +config FB_MSM_EBI2 + bool + default n + +config FB_MSM_MDDI + bool + default n + +config FB_MSM_MIPI_DSI + bool + default n + +config FB_MSM_LCDC + bool + default n + +config FB_MSM_LVDS + bool + default n + +config FB_MSM_OVERLAY + depends on FB_MSM_MDP40 && ANDROID_PMEM + bool "MDP4 overlay support" + default n + +config FB_MSM_DTV + depends on FB_MSM_OVERLAY + bool + default n + +config FB_MSM_EXTMDDI + bool + default n + +config FB_MSM_TVOUT + bool + default n + +config FB_MSM_MDDI_TOSHIBA_COMMON + bool + select FB_MSM_MDDI + default n + +config FB_MSM_MDDI_TOSHIBA_COMMON_VGA + bool + select FB_MSM_MDDI_TOSHIBA_COMMON + default n + +config FB_MSM_MDDI_ORISE + bool + select FB_MSM_MDDI + default n + +config FB_MSM_MDDI_QUICKVX + bool + select FB_MSM_MDDI + default n + +config FB_MSM_MDDI_AUTO_DETECT + bool + select FB_MSM_MDDI + default n + +config FB_MSM_LCDC_AUTO_DETECT + bool + select FB_MSM_LCDC + default n + +config FB_MSM_LCDC_PANEL + bool + select FB_MSM_LCDC + default n + +config FB_MSM_MIPI_DSI_TOSHIBA + bool + select FB_MSM_MIPI_DSI + default n + +config FB_MSM_MIPI_DSI_RENESAS + bool + select FB_MSM_MIPI_DSI + default n + +config FB_MSM_MIPI_DSI_TRULY + bool + select FB_MSM_MIPI_DSI + +config FB_MSM_MIPI_DSI_SIMULATOR + bool + select FB_MSM_MIPI_DSI + default n + +config FB_MSM_MIPI_DSI_NOVATEK + bool + select FB_MSM_MIPI_DSI + default n + +config FB_MSM_MIPI_DSI_NT35510 + bool + select FB_MSM_MIPI_DSI + +config FB_MSM_MIPI_DSI_ORISE + bool + select FB_MSM_MIPI_DSI + default n + +config FB_MSM_MIPI_DSI_NT35516 + bool + select FB_MSM_MIPI_DSI + +config FB_MSM_MIPI_DSI_TC358764_DSI2LVDS + bool + select FB_MSM_MIPI_DSI + ---help--- + Support for Toshiba MIPI DSI-to-LVDS bridge. + The chip supports 1366x768 24-bit + using a single LVDS link + and up to WUXGA 1920x1200 18-bit + using a dual LVDS link. + +config FB_MSM_LCDC_ST15_WXGA + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_ST15_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC ST1.5 Panel" + select FB_MSM_LCDC_ST15_WXGA + ---help--- + Support for ST1.5 WXGA (1366x768) panel + +config FB_MSM_LCDC_PRISM_WVGA + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_SAMSUNG_WSVGA + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_CHIMEI_WXGA + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_GORDON_VGA + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_TOSHIBA_WVGA_PT + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_TOSHIBA_FWVGA_PT + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_SHARP_WVGA_PT + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_AUO_WVGA + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_TRULY_HVGA_IPS3P2335 + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_TRULY_HVGA_IPS3P2335_PT_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Truly HVGA PT Panel" + select FB_MSM_LCDC_TRULY_HVGA_IPS3P2335 + default n + ---help--- + Support for LCDC Truly HVGA PT panel + + +config FB_MSM_LCDC_SAMSUNG_OLED_PT + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_NT35582_WVGA + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LCDC_WXGA + bool + select FB_MSM_LCDC_PANEL + default n + +config FB_MSM_LVDS_CHIMEI_WXGA + bool + select FB_MSM_LVDS + default n + +config FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT + bool + select FB_MSM_MIPI_DSI_TOSHIBA + default n + +config FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT + bool + select FB_MSM_MIPI_DSI_TOSHIBA + default n + +config FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA + bool + select FB_MSM_MIPI_DSI_TOSHIBA + default n + +config FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT + bool + select FB_MSM_MIPI_DSI_NOVATEK + default n + +config FB_MSM_MIPI_NOVATEK_CMD_QHD_PT + bool + select FB_MSM_MIPI_DSI_NOVATEK + default n + +config FB_MSM_MIPI_ORISE_VIDEO_720P_PT + bool + select FB_MSM_MIPI_DSI_ORISE + default n + +config FB_MSM_MIPI_ORISE_CMD_720P_PT + bool + select FB_MSM_MIPI_DSI_ORISE + default n + +config FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT + bool + select FB_MSM_MIPI_DSI_RENESAS + default n + +config FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT + bool + select FB_MSM_MIPI_DSI_RENESAS + default n + +config FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT + bool + select FB_MSM_MIPI_DSI_NT35510 + default n + +config FB_MSM_MIPI_NT35510_CMD_WVGA_PT + bool + select FB_MSM_MIPI_DSI_NT35510 + default n + +config FB_MSM_MIPI_NT35516_VIDEO_QHD_PT + bool + select FB_MSM_MIPI_DSI_NT35516 + default n + +config FB_MSM_MIPI_NT35516_CMD_QHD_PT + bool + select FB_MSM_MIPI_DSI_NT35516 + default n + + +config FB_MSM_MIPI_CHIMEI_WXGA + bool "LVDS Chimei WXGA Panel using Toshiba MIPI DSI-to-LVDS bridge." + select FB_MSM_MIPI_DSI_TC358764_DSI2LVDS + ---help--- + Support for Chimei WXGA (1366x768) panel. + The panel is using a serial LVDS input. + The panel is connected to the host + via Toshiba DSI-to-LVDS bridge. + +config FB_MSM_MIPI_CHIMEI_WUXGA + bool "LVDS Chimei WUXGA Panel using Toshiba MIPI DSI-to-LVDS bridge." + select FB_MSM_MIPI_DSI_TC358764_DSI2LVDS + ---help--- + Support for Chimei WUXGA (1920x1200) panel. + The panel is using a serial LVDS input. + The panel is connected to the host + via Toshiba DSI-to-LVDS bridge. + +config FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT + bool + select FB_MSM_MIPI_DSI_TRULY + +config FB_MSM_MIPI_SIMULATOR_VIDEO + bool + select FB_MSM_MIPI_DSI_SIMULATOR + default n + +config FB_MSM_OVERLAY0_WRITEBACK + depends on FB_MSM_OVERLAY + bool "MDP overlay0 write back mode enable" + ---help--- + Support for MDP4 OVERLAY0 write back mode + + +config FB_MSM_OVERLAY1_WRITEBACK + depends on FB_MSM_OVERLAY + bool "MDP overlay1 write back mode enable" + ---help--- + Support for MDP4 OVERLAY1 write back mode + +config FB_MSM_WRITEBACK_MSM_PANEL + depends on FB_MSM_OVERLAY + bool "MDP overlay write back panel enable" + ---help--- + Support for MDP4 OVERLAY write back mode +choice + prompt "LCD Panel" + default FB_MSM_MDDI_AUTO_DETECT + +config FB_MSM_LCDC_PRISM_WVGA_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Prism WVGA Panel" + select FB_MSM_LCDC_PRISM_WVGA + ---help--- + Support for LCDC Prism WVGA (800x480) panel + +config FB_MSM_LCDC_SAMSUNG_WSVGA_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Samsung WSVGA Panel" + select FB_MSM_LCDC_SAMSUNG_WSVGA + ---help--- + Support for LCDC Samsung WSVGA (1024x600) panel + +config FB_MSM_LCDC_CHIMEI_WXGA_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Chimei WXGA Panel" + select FB_MSM_LCDC_CHIMEI_WXGA + ---help--- + Support for LCDC Chimei WXGA (1366x768) panel + +config FB_MSM_LCDC_GORDON_VGA_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Gordon VGA Panel" + select FB_MSM_LCDC_GORDON_VGA + ---help--- + Support for LCDC Gordon VGA (480x640) panel + +config FB_MSM_LCDC_TOSHIBA_WVGA_PT_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Toshiba WVGA PT Panel" + select FB_MSM_LCDC_TOSHIBA_WVGA_PT + ---help--- + Support for LCDC Toshiba WVGA PT (480x800) panel + +config FB_MSM_LCDC_TOSHIBA_FWVGA_PT_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Toshiba FWVGA PT Panel" + select FB_MSM_LCDC_TOSHIBA_FWVGA_PT + ---help--- + Support for LCDC Toshiba FWVGA PT (480x864) panel. This + configuration has to be selected to support the Toshiba + FWVGA (480x864) portrait panel. + +config FB_MSM_LCDC_SHARP_WVGA_PT_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Sharp WVGA PT Panel" + select FB_MSM_LCDC_SHARP_WVGA_PT + ---help--- + Support for LCDC Sharp WVGA PT (480x800) panel + +config FB_MSM_LCDC_AUO_WVGA_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC AUO WVGA Panel" + select FB_MSM_LCDC_AUO_WVGA + ---help--- + Support for LCDC AUO WVGA(480x800) panel + +config FB_MSM_LCDC_NT35582_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC NT35582 WVGA Panel" + select FB_MSM_LCDC_NT35582_WVGA + ---help--- + Support for LCDC NT35582 WVGA(480x800) panel + +config FB_MSM_LCDC_SAMSUNG_OLED_PT_PANEL + depends on FB_MSM_LCDC_HW + bool "LCDC Samsung OLED PT Panel" + select FB_MSM_LCDC_SAMSUNG_OLED_PT + ---help--- + Support for LCDC Samsung OLED PT (480x800) panel + +config FB_MSM_LVDS_CHIMEI_WXGA_PANEL + bool "LVDS Chimei WXGA Panel" + select FB_MSM_LVDS_CHIMEI_WXGA + ---help--- + Support for LVDS Chimei WXGA(1366x768) panel + +config FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM + depends on FB_MSM_LCDC_HW + bool "MDDI Panel Auto Detect + LCDC Prism WVGA" + select FB_MSM_MDDI_AUTO_DETECT + select FB_MSM_LCDC_PRISM_WVGA + select FB_MSM_LCDC_GORDON_VGA + select FB_MSM_LCDC_WXGA + select FB_MSM_LCDC_TOSHIBA_WVGA_PT + select FB_MSM_LCDC_TOSHIBA_FWVGA_PT + select FB_MSM_LCDC_SHARP_WVGA_PT + select FB_MSM_LCDC_ST15_WXGA + ---help--- + Support for MDDI panel auto detect. + If it can't find any MDDI panel, it will load an LCDC panel. + +config FB_MSM_MIPI_PANEL_DETECT + bool "MIPI Panel Detect" + select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT + select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT + select FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA + select FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT + select FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT + select FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT + select FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT + select FB_MSM_MIPI_NOVATEK_CMD_QHD_PT + select FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT + select FB_MSM_MIPI_NT35510_CMD_WVGA_PT + select FB_MSM_MIPI_ORISE_VIDEO_720P_PT + select FB_MSM_MIPI_ORISE_CMD_720P_PT + select FB_MSM_MIPI_NT35516_VIDEO_QHD_PT + select FB_MSM_MIPI_NT35516_CMD_QHD_PT + select FB_MSM_MIPI_SIMULATOR_VIDEO + select FB_MSM_MIPI_CHIMEI_WXGA + select FB_MSM_MIPI_CHIMEI_WUXGA + ---help--- + Support for MIPI panel auto detect + +config FB_MSM_MDDI_PANEL_AUTO_DETECT + bool "MDDI Panel Auto Detect" + select FB_MSM_MDDI_AUTO_DETECT + ---help--- + Support for MDDI panel auto detect + +config FB_MSM_LCDC_PANEL_AUTO_DETECT + bool "LCDC Panel Auto Detect" + select FB_MSM_LCDC_AUTO_DETECT + select FB_MSM_LCDC_SAMSUNG_WSVGA + select FB_MSM_LCDC_AUO_WVGA + select FB_MSM_LCDC_NT35582_WVGA + select FB_MSM_LCDC_SAMSUNG_OLED_PT + ---help--- + Support for LCDC panel auto detect + +config FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT + bool "LCDC + MIPI Panel Auto Detect" + select FB_MSM_LCDC_AUTO_DETECT + select FB_MSM_LCDC_SAMSUNG_WSVGA + select FB_MSM_LCDC_AUO_WVGA + select FB_MSM_LCDC_SAMSUNG_OLED_PT + select FB_MSM_LCDC_NT35582_WVGA + select FB_MSM_LCDC_TOSHIBA_FWVGA_PT + select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT + select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT + select FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT + select FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT + select FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT + select FB_MSM_MIPI_NOVATEK_CMD_QHD_PT + select FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT + select FB_MSM_MIPI_NT35510_CMD_WVGA_PT + select FB_MSM_MIPI_NT35516_VIDEO_QHD_PT + select FM_MSM_MIPI_NT35516_CMD_QHD_PT + select FB_MSM_MIPI_SIMULATOR_VIDEO + ---help--- + Support for LCDC + MIPI panel auto detect + +config FB_MSM_LVDS_MIPI_PANEL_DETECT + bool "LVDS + MIPI Panel Auto Detect" + select FB_MSM_LVDS_CHIMEI_WXGA + select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT + select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT + select FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA + select FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT + select FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT + select FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT + select FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT + select FB_MSM_MIPI_NOVATEK_CMD_QHD_PT + select FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT + select FB_MSM_MIPI_NT35510_CMD_WVGA_PT + select FB_MSM_MIPI_ORISE_VIDEO_720P_PT + select FB_MSM_MIPI_ORISE_CMD_720P_PT + select FB_MSM_MIPI_SIMULATOR_VIDEO + select FB_MSM_MIPI_CHIMEI_WXGA + select FB_MSM_MIPI_CHIMEI_WUXGA + ---help--- + Support for LVDS + MIPI panel auto detect + +config FB_MSM_MDDI_PRISM_WVGA + bool "MDDI Prism WVGA Panel" + select FB_MSM_MDDI + ---help--- + Support for MDDI Prism WVGA (800x480) panel + +config FB_MSM_MDDI_TOSHIBA_WVGA_PORTRAIT + bool "MDDI Toshiba WVGA Portrait Panel" + select FB_MSM_MDDI_TOSHIBA_COMMON + ---help--- + Support for MDDI Toshiba WVGA (480x800) panel + +config FB_MSM_MDDI_TOSHIBA_VGA + bool "MDDI Toshiba VGA Panel" + select FB_MSM_MDDI_TOSHIBA_COMMON_VGA + ---help--- + Support for MDDI Toshiba VGA (480x640) and QCIF (176x220) panel + +config FB_MSM_MDDI_TOSHIBA_WVGA + bool "MDDI Toshiba WVGA panel" + select FB_MSM_MDDI_TOSHIBA_COMMON + ---help--- + Support for MDDI Toshiba (800x480) WVGA panel + +config FB_MSM_MDDI_SHARP_QVGA_128x128 + bool "MDDI Sharp QVGA Dual Panel" + select FB_MSM_MDDI + ---help--- + Support for MDDI Sharp QVGA (240x320) and 128x128 dual panel + +config FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT_PANEL + bool "MIPI Toshiba WVGA PT Panel" + select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT + +config FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT_PANEL + bool "MIPI Toshiba WSVGA PT Panel" + select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT + +config FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA_PANEL + bool "MIPI Toshiba WUXGA (1920x1200) Panel" + select FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA + +config FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT_PANEL + bool "MIPI NOVATEK VIDEO QHD PT Panel" + select FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT + +config FB_MSM_MIPI_NOVATEK_CMD_QHD_PT_PANEL + bool "MIPI NOVATEK CMD QHD PT Panel" + select FB_MSM_MIPI_NOVATEK_CMD_QHD_PT + +config FB_MSM_MIPI_ORISE_VIDEO_720P_PT_PANEL + bool "MIPI ORISE VIDEO 720P PT Panel" + select FB_MSM_MIPI_ORISE_VIDEO_720P_PT + +config FB_MSM_MIPI_ORISE_CMD_720P_PT_PANEL + bool "MIPI ORISE CMD 720P PT Panel" + select FB_MSM_MIPI_ORISE_CMD_720P_PT + +config FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT_PANEL + bool "MIPI Renesas Video FWVGA PT Panel" + select FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT + +config FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT_PANEL + bool "MIPI Renesas Command FWVGA PT Panel" + select FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT + +config FB_MSM_MIPI_CHIMEI_WXGA_PANEL + bool "MIPI Chimei WXGA PT Panel" + select FB_MSM_MIPI_CHIMEI_WXGA + +config FB_MSM_MIPI_CHIMEI_WUXGA_PANEL + bool "MIPI Chimei WUXGA Panel" + select FB_MSM_MIPI_CHIMEI_WUXGA + +config FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT_PANEL + bool "MIPI Truly Video WVGA PT Panel" + select FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT + +config FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT_PANEL + bool "MIPI NT35510 Video WVGA PT Panel" + select FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT + +config FB_MSM_MIPI_NT35510_CMD_WVGA_PT_PANEL + bool "MIPI NT35510 Command WVGA PT Panel" + select FB_MSM_MIPI_NT35510_CMD_WVGA_PT + +config FB_MSM_MIPI_NT35516_VIDEO_QHD_PT_PANEL + bool "MIPI NT35516 Video qHD PT Panel" + select FB_MSM_MIPI_NT35516_VIDEO_QHD_PT + +config FB_MSM_MIPI_NT35516_CMD_QHD_PT_PANEL + bool "MIPI NT35516 Command qHD PT Panel" + select FB_MSM_MIPI_NT35516_CMD_QHD_PT + +config FB_MSM_MIPI_SIMULATOR_VIDEO_PANEL + bool "MIPI Simulator Video Panel" + select FB_MSM_MIPI_SIMULATOR_VIDEO + +config FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF + bool "EBI2 TMD QVGA Epson QCIF Dual Panel" + select FB_MSM_EBI2 + ---help--- + Support for EBI2 TMD QVGA (240x320) and Epson QCIF (176x220) panel + +config FB_MSM_HDMI_AS_PRIMARY + depends on FB_MSM_HDMI_COMMON + bool "Use HDMI as primary panel" + ---help--- + Support for using HDMI as primary + +config FB_MSM_PANEL_NONE + bool "NONE" + ---help--- + This will disable LCD panel +endchoice + +choice + prompt "Secondary LCD Panel" + depends on FB_MSM_MDP31 + default FB_MSM_SECONDARY_PANEL_NONE + +config FB_MSM_LCDC_EXTERNAL_WXGA + depends on FB_MSM_MDP31 + bool "External WXGA on LCDC" + select FB_MSM_LCDC_PANEL + ---help--- + Support for external WXGA display (1280x720) + +config FB_MSM_HDMI_SII_EXTERNAL_720P + depends on FB_MSM_MDP31 + bool "External SiI9022 HDMI 720P" + select FB_MSM_LCDC_PANEL + ---help--- + Support for external HDMI 720p display (1280x720p) + Using SiI9022 chipset + +config FB_MSM_SECONDARY_PANEL_NONE + bool "NONE" + ---help--- + No secondary panel +endchoice + +config FB_MSM_LCDC_DSUB + depends on FB_MSM_LCDC_SAMSUNG_WSVGA && FB_MSM_MDP40 && FB_MSM_LCDC_HW + bool "External DSUB support" + default n + ---help--- + Support for external DSUB (VGA) display up to 1440x900. The DSUB + display shares the same video bus as the primary LCDC attached display. + Typically only one of the two displays can be used at one time. + +config FB_MSM_EXT_INTERFACE_COMMON + bool + default n + +config FB_MSM_HDMI_COMMON + bool + default n + +config FB_MSM_HDMI_3D + bool + default n + +config FB_MSM_HDMI_ADV7520_PANEL + depends on FB_MSM_MDP40 && FB_MSM_OVERLAY + bool "LCDC HDMI ADV7520 720p Panel" + select FB_MSM_DTV + select FB_MSM_EXT_INTERFACE_COMMON + select FB_MSM_HDMI_COMMON + default n + ---help--- + Support for LCDC 720p HDMI panel attached to ADV7520 + +config FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + depends on FB_MSM_HDMI_ADV7520_PANEL + bool "Use HDCP mode" + default y + ---help--- + Support for HDCP mode for ADV7520 HDMI 720p Panel + Choose to enable HDCP + + +config FB_MSM_HDMI_MSM_PANEL + depends on FB_MSM_MDP40 + bool "MSM HDMI 1080p Panel" + select FB_MSM_DTV + select FB_MSM_EXT_INTERFACE_COMMON + select FB_MSM_HDMI_COMMON + select FB_MSM_HDMI_3D + default n + ---help--- + Support for 480p/720p/1080i/1080p output through MSM HDMI + +config FB_MSM_HDMI_MSM_PANEL_DVI_SUPPORT + depends on FB_MSM_HDMI_MSM_PANEL + bool "Use DVI mode" + default n + ---help--- + Support for DVI mode for MSM HDMI 1080p Panel + +config FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + depends on FB_MSM_HDMI_MSM_PANEL + bool "Use HDCP mode" + default y + ---help--- + Support for HDCP mode for MSM HDMI 1080p Panel + Choose to enable HDCP + +config FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + depends on FB_MSM_HDMI_MSM_PANEL + bool "Enable CEC" + default n + ---help--- + Support for HDMI CEC Feature + Choose to enable CEC + +config FB_MSM_HDMI_MHL_9244 + depends on FB_MSM_HDMI_MSM_PANEL + bool 'SI_MHL 9244 support' + default n + ---help--- + Support the HDMI to MHL conversion. + MHL (Mobile High-Definition Link) technology + uses USB connector to output HDMI content + +config FB_MSM_HDMI_MHL_8334 + depends on FB_MSM_HDMI_MSM_PANEL + bool 'SI_MHL 8334 support ' + default n + ---help--- + Support the HDMI to MHL conversion. + MHL (Mobile High-Definition Link) technology + uses USB connector to output HDMI content + +choice + depends on (FB_MSM_MDP22 || FB_MSM_MDP31 || FB_MSM_MDP40) + prompt "TVOut Region" + default FB_MSM_TVOUT_NONE + +config FB_MSM_TVOUT_NTSC_M + bool "NTSC M" + select FB_MSM_TVOUT + select FB_MSM_EXT_INTERFACE_COMMON + ---help--- + Support for NTSC M region (North American and Korea) + +config FB_MSM_TVOUT_NTSC_J + bool "NTSC J" + select FB_MSM_TVOUT + select FB_MSM_EXT_INTERFACE_COMMON + ---help--- + Support for NTSC J region (Japan) + +config FB_MSM_TVOUT_PAL_BDGHIN + bool "PAL BDGHIN" + select FB_MSM_TVOUT + select FB_MSM_EXT_INTERFACE_COMMON + ---help--- + Support for PAL BDGHIN region (Non-argentina PAL-N) + +config FB_MSM_TVOUT_PAL_M + bool "PAL M" + select FB_MSM_TVOUT + select FB_MSM_EXT_INTERFACE_COMMON + ---help--- + Support for PAL M region + +config FB_MSM_TVOUT_PAL_N + bool "PAL N" + select FB_MSM_TVOUT + select FB_MSM_EXT_INTERFACE_COMMON + ---help--- + Support for PAL N region (Argentina PAL-N) + +config FB_MSM_TVOUT_NONE + bool "NONE" + ---help--- + This will disable TV Out functionality. +endchoice + +config FB_MSM_TVOUT_SVIDEO + bool "TVOut on S-video" + depends on FB_MSM_TVOUT + default n + ---help--- + Selects whether the TVOut signal uses S-video. + Choose n for composite output. + +choice + depends on FB_MSM_MDP22 + prompt "External MDDI" + default FB_MSM_EXTMDDI_SVGA + +config FB_MSM_EXTMDDI_SVGA + bool "External MDDI SVGA" + select FB_MSM_MDDI + select FB_MSM_EXTMDDI + ---help--- + Support for MSM SVGA (800x600) external MDDI panel + +config FB_MSM_EXTMDDI_NONE + bool "NONE" + ---help--- + This will disable External MDDI functionality. +endchoice + +choice + prompt "Default framebuffer color depth" + depends on FB_MSM_MDP40 || FB_MSM_MDP31 || FB_MSM_MDP303 + default FB_MSM_DEFAULT_DEPTH_RGBA8888 + +config FB_MSM_DEFAULT_DEPTH_RGB565 + bool "16 bits per pixel (RGB565)" + +config FB_MSM_DEFAULT_DEPTH_ARGB8888 + bool "32 bits per pixel (ARGB8888)" + +config FB_MSM_DEFAULT_DEPTH_RGBA8888 + bool "32 bits per pixel (RGBA8888)" + +endchoice + +config FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL + bool "EBI2 Epson QVGA Panel" + select FB_MSM_EBI2 + default n + ---help--- + Support for EBI2 Epson QVGA (240x320) panel + +config FB_MSM_EBI2_PANEL_DETECT + bool "EBI2 Panel Detect" + select FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL + default n + ---help--- + Support for EBI2 panel auto detect +endif diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0a37cf3a78aefd5157c44dd60e247073de38ba92 --- /dev/null +++ b/drivers/video/msm/Makefile @@ -0,0 +1,192 @@ +obj-y := msm_fb.o + +obj-$(CONFIG_FB_MSM_LOGO) += logo.o +obj-$(CONFIG_FB_BACKLIGHT) += msm_fb_bl.o + +ifeq ($(CONFIG_FB_MSM_MDP_HW),y) + +# MDP +obj-y += mdp.o + +obj-$(CONFIG_DEBUG_FS) += mdp_debugfs.o + +ifeq ($(CONFIG_FB_MSM_MDP40),y) +obj-y += mdp4_util.o +obj-y += mdp4_hsic.o +else +obj-y += mdp_hw_init.o +obj-y += mdp_ppp.o +ifeq ($(CONFIG_FB_MSM_MDP31),y) +obj-y += mdp_ppp_v31.o +else +obj-y += mdp_ppp_v20.o +endif +endif + +ifeq ($(CONFIG_FB_MSM_OVERLAY),y) +obj-y += mdp4_overlay.o +obj-y += mdp4_overlay_lcdc.o +ifeq ($(CONFIG_FB_MSM_MIPI_DSI),y) +obj-y += mdp4_overlay_dsi_video.o +obj-y += mdp4_overlay_dsi_cmd.o +else +obj-y += mdp4_overlay_mddi.o +endif +else +obj-y += mdp_dma_lcdc.o +endif + +obj-$(CONFIG_FB_MSM_MDP303) += mdp_dma_dsi_video.o + +ifeq ($(CONFIG_FB_MSM_DTV),y) +obj-y += mdp4_dtv.o +obj-y += mdp4_overlay_dtv.o +endif + +obj-y += mdp_dma.o +obj-y += mdp_dma_s.o +obj-y += mdp_vsync.o +obj-y += mdp_cursor.o +obj-y += mdp_dma_tv.o +obj-$(CONFIG_ARCH_MSM7X27A) += msm_dss_io_7x27a.o +obj-$(CONFIG_ARCH_MSM8X60) += msm_dss_io_8x60.o +obj-$(CONFIG_ARCH_MSM8960) += msm_dss_io_8960.o + +# EBI2 +obj-$(CONFIG_FB_MSM_EBI2) += ebi2_lcd.o + +# LCDC +obj-$(CONFIG_FB_MSM_LCDC) += lcdc.o + +# LVDS +obj-$(CONFIG_FB_MSM_LVDS) += lvds.o + +# MDDI +msm_mddi-objs := mddi.o mddihost.o mddihosti.o +obj-$(CONFIG_FB_MSM_MDDI) += msm_mddi.o + +# External MDDI +msm_mddi_ext-objs := mddihost_e.o mddi_ext.o +obj-$(CONFIG_FB_MSM_EXTMDDI) += msm_mddi_ext.o + +# MIPI gereric +msm_mipi-objs := mipi_dsi.o mipi_dsi_host.o +obj-$(CONFIG_FB_MSM_MIPI_DSI) += msm_mipi.o + +# MIPI manufacture +obj-$(CONFIG_FB_MSM_MIPI_DSI_TOSHIBA) += mipi_toshiba.o +obj-$(CONFIG_FB_MSM_MIPI_DSI_NOVATEK) += mipi_novatek.o +obj-$(CONFIG_FB_MSM_MIPI_DSI_ORISE) += mipi_orise.o +obj-$(CONFIG_FB_MSM_MIPI_DSI_RENESAS) += mipi_renesas.o +obj-$(CONFIG_FB_MSM_MIPI_DSI_TRULY) += mipi_truly.o +obj-$(CONFIG_FB_MSM_MIPI_DSI_NT35510) += mipi_NT35510.o +obj-$(CONFIG_FB_MSM_MIPI_DSI_NT35516) += mipi_truly_tft540960_1_e.o +obj-$(CONFIG_FB_MSM_MIPI_DSI_SIMULATOR) += mipi_simulator.o + +# MIPI Bridge +obj-$(CONFIG_FB_MSM_MIPI_DSI_TC358764_DSI2LVDS) += mipi_tc358764_dsi2lvds.o + +# TVEnc +obj-$(CONFIG_FB_MSM_TVOUT) += tvenc.o +ifeq ($(CONFIG_FB_MSM_OVERLAY),y) +obj-$(CONFIG_FB_MSM_TVOUT) += mdp4_overlay_atv.o +endif + +# MSM FB Panel +obj-y += msm_fb_panel.o +obj-$(CONFIG_FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF) += ebi2_tmd20.o +obj-$(CONFIG_FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF) += ebi2_l2f.o + +ifeq ($(CONFIG_FB_MSM_MDDI_AUTO_DETECT),y) +obj-y += mddi_prism.o +obj-y += mddi_toshiba.o +obj-y += mddi_toshiba_vga.o +obj-y += mddi_toshiba_wvga_pt.o +obj-y += mddi_toshiba_wvga.o +obj-y += mddi_sharp.o +obj-y += mddi_orise.o +obj-y += mddi_quickvx.o +else +obj-$(CONFIG_FB_MSM_MDDI_PRISM_WVGA) += mddi_prism.o +obj-$(CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON) += mddi_toshiba.o +obj-$(CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON_VGA) += mddi_toshiba_vga.o +obj-$(CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA_PORTRAIT) += mddi_toshiba_wvga_pt.o +obj-$(CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA) += mddi_toshiba_wvga.o +obj-$(CONFIG_FB_MSM_MDDI_SHARP_QVGA_128x128) += mddi_sharp.o +obj-$(CONFIG_FB_MSM_MDDI_ORISE) += mddi_orise.o +obj-$(CONFIG_FB_MSM_MDDI_QUICKVX) += mddi_quickvx.o +endif + +ifeq ($(CONFIG_FB_MSM_MIPI_PANEL_DETECT),y) +obj-y += mipi_toshiba_video_wvga_pt.o mipi_toshiba_video_wsvga_pt.o mipi_toshiba_video_wuxga.o +obj-y += mipi_novatek_video_qhd_pt.o mipi_novatek_cmd_qhd_pt.o +obj-y += mipi_orise_video_720p_pt.o mipi_orise_cmd_720p_pt.o +obj-y += mipi_renesas_video_fwvga_pt.o mipi_renesas_cmd_fwvga_pt.o +obj-y += mipi_NT35510_video_wvga_pt.o mipi_NT35510_cmd_wvga_pt.o +obj-y += mipi_truly_tft540960_1_e_video_qhd_pt.o mipi_truly_tft540960_1_e_cmd_qhd_pt.o +obj-y += mipi_chimei_wxga_pt.o +obj-y += mipi_chimei_wuxga.o +obj-y += mipi_truly_video_wvga_pt.o +else +obj-$(CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT) += mipi_toshiba_video_wvga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT) += mipi_toshiba_video_wsvga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA) += mipi_toshiba_video_wuxga.o +obj-$(CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT) += mipi_novatek_video_qhd_pt.o +obj-$(CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT) += mipi_orise_video_720p_pt.o +obj-$(CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT) += mipi_orise_cmd_720p_pt.o +obj-$(CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT) += mipi_novatek_cmd_qhd_pt.o +obj-$(CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT) += mipi_renesas_video_fwvga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT) += mipi_renesas_cmd_fwvga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT) += mipi_renesas_video_fwvga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT) += mipi_truly_video_wvga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT) += mipi_NT35510_cmd_wvga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT) += mipi_NT35510_video_wvga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT) += mipi_truly_tft540960_1_e_cmd_qhd_pt.o +obj-$(CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT) += mipi_truly_tft540960_1_e_video_qhd_pt.o +obj-$(CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO) += mipi_simulator_video.o +obj-$(CONFIG_FB_MSM_MIPI_CHIMEI_WXGA) += mipi_chimei_wxga_pt.o +obj-$(CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA) += mipi_chimei_wuxga.o +endif + +obj-$(CONFIG_FB_MSM_LCDC_PANEL) += lcdc_panel.o +obj-$(CONFIG_FB_MSM_LCDC_PRISM_WVGA) += lcdc_prism.o +obj-$(CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA) += lcdc_samsung_wsvga.o +obj-$(CONFIG_FB_MSM_LCDC_CHIMEI_WXGA) += lcdc_chimei_wxga.o +obj-$(CONFIG_FB_MSM_LCDC_NT35582_WVGA) += lcdc_nt35582_wvga.o +obj-$(CONFIG_FB_MSM_LCDC_EXTERNAL_WXGA) += lcdc_external.o +obj-$(CONFIG_FB_MSM_HDMI_SII_EXTERNAL_720P) += hdmi_sii9022.o +obj-$(CONFIG_FB_MSM_LCDC_GORDON_VGA) += lcdc_gordon.o +obj-$(CONFIG_FB_MSM_LCDC_WXGA) += lcdc_wxga.o +obj-$(CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT) += lcdc_toshiba_wvga_pt.o +obj-$(CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT) += lcdc_toshiba_fwvga_pt.o +obj-$(CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT) += lcdc_sharp_wvga_pt.o +obj-$(CONFIG_FB_MSM_LCDC_AUO_WVGA) += lcdc_auo_wvga.o +obj-$(CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT) += lcdc_samsung_oled_pt.o +obj-$(CONFIG_FB_MSM_HDMI_ADV7520_PANEL) += adv7520.o +obj-$(CONFIG_FB_MSM_LCDC_ST15_WXGA) += lcdc_st15.o +obj-$(CONFIG_FB_MSM_LVDS_CHIMEI_WXGA) += lvds_chimei_wxga.o +obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += hdmi_msm.o +obj-$(CONFIG_FB_MSM_EXT_INTERFACE_COMMON) += external_common.o +obj-$(CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335) += lcdc_truly_ips3p2335.o + +obj-$(CONFIG_FB_MSM_TVOUT) += tvout_msm.o + +ccflags-y := -I$(src)/mhl +obj-$(CONFIG_FB_MSM_HDMI_MHL_8334) += mhl-8334.o +mhl-8334-objs += mhl/mhl_8334.o +mhl-8334-objs += mhl/mhl_i2c_utils.o + +obj-$(CONFIG_FB_MSM_EXTMDDI_SVGA) += mddi_ext_lcd.o + +obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback_panel.o +obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback.o +obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_overlay_writeback.o +else +obj-$(CONFIG_FB_MSM_EBI2) += ebi2_host.o +obj-$(CONFIG_FB_MSM_EBI2) += ebi2_lcd.o +obj-y += msm_fb_panel.o +obj-$(CONFIG_FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL) += ebi2_epson_s1d_qvga.o +endif + +clean: + rm *.o .*cmd diff --git a/drivers/video/msm/adv7520.c b/drivers/video/msm/adv7520.c new file mode 100644 index 0000000000000000000000000000000000000000..2ac22de838a5d3eb7ca6f6b589a557553cc3cccb --- /dev/null +++ b/drivers/video/msm/adv7520.c @@ -0,0 +1,1035 @@ +/* Copyright (c) 2010,2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "msm_fb.h" + +#define DEBUG +#define DEV_DBG_PREFIX "HDMI: " + +#include "external_common.h" + +/* #define PORT_DEBUG */ +/* #define TESTING_FORCE_480p */ + +#define HPD_DUTY_CYCLE 4 /*secs*/ + +static struct external_common_state_type hdmi_common; + +static struct i2c_client *hclient; +static struct clk *tv_enc_clk; + +static bool chip_power_on = FALSE; /* For chip power on/off */ +static bool enable_5v_on = FALSE; +static bool hpd_power_on = FALSE; +static atomic_t comm_power_on; /* For dtv power on/off (I2C) */ +static int suspend_count; + +static u8 reg[256]; /* HDMI panel registers */ + +struct hdmi_data { + struct msm_hdmi_platform_data *pd; + struct work_struct isr_work; +}; +static struct hdmi_data *dd; +static struct work_struct hpd_timer_work; + +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT +static struct work_struct hdcp_handle_work; +static int hdcp_activating; +static DEFINE_MUTEX(hdcp_state_mutex); +static int has_hdcp_hw_support = true; +#endif + +static struct timer_list hpd_timer; +static struct timer_list hpd_duty_timer; +static struct work_struct hpd_duty_work; +static unsigned int monitor_sense; +static boolean hpd_cable_chg_detected; + +static struct pm_qos_request pm_qos_req; + +/* Change HDMI state */ +static void change_hdmi_state(int online) +{ + if (!external_common_state) + return; + + mutex_lock(&external_common_state_hpd_mutex); + external_common_state->hpd_state = online; + mutex_unlock(&external_common_state_hpd_mutex); + + if (!external_common_state->uevent_kobj) + return; + + if (online) { + kobject_uevent(external_common_state->uevent_kobj, + KOBJ_ONLINE); + switch_set_state(&external_common_state->sdev, 1); + } else { + kobject_uevent(external_common_state->uevent_kobj, + KOBJ_OFFLINE); + switch_set_state(&external_common_state->sdev, 0); + } + DEV_INFO("adv7520_uevent: %d [suspend# %d]\n", online, suspend_count); +} + + +/* + * Read a value from a register on ADV7520 device + * If sucessfull returns value read , otherwise error. + */ +static u8 adv7520_read_reg(struct i2c_client *client, u8 reg) +{ + int err; + struct i2c_msg msg[2]; + u8 reg_buf[] = { reg }; + u8 data_buf[] = { 0 }; + + if (!client->adapter) + return -ENODEV; + if (!atomic_read(&comm_power_on)) { + DEV_WARN("%s: WARN: missing GPIO power\n", __func__); + return -ENODEV; + } + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = reg_buf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = data_buf; + + err = i2c_transfer(client->adapter, msg, 2); + + if (err < 0) { + DEV_INFO("%s: I2C err: %d\n", __func__, err); + return err; + } + +#ifdef PORT_DEBUG + DEV_INFO("HDMI[%02x] [R] %02x\n", reg, data); +#endif + return *data_buf; +} + +/* + * Write a value to a register on adv7520 device. + * Returns zero if successful, or non-zero otherwise. + */ +static int adv7520_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + if (!client->adapter) + return -ENODEV; + if (!atomic_read(&comm_power_on)) { + DEV_WARN("%s: WARN: missing GPIO power\n", __func__); + return -ENODEV; + } + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + data[0] = reg; + data[1] = val; + + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) + return 0; +#ifdef PORT_DEBUG + DEV_INFO("HDMI[%02x] [W] %02x [%d]\n", reg, val, err); +#endif + return err; +} + +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT +static void adv7520_close_hdcp_link(void) +{ + if (!external_common_state->hdcp_active && !hdcp_activating) + return; + + DEV_INFO("HDCP: Close link\n"); + + reg[0xD5] = adv7520_read_reg(hclient, 0xD5); + reg[0xD5] &= 0xFE; + adv7520_write_reg(hclient, 0xD5, (u8)reg[0xD5]); + + reg[0x16] = adv7520_read_reg(hclient, 0x16); + reg[0x16] &= 0xFE; + adv7520_write_reg(hclient, 0x16, (u8)reg[0x16]); + + /* UnMute Audio */ + adv7520_write_reg(hclient, 0x0C, (u8)0x84); + + external_common_state->hdcp_active = FALSE; + mutex_lock(&hdcp_state_mutex); + hdcp_activating = FALSE; + mutex_unlock(&hdcp_state_mutex); +} + +static void adv7520_comm_power(int on, int show); +static void adv7520_hdcp_enable(struct work_struct *work) +{ + DEV_INFO("HDCP: Start reg[0xaf]=%02x (mute audio)\n", reg[0xaf]); + + adv7520_comm_power(1, 1); + + /* Mute Audio */ + adv7520_write_reg(hclient, 0x0C, (u8)0xC3); + + msleep(200); + /* Wait for BKSV ready interrupt */ + /* Read BKSV's keys from HDTV */ + reg[0xBF] = adv7520_read_reg(hclient, 0xBF); + reg[0xC0] = adv7520_read_reg(hclient, 0xC0); + reg[0xC1] = adv7520_read_reg(hclient, 0xC1); + reg[0xC2] = adv7520_read_reg(hclient, 0xC2); + reg[0xc3] = adv7520_read_reg(hclient, 0xC3); + + DEV_DBG("HDCP: BKSV={%02x,%02x,%02x,%02x,%02x}\n", reg[0xbf], reg[0xc0], + reg[0xc1], reg[0xc2], reg[0xc3]); + + /* Is SINK repeater */ + reg[0xBE] = adv7520_read_reg(hclient, 0xBE); + if (~(reg[0xBE] & 0x40)) { + ; /* compare with revocation list */ + /* Check 20 1's and 20 zero's */ + } else { + /* Don't implement HDCP if sink as a repeater */ + adv7520_write_reg(hclient, 0x0C, (u8)0x84); + mutex_lock(&hdcp_state_mutex); + hdcp_activating = FALSE; + mutex_unlock(&hdcp_state_mutex); + DEV_WARN("HDCP: Sink Repeater (%02x), (unmute audio)\n", + reg[0xbe]); + + adv7520_comm_power(0, 1); + return; + } + + msleep(200); + reg[0xB8] = adv7520_read_reg(hclient, 0xB8); + DEV_INFO("HDCP: Status reg[0xB8] is %02x\n", reg[0xb8]); + if (reg[0xb8] & 0x40) { + /* UnMute Audio */ + adv7520_write_reg(hclient, 0x0C, (u8)0x84); + DEV_INFO("HDCP: A/V content Encrypted (unmute audio)\n"); + external_common_state->hdcp_active = TRUE; + } + adv7520_comm_power(0, 1); + + mutex_lock(&hdcp_state_mutex); + hdcp_activating = FALSE; + mutex_unlock(&hdcp_state_mutex); +} +#endif + +static int adv7520_read_edid_block(int block, uint8 *edid_buf) +{ + u8 r = 0; + int ret; + struct i2c_msg msg[] = { + { .addr = reg[0x43] >> 1, + .flags = 0, + .len = 1, + .buf = &r }, + { .addr = reg[0x43] >> 1, + .flags = I2C_M_RD, + .len = 0x100, + .buf = edid_buf } }; + + if (block > 0) + return 0; + ret = i2c_transfer(hclient->adapter, msg, 2); + DEV_DBG("EDID block: addr=%02x, ret=%d\n", reg[0x43] >> 1, ret); + return (ret < 2) ? -ENODEV : 0; +} + +static void adv7520_read_edid(void) +{ + external_common_state->read_edid_block = adv7520_read_edid_block; + if (hdmi_common_read_edid()) { + u8 timeout; + DEV_INFO("%s: retry\n", __func__); + adv7520_write_reg(hclient, 0xc9, 0x13); + msleep(500); + timeout = (adv7520_read_reg(hclient, 0x96) & (1 << 2)); + if (timeout) { + hdmi_common_read_edid(); + } + } +} + +static void adv7520_chip_on(void) +{ + if (!chip_power_on) { + /* Get the current register holding the power bit. */ + unsigned long reg0xaf = adv7520_read_reg(hclient, 0xaf); + + dd->pd->core_power(1, 1); + + /* Set the HDMI select bit. */ + set_bit(1, ®0xaf); + DEV_INFO("%s: turn on chip power\n", __func__); + adv7520_write_reg(hclient, 0x41, 0x10); + adv7520_write_reg(hclient, 0xaf, (u8)reg0xaf); + chip_power_on = TRUE; + } else + DEV_INFO("%s: chip already has power\n", __func__); +} + +static void adv7520_chip_off(void) +{ + if (chip_power_on) { +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + if (has_hdcp_hw_support) + adv7520_close_hdcp_link(); +#endif + + DEV_INFO("%s: turn off chip power\n", __func__); + adv7520_write_reg(hclient, 0x41, 0x50); + dd->pd->core_power(0, 1); + chip_power_on = FALSE; + } else + DEV_INFO("%s: chip is already off\n", __func__); + + monitor_sense = 0; + hpd_cable_chg_detected = FALSE; + + if (enable_5v_on) { + dd->pd->enable_5v(0); + enable_5v_on = FALSE; + } +} + +/* Power ON/OFF ADV7520 chip */ +static void adv7520_isr_w(struct work_struct *work); +static void adv7520_comm_power(int on, int show) +{ + if (!on) + atomic_dec(&comm_power_on); + dd->pd->comm_power(on, 0/*show*/); + if (on) + atomic_inc(&comm_power_on); +} + +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT +static void adv7520_start_hdcp(void); +#endif +static int adv7520_power_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + clk_prepare_enable(tv_enc_clk); + external_common_state->dev = &pdev->dev; + if (mfd != NULL) { + DEV_INFO("adv7520_power: ON (%dx%d %d)\n", + mfd->var_xres, mfd->var_yres, mfd->var_pixclock); + hdmi_common_get_video_format_from_drv_data(mfd); + } + + adv7520_comm_power(1, 1); + /* Check if HPD is signaled */ + if (adv7520_read_reg(hclient, 0x42) & (1 << 6)) { + DEV_INFO("power_on: cable detected\n"); + monitor_sense = adv7520_read_reg(hclient, 0xC6); +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + if (has_hdcp_hw_support) { + if (!hdcp_activating) + adv7520_start_hdcp(); + } +#endif + } else + DEV_INFO("power_on: cable NOT detected\n"); + adv7520_comm_power(0, 1); + pm_qos_update_request(&pm_qos_req, msm_cpuidle_get_deep_idle_latency()); + + return 0; +} + +static int adv7520_power_off(struct platform_device *pdev) +{ + DEV_INFO("power_off\n"); + adv7520_comm_power(1, 1); + adv7520_chip_off(); + pm_qos_update_request(&pm_qos_req, PM_QOS_DEFAULT_VALUE); + adv7520_comm_power(0, 1); + clk_disable_unprepare(tv_enc_clk); + return 0; +} + + +/* AV7520 chip specific initialization */ +static void adv7520_chip_init(void) +{ + /* Initialize the variables used to read/write the ADV7520 chip. */ + memset(®, 0xff, sizeof(reg)); + + /* Get the values from the "Fixed Registers That Must Be Set". */ + reg[0x98] = adv7520_read_reg(hclient, 0x98); + reg[0x9c] = adv7520_read_reg(hclient, 0x9c); + reg[0x9d] = adv7520_read_reg(hclient, 0x9d); + reg[0xa2] = adv7520_read_reg(hclient, 0xa2); + reg[0xa3] = adv7520_read_reg(hclient, 0xa3); + reg[0xde] = adv7520_read_reg(hclient, 0xde); + + /* Get the "HDMI/DVI Selection" register. */ + reg[0xaf] = adv7520_read_reg(hclient, 0xaf); + + /* Read Packet Memory I2C Address */ + reg[0x45] = adv7520_read_reg(hclient, 0x45); + + /* Hard coded values provided by ADV7520 data sheet. */ + reg[0x98] = 0x03; + reg[0x9c] = 0x38; + reg[0x9d] = 0x61; + reg[0xa2] = 0x94; + reg[0xa3] = 0x94; + reg[0xde] = 0x88; + + /* Set the HDMI select bit. */ + reg[0xaf] |= 0x16; + + /* Set the audio related registers. */ + reg[0x01] = 0x00; + reg[0x02] = 0x2d; + reg[0x03] = 0x80; + reg[0x0a] = 0x4d; + reg[0x0b] = 0x0e; + reg[0x0c] = 0x84; + reg[0x0d] = 0x10; + reg[0x12] = 0x00; + reg[0x14] = 0x00; + reg[0x15] = 0x20; + reg[0x44] = 0x79; + reg[0x73] = 0x01; + reg[0x76] = 0x00; + + /* Set 720p display related registers */ + reg[0x16] = 0x00; + + reg[0x18] = 0x46; + reg[0x55] = 0x00; + reg[0x3c] = 0x04; + + /* Set Interrupt Mask register for HPD/HDCP */ + reg[0x94] = 0xC0; +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + if (has_hdcp_hw_support) + reg[0x95] = 0xC0; + else + reg[0x95] = 0x00; +#else + reg[0x95] = 0x00; +#endif + adv7520_write_reg(hclient, 0x94, reg[0x94]); + adv7520_write_reg(hclient, 0x95, reg[0x95]); + + /* Set Packet Memory I2C Address */ + reg[0x45] = 0x74; + + /* Set the values from the "Fixed Registers That Must Be Set". */ + adv7520_write_reg(hclient, 0x98, reg[0x98]); + adv7520_write_reg(hclient, 0x9c, reg[0x9c]); + adv7520_write_reg(hclient, 0x9d, reg[0x9d]); + adv7520_write_reg(hclient, 0xa2, reg[0xa2]); + adv7520_write_reg(hclient, 0xa3, reg[0xa3]); + adv7520_write_reg(hclient, 0xde, reg[0xde]); + + /* Set the "HDMI/DVI Selection" register. */ + adv7520_write_reg(hclient, 0xaf, reg[0xaf]); + + /* Set EDID Monitor address */ + reg[0x43] = 0x7E; + adv7520_write_reg(hclient, 0x43, reg[0x43]); + + /* Enable the i2s audio input. */ + adv7520_write_reg(hclient, 0x01, reg[0x01]); + adv7520_write_reg(hclient, 0x02, reg[0x02]); + adv7520_write_reg(hclient, 0x03, reg[0x03]); + adv7520_write_reg(hclient, 0x0a, reg[0x0a]); + adv7520_write_reg(hclient, 0x0b, reg[0x0b]); + adv7520_write_reg(hclient, 0x0c, reg[0x0c]); + adv7520_write_reg(hclient, 0x0d, reg[0x0d]); + adv7520_write_reg(hclient, 0x12, reg[0x12]); + adv7520_write_reg(hclient, 0x14, reg[0x14]); + adv7520_write_reg(hclient, 0x15, reg[0x15]); + adv7520_write_reg(hclient, 0x44, reg[0x44]); + adv7520_write_reg(hclient, 0x73, reg[0x73]); + adv7520_write_reg(hclient, 0x76, reg[0x76]); + + /* Enable 720p display */ + adv7520_write_reg(hclient, 0x16, reg[0x16]); + adv7520_write_reg(hclient, 0x18, reg[0x18]); + adv7520_write_reg(hclient, 0x55, reg[0x55]); + adv7520_write_reg(hclient, 0x3c, reg[0x3c]); + + /* Set Packet Memory address to avoid conflict + with Bosch Accelerometer */ + adv7520_write_reg(hclient, 0x45, reg[0x45]); + + /* Ensure chip is in low-power state */ + adv7520_write_reg(hclient, 0x41, 0x50); +} + +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT +static void adv7520_start_hdcp(void) +{ + mutex_lock(&hdcp_state_mutex); + if (hdcp_activating) { + DEV_WARN("adv7520_timer: HDCP already" + " activating, skipping\n"); + mutex_unlock(&hdcp_state_mutex); + return; + } + hdcp_activating = TRUE; + mutex_unlock(&hdcp_state_mutex); + + del_timer(&hpd_duty_timer); + + adv7520_comm_power(1, 1); + + if (!enable_5v_on) { + dd->pd->enable_5v(1); + enable_5v_on = TRUE; + adv7520_chip_on(); + } + + /* request for HDCP */ + reg[0xaf] = adv7520_read_reg(hclient, 0xaf); + reg[0xaf] |= 0x90; + adv7520_write_reg(hclient, 0xaf, reg[0xaf]); + reg[0xaf] = adv7520_read_reg(hclient, 0xaf); + + reg[0xba] = adv7520_read_reg(hclient, 0xba); + reg[0xba] |= 0x10; + adv7520_write_reg(hclient, 0xba, reg[0xba]); + reg[0xba] = adv7520_read_reg(hclient, 0xba); + adv7520_comm_power(0, 1); + + DEV_INFO("HDCP: reg[0xaf]=0x%02x, reg[0xba]=0x%02x, waiting for BKSV\n", + reg[0xaf], reg[0xba]); + + /* will check for HDCP Error or BKSV ready */ + mod_timer(&hpd_duty_timer, jiffies + HZ/2); +} +#endif + +static void adv7520_hpd_timer_w(struct work_struct *work) +{ + if (!external_common_state->hpd_feature_on) { + DEV_INFO("adv7520_timer: skipping, feature off\n"); + return; + } + + if ((monitor_sense & 0x4) && !external_common_state->hpd_state) { + int timeout; + DEV_DBG("adv7520_timer: Cable Detected\n"); + adv7520_comm_power(1, 1); + adv7520_chip_on(); + + if (hpd_cable_chg_detected) { + hpd_cable_chg_detected = FALSE; + /* Ensure 5V to read EDID */ + if (!enable_5v_on) { + dd->pd->enable_5v(1); + enable_5v_on = TRUE; + } + msleep(500); + timeout = (adv7520_read_reg(hclient, 0x96) & (1 << 2)); + if (timeout) { + DEV_DBG("adv7520_timer: EDID-Ready..\n"); + adv7520_read_edid(); + } else + DEV_DBG("adv7520_timer: EDID TIMEOUT (C9=%02x)" + "\n", adv7520_read_reg(hclient, 0xC9)); + } +#ifdef TESTING_FORCE_480p + external_common_state->disp_mode_list.num_of_elements = 1; + external_common_state->disp_mode_list.disp_mode_list[0] = + HDMI_VFRMT_720x480p60_16_9; +#endif + adv7520_comm_power(0, 1); +#ifndef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + /* HDMI_5V_EN not needed anymore */ + if (enable_5v_on) { + DEV_DBG("adv7520_timer: EDID done, no HDCP, 5V not " + "needed anymore\n"); + dd->pd->enable_5v(0); + enable_5v_on = FALSE; + } +#endif + change_hdmi_state(1); + } else if (external_common_state->hpd_state) { + adv7520_comm_power(1, 1); + adv7520_chip_off(); + adv7520_comm_power(0, 1); + DEV_DBG("adv7520_timer: Cable Removed\n"); + change_hdmi_state(0); + } +} + +static void adv7520_hpd_timer_f(unsigned long data) +{ + schedule_work(&hpd_timer_work); +} + +static void adv7520_isr_w(struct work_struct *work) +{ + static int state_count; + static u8 last_reg0x96; + u8 reg0xc8; + u8 reg0x96; +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + static u8 last_reg0x97; + u8 reg0x97 = 0; +#endif + if (!external_common_state->hpd_feature_on) { + DEV_DBG("adv7520_irq: skipping, hpd off\n"); + return; + } + + adv7520_comm_power(1, 1); + reg0x96 = adv7520_read_reg(hclient, 0x96); +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + if (has_hdcp_hw_support) { + reg0x97 = adv7520_read_reg(hclient, 0x97); + /* Clearing the Interrupts */ + adv7520_write_reg(hclient, 0x97, reg0x97); + } +#endif + /* Clearing the Interrupts */ + adv7520_write_reg(hclient, 0x96, reg0x96); + + if ((reg0x96 == 0xC0) || (reg0x96 & 0x40)) { +#ifdef DEBUG + unsigned int hpd_state = adv7520_read_reg(hclient, 0x42); +#endif + monitor_sense = adv7520_read_reg(hclient, 0xC6); + DEV_DBG("adv7520_irq: reg[0x42]=%02x && reg[0xC6]=%02x\n", + hpd_state, monitor_sense); + + if (!enable_5v_on) { + dd->pd->enable_5v(1); + enable_5v_on = TRUE; + } + if (!hpd_power_on) { + dd->pd->core_power(1, 1); + hpd_power_on = TRUE; + } + + /* Timer for catching interrupt debouning */ + DEV_DBG("adv7520_irq: Timer in .5sec\n"); + hpd_cable_chg_detected = TRUE; + mod_timer(&hpd_timer, jiffies + HZ/2); + } +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + if (has_hdcp_hw_support) { + if (hdcp_activating) { + /* HDCP controller error Interrupt */ + if (reg0x97 & 0x80) { + DEV_ERR("adv7520_irq: HDCP_ERROR\n"); + state_count = 0; + adv7520_close_hdcp_link(); + /* BKSV Ready interrupts */ + } else if (reg0x97 & 0x40) { + DEV_INFO("adv7520_irq: BKSV keys ready, Begin" + " HDCP encryption\n"); + state_count = 0; + schedule_work(&hdcp_handle_work); + } else if (++state_count > 2 && (monitor_sense & 0x4)) { + DEV_INFO("adv7520_irq: Still waiting for BKSV," + "restart HDCP\n"); + hdcp_activating = FALSE; + state_count = 0; + adv7520_chip_off(); + adv7520_start_hdcp(); + } + reg0xc8 = adv7520_read_reg(hclient, 0xc8); + DEV_INFO("adv7520_irq: DDC controller reg[0xC8]=0x%02x," + "state_count=%d, monitor_sense=%x\n", + reg0xc8, state_count, monitor_sense); + } else if (!external_common_state->hdcp_active + && (monitor_sense & 0x4)) { + DEV_INFO("adv7520_irq: start HDCP with" + " monitor sense\n"); + state_count = 0; + adv7520_start_hdcp(); + } else + state_count = 0; + if (last_reg0x97 != reg0x97 || last_reg0x96 != reg0x96) + DEV_DBG("adv7520_irq: reg[0x96]=%02x " + "reg[0x97]=%02x: HDCP: %d\n", reg0x96, reg0x97, + external_common_state->hdcp_active); + last_reg0x97 = reg0x97; + } else { + if (last_reg0x96 != reg0x96) + DEV_DBG("adv7520_irq: reg[0x96]=%02x\n", reg0x96); + } +#else + if (last_reg0x96 != reg0x96) + DEV_DBG("adv7520_irq: reg[0x96]=%02x\n", reg0x96); +#endif + last_reg0x96 = reg0x96; + adv7520_comm_power(0, 1); +} + +static void adv7520_hpd_duty_work(struct work_struct *work) +{ + if (!external_common_state->hpd_feature_on) { + DEV_WARN("%s: hpd feature is off, skipping\n", __func__); + return; + } + + dd->pd->core_power(1, 0); + msleep(10); + adv7520_isr_w(NULL); + dd->pd->core_power(0, 0); +} + +static void adv7520_hpd_duty_timer_f(unsigned long data) +{ + if (!external_common_state->hpd_feature_on) { + DEV_WARN("%s: hpd feature is off, skipping\n", __func__); + return; + } + + mod_timer(&hpd_duty_timer, jiffies + HPD_DUTY_CYCLE*HZ); + schedule_work(&hpd_duty_work); +} + +static const struct i2c_device_id adv7520_id[] = { + { ADV7520_DRV_NAME , 0}, + {} +}; + +static struct msm_fb_panel_data hdmi_panel_data = { + .on = adv7520_power_on, + .off = adv7520_power_off, +}; + +static struct platform_device hdmi_device = { + .name = ADV7520_DRV_NAME , + .id = 2, + .dev = { + .platform_data = &hdmi_panel_data, + } +}; + +static void adv7520_ensure_init(void) +{ + static boolean init_done; + if (!init_done) { + int rc = dd->pd->init_irq(); + if (rc) { + DEV_ERR("adv7520_init: init_irq: %d\n", rc); + return; + } + + init_done = TRUE; + } + DEV_INFO("adv7520_init: chip init\n"); + adv7520_comm_power(1, 1); + adv7520_chip_init(); + adv7520_comm_power(0, 1); +} + +static int adv7520_hpd_feature(int on) +{ + int rc = 0; + + if (!on) { + if (enable_5v_on) { + dd->pd->enable_5v(0); + enable_5v_on = FALSE; + } + if (hpd_power_on) { + dd->pd->core_power(0, 1); + hpd_power_on = FALSE; + } + + DEV_DBG("adv7520_hpd: %d: stop duty timer\n", on); + del_timer(&hpd_timer); + del_timer(&hpd_duty_timer); + external_common_state->hpd_state = 0; + } + + if (on) { + dd->pd->core_power(1, 0); + adv7520_ensure_init(); + + adv7520_comm_power(1, 1); + monitor_sense = adv7520_read_reg(hclient, 0xC6); + DEV_DBG("adv7520_irq: reg[0xC6]=%02x\n", monitor_sense); + adv7520_comm_power(0, 1); + dd->pd->core_power(0, 0); + + if (monitor_sense & 0x4) { + if (!enable_5v_on) { + dd->pd->enable_5v(1); + enable_5v_on = TRUE; + } + if (!hpd_power_on) { + dd->pd->core_power(1, 1); + hpd_power_on = TRUE; + } + + hpd_cable_chg_detected = TRUE; + mod_timer(&hpd_timer, jiffies + HZ/2); + } + + DEV_DBG("adv7520_hpd: %d start duty timer\n", on); + mod_timer(&hpd_duty_timer, jiffies + HZ/100); + } + + DEV_INFO("adv7520_hpd: %d\n", on); + return rc; +} + +static int + adv7520_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int rc; + struct platform_device *fb_dev; + + dd = kzalloc(sizeof *dd, GFP_KERNEL); + if (!dd) { + rc = -ENOMEM; + goto probe_exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + external_common_state->dev = &client->dev; + + /* Init real i2c_client */ + hclient = client; + + i2c_set_clientdata(client, dd); + dd->pd = client->dev.platform_data; + if (!dd->pd) { + rc = -ENODEV; + goto probe_free; + } + + INIT_WORK(&dd->isr_work, adv7520_isr_w); + INIT_WORK(&hpd_timer_work, adv7520_hpd_timer_w); +#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT + if (dd->pd->check_hdcp_hw_support) + has_hdcp_hw_support = dd->pd->check_hdcp_hw_support(); + + if (has_hdcp_hw_support) + INIT_WORK(&hdcp_handle_work, adv7520_hdcp_enable); + else + DEV_INFO("%s: no hdcp hw support.\n", __func__); +#endif + + init_timer(&hpd_timer); + hpd_timer.function = adv7520_hpd_timer_f; + hpd_timer.data = (unsigned long)NULL; + hpd_timer.expires = 0xffffffff; + add_timer(&hpd_timer); + + external_common_state->hpd_feature = adv7520_hpd_feature; + DEV_INFO("adv7520_probe: HPD detection on request\n"); + init_timer(&hpd_duty_timer); + hpd_duty_timer.function = adv7520_hpd_duty_timer_f; + hpd_duty_timer.data = (unsigned long)NULL; + hpd_duty_timer.expires = 0xffffffff; + add_timer(&hpd_duty_timer); + INIT_WORK(&hpd_duty_work, adv7520_hpd_duty_work); + DEV_INFO("adv7520_probe: HPD detection ON (duty)\n"); + + fb_dev = msm_fb_add_device(&hdmi_device); + + if (fb_dev) { + rc = external_common_state_create(fb_dev); + if (rc) + goto probe_free; + } else + DEV_ERR("adv7520_probe: failed to add fb device\n"); + + if (hdmi_prim_display) + external_common_state->sdev.name = "hdmi_as_primary"; + else + external_common_state->sdev.name = "hdmi"; + + if (switch_dev_register(&external_common_state->sdev) < 0) + DEV_ERR("Hdmi switch registration failed\n"); + + return 0; + +probe_free: + kfree(dd); + dd = NULL; +probe_exit: + return rc; + +} + +static int adv7520_remove(struct i2c_client *client) +{ + if (!client->adapter) { + DEV_ERR("%s: No HDMI Device\n", __func__); + return -ENODEV; + } + switch_dev_unregister(&external_common_state->sdev); + pm_qos_remove_request(&pm_qos_req); + kfree(dd); + dd = NULL; + return 0; +} + +#ifdef CONFIG_SUSPEND +static int adv7520_i2c_suspend(struct device *dev) +{ + DEV_INFO("%s\n", __func__); + + ++suspend_count; + + if (external_common_state->hpd_feature_on) { + DEV_DBG("%s: stop duty timer\n", __func__); + del_timer(&hpd_duty_timer); + del_timer(&hpd_timer); + } + + /* Turn off LDO8 and go into low-power state */ + if (chip_power_on) { + DEV_DBG("%s: turn off power\n", __func__); + adv7520_comm_power(1, 1); + adv7520_write_reg(hclient, 0x41, 0x50); + adv7520_comm_power(0, 1); + dd->pd->core_power(0, 1); + } + + return 0; +} + +static int adv7520_i2c_resume(struct device *dev) +{ + DEV_INFO("%s\n", __func__); + + /* Turn on LDO8 and go into normal-power state */ + if (chip_power_on) { + DEV_DBG("%s: turn on power\n", __func__); + dd->pd->core_power(1, 1); + adv7520_comm_power(1, 1); + adv7520_write_reg(hclient, 0x41, 0x10); + adv7520_comm_power(0, 1); + } + + if (external_common_state->hpd_feature_on) { + DEV_DBG("%s: start duty timer\n", __func__); + mod_timer(&hpd_duty_timer, jiffies + HPD_DUTY_CYCLE*HZ); + } + + return 0; +} +#else +#define adv7520_i2c_suspend NULL +#define adv7520_i2c_resume NULL +#endif + +static const struct dev_pm_ops adv7520_device_pm_ops = { + .suspend = adv7520_i2c_suspend, + .resume = adv7520_i2c_resume, +}; + +static struct i2c_driver hdmi_i2c_driver = { + .driver = { + .name = ADV7520_DRV_NAME, + .owner = THIS_MODULE, + .pm = &adv7520_device_pm_ops, + }, + .probe = adv7520_probe, + .id_table = adv7520_id, + .remove = adv7520_remove, +}; + +static int __init adv7520_init(void) +{ + int rc; + + pr_info("%s\n", __func__); + external_common_state = &hdmi_common; + external_common_state->video_resolution = HDMI_VFRMT_1280x720p60_16_9; + + tv_enc_clk = clk_get(NULL, "tv_enc_clk"); + if (IS_ERR(tv_enc_clk)) { + printk(KERN_ERR "error: can't get tv_enc_clk!\n"); + return IS_ERR(tv_enc_clk); + } + + HDMI_SETUP_LUT(640x480p60_4_3); /* 25.20MHz */ + HDMI_SETUP_LUT(720x480p60_16_9); /* 27.03MHz */ + HDMI_SETUP_LUT(1280x720p60_16_9); /* 74.25MHz */ + + HDMI_SETUP_LUT(720x576p50_16_9); /* 27.00MHz */ + HDMI_SETUP_LUT(1280x720p50_16_9); /* 74.25MHz */ + + hdmi_common_init_panel_info(&hdmi_panel_data.panel_info); + + rc = i2c_add_driver(&hdmi_i2c_driver); + if (rc) { + pr_err("hdmi_init FAILED: i2c_add_driver rc=%d\n", rc); + goto init_exit; + } + + if (machine_is_msm7x30_surf() || machine_is_msm8x55_surf()) { + short *hdtv_mux = (short *)ioremap(0x8e000170 , 0x100); + *hdtv_mux++ = 0x020b; + *hdtv_mux = 0x8000; + iounmap(hdtv_mux); + } + pm_qos_add_request(&pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + return 0; + +init_exit: + if (tv_enc_clk) + clk_put(tv_enc_clk); + return rc; +} + +static void __exit adv7520_exit(void) +{ + i2c_del_driver(&hdmi_i2c_driver); +} + +module_init(adv7520_init); +module_exit(adv7520_exit); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Qualcomm Innovation Center, Inc."); +MODULE_DESCRIPTION("ADV7520 HDMI driver"); diff --git a/drivers/video/msm/ebi2_epson_s1d_qvga.c b/drivers/video/msm/ebi2_epson_s1d_qvga.c new file mode 100644 index 0000000000000000000000000000000000000000..8db3cf9bf3be8bbf83a9e1c512520b6111d2ed3a --- /dev/null +++ b/drivers/video/msm/ebi2_epson_s1d_qvga.c @@ -0,0 +1,374 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" + +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" + +#include + +#include +#include + +#include +#include + +#define CMD_NOP_C 0x00 +#define CMD_SOFT_RESET_C 0x99 +#define CMD_DISPLAY_ON_C 0xAF +#define CMD_DISPLAY_OFF_C 0xAE +#define CMD_SET_DISPLAY_C 0xCA +#define CMD_SET_DISPLAY_TIMING_C 0xA1 +#define CMD_SET_DATA_C 0xBC +#define CMD_SET_START_ADDRESS_C 0x15 +#define CMD_SET_END_ADDRESS_C 0x75 +#define CMD_RAM_WRITE_C 0x5C +#define CMD_RAM_READ_C 0x5D +#define CMD_SET_AREA_SCROLLING_C 0xAA +#define CMD_SET_DISPLAY_START_LINE_C 0xAB +#define CMD_PARTIAL_DISPLAY_IN_C 0xA8 +#define CMD_PARTIAL_DISPLAY_OUT_C 0xA9 +#define CMD_SET_DISPLAY_DATA_INTERFACE_C 0x31 +#define CMD_SET_DISPLAY_COLOR_MODE_C 0x8B +#define CMD_SELECT_MTP_ROM_MODE_C 0x65 +#define CMD_MTP_ROM_MODE_IN_C 0x67 +#define CMD_MTP_ROM_MODE_OUT_C 0x68 +#define CMD_MTP_ROM_OPERATION_IN_C 0x69 +#define CMD_MTP_ROM_OPERATION_OUT_C 0x70 +#define CMD_GATE_LINE_SCAN_MODE_C 0x6F +#define CMD_SET_AC_OPERATION_DRIVE_C 0x8C +#define CMD_SET_ELECTRONIC_CONTROL_C 0x20 +#define CMD_SET_POSITIVE_CORRECTION_CHARS_C 0x22 +#define CMD_SET_NEGATIVE_CORRECTION_CHARS_C 0x25 +#define CMD_SET_POWER_CONTROL_C 0x21 +#define CMD_SET_PARTIAL_POWER_CONTROL_C 0x23 +#define CMD_SET_8_COLOR_CONTROL_C 0x24 +#define CMD_SLEEP_IN_C 0x95 +#define CMD_SLEEP_OUT_C 0x94 +#define CMD_VDD_OFF_C 0x97 +#define CMD_VDD_ON_C 0x96 +#define CMD_STOP_OSCILLATION_C 0x93 +#define CMD_START_OSCILLATION_C 0x92 +#define CMD_TEST_SOURCE_C 0xFD +#define CMD_TEST_FUSE_C 0xFE +#define CMD_TEST_C 0xFF +#define CMD_STATUS_READ_C 0xE8 +#define CMD_REVISION_READ_C 0xE9 + +#define PANEL_WIDTH 240 +#define PANEL_HEIGHT 320 +#define ACTIVE_WIN_WIDTH PANEL_WIDTH +#define ACTIVE_WIN_HEIGHT PANEL_HEIGHT + +#define ACTIVE_WIN_H_START 0 +#define ACTIVE_WIN_V_START 0 + +#define DISP_CMD_OUT(cmd) outpw(DISP_CMD_PORT, (cmd << 1)); +#define DISP_DATA_OUT(data) outpw(DISP_DATA_PORT, (data << 1)); +#define DISP_DATA_IN() inpw(DISP_DATA_PORT); + +static void *DISP_CMD_PORT; +static void *DISP_DATA_PORT; +static boolean disp_initialized; +static boolean display_on; +static struct msm_panel_common_pdata *ebi2_epson_pdata; + +static void epson_s1d_disp_init(struct platform_device *pdev); +static int epson_s1d_disp_off(struct platform_device *pdev); +static int epson_s1d_disp_on(struct platform_device *pdev); +static void epson_s1d_disp_set_rect(int x, int y, int xres, int yres); + +static void epson_s1d_disp_set_rect(int x, int y, int xres, int yres) +{ + int right, bottom; + + if (!disp_initialized) + return; + + right = x + xres - 1; + bottom = y + yres - 1; + + x += ACTIVE_WIN_H_START; + y += ACTIVE_WIN_V_START; + right += ACTIVE_WIN_H_START; + bottom += ACTIVE_WIN_V_START; + + if ((PANEL_WIDTH > x) && + (PANEL_HEIGHT > y) && + (PANEL_WIDTH > right) && + (PANEL_HEIGHT > bottom)) { + DISP_CMD_OUT(CMD_SET_START_ADDRESS_C); + DISP_DATA_OUT((uint8)x); + DISP_DATA_OUT((uint8)(y>>8)); + DISP_DATA_OUT((uint8)y); + + DISP_CMD_OUT(CMD_SET_END_ADDRESS_C); + DISP_DATA_OUT((uint8)right); + DISP_DATA_OUT((uint8)(bottom>>8)); + DISP_DATA_OUT((uint8)bottom); + DISP_CMD_OUT(CMD_RAM_WRITE_C); + } +} + +static void epson_s1d_disp_init(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + if (disp_initialized) + return; + + mfd = platform_get_drvdata(pdev); + + DISP_CMD_PORT = mfd->cmd_port; + DISP_DATA_PORT = mfd->data_port; + + disp_initialized = TRUE; +} + +static int epson_s1d_disp_off(struct platform_device *pdev) +{ + if (!disp_initialized) + epson_s1d_disp_init(pdev); + + if (display_on) { + DISP_CMD_OUT(CMD_SOFT_RESET_C); + DISP_CMD_OUT(CMD_VDD_OFF_C); + display_on = FALSE; + } + + return 0; +} + +static int epson_s1d_disp_on(struct platform_device *pdev) +{ + int i; + if (!disp_initialized) + epson_s1d_disp_init(pdev); + + if (!display_on) { + /* Enable Vdd regulator */ + DISP_CMD_OUT(CMD_VDD_ON_C); + msleep(20); + + /* Soft Reset before configuring display */ + DISP_CMD_OUT(CMD_SOFT_RESET_C); + msleep(20); + + /* Set display attributes */ + + /* GATESCAN */ + DISP_CMD_OUT(CMD_GATE_LINE_SCAN_MODE_C); + DISP_DATA_OUT(0x0); + + /* DISSET */ + DISP_CMD_OUT(CMD_SET_DISPLAY_C); + DISP_DATA_OUT(0x31); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT((uint8)((PANEL_HEIGHT - 1)>>8)); + DISP_DATA_OUT((uint8)(PANEL_HEIGHT - 1)); + DISP_DATA_OUT(0x03); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x08); + + /* VOLSET */ + DISP_CMD_OUT( + CMD_SET_ELECTRONIC_CONTROL_C); + DISP_DATA_OUT(0x10); + DISP_DATA_OUT(0x80); + DISP_DATA_OUT(0x11); + DISP_DATA_OUT(0x1B); + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x0D); + DISP_DATA_OUT(0x00); + + /* PWRCTL */ + DISP_CMD_OUT(CMD_SET_POWER_CONTROL_C); + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x24); + DISP_DATA_OUT(0x0F); + DISP_DATA_OUT(0xFE); + DISP_DATA_OUT(0x33); + DISP_DATA_OUT(0x31); + DISP_DATA_OUT(0xFF); + DISP_DATA_OUT(0x03); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x77); + DISP_DATA_OUT(0x33); + DISP_DATA_OUT(0x11); + DISP_DATA_OUT(0x44); + DISP_DATA_OUT(0x00); + + /* PPWRCTL */ + DISP_CMD_OUT(CMD_SET_PARTIAL_POWER_CONTROL_C); + DISP_DATA_OUT(0x33); + DISP_DATA_OUT(0xFF); + DISP_DATA_OUT(0x03); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x44); + DISP_DATA_OUT(0x00); + + /* SPLOUT */ + DISP_CMD_OUT(CMD_SLEEP_OUT_C); + msleep(100); + + /* DATSET */ + DISP_CMD_OUT(CMD_SET_DATA_C); + DISP_DATA_OUT(0x00); + + /* DISTMEMSET */ + DISP_CMD_OUT(CMD_SET_DISPLAY_TIMING_C); + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x2E); + DISP_DATA_OUT(0x0A); + DISP_DATA_OUT(0x2C); + DISP_DATA_OUT(0x23); + DISP_DATA_OUT(0x2F); + DISP_DATA_OUT(0x00); + + /* GAMSETP */ + DISP_CMD_OUT(CMD_SET_POSITIVE_CORRECTION_CHARS_C); + DISP_DATA_OUT(0x37); + DISP_DATA_OUT(0xFF); + DISP_DATA_OUT(0x7F); + DISP_DATA_OUT(0x15); + DISP_DATA_OUT(0x37); + DISP_DATA_OUT(0x05); + + /* GAMSETN */ + DISP_CMD_OUT(CMD_SET_NEGATIVE_CORRECTION_CHARS_C); + DISP_DATA_OUT(0x37); + DISP_DATA_OUT(0xFF); + DISP_DATA_OUT(0x7F); + DISP_DATA_OUT(0x15); + DISP_DATA_OUT(0x37); + DISP_DATA_OUT(0x05); + + /* ACDRIVE */ + DISP_CMD_OUT(CMD_SET_AC_OPERATION_DRIVE_C); + DISP_DATA_OUT(0x00); + + /* TEST */ + DISP_CMD_OUT(CMD_TEST_C); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x01); + + /* COLMOD */ + DISP_CMD_OUT(CMD_SET_DISPLAY_COLOR_MODE_C); + DISP_DATA_OUT(0x00); + + /* STADDSET */ + DISP_CMD_OUT(CMD_SET_START_ADDRESS_C); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x00); + + /* EDADDSET */ + DISP_CMD_OUT(CMD_SET_END_ADDRESS_C); + DISP_DATA_OUT(0xEF); + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x3F); + + /* Set Display Start Line */ + DISP_CMD_OUT(CMD_SET_DISPLAY_START_LINE_C); + DISP_DATA_OUT(0x00); + + /* Set Display Data Interface */ + DISP_CMD_OUT(CMD_SET_DISPLAY_DATA_INTERFACE_C); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x04); + + epson_s1d_disp_set_rect(0, + 0, + ACTIVE_WIN_WIDTH, + ACTIVE_WIN_HEIGHT); + + for (i = 0; i < (ACTIVE_WIN_WIDTH * ACTIVE_WIN_HEIGHT); i++) + outpdw(DISP_DATA_PORT, 0); + + /* DISON */ + DISP_CMD_OUT(CMD_DISPLAY_ON_C); + msleep(60); + + display_on = TRUE; + } + + return 0; +} + +static int epson_s1d_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + ebi2_epson_pdata = pdev->dev.platform_data; + return 0; + } + + msm_fb_add_device(pdev); + return 0; +} + +static struct platform_driver this_driver = { + .probe = epson_s1d_probe, + .driver = { + .name = "ebi2_epson_s1d_qvga", + }, +}; + +static struct msm_fb_panel_data epson_s1d_panel_data = { + .on = epson_s1d_disp_on, + .off = epson_s1d_disp_off, + .set_rect = epson_s1d_disp_set_rect, +}; + +static struct platform_device this_device = { + .name = "ebi2_epson_s1d_qvga", + .id = 1, + .dev = { + .platform_data = &epson_s1d_panel_data, + } +}; + +static int __init epson_s1d_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + ret = platform_driver_register(&this_driver); + if (!ret) { + pinfo = &epson_s1d_panel_data.panel_info; + pinfo->xres = PANEL_WIDTH; + pinfo->yres = PANEL_HEIGHT; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = EBI2_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0x048423E8; + pinfo->bpp = 18; + pinfo->fb_num = 2; + pinfo->lcd.vsync_enable = FALSE; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + } + + return ret; +} + +module_init(epson_s1d_init); diff --git a/drivers/video/msm/ebi2_host.c b/drivers/video/msm/ebi2_host.c new file mode 100644 index 0000000000000000000000000000000000000000..bebc36ecbd43506ea131b0a432a8f73812341a84 --- /dev/null +++ b/drivers/video/msm/ebi2_host.c @@ -0,0 +1,307 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_fb.h" + +struct mdp_ccs mdp_ccs_rgb2yuv; +struct mdp_ccs mdp_ccs_yuv2rgb; + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; +static int ebi2_host_resource_initialized; +static struct msm_panel_common_pdata *ebi2_host_pdata; + +static int ebi2_host_probe(struct platform_device *pdev); +static int ebi2_host_remove(struct platform_device *pdev); + +static int ebi2_host_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int ebi2_host_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static const struct dev_pm_ops ebi2_host_dev_pm_ops = { + .runtime_suspend = ebi2_host_runtime_suspend, + .runtime_resume = ebi2_host_runtime_resume, +}; + + +static struct platform_driver ebi2_host_driver = { + .probe = ebi2_host_probe, + .remove = ebi2_host_remove, + .shutdown = NULL, + .driver = { + /* + * Simulate mdp hw + */ + .name = "mdp", + .pm = &ebi2_host_dev_pm_ops, + }, +}; + +void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state, + boolean isr) +{ + return; +} +int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req) +{ + return 0; +} +int mdp_start_histogram(struct fb_info *info) +{ + return 0; +} +int mdp_stop_histogram(struct fb_info *info) +{ + return 0; +} +void mdp_refresh_screen(unsigned long data) +{ + return; +} + +static int ebi2_host_off(struct platform_device *pdev) +{ + int ret; + ret = panel_next_off(pdev); + return ret; +} + +static int ebi2_host_on(struct platform_device *pdev) +{ + int ret; + ret = panel_next_on(pdev); + return ret; +} + + +static int ebi2_host_probe(struct platform_device *pdev) +{ + struct platform_device *msm_fb_dev = NULL; + struct msm_fb_data_type *mfd; + struct msm_fb_panel_data *pdata = NULL; + int rc; + + if ((pdev->id == 0) && (pdev->num_resources > 0)) { + + ebi2_host_pdata = pdev->dev.platform_data; + + ebi2_host_resource_initialized = 1; + return 0; + } + + ebi2_host_resource_initialized = 1; + if (!ebi2_host_resource_initialized) + return -EPERM; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + msm_fb_dev = platform_device_alloc("msm_fb", pdev->id); + if (!msm_fb_dev) + return -ENOMEM; + + /* link to the latest pdev */ + mfd->pdev = msm_fb_dev; + + if (ebi2_host_pdata) { + mfd->mdp_rev = ebi2_host_pdata->mdp_rev; + mfd->mem_hid = ebi2_host_pdata->mem_hid; + } + + /* add panel data */ + if (platform_device_add_data + (msm_fb_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + pr_err("ebi2_host_probe: platform_device_add_data failed!\n"); + rc = -ENOMEM; + goto ebi2_host_probe_err; + } + /* data chain */ + pdata = msm_fb_dev->dev.platform_data; + pdata->on = ebi2_host_on; + pdata->off = ebi2_host_off; + pdata->next = pdev; + + /* set driver data */ + platform_set_drvdata(msm_fb_dev, mfd); + + rc = platform_device_add(msm_fb_dev); + if (rc) + goto ebi2_host_probe_err; + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + pdev_list[pdev_list_cnt++] = pdev; + return 0; + +ebi2_host_probe_err: + platform_device_put(msm_fb_dev); + return rc; +} + +void mdp_set_dma_pan_info(struct fb_info *info, struct mdp_dirty_region *dirty, + boolean sync) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_info *fbi = mfd->fbi; + struct msm_panel_info *panel_info = &mfd->panel_info; + MDPIBUF *iBuf; + int bpp = info->var.bits_per_pixel / 8; + int yres, remainder; + + if (panel_info->mode2_yres != 0) { + yres = panel_info->mode2_yres; + remainder = (fbi->fix.line_length*yres)%PAGE_SIZE; + } else { + yres = panel_info->yres; + remainder = (fbi->fix.line_length*yres)%PAGE_SIZE; + } + + if (!remainder) + remainder = PAGE_SIZE; + + down(&mfd->sem); + + iBuf = &mfd->ibuf; + /* use virtual address */ + iBuf->buf = (uint8 *) fbi->screen_base; + + if (fbi->var.yoffset < yres) { + iBuf->buf += fbi->var.xoffset * bpp; + } else if (fbi->var.yoffset >= yres && fbi->var.yoffset < 2 * yres) { + iBuf->buf += fbi->var.xoffset * bpp + yres * + fbi->fix.line_length + PAGE_SIZE - remainder; + } else { + iBuf->buf += fbi->var.xoffset * bpp + 2 * yres * + fbi->fix.line_length + 2 * (PAGE_SIZE - remainder); + } + + iBuf->ibuf_width = info->var.xres_virtual; + iBuf->bpp = bpp; + + iBuf->vsync_enable = sync; + + if (dirty) { + /* + * ToDo: dirty region check inside var.xoffset+xres + * <-> var.yoffset+yres + */ + iBuf->dma_x = dirty->xoffset % info->var.xres; + iBuf->dma_y = dirty->yoffset % info->var.yres; + iBuf->dma_w = dirty->width; + iBuf->dma_h = dirty->height; + } else { + iBuf->dma_x = 0; + iBuf->dma_y = 0; + iBuf->dma_w = info->var.xres; + iBuf->dma_h = info->var.yres; + } + mfd->ibuf_flushed = FALSE; + up(&mfd->sem); +} + +void mdp_dma_pan_update(struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + MDPIBUF *iBuf; + int i, j; + uint32 data; + uint8 *src; + struct msm_fb_panel_data *pdata = + (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + struct fb_info *fbi = mfd->fbi; + + iBuf = &mfd->ibuf; + + invalidate_caches((unsigned long)fbi->screen_base, + (unsigned long)info->fix.smem_len, + (unsigned long)info->fix.smem_start); + + pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w, + iBuf->dma_h); + for (i = 0; i < iBuf->dma_h; i++) { + src = iBuf->buf + (fbi->fix.line_length * (iBuf->dma_y + i)) + + (iBuf->dma_x * iBuf->bpp); + for (j = 0; j < iBuf->dma_w; j++) { + data = (uint32)(*src++ >> 2) << 12; + data |= (uint32)(*src++ >> 2) << 6; + data |= (uint32)(*src++ >> 2); + data = ((data&0x1FF)<<16) | ((data&0x3FE00)>>9); + outpdw(mfd->data_port, data); + } + } +} + +static int ebi2_host_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int ebi2_host_register_driver(void) +{ + return platform_driver_register(&ebi2_host_driver); +} + +static int __init ebi2_host_driver_init(void) +{ + int ret; + + ret = ebi2_host_register_driver(); + if (ret) { + pr_err("ebi2_host_register_driver() failed!\n"); + return ret; + } + + return 0; +} + +module_init(ebi2_host_driver_init); diff --git a/drivers/video/msm/ebi2_l2f.c b/drivers/video/msm/ebi2_l2f.c new file mode 100644 index 0000000000000000000000000000000000000000..2e944bea79759958ded015003fafa2b4a67b404c --- /dev/null +++ b/drivers/video/msm/ebi2_l2f.c @@ -0,0 +1,566 @@ +/* Copyright (c) 2008-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" + +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" + +#include + +#include +#include + +#include +#include + +/* The following are for MSM5100 on Gator +*/ +#ifdef FEATURE_PM1000 +#include "pm1000.h" +#endif /* FEATURE_PM1000 */ +/* The following are for MSM6050 on Bambi +*/ +#ifdef FEATURE_PMIC_LCDKBD_LED_DRIVER +#include "pm.h" +#endif /* FEATURE_PMIC_LCDKBD_LED_DRIVER */ + +#ifdef DISP_DEVICE_18BPP +#undef DISP_DEVICE_18BPP +#define DISP_DEVICE_16BPP +#endif + +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 220 + +static void *DISP_CMD_PORT; +static void *DISP_DATA_PORT; + +#define DISP_CMD_DISON 0xaf +#define DISP_CMD_DISOFF 0xae +#define DISP_CMD_DISNOR 0xa6 +#define DISP_CMD_DISINV 0xa7 +#define DISP_CMD_DISCTL 0xca +#define DISP_CMD_GCP64 0xcb +#define DISP_CMD_GCP16 0xcc +#define DISP_CMD_GSSET 0xcd +#define DISP_GS_2 0x02 +#define DISP_GS_16 0x01 +#define DISP_GS_64 0x00 +#define DISP_CMD_SLPIN 0x95 +#define DISP_CMD_SLPOUT 0x94 +#define DISP_CMD_SD_PSET 0x75 +#define DISP_CMD_MD_PSET 0x76 +#define DISP_CMD_SD_CSET 0x15 +#define DISP_CMD_MD_CSET 0x16 +#define DISP_CMD_DATCTL 0xbc +#define DISP_DATCTL_666 0x08 +#define DISP_DATCTL_565 0x28 +#define DISP_DATCTL_444 0x38 +#define DISP_CMD_RAMWR 0x5c +#define DISP_CMD_RAMRD 0x5d +#define DISP_CMD_PTLIN 0xa8 +#define DISP_CMD_PTLOUT 0xa9 +#define DISP_CMD_ASCSET 0xaa +#define DISP_CMD_SCSTART 0xab +#define DISP_CMD_VOLCTL 0xc6 +#define DISP_VOLCTL_TONE 0x80 +#define DISP_CMD_NOp 0x25 +#define DISP_CMD_OSSEL 0xd0 +#define DISP_CMD_3500KSET 0xd1 +#define DISP_CMD_3500KEND 0xd2 +#define DISP_CMD_14MSET 0xd3 +#define DISP_CMD_14MEND 0xd4 + +#define DISP_CMD_OUT(cmd) outpw(DISP_CMD_PORT, cmd); + +#define DISP_DATA_OUT(data) outpw(DISP_DATA_PORT, data); + +#define DISP_DATA_IN() inpw(DISP_DATA_PORT); + +/* Epson device column number starts at 2 +*/ +#define DISP_SET_RECT(ulhc_row, lrhc_row, ulhc_col, lrhc_col) \ + DISP_CMD_OUT(DISP_CMD_SD_PSET) \ + DISP_DATA_OUT((ulhc_row) & 0xFF) \ + DISP_DATA_OUT((ulhc_row) >> 8) \ + DISP_DATA_OUT((lrhc_row) & 0xFF) \ + DISP_DATA_OUT((lrhc_row) >> 8) \ + DISP_CMD_OUT(DISP_CMD_SD_CSET) \ + DISP_DATA_OUT(((ulhc_col)+2) & 0xFF) \ + DISP_DATA_OUT(((ulhc_col)+2) >> 8) \ + DISP_DATA_OUT(((lrhc_col)+2) & 0xFF) \ + DISP_DATA_OUT(((lrhc_col)+2) >> 8) + +#define DISP_MIN_CONTRAST 0 +#define DISP_MAX_CONTRAST 127 +#define DISP_DEFAULT_CONTRAST 80 + +#define DISP_MIN_BACKLIGHT 0 +#define DISP_MAX_BACKLIGHT 15 +#define DISP_DEFAULT_BACKLIGHT 2 + +#define WAIT_SEC(sec) mdelay((sec)/1000) + +static word disp_area_start_row; +static word disp_area_end_row; +static byte disp_contrast = DISP_DEFAULT_CONTRAST; +static boolean disp_powered_up; +static boolean disp_initialized = FALSE; +/* For some reason the contrast set at init time is not good. Need to do + * it again + */ +static boolean display_on = FALSE; +static void epsonQcif_disp_init(struct platform_device *pdev); +static void epsonQcif_disp_set_contrast(word contrast); +static void epsonQcif_disp_set_display_area(word start_row, word end_row); +static int epsonQcif_disp_off(struct platform_device *pdev); +static int epsonQcif_disp_on(struct platform_device *pdev); +static void epsonQcif_disp_set_rect(int x, int y, int xres, int yres); + +volatile word databack; +static void epsonQcif_disp_init(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + int i; + + if (disp_initialized) + return; + + mfd = platform_get_drvdata(pdev); + + DISP_CMD_PORT = mfd->cmd_port; + DISP_DATA_PORT = mfd->data_port; + + /* Sleep in */ + DISP_CMD_OUT(DISP_CMD_SLPIN); + + /* Display off */ + DISP_CMD_OUT(DISP_CMD_DISOFF); + + /* Display normal */ + DISP_CMD_OUT(DISP_CMD_DISNOR); + + /* Set data mode */ + DISP_CMD_OUT(DISP_CMD_DATCTL); + DISP_DATA_OUT(DISP_DATCTL_565); + + /* Set display timing */ + DISP_CMD_OUT(DISP_CMD_DISCTL); + DISP_DATA_OUT(0x1c); /* p1 */ + DISP_DATA_OUT(0x02); /* p1 */ + DISP_DATA_OUT(0x82); /* p2 */ + DISP_DATA_OUT(0x00); /* p3 */ + DISP_DATA_OUT(0x00); /* p4 */ + DISP_DATA_OUT(0xe0); /* p5 */ + DISP_DATA_OUT(0x00); /* p5 */ + DISP_DATA_OUT(0xdc); /* p6 */ + DISP_DATA_OUT(0x00); /* p6 */ + DISP_DATA_OUT(0x02); /* p7 */ + DISP_DATA_OUT(0x00); /* p8 */ + + /* Set 64 gray scale level */ + DISP_CMD_OUT(DISP_CMD_GCP64); + DISP_DATA_OUT(0x08); /* p01 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x2a); /* p02 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x4e); /* p03 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x6b); /* p04 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x88); /* p05 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0xa3); /* p06 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0xba); /* p07 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0xd1); /* p08 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0xe5); /* p09 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0xf3); /* p10 */ + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x03); /* p11 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x13); /* p12 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x22); /* p13 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x2f); /* p14 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x3b); /* p15 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x46); /* p16 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x51); /* p17 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x5b); /* p18 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x64); /* p19 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x6c); /* p20 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x74); /* p21 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x7c); /* p22 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x83); /* p23 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x8a); /* p24 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x91); /* p25 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x98); /* p26 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x9f); /* p27 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xa6); /* p28 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xac); /* p29 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xb2); /* p30 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xb7); /* p31 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xbc); /* p32 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xc1); /* p33 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xc6); /* p34 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xcb); /* p35 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xd0); /* p36 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xd4); /* p37 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xd8); /* p38 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xdc); /* p39 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xe0); /* p40 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xe4); /* p41 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xe8); /* p42 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xec); /* p43 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xf0); /* p44 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xf4); /* p45 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xf8); /* p46 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xfb); /* p47 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xfe); /* p48 */ + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0x01); /* p49 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x03); /* p50 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x05); /* p51 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x07); /* p52 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x09); /* p53 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x0b); /* p54 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x0d); /* p55 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x0f); /* p56 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x11); /* p57 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x13); /* p58 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x15); /* p59 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x17); /* p60 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x19); /* p61 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x1b); /* p62 */ + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x1c); /* p63 */ + DISP_DATA_OUT(0x02); + + /* Set 16 gray scale level */ + DISP_CMD_OUT(DISP_CMD_GCP16); + DISP_DATA_OUT(0x1a); /* p01 */ + DISP_DATA_OUT(0x32); /* p02 */ + DISP_DATA_OUT(0x42); /* p03 */ + DISP_DATA_OUT(0x4c); /* p04 */ + DISP_DATA_OUT(0x58); /* p05 */ + DISP_DATA_OUT(0x5f); /* p06 */ + DISP_DATA_OUT(0x66); /* p07 */ + DISP_DATA_OUT(0x6b); /* p08 */ + DISP_DATA_OUT(0x70); /* p09 */ + DISP_DATA_OUT(0x74); /* p10 */ + DISP_DATA_OUT(0x78); /* p11 */ + DISP_DATA_OUT(0x7b); /* p12 */ + DISP_DATA_OUT(0x7e); /* p13 */ + DISP_DATA_OUT(0x80); /* p14 */ + DISP_DATA_OUT(0x82); /* p15 */ + + /* Set DSP column */ + DISP_CMD_OUT(DISP_CMD_MD_CSET); + DISP_DATA_OUT(0xff); + DISP_DATA_OUT(0x03); + DISP_DATA_OUT(0xff); + DISP_DATA_OUT(0x03); + + /* Set DSP page */ + DISP_CMD_OUT(DISP_CMD_MD_PSET); + DISP_DATA_OUT(0xff); + DISP_DATA_OUT(0x01); + DISP_DATA_OUT(0xff); + DISP_DATA_OUT(0x01); + + /* Set ARM column */ + DISP_CMD_OUT(DISP_CMD_SD_CSET); + DISP_DATA_OUT(0x02); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT((QCIF_WIDTH + 1) & 0xFF); + DISP_DATA_OUT((QCIF_WIDTH + 1) >> 8); + + /* Set ARM page */ + DISP_CMD_OUT(DISP_CMD_SD_PSET); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT(0x00); + DISP_DATA_OUT((QCIF_HEIGHT - 1) & 0xFF); + DISP_DATA_OUT((QCIF_HEIGHT - 1) >> 8); + + /* Set 64 gray scales */ + DISP_CMD_OUT(DISP_CMD_GSSET); + DISP_DATA_OUT(DISP_GS_64); + + DISP_CMD_OUT(DISP_CMD_OSSEL); + DISP_DATA_OUT(0); + + /* Sleep out */ + DISP_CMD_OUT(DISP_CMD_SLPOUT); + + WAIT_SEC(40000); + + /* Initialize power IC */ + DISP_CMD_OUT(DISP_CMD_VOLCTL); + DISP_DATA_OUT(DISP_VOLCTL_TONE); + + WAIT_SEC(40000); + + /* Set electronic volume, d'xx */ + DISP_CMD_OUT(DISP_CMD_VOLCTL); + DISP_DATA_OUT(DISP_DEFAULT_CONTRAST); /* value from 0 to 127 */ + + /* Initialize display data */ + DISP_SET_RECT(0, (QCIF_HEIGHT - 1), 0, (QCIF_WIDTH - 1)); + DISP_CMD_OUT(DISP_CMD_RAMWR); + for (i = 0; i < QCIF_HEIGHT * QCIF_WIDTH; i++) + DISP_DATA_OUT(0xffff); + + DISP_CMD_OUT(DISP_CMD_RAMRD); + databack = DISP_DATA_IN(); + databack = DISP_DATA_IN(); + databack = DISP_DATA_IN(); + databack = DISP_DATA_IN(); + + WAIT_SEC(80000); + + DISP_CMD_OUT(DISP_CMD_DISON); + + disp_area_start_row = 0; + disp_area_end_row = QCIF_HEIGHT - 1; + disp_powered_up = TRUE; + disp_initialized = TRUE; + epsonQcif_disp_set_display_area(0, QCIF_HEIGHT - 1); + display_on = TRUE; +} + +static void epsonQcif_disp_set_rect(int x, int y, int xres, int yres) +{ + if (!disp_initialized) + return; + + DISP_SET_RECT(y, y + yres - 1, x, x + xres - 1); + DISP_CMD_OUT(DISP_CMD_RAMWR); +} + +static void epsonQcif_disp_set_display_area(word start_row, word end_row) +{ + if (!disp_initialized) + return; + + if ((start_row == disp_area_start_row) + && (end_row == disp_area_end_row)) + return; + disp_area_start_row = start_row; + disp_area_end_row = end_row; + + /* Range checking + */ + if (end_row >= QCIF_HEIGHT) + end_row = QCIF_HEIGHT - 1; + if (start_row > end_row) + start_row = end_row; + + /* When display is not the full screen, gray scale is set to + ** 2; otherwise it is set to 64. + */ + if ((start_row == 0) && (end_row == (QCIF_HEIGHT - 1))) { + /* The whole screen */ + DISP_CMD_OUT(DISP_CMD_PTLOUT); + WAIT_SEC(10000); + DISP_CMD_OUT(DISP_CMD_DISOFF); + WAIT_SEC(100000); + DISP_CMD_OUT(DISP_CMD_GSSET); + DISP_DATA_OUT(DISP_GS_64); + WAIT_SEC(100000); + DISP_CMD_OUT(DISP_CMD_DISON); + } else { + /* partial screen */ + DISP_CMD_OUT(DISP_CMD_PTLIN); + DISP_DATA_OUT(start_row); + DISP_DATA_OUT(start_row >> 8); + DISP_DATA_OUT(end_row); + DISP_DATA_OUT(end_row >> 8); + DISP_CMD_OUT(DISP_CMD_GSSET); + DISP_DATA_OUT(DISP_GS_2); + } +} + +static int epsonQcif_disp_off(struct platform_device *pdev) +{ + if (!disp_initialized) + epsonQcif_disp_init(pdev); + + if (display_on) { + DISP_CMD_OUT(DISP_CMD_DISOFF); + DISP_CMD_OUT(DISP_CMD_SLPIN); + display_on = FALSE; + } + + return 0; +} + +static int epsonQcif_disp_on(struct platform_device *pdev) +{ + if (!disp_initialized) + epsonQcif_disp_init(pdev); + + if (!display_on) { + DISP_CMD_OUT(DISP_CMD_SLPOUT); + WAIT_SEC(40000); + DISP_CMD_OUT(DISP_CMD_DISON); + epsonQcif_disp_set_contrast(disp_contrast); + display_on = TRUE; + } + + return 0; +} + +static void epsonQcif_disp_set_contrast(word contrast) +{ + if (!disp_initialized) + return; + + /* Initialize power IC, d'24 */ + DISP_CMD_OUT(DISP_CMD_VOLCTL); + DISP_DATA_OUT(DISP_VOLCTL_TONE); + + WAIT_SEC(40000); + + /* Set electronic volume, d'xx */ + DISP_CMD_OUT(DISP_CMD_VOLCTL); + if (contrast > 127) + contrast = 127; + DISP_DATA_OUT(contrast); /* value from 0 to 127 */ + disp_contrast = (byte) contrast; +} /* End disp_set_contrast */ + +static void epsonQcif_disp_clear_screen_area( + word start_row, word end_row, word start_column, word end_column) { + int32 i; + + /* Clear the display screen */ + DISP_SET_RECT(start_row, end_row, start_column, end_column); + DISP_CMD_OUT(DISP_CMD_RAMWR); + i = (end_row - start_row + 1) * (end_column - start_column + 1); + for (; i > 0; i--) + DISP_DATA_OUT(0xffff); +} + +static int __init epsonQcif_probe(struct platform_device *pdev) +{ + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = epsonQcif_probe, + .driver = { + .name = "ebi2_epson_qcif", + }, +}; + +static struct msm_fb_panel_data epsonQcif_panel_data = { + .on = epsonQcif_disp_on, + .off = epsonQcif_disp_off, + .set_rect = epsonQcif_disp_set_rect, +}; + +static struct platform_device this_device = { + .name = "ebi2_epson_qcif", + .id = 0, + .dev = { + .platform_data = &epsonQcif_panel_data, + } +}; + +static int __init epsonQcif_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + ret = platform_driver_register(&this_driver); + if (!ret) { + pinfo = &epsonQcif_panel_data.panel_info; + pinfo->xres = QCIF_WIDTH; + pinfo->yres = QCIF_HEIGHT; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = EBI2_PANEL; + pinfo->pdest = DISPLAY_2; + pinfo->wait_cycle = 0x808000; + pinfo->bpp = 16; + pinfo->fb_num = 2; + pinfo->lcd.vsync_enable = FALSE; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + } + + return ret; +} + +module_init(epsonQcif_init); diff --git a/drivers/video/msm/ebi2_lcd.c b/drivers/video/msm/ebi2_lcd.c new file mode 100644 index 0000000000000000000000000000000000000000..a19763c4666de8cefa846a968413a3cbcafe9d60 --- /dev/null +++ b/drivers/video/msm/ebi2_lcd.c @@ -0,0 +1,308 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" + +static int ebi2_lcd_probe(struct platform_device *pdev); +static int ebi2_lcd_remove(struct platform_device *pdev); + +static int ebi2_lcd_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int ebi2_lcd_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static struct dev_pm_ops ebi2_lcd_dev_pm_ops = { + .runtime_suspend = ebi2_lcd_runtime_suspend, + .runtime_resume = ebi2_lcd_runtime_resume, +}; + +static struct platform_driver ebi2_lcd_driver = { + .probe = ebi2_lcd_probe, + .remove = ebi2_lcd_remove, + .suspend = NULL, + .resume = NULL, + .shutdown = NULL, + .driver = { + .name = "ebi2_lcd", + .pm = &ebi2_lcd_dev_pm_ops, + }, +}; + +static void *ebi2_base; +static void *ebi2_lcd_cfg0; +static void *ebi2_lcd_cfg1; +static void __iomem *lcd01_base; +static void __iomem *lcd02_base; +static int lcd01_base_phys; +static int ebi2_lcd_resource_initialized; + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; +static struct lcdc_platform_data *ebi2_pdata; + +static int ebi2_lcd_on(struct platform_device *pdev) +{ + int ret; + + if (ebi2_pdata && ebi2_pdata->lcdc_power_save) + ebi2_pdata->lcdc_power_save(1); + + ret = panel_next_on(pdev); + return ret; +} + +static int ebi2_lcd_off(struct platform_device *pdev) +{ + int ret; + + ret = panel_next_off(pdev); + + if (ebi2_pdata && ebi2_pdata->lcdc_power_save) + ebi2_pdata->lcdc_power_save(0); + + return ret; +} + +static int ebi2_lcd_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc, i, hw_version; + + if (pdev->id == 0) { + for (i = 0; i < pdev->num_resources; i++) { + if (!strncmp(pdev->resource[i].name, "base", 4)) { + ebi2_base = ioremap(pdev->resource[i].start, + pdev->resource[i].end - + pdev->resource[i].start + 1); + if (!ebi2_base) { + printk(KERN_ERR + "ebi2_base ioremap failed!\n"); + return -ENOMEM; + } + ebi2_lcd_cfg0 = (void *)(ebi2_base + 0x20); + ebi2_lcd_cfg1 = (void *)(ebi2_base + 0x24); + } else if (!strncmp(pdev->resource[i].name, + "lcd01", 5)) { + lcd01_base_phys = pdev->resource[i].start; + lcd01_base = ioremap(pdev->resource[i].start, + pdev->resource[i].end - + pdev->resource[i].start + 1); + if (!lcd01_base) { + printk(KERN_ERR + "lcd01_base ioremap failed!\n"); + return -ENOMEM; + } + } else if (!strncmp(pdev->resource[i].name, + "lcd02", 5)) { + lcd02_base = ioremap(pdev->resource[i].start, + pdev->resource[i].end - + pdev->resource[i].start + 1); + if (!lcd02_base) { + printk(KERN_ERR + "lcd02_base ioremap failed!\n"); + return -ENOMEM; + } + } + } + ebi2_pdata = pdev->dev.platform_data; + ebi2_lcd_resource_initialized = 1; + + return 0; + } + + if (!ebi2_lcd_resource_initialized) + return -EPERM; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + if (ebi2_base == NULL) + return -ENOMEM; + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + + /* link to the latest pdev */ + mfd->pdev = mdp_dev; + mfd->dest = DISPLAY_LCD; + + /* add panel data */ + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + printk(KERN_ERR "ebi2_lcd_probe: platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + + /* data chain */ + pdata = mdp_dev->dev.platform_data; + pdata->on = ebi2_lcd_on; + pdata->off = ebi2_lcd_off; + pdata->next = pdev; + + /* get/set panel specific fb info */ + mfd->panel_info = pdata->panel_info; + + hw_version = inp32((int)ebi2_base + 8); + + if (mfd->panel_info.bpp == 24) + mfd->fb_imgType = MDP_RGB_888; + else if (mfd->panel_info.bpp == 18) + mfd->fb_imgType = MDP_RGB_888; + else + mfd->fb_imgType = MDP_RGB_565; + + /* config msm ebi2 lcd register */ + if (mfd->panel_info.pdest == DISPLAY_1) { + outp32(ebi2_base, + (inp32(ebi2_base) & (~(EBI2_PRIM_LCD_CLR))) | + EBI2_PRIM_LCD_SEL); + /* + * current design has one set of cfg0/1 register to control + * both EBI2 channels. so, we're using the PRIM channel to + * configure both. + */ + outp32(ebi2_lcd_cfg0, mfd->panel_info.wait_cycle); + if (hw_version < 0x2020) { + if (mfd->panel_info.bpp == 18) + outp32(ebi2_lcd_cfg1, 0x01000000); + else + outp32(ebi2_lcd_cfg1, 0x0); + } + } else { +#ifdef DEBUG_EBI2_LCD + /* + * confliting with QCOM SURF FPGA CS. + * OEM should enable below for their CS mapping + */ + outp32(ebi2_base, (inp32(ebi2_base)&(~(EBI2_SECD_LCD_CLR))) + |EBI2_SECD_LCD_SEL); +#endif + } + + /* + * map cs (chip select) address + */ + if (mfd->panel_info.pdest == DISPLAY_1) { + mfd->cmd_port = lcd01_base; + if (hw_version >= 0x2020) { + mfd->data_port = + (void *)((uint32) mfd->cmd_port + 0x80); + mfd->data_port_phys = + (void *)(lcd01_base_phys + 0x80); + } else { + mfd->data_port = + (void *)((uint32) mfd->cmd_port + + EBI2_PRIM_LCD_RS_PIN); + mfd->data_port_phys = + (void *)(LCD_PRIM_BASE_PHYS + EBI2_PRIM_LCD_RS_PIN); + } + } else { + mfd->cmd_port = lcd01_base; + mfd->data_port = + (void *)((uint32) mfd->cmd_port + EBI2_SECD_LCD_RS_PIN); + mfd->data_port_phys = + (void *)(LCD_SECD_BASE_PHYS + EBI2_SECD_LCD_RS_PIN); + } + + /* + * set driver data + */ + platform_set_drvdata(mdp_dev, mfd); + + /* + * register in mdp driver + */ + rc = platform_device_add(mdp_dev); + if (rc) { + goto ebi2_lcd_probe_err; + } + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + + pdev_list[pdev_list_cnt++] = pdev; + return 0; + + ebi2_lcd_probe_err: + platform_device_put(mdp_dev); + return rc; +} + +static int ebi2_lcd_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return 0; + + if (mfd->key != MFD_KEY) + return 0; + + iounmap(mfd->cmd_port); + pm_runtime_disable(&pdev->dev); + return 0; +} + +static int ebi2_lcd_register_driver(void) +{ + return platform_driver_register(&ebi2_lcd_driver); +} + +static int __init ebi2_lcd_driver_init(void) +{ + return ebi2_lcd_register_driver(); +} + +module_init(ebi2_lcd_driver_init); diff --git a/drivers/video/msm/ebi2_tmd20.c b/drivers/video/msm/ebi2_tmd20.c new file mode 100644 index 0000000000000000000000000000000000000000..7c7b0efd33011aea3e35b8bf3d01574ba98f69a3 --- /dev/null +++ b/drivers/video/msm/ebi2_tmd20.c @@ -0,0 +1,1120 @@ +/* Copyright (c) 2008-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" + +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" + +#include + +#include +#include + +#include +#include + +/* #define TMD20QVGA_LCD_18BPP */ +#define QVGA_WIDTH 240 +#define QVGA_HEIGHT 320 + +#ifdef TMD20QVGA_LCD_18BPP +#define DISP_QVGA_18BPP(x) ((((x)<<2) & 0x3FC00)|(( (x)<<1)& 0x1FE)) +#define DISP_REG(name) uint32 register_##name; +#define OUTPORT(x, y) outpdw(x, y) +#define INPORT(x) inpdw(x) +#else +#define DISP_QVGA_18BPP(x) (x) +#define DISP_REG(name) uint16 register_##name; +#define OUTPORT(x, y) outpw(x, y) +#define INPORT(x) intpw(x) +#endif + +static void *DISP_CMD_PORT; +static void *DISP_DATA_PORT; + +#define DISP_RNTI 0x10 + +#define DISP_CMD_OUT(cmd) OUTPORT(DISP_CMD_PORT, DISP_QVGA_18BPP(cmd)) +#define DISP_DATA_OUT(data) OUTPORT(DISP_DATA_PORT, data) +#define DISP_DATA_IN() INPORT(DISP_DATA_PORT) + +#if (defined(TMD20QVGA_LCD_18BPP)) +#define DISP_DATA_OUT_16TO18BPP(x) \ + DISP_DATA_OUT((((x)&0xf800)<<2|((x)&0x80000)>>3) \ + | (((x)&0x7e0)<<1) \ + | (((x)&0x1F)<<1|((x)&0x10)>>4)) +#else +#define DISP_DATA_OUT_16TO18BPP(x) \ + DISP_DATA_OUT(x) +#endif + +#define DISP_WRITE_OUT(addr, data) \ + register_##addr = DISP_QVGA_18BPP(data); \ + DISP_CMD_OUT(addr); \ + DISP_DATA_OUT(register_##addr); + +#define DISP_UPDATE_VALUE(addr, bitmask, data) \ + DISP_WRITE_OUT(##addr, (register_##addr & ~(bitmask)) | (data)); + +#define DISP_VAL_IF(bitvalue, bitmask) \ + ((bitvalue) ? (bitmask) : 0) + +/* QVGA = 256 x 320 */ +/* actual display is 240 x 320...offset by 0x10 */ +#define DISP_ROW_COL_TO_ADDR(row, col) ((row) * 0x100 + col) +#define DISP_SET_RECT(ulhc_row, lrhc_row, ulhc_col, lrhc_col) \ + { \ + DISP_WRITE_OUT(DISP_HORZ_RAM_ADDR_POS_1_ADDR, (ulhc_col) + tmd20qvga_panel_offset); \ + DISP_WRITE_OUT(DISP_HORZ_RAM_ADDR_POS_2_ADDR, (lrhc_col) + tmd20qvga_panel_offset); \ + DISP_WRITE_OUT(DISP_VERT_RAM_ADDR_POS_1_ADDR, (ulhc_row)); \ + DISP_WRITE_OUT(DISP_VERT_RAM_ADDR_POS_2_ADDR, (lrhc_row)); \ + DISP_WRITE_OUT(DISP_RAM_ADDR_SET_1_ADDR, (ulhc_col) + tmd20qvga_panel_offset); \ + DISP_WRITE_OUT(DISP_RAM_ADDR_SET_2_ADDR, (ulhc_row)); \ + } + +#define WAIT_MSEC(msec) mdelay(msec) + +/* + * TMD QVGA Address + */ +/* Display Control */ +#define DISP_START_OSCILLATION_ADDR 0x000 +DISP_REG(DISP_START_OSCILLATION_ADDR) +#define DISP_DRIVER_OUTPUT_CTL_ADDR 0x001 + DISP_REG(DISP_DRIVER_OUTPUT_CTL_ADDR) +#define DISP_LCD_DRIVING_SIG_ADDR 0x002 + DISP_REG(DISP_LCD_DRIVING_SIG_ADDR) +#define DISP_ENTRY_MODE_ADDR 0x003 + DISP_REG(DISP_ENTRY_MODE_ADDR) +#define DISP_DISPLAY_CTL_1_ADDR 0x007 + DISP_REG(DISP_DISPLAY_CTL_1_ADDR) +#define DISP_DISPLAY_CTL_2_ADDR 0x008 + DISP_REG(DISP_DISPLAY_CTL_2_ADDR) + +/* DISPLAY MODE 0x009 partial display not supported */ +#define DISP_POWER_SUPPLY_INTF_ADDR 0x00A + DISP_REG(DISP_POWER_SUPPLY_INTF_ADDR) + +/* DISPLAY MODE 0x00B xZoom feature is not supported */ +#define DISP_EXT_DISPLAY_CTL_1_ADDR 0x00C + DISP_REG(DISP_EXT_DISPLAY_CTL_1_ADDR) + +#define DISP_FRAME_CYCLE_CTL_ADDR 0x00D + DISP_REG(DISP_FRAME_CYCLE_CTL_ADDR) + +#define DISP_EXT_DISPLAY_CTL_2_ADDR 0x00E + DISP_REG(DISP_EXT_DISPLAY_CTL_2_ADDR) + +#define DISP_EXT_DISPLAY_CTL_3_ADDR 0x00F + DISP_REG(DISP_EXT_DISPLAY_CTL_3_ADDR) + +#define DISP_LTPS_CTL_1_ADDR 0x012 + DISP_REG(DISP_LTPS_CTL_1_ADDR) +#define DISP_LTPS_CTL_2_ADDR 0x013 + DISP_REG(DISP_LTPS_CTL_2_ADDR) +#define DISP_LTPS_CTL_3_ADDR 0x014 + DISP_REG(DISP_LTPS_CTL_3_ADDR) +#define DISP_LTPS_CTL_4_ADDR 0x018 + DISP_REG(DISP_LTPS_CTL_4_ADDR) +#define DISP_LTPS_CTL_5_ADDR 0x019 + DISP_REG(DISP_LTPS_CTL_5_ADDR) +#define DISP_LTPS_CTL_6_ADDR 0x01A + DISP_REG(DISP_LTPS_CTL_6_ADDR) +#define DISP_AMP_SETTING_ADDR 0x01C + DISP_REG(DISP_AMP_SETTING_ADDR) +#define DISP_MODE_SETTING_ADDR 0x01D + DISP_REG(DISP_MODE_SETTING_ADDR) +#define DISP_POFF_LN_SETTING_ADDR 0x01E + DISP_REG(DISP_POFF_LN_SETTING_ADDR) +/* Power Contol */ +#define DISP_POWER_CTL_1_ADDR 0x100 + DISP_REG(DISP_POWER_CTL_1_ADDR) +#define DISP_POWER_CTL_2_ADDR 0x101 + DISP_REG(DISP_POWER_CTL_2_ADDR) +#define DISP_POWER_CTL_3_ADDR 0x102 + DISP_REG(DISP_POWER_CTL_3_ADDR) +#define DISP_POWER_CTL_4_ADDR 0x103 + DISP_REG(DISP_POWER_CTL_4_ADDR) +#define DISP_POWER_CTL_5_ADDR 0x104 + DISP_REG(DISP_POWER_CTL_5_ADDR) +#define DISP_POWER_CTL_6_ADDR 0x105 + DISP_REG(DISP_POWER_CTL_6_ADDR) +#define DISP_POWER_CTL_7_ADDR 0x106 + DISP_REG(DISP_POWER_CTL_7_ADDR) +/* RAM Access */ +#define DISP_RAM_ADDR_SET_1_ADDR 0x200 + DISP_REG(DISP_RAM_ADDR_SET_1_ADDR) +#define DISP_RAM_ADDR_SET_2_ADDR 0x201 + DISP_REG(DISP_RAM_ADDR_SET_2_ADDR) +#define DISP_CMD_RAMRD DISP_CMD_RAMWR +#define DISP_CMD_RAMWR 0x202 + DISP_REG(DISP_CMD_RAMWR) +#define DISP_RAM_DATA_MASK_1_ADDR 0x203 + DISP_REG(DISP_RAM_DATA_MASK_1_ADDR) +#define DISP_RAM_DATA_MASK_2_ADDR 0x204 + DISP_REG(DISP_RAM_DATA_MASK_2_ADDR) +/* Gamma Control, Contrast, Gray Scale Setting */ +#define DISP_GAMMA_CONTROL_1_ADDR 0x300 + DISP_REG(DISP_GAMMA_CONTROL_1_ADDR) +#define DISP_GAMMA_CONTROL_2_ADDR 0x301 + DISP_REG(DISP_GAMMA_CONTROL_2_ADDR) +#define DISP_GAMMA_CONTROL_3_ADDR 0x302 + DISP_REG(DISP_GAMMA_CONTROL_3_ADDR) +#define DISP_GAMMA_CONTROL_4_ADDR 0x303 + DISP_REG(DISP_GAMMA_CONTROL_4_ADDR) +#define DISP_GAMMA_CONTROL_5_ADDR 0x304 + DISP_REG(DISP_GAMMA_CONTROL_5_ADDR) +/* Coordinate Control */ +#define DISP_VERT_SCROLL_CTL_1_ADDR 0x400 + DISP_REG(DISP_VERT_SCROLL_CTL_1_ADDR) +#define DISP_VERT_SCROLL_CTL_2_ADDR 0x401 + DISP_REG(DISP_VERT_SCROLL_CTL_2_ADDR) +#define DISP_SCREEN_1_DRV_POS_1_ADDR 0x402 + DISP_REG(DISP_SCREEN_1_DRV_POS_1_ADDR) +#define DISP_SCREEN_1_DRV_POS_2_ADDR 0x403 + DISP_REG(DISP_SCREEN_1_DRV_POS_2_ADDR) +#define DISP_SCREEN_2_DRV_POS_1_ADDR 0x404 + DISP_REG(DISP_SCREEN_2_DRV_POS_1_ADDR) +#define DISP_SCREEN_2_DRV_POS_2_ADDR 0x405 + DISP_REG(DISP_SCREEN_2_DRV_POS_2_ADDR) +#define DISP_HORZ_RAM_ADDR_POS_1_ADDR 0x406 + DISP_REG(DISP_HORZ_RAM_ADDR_POS_1_ADDR) +#define DISP_HORZ_RAM_ADDR_POS_2_ADDR 0x407 + DISP_REG(DISP_HORZ_RAM_ADDR_POS_2_ADDR) +#define DISP_VERT_RAM_ADDR_POS_1_ADDR 0x408 + DISP_REG(DISP_VERT_RAM_ADDR_POS_1_ADDR) +#define DISP_VERT_RAM_ADDR_POS_2_ADDR 0x409 + DISP_REG(DISP_VERT_RAM_ADDR_POS_2_ADDR) +#define DISP_TMD_700_ADDR 0x700 /* 0x700 */ + DISP_REG(DISP_TMD_700_ADDR) +#define DISP_TMD_015_ADDR 0x015 /* 0x700 */ + DISP_REG(DISP_TMD_015_ADDR) +#define DISP_TMD_305_ADDR 0x305 /* 0x700 */ + DISP_REG(DISP_TMD_305_ADDR) + +/* + * TMD QVGA Bit Definations + */ + +#define DISP_BIT_IB15 0x8000 +#define DISP_BIT_IB14 0x4000 +#define DISP_BIT_IB13 0x2000 +#define DISP_BIT_IB12 0x1000 +#define DISP_BIT_IB11 0x0800 +#define DISP_BIT_IB10 0x0400 +#define DISP_BIT_IB09 0x0200 +#define DISP_BIT_IB08 0x0100 +#define DISP_BIT_IB07 0x0080 +#define DISP_BIT_IB06 0x0040 +#define DISP_BIT_IB05 0x0020 +#define DISP_BIT_IB04 0x0010 +#define DISP_BIT_IB03 0x0008 +#define DISP_BIT_IB02 0x0004 +#define DISP_BIT_IB01 0x0002 +#define DISP_BIT_IB00 0x0001 +/* + * Display Control + * DISP_START_OSCILLATION_ADDR Start Oscillation + * DISP_DRIVER_OUTPUT_CTL_ADDR Driver Output Control + */ +#define DISP_BITMASK_SS DISP_BIT_IB08 +#define DISP_BITMASK_NL5 DISP_BIT_IB05 +#define DISP_BITMASK_NL4 DISP_BIT_IB04 +#define DISP_BITMASK_NL3 DISP_BIT_IB03 +#define DISP_BITMASK_NL2 DISP_BIT_IB02 +#define DISP_BITMASK_NL1 DISP_BIT_IB01 +#define DISP_BITMASK_NL0 DISP_BIT_IB00 +/* DISP_LCD_DRIVING_SIG_ADDR LCD Driving Signal Setting */ +#define DISP_BITMASK_BC DISP_BIT_IB09 +/* DISP_ENTRY_MODE_ADDR Entry Mode */ +#define DISP_BITMASK_TRI DISP_BIT_IB15 +#define DISP_BITMASK_DFM1 DISP_BIT_IB14 +#define DISP_BITMASK_DFM0 DISP_BIT_IB13 +#define DISP_BITMASK_BGR DISP_BIT_IB12 +#define DISP_BITMASK_HWM0 DISP_BIT_IB08 +#define DISP_BITMASK_ID1 DISP_BIT_IB05 +#define DISP_BITMASK_ID0 DISP_BIT_IB04 +#define DISP_BITMASK_AM DISP_BIT_IB03 +/* DISP_DISPLAY_CTL_1_ADDR Display Control (1) */ +#define DISP_BITMASK_COL1 DISP_BIT_IB15 +#define DISP_BITMASK_COL0 DISP_BIT_IB14 +#define DISP_BITMASK_VLE2 DISP_BIT_IB10 +#define DISP_BITMASK_VLE1 DISP_BIT_IB09 +#define DISP_BITMASK_SPT DISP_BIT_IB08 +#define DISP_BITMASK_PT1 DISP_BIT_IB07 +#define DISP_BITMASK_PT0 DISP_BIT_IB06 +#define DISP_BITMASK_REV DISP_BIT_IB02 +/* DISP_DISPLAY_CTL_2_ADDR Display Control (2) */ +#define DISP_BITMASK_FP3 DISP_BIT_IB11 +#define DISP_BITMASK_FP2 DISP_BIT_IB10 +#define DISP_BITMASK_FP1 DISP_BIT_IB09 +#define DISP_BITMASK_FP0 DISP_BIT_IB08 +#define DISP_BITMASK_BP3 DISP_BIT_IB03 +#define DISP_BITMASK_BP2 DISP_BIT_IB02 +#define DISP_BITMASK_BP1 DISP_BIT_IB01 +#define DISP_BITMASK_BP0 DISP_BIT_IB00 +/* DISP_POWER_SUPPLY_INTF_ADDR Power Supply IC Interface Control */ +#define DISP_BITMASK_CSE DISP_BIT_IB12 +#define DISP_BITMASK_TE DISP_BIT_IB08 +#define DISP_BITMASK_IX3 DISP_BIT_IB03 +#define DISP_BITMASK_IX2 DISP_BIT_IB02 +#define DISP_BITMASK_IX1 DISP_BIT_IB01 +#define DISP_BITMASK_IX0 DISP_BIT_IB00 +/* DISP_EXT_DISPLAY_CTL_1_ADDR External Display Interface Control (1) */ +#define DISP_BITMASK_RM DISP_BIT_IB08 +#define DISP_BITMASK_DM1 DISP_BIT_IB05 +#define DISP_BITMASK_DM0 DISP_BIT_IB04 +#define DISP_BITMASK_RIM1 DISP_BIT_IB01 +#define DISP_BITMASK_RIM0 DISP_BIT_IB00 +/* DISP_FRAME_CYCLE_CTL_ADDR Frame Frequency Adjustment Control */ +#define DISP_BITMASK_DIVI1 DISP_BIT_IB09 +#define DISP_BITMASK_DIVI0 DISP_BIT_IB08 +#define DISP_BITMASK_RTNI4 DISP_BIT_IB04 +#define DISP_BITMASK_RTNI3 DISP_BIT_IB03 +#define DISP_BITMASK_RTNI2 DISP_BIT_IB02 +#define DISP_BITMASK_RTNI1 DISP_BIT_IB01 +#define DISP_BITMASK_RTNI0 DISP_BIT_IB00 +/* DISP_EXT_DISPLAY_CTL_2_ADDR External Display Interface Control (2) */ +#define DISP_BITMASK_DIVE1 DISP_BIT_IB09 +#define DISP_BITMASK_DIVE0 DISP_BIT_IB08 +#define DISP_BITMASK_RTNE7 DISP_BIT_IB07 +#define DISP_BITMASK_RTNE6 DISP_BIT_IB06 +#define DISP_BITMASK_RTNE5 DISP_BIT_IB05 +#define DISP_BITMASK_RTNE4 DISP_BIT_IB04 +#define DISP_BITMASK_RTNE3 DISP_BIT_IB03 +#define DISP_BITMASK_RTNE2 DISP_BIT_IB02 +#define DISP_BITMASK_RTNE1 DISP_BIT_IB01 +#define DISP_BITMASK_RTNE0 DISP_BIT_IB00 +/* DISP_EXT_DISPLAY_CTL_3_ADDR External Display Interface Control (3) */ +#define DISP_BITMASK_VSPL DISP_BIT_IB04 +#define DISP_BITMASK_HSPL DISP_BIT_IB03 +#define DISP_BITMASK_VPL DISP_BIT_IB02 +#define DISP_BITMASK_EPL DISP_BIT_IB01 +#define DISP_BITMASK_DPL DISP_BIT_IB00 +/* DISP_LTPS_CTL_1_ADDR LTPS Interface Control (1) */ +#define DISP_BITMASK_CLWI3 DISP_BIT_IB11 +#define DISP_BITMASK_CLWI2 DISP_BIT_IB10 +#define DISP_BITMASK_CLWI1 DISP_BIT_IB09 +#define DISP_BITMASK_CLWI0 DISP_BIT_IB08 +#define DISP_BITMASK_CLTI1 DISP_BIT_IB01 +#define DISP_BITMASK_CLTI0 DISP_BIT_IB00 +/* DISP_LTPS_CTL_2_ADDR LTPS Interface Control (2) */ +#define DISP_BITMASK_OEVBI1 DISP_BIT_IB09 +#define DISP_BITMASK_OEVBI0 DISP_BIT_IB08 +#define DISP_BITMASK_OEVFI1 DISP_BIT_IB01 +#define DISP_BITMASK_OEVFI0 DISP_BIT_IB00 +/* DISP_LTPS_CTL_3_ADDR LTPS Interface Control (3) */ +#define DISP_BITMASK_SHI1 DISP_BIT_IB01 +#define DISP_BITMASK_SHI0 DISP_BIT_IB00 +/* DISP_LTPS_CTL_4_ADDR LTPS Interface Control (4) */ +#define DISP_BITMASK_CLWE5 DISP_BIT_IB13 +#define DISP_BITMASK_CLWE4 DISP_BIT_IB12 +#define DISP_BITMASK_CLWE3 DISP_BIT_IB11 +#define DISP_BITMASK_CLWE2 DISP_BIT_IB10 +#define DISP_BITMASK_CLWE1 DISP_BIT_IB09 +#define DISP_BITMASK_CLWE0 DISP_BIT_IB08 +#define DISP_BITMASK_CLTE3 DISP_BIT_IB03 +#define DISP_BITMASK_CLTE2 DISP_BIT_IB02 +#define DISP_BITMASK_CLTE1 DISP_BIT_IB01 +#define DISP_BITMASK_CLTE0 DISP_BIT_IB00 +/* DISP_LTPS_CTL_5_ADDR LTPS Interface Control (5) */ +#define DISP_BITMASK_OEVBE3 DISP_BIT_IB11 +#define DISP_BITMASK_OEVBE2 DISP_BIT_IB10 +#define DISP_BITMASK_OEVBE1 DISP_BIT_IB09 +#define DISP_BITMASK_OEVBE0 DISP_BIT_IB08 +#define DISP_BITMASK_OEVFE3 DISP_BIT_IB03 +#define DISP_BITMASK_OEVFE2 DISP_BIT_IB02 +#define DISP_BITMASK_OEVFE1 DISP_BIT_IB01 +#define DISP_BITMASK_OEVFE0 DISP_BIT_IB00 +/* DISP_LTPS_CTL_6_ADDR LTPS Interface Control (6) */ +#define DISP_BITMASK_SHE3 DISP_BIT_IB03 +#define DISP_BITMASK_SHE2 DISP_BIT_IB02 +#define DISP_BITMASK_SHE1 DISP_BIT_IB01 +#define DISP_BITMASK_SHE0 DISP_BIT_IB00 +/* DISP_AMP_SETTING_ADDR Amplify Setting */ +#define DISP_BITMASK_ABSW1 DISP_BIT_IB01 +#define DISP_BITMASK_ABSW0 DISP_BIT_IB00 +/* DISP_MODE_SETTING_ADDR Mode Setting */ +#define DISP_BITMASK_DSTB DISP_BIT_IB02 +#define DISP_BITMASK_STB DISP_BIT_IB00 +/* DISP_POFF_LN_SETTING_ADDR Power Off Line Setting */ +#define DISP_BITMASK_POFH3 DISP_BIT_IB03 +#define DISP_BITMASK_POFH2 DISP_BIT_IB02 +#define DISP_BITMASK_POFH1 DISP_BIT_IB01 +#define DISP_BITMASK_POFH0 DISP_BIT_IB00 + +/* Power Contol */ +/* DISP_POWER_CTL_1_ADDR Power Control (1) */ +#define DISP_BITMASK_PO DISP_BIT_IB11 +#define DISP_BITMASK_VCD DISP_BIT_IB09 +#define DISP_BITMASK_VSC DISP_BIT_IB08 +#define DISP_BITMASK_CON DISP_BIT_IB07 +#define DISP_BITMASK_ASW1 DISP_BIT_IB06 +#define DISP_BITMASK_ASW0 DISP_BIT_IB05 +#define DISP_BITMASK_OEV DISP_BIT_IB04 +#define DISP_BITMASK_OEVE DISP_BIT_IB03 +#define DISP_BITMASK_FR DISP_BIT_IB02 +#define DISP_BITMASK_D1 DISP_BIT_IB01 +#define DISP_BITMASK_D0 DISP_BIT_IB00 +/* DISP_POWER_CTL_2_ADDR Power Control (2) */ +#define DISP_BITMASK_DC4 DISP_BIT_IB15 +#define DISP_BITMASK_DC3 DISP_BIT_IB14 +#define DISP_BITMASK_SAP2 DISP_BIT_IB13 +#define DISP_BITMASK_SAP1 DISP_BIT_IB12 +#define DISP_BITMASK_SAP0 DISP_BIT_IB11 +#define DISP_BITMASK_BT2 DISP_BIT_IB10 +#define DISP_BITMASK_BT1 DISP_BIT_IB09 +#define DISP_BITMASK_BT0 DISP_BIT_IB08 +#define DISP_BITMASK_DC2 DISP_BIT_IB07 +#define DISP_BITMASK_DC1 DISP_BIT_IB06 +#define DISP_BITMASK_DC0 DISP_BIT_IB05 +#define DISP_BITMASK_AP2 DISP_BIT_IB04 +#define DISP_BITMASK_AP1 DISP_BIT_IB03 +#define DISP_BITMASK_AP0 DISP_BIT_IB02 +/* DISP_POWER_CTL_3_ADDR Power Control (3) */ +#define DISP_BITMASK_VGL4 DISP_BIT_IB10 +#define DISP_BITMASK_VGL3 DISP_BIT_IB09 +#define DISP_BITMASK_VGL2 DISP_BIT_IB08 +#define DISP_BITMASK_VGL1 DISP_BIT_IB07 +#define DISP_BITMASK_VGL0 DISP_BIT_IB06 +#define DISP_BITMASK_VGH4 DISP_BIT_IB04 +#define DISP_BITMASK_VGH3 DISP_BIT_IB03 +#define DISP_BITMASK_VGH2 DISP_BIT_IB02 +#define DISP_BITMASK_VGH1 DISP_BIT_IB01 +#define DISP_BITMASK_VGH0 DISP_BIT_IB00 +/* DISP_POWER_CTL_4_ADDR Power Control (4) */ +#define DISP_BITMASK_VC2 DISP_BIT_IB02 +#define DISP_BITMASK_VC1 DISP_BIT_IB01 +#define DISP_BITMASK_VC0 DISP_BIT_IB00 +/* DISP_POWER_CTL_5_ADDR Power Control (5) */ +#define DISP_BITMASK_VRL3 DISP_BIT_IB11 +#define DISP_BITMASK_VRL2 DISP_BIT_IB10 +#define DISP_BITMASK_VRL1 DISP_BIT_IB09 +#define DISP_BITMASK_VRL0 DISP_BIT_IB08 +#define DISP_BITMASK_PON DISP_BIT_IB04 +#define DISP_BITMASK_VRH3 DISP_BIT_IB03 +#define DISP_BITMASK_VRH2 DISP_BIT_IB02 +#define DISP_BITMASK_VRH1 DISP_BIT_IB01 +#define DISP_BITMASK_VRH0 DISP_BIT_IB00 +/* DISP_POWER_CTL_6_ADDR Power Control (6) */ +#define DISP_BITMASK_VCOMG DISP_BIT_IB13 +#define DISP_BITMASK_VDV4 DISP_BIT_IB12 +#define DISP_BITMASK_VDV3 DISP_BIT_IB11 +#define DISP_BITMASK_VDV2 DISP_BIT_IB10 +#define DISP_BITMASK_VDV1 DISP_BIT_IB09 +#define DISP_BITMASK_VDV0 DISP_BIT_IB08 +#define DISP_BITMASK_VCM4 DISP_BIT_IB04 +#define DISP_BITMASK_VCM3 DISP_BIT_IB03 +#define DISP_BITMASK_VCM2 DISP_BIT_IB02 +#define DISP_BITMASK_VCM1 DISP_BIT_IB01 +#define DISP_BITMASK_VCM0 DISP_BIT_IB00 +/* RAM Access */ +/* DISP_RAM_ADDR_SET_1_ADDR RAM Address Set (1) */ +#define DISP_BITMASK_AD7 DISP_BIT_IB07 +#define DISP_BITMASK_AD6 DISP_BIT_IB06 +#define DISP_BITMASK_AD5 DISP_BIT_IB05 +#define DISP_BITMASK_AD4 DISP_BIT_IB04 +#define DISP_BITMASK_AD3 DISP_BIT_IB03 +#define DISP_BITMASK_AD2 DISP_BIT_IB02 +#define DISP_BITMASK_AD1 DISP_BIT_IB01 +#define DISP_BITMASK_AD0 DISP_BIT_IB00 +/* DISP_RAM_ADDR_SET_2_ADDR RAM Address Set (2) */ +#define DISP_BITMASK_AD16 DISP_BIT_IB08 +#define DISP_BITMASK_AD15 DISP_BIT_IB07 +#define DISP_BITMASK_AD14 DISP_BIT_IB06 +#define DISP_BITMASK_AD13 DISP_BIT_IB05 +#define DISP_BITMASK_AD12 DISP_BIT_IB04 +#define DISP_BITMASK_AD11 DISP_BIT_IB03 +#define DISP_BITMASK_AD10 DISP_BIT_IB02 +#define DISP_BITMASK_AD9 DISP_BIT_IB01 +#define DISP_BITMASK_AD8 DISP_BIT_IB00 +/* + * DISP_CMD_RAMWR RAM Data Read/Write + * Use Data Bit Configuration + */ +/* DISP_RAM_DATA_MASK_1_ADDR RAM Write Data Mask (1) */ +#define DISP_BITMASK_WM11 DISP_BIT_IB13 +#define DISP_BITMASK_WM10 DISP_BIT_IB12 +#define DISP_BITMASK_WM9 DISP_BIT_IB11 +#define DISP_BITMASK_WM8 DISP_BIT_IB10 +#define DISP_BITMASK_WM7 DISP_BIT_IB09 +#define DISP_BITMASK_WM6 DISP_BIT_IB08 +#define DISP_BITMASK_WM5 DISP_BIT_IB05 +#define DISP_BITMASK_WM4 DISP_BIT_IB04 +#define DISP_BITMASK_WM3 DISP_BIT_IB03 +#define DISP_BITMASK_WM2 DISP_BIT_IB02 +#define DISP_BITMASK_WM1 DISP_BIT_IB01 +#define DISP_BITMASK_WM0 DISP_BIT_IB00 +/* DISP_RAM_DATA_MASK_2_ADDR RAM Write Data Mask (2) */ +#define DISP_BITMASK_WM17 DISP_BIT_IB05 +#define DISP_BITMASK_WM16 DISP_BIT_IB04 +#define DISP_BITMASK_WM15 DISP_BIT_IB03 +#define DISP_BITMASK_WM14 DISP_BIT_IB02 +#define DISP_BITMASK_WM13 DISP_BIT_IB01 +#define DISP_BITMASK_WM12 DISP_BIT_IB00 +/*Gamma Control */ +/* DISP_GAMMA_CONTROL_1_ADDR Gamma Control (1) */ +#define DISP_BITMASK_PKP12 DISP_BIT_IB10 +#define DISP_BITMASK_PKP11 DISP_BIT_IB08 +#define DISP_BITMASK_PKP10 DISP_BIT_IB09 +#define DISP_BITMASK_PKP02 DISP_BIT_IB02 +#define DISP_BITMASK_PKP01 DISP_BIT_IB01 +#define DISP_BITMASK_PKP00 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_2_ADDR Gamma Control (2) */ +#define DISP_BITMASK_PKP32 DISP_BIT_IB10 +#define DISP_BITMASK_PKP31 DISP_BIT_IB09 +#define DISP_BITMASK_PKP30 DISP_BIT_IB08 +#define DISP_BITMASK_PKP22 DISP_BIT_IB02 +#define DISP_BITMASK_PKP21 DISP_BIT_IB01 +#define DISP_BITMASK_PKP20 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_3_ADDR Gamma Control (3) */ +#define DISP_BITMASK_PKP52 DISP_BIT_IB10 +#define DISP_BITMASK_PKP51 DISP_BIT_IB09 +#define DISP_BITMASK_PKP50 DISP_BIT_IB08 +#define DISP_BITMASK_PKP42 DISP_BIT_IB02 +#define DISP_BITMASK_PKP41 DISP_BIT_IB01 +#define DISP_BITMASK_PKP40 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_4_ADDR Gamma Control (4) */ +#define DISP_BITMASK_PRP12 DISP_BIT_IB10 +#define DISP_BITMASK_PRP11 DISP_BIT_IB08 +#define DISP_BITMASK_PRP10 DISP_BIT_IB09 +#define DISP_BITMASK_PRP02 DISP_BIT_IB02 +#define DISP_BITMASK_PRP01 DISP_BIT_IB01 +#define DISP_BITMASK_PRP00 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_5_ADDR Gamma Control (5) */ +#define DISP_BITMASK_VRP14 DISP_BIT_IB12 +#define DISP_BITMASK_VRP13 DISP_BIT_IB11 +#define DISP_BITMASK_VRP12 DISP_BIT_IB10 +#define DISP_BITMASK_VRP11 DISP_BIT_IB08 +#define DISP_BITMASK_VRP10 DISP_BIT_IB09 +#define DISP_BITMASK_VRP03 DISP_BIT_IB03 +#define DISP_BITMASK_VRP02 DISP_BIT_IB02 +#define DISP_BITMASK_VRP01 DISP_BIT_IB01 +#define DISP_BITMASK_VRP00 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_6_ADDR Gamma Control (6) */ +#define DISP_BITMASK_PKN12 DISP_BIT_IB10 +#define DISP_BITMASK_PKN11 DISP_BIT_IB08 +#define DISP_BITMASK_PKN10 DISP_BIT_IB09 +#define DISP_BITMASK_PKN02 DISP_BIT_IB02 +#define DISP_BITMASK_PKN01 DISP_BIT_IB01 +#define DISP_BITMASK_PKN00 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_7_ADDR Gamma Control (7) */ +#define DISP_BITMASK_PKN32 DISP_BIT_IB10 +#define DISP_BITMASK_PKN31 DISP_BIT_IB08 +#define DISP_BITMASK_PKN30 DISP_BIT_IB09 +#define DISP_BITMASK_PKN22 DISP_BIT_IB02 +#define DISP_BITMASK_PKN21 DISP_BIT_IB01 +#define DISP_BITMASK_PKN20 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_8_ADDR Gamma Control (8) */ +#define DISP_BITMASK_PKN52 DISP_BIT_IB10 +#define DISP_BITMASK_PKN51 DISP_BIT_IB08 +#define DISP_BITMASK_PKN50 DISP_BIT_IB09 +#define DISP_BITMASK_PKN42 DISP_BIT_IB02 +#define DISP_BITMASK_PKN41 DISP_BIT_IB01 +#define DISP_BITMASK_PKN40 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_9_ADDR Gamma Control (9) */ +#define DISP_BITMASK_PRN12 DISP_BIT_IB10 +#define DISP_BITMASK_PRN11 DISP_BIT_IB08 +#define DISP_BITMASK_PRN10 DISP_BIT_IB09 +#define DISP_BITMASK_PRN02 DISP_BIT_IB02 +#define DISP_BITMASK_PRN01 DISP_BIT_IB01 +#define DISP_BITMASK_PRN00 DISP_BIT_IB00 +/* DISP_GAMMA_CONTROL_10_ADDR Gamma Control (10) */ +#define DISP_BITMASK_VRN14 DISP_BIT_IB12 +#define DISP_BITMASK_VRN13 DISP_BIT_IB11 +#define DISP_BITMASK_VRN12 DISP_BIT_IB10 +#define DISP_BITMASK_VRN11 DISP_BIT_IB08 +#define DISP_BITMASK_VRN10 DISP_BIT_IB09 +#define DISP_BITMASK_VRN03 DISP_BIT_IB03 +#define DISP_BITMASK_VRN02 DISP_BIT_IB02 +#define DISP_BITMASK_VRN01 DISP_BIT_IB01 +#define DISP_BITMASK_VRN00 DISP_BIT_IB00 +/* Coordinate Control */ +/* DISP_VERT_SCROLL_CTL_1_ADDR Vertical Scroll Control (1) */ +#define DISP_BITMASK_VL18 DISP_BIT_IB08 +#define DISP_BITMASK_VL17 DISP_BIT_IB07 +#define DISP_BITMASK_VL16 DISP_BIT_IB06 +#define DISP_BITMASK_VL15 DISP_BIT_IB05 +#define DISP_BITMASK_VL14 DISP_BIT_IB04 +#define DISP_BITMASK_VL13 DISP_BIT_IB03 +#define DISP_BITMASK_VL12 DISP_BIT_IB02 +#define DISP_BITMASK_VL11 DISP_BIT_IB01 +#define DISP_BITMASK_VL10 DISP_BIT_IB00 +/* DISP_VERT_SCROLL_CTL_2_ADDR Vertical Scroll Control (2) */ +#define DISP_BITMASK_VL28 DISP_BIT_IB08 +#define DISP_BITMASK_VL27 DISP_BIT_IB07 +#define DISP_BITMASK_VL26 DISP_BIT_IB06 +#define DISP_BITMASK_VL25 DISP_BIT_IB05 +#define DISP_BITMASK_VL24 DISP_BIT_IB04 +#define DISP_BITMASK_VL23 DISP_BIT_IB03 +#define DISP_BITMASK_VL22 DISP_BIT_IB02 +#define DISP_BITMASK_VL21 DISP_BIT_IB01 +#define DISP_BITMASK_VL20 DISP_BIT_IB00 +/* DISP_SCREEN_1_DRV_POS_1_ADDR First Screen Driving Position (1) */ +#define DISP_BITMASK_SS18 DISP_BIT_IB08 +#define DISP_BITMASK_SS17 DISP_BIT_IB07 +#define DISP_BITMASK_SS16 DISP_BIT_IB06 +#define DISP_BITMASK_SS15 DISP_BIT_IB05 +#define DISP_BITMASK_SS14 DISP_BIT_IB04 +#define DISP_BITMASK_SS13 DISP_BIT_IB03 +#define DISP_BITMASK_SS12 DISP_BIT_IB02 +#define DISP_BITMASK_SS11 DISP_BIT_IB01 +#define DISP_BITMASK_SS10 DISP_BIT_IB00 +/* DISP_SCREEN_1_DRV_POS_2_ADDR First Screen Driving Position (2) */ +#define DISP_BITMASK_SE18 DISP_BIT_IB08 +#define DISP_BITMASK_SE17 DISP_BIT_IB07 +#define DISP_BITMASK_SE16 DISP_BIT_IB06 +#define DISP_BITMASK_SE15 DISP_BIT_IB05 +#define DISP_BITMASK_SE14 DISP_BIT_IB04 +#define DISP_BITMASK_SE13 DISP_BIT_IB03 +#define DISP_BITMASK_SE12 DISP_BIT_IB02 +#define DISP_BITMASK_SE11 DISP_BIT_IB01 +#define DISP_BITMASK_SE10 DISP_BIT_IB00 +/* DISP_SCREEN_2_DRV_POS_1_ADDR Second Screen Driving Position (1) */ +#define DISP_BITMASK_SS28 DISP_BIT_IB08 +#define DISP_BITMASK_SS27 DISP_BIT_IB07 +#define DISP_BITMASK_SS26 DISP_BIT_IB06 +#define DISP_BITMASK_SS25 DISP_BIT_IB05 +#define DISP_BITMASK_SS24 DISP_BIT_IB04 +#define DISP_BITMASK_SS23 DISP_BIT_IB03 +#define DISP_BITMASK_SS22 DISP_BIT_IB02 +#define DISP_BITMASK_SS21 DISP_BIT_IB01 +#define DISP_BITMASK_SS20 DISP_BIT_IB00 +/* DISP_SCREEN_3_DRV_POS_2_ADDR Second Screen Driving Position (2) */ +#define DISP_BITMASK_SE28 DISP_BIT_IB08 +#define DISP_BITMASK_SE27 DISP_BIT_IB07 +#define DISP_BITMASK_SE26 DISP_BIT_IB06 +#define DISP_BITMASK_SE25 DISP_BIT_IB05 +#define DISP_BITMASK_SE24 DISP_BIT_IB04 +#define DISP_BITMASK_SE23 DISP_BIT_IB03 +#define DISP_BITMASK_SE22 DISP_BIT_IB02 +#define DISP_BITMASK_SE21 DISP_BIT_IB01 +#define DISP_BITMASK_SE20 DISP_BIT_IB00 +/* DISP_HORZ_RAM_ADDR_POS_1_ADDR Horizontal RAM Address Position (1) */ +#define DISP_BITMASK_HSA7 DISP_BIT_IB07 +#define DISP_BITMASK_HSA6 DISP_BIT_IB06 +#define DISP_BITMASK_HSA5 DISP_BIT_IB05 +#define DISP_BITMASK_HSA4 DISP_BIT_IB04 +#define DISP_BITMASK_HSA3 DISP_BIT_IB03 +#define DISP_BITMASK_HSA2 DISP_BIT_IB02 +#define DISP_BITMASK_HSA1 DISP_BIT_IB01 +#define DISP_BITMASK_HSA0 DISP_BIT_IB00 +/* DISP_HORZ_RAM_ADDR_POS_2_ADDR Horizontal RAM Address Position (2) */ +#define DISP_BITMASK_HEA7 DISP_BIT_IB07 +#define DISP_BITMASK_HEA6 DISP_BIT_IB06 +#define DISP_BITMASK_HEA5 DISP_BIT_IB05 +#define DISP_BITMASK_HEA4 DISP_BIT_IB04 +#define DISP_BITMASK_HEA3 DISP_BIT_IB03 +#define DISP_BITMASK_HEA2 DISP_BIT_IB02 +#define DISP_BITMASK_HEA1 DISP_BIT_IB01 +#define DISP_BITMASK_HEA0 DISP_BIT_IB00 +/* DISP_VERT_RAM_ADDR_POS_1_ADDR Vertical RAM Address Position (1) */ +#define DISP_BITMASK_VSA8 DISP_BIT_IB08 +#define DISP_BITMASK_VSA7 DISP_BIT_IB07 +#define DISP_BITMASK_VSA6 DISP_BIT_IB06 +#define DISP_BITMASK_VSA5 DISP_BIT_IB05 +#define DISP_BITMASK_VSA4 DISP_BIT_IB04 +#define DISP_BITMASK_VSA3 DISP_BIT_IB03 +#define DISP_BITMASK_VSA2 DISP_BIT_IB02 +#define DISP_BITMASK_VSA1 DISP_BIT_IB01 +#define DISP_BITMASK_VSA0 DISP_BIT_IB00 +/* DISP_VERT_RAM_ADDR_POS_2_ADDR Vertical RAM Address Position (2) */ +#define DISP_BITMASK_VEA8 DISP_BIT_IB08 +#define DISP_BITMASK_VEA7 DISP_BIT_IB07 +#define DISP_BITMASK_VEA6 DISP_BIT_IB06 +#define DISP_BITMASK_VEA5 DISP_BIT_IB05 +#define DISP_BITMASK_VEA4 DISP_BIT_IB04 +#define DISP_BITMASK_VEA3 DISP_BIT_IB03 +#define DISP_BITMASK_VEA2 DISP_BIT_IB02 +#define DISP_BITMASK_VEA1 DISP_BIT_IB01 +#define DISP_BITMASK_VEA0 DISP_BIT_IB00 +static word disp_area_start_row; +static word disp_area_end_row; +static boolean disp_initialized = FALSE; +/* For some reason the contrast set at init time is not good. Need to do +* it again +*/ +static boolean display_on = FALSE; + +static uint32 tmd20qvga_lcd_rev; +uint16 tmd20qvga_panel_offset; + +#ifdef DISP_DEVICE_8BPP +static word convert_8_to_16_tbl[256] = { + 0x0000, 0x2000, 0x4000, 0x6000, 0x8000, 0xA000, 0xC000, 0xE000, + 0x0100, 0x2100, 0x4100, 0x6100, 0x8100, 0xA100, 0xC100, 0xE100, + 0x0200, 0x2200, 0x4200, 0x6200, 0x8200, 0xA200, 0xC200, 0xE200, + 0x0300, 0x2300, 0x4300, 0x6300, 0x8300, 0xA300, 0xC300, 0xE300, + 0x0400, 0x2400, 0x4400, 0x6400, 0x8400, 0xA400, 0xC400, 0xE400, + 0x0500, 0x2500, 0x4500, 0x6500, 0x8500, 0xA500, 0xC500, 0xE500, + 0x0600, 0x2600, 0x4600, 0x6600, 0x8600, 0xA600, 0xC600, 0xE600, + 0x0700, 0x2700, 0x4700, 0x6700, 0x8700, 0xA700, 0xC700, 0xE700, + 0x0008, 0x2008, 0x4008, 0x6008, 0x8008, 0xA008, 0xC008, 0xE008, + 0x0108, 0x2108, 0x4108, 0x6108, 0x8108, 0xA108, 0xC108, 0xE108, + 0x0208, 0x2208, 0x4208, 0x6208, 0x8208, 0xA208, 0xC208, 0xE208, + 0x0308, 0x2308, 0x4308, 0x6308, 0x8308, 0xA308, 0xC308, 0xE308, + 0x0408, 0x2408, 0x4408, 0x6408, 0x8408, 0xA408, 0xC408, 0xE408, + 0x0508, 0x2508, 0x4508, 0x6508, 0x8508, 0xA508, 0xC508, 0xE508, + 0x0608, 0x2608, 0x4608, 0x6608, 0x8608, 0xA608, 0xC608, 0xE608, + 0x0708, 0x2708, 0x4708, 0x6708, 0x8708, 0xA708, 0xC708, 0xE708, + 0x0010, 0x2010, 0x4010, 0x6010, 0x8010, 0xA010, 0xC010, 0xE010, + 0x0110, 0x2110, 0x4110, 0x6110, 0x8110, 0xA110, 0xC110, 0xE110, + 0x0210, 0x2210, 0x4210, 0x6210, 0x8210, 0xA210, 0xC210, 0xE210, + 0x0310, 0x2310, 0x4310, 0x6310, 0x8310, 0xA310, 0xC310, 0xE310, + 0x0410, 0x2410, 0x4410, 0x6410, 0x8410, 0xA410, 0xC410, 0xE410, + 0x0510, 0x2510, 0x4510, 0x6510, 0x8510, 0xA510, 0xC510, 0xE510, + 0x0610, 0x2610, 0x4610, 0x6610, 0x8610, 0xA610, 0xC610, 0xE610, + 0x0710, 0x2710, 0x4710, 0x6710, 0x8710, 0xA710, 0xC710, 0xE710, + 0x0018, 0x2018, 0x4018, 0x6018, 0x8018, 0xA018, 0xC018, 0xE018, + 0x0118, 0x2118, 0x4118, 0x6118, 0x8118, 0xA118, 0xC118, 0xE118, + 0x0218, 0x2218, 0x4218, 0x6218, 0x8218, 0xA218, 0xC218, 0xE218, + 0x0318, 0x2318, 0x4318, 0x6318, 0x8318, 0xA318, 0xC318, 0xE318, + 0x0418, 0x2418, 0x4418, 0x6418, 0x8418, 0xA418, 0xC418, 0xE418, + 0x0518, 0x2518, 0x4518, 0x6518, 0x8518, 0xA518, 0xC518, 0xE518, + 0x0618, 0x2618, 0x4618, 0x6618, 0x8618, 0xA618, 0xC618, 0xE618, + 0x0718, 0x2718, 0x4718, 0x6718, 0x8718, 0xA718, 0xC718, 0xE718 +}; +#endif /* DISP_DEVICE_8BPP */ + +static void tmd20qvga_disp_set_rect(int x, int y, int xres, int yres); +static void tmd20qvga_disp_init(struct platform_device *pdev); +static void tmd20qvga_disp_set_contrast(void); +static void tmd20qvga_disp_set_display_area(word start_row, word end_row); +static int tmd20qvga_disp_off(struct platform_device *pdev); +static int tmd20qvga_disp_on(struct platform_device *pdev); +static void tmd20qvga_set_revId(int); + +/* future use */ +void tmd20qvga_disp_clear_screen_area(word start_row, word end_row, + word start_column, word end_column); + +static void tmd20qvga_set_revId(int id) +{ + + tmd20qvga_lcd_rev = id; + + if (tmd20qvga_lcd_rev == 1) + tmd20qvga_panel_offset = 0x10; + else + tmd20qvga_panel_offset = 0; +} + +static void tmd20qvga_disp_init(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + if (disp_initialized) + return; + + mfd = platform_get_drvdata(pdev); + + DISP_CMD_PORT = mfd->cmd_port; + DISP_DATA_PORT = mfd->data_port; + +#ifdef TMD20QVGA_LCD_18BPP + tmd20qvga_set_revId(2); +#else + tmd20qvga_set_revId(1); +#endif + + disp_initialized = TRUE; + tmd20qvga_disp_set_contrast(); + tmd20qvga_disp_set_display_area(0, QVGA_HEIGHT - 1); +} + +static void tmd20qvga_disp_set_rect(int x, int y, int xres, int yres) +{ + if (!disp_initialized) + return; + + DISP_SET_RECT(y, y + yres - 1, x, x + xres - 1); + + DISP_CMD_OUT(DISP_CMD_RAMWR); +} + +static void tmd20qvga_disp_set_display_area(word start_row, word end_row) +{ + word start_driving = start_row; + word end_driving = end_row; + + if (!disp_initialized) + return; + + /* Range checking + */ + if (end_driving >= QVGA_HEIGHT) + end_driving = QVGA_HEIGHT - 1; + if (start_driving > end_driving) { + /* Probably Backwards Switch */ + start_driving = end_driving; + end_driving = start_row; /* Has not changed */ + if (end_driving >= QVGA_HEIGHT) + end_driving = QVGA_HEIGHT - 1; + } + + if ((start_driving == disp_area_start_row) + && (end_driving == disp_area_end_row)) + return; + + disp_area_start_row = start_driving; + disp_area_end_row = end_driving; + + DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_1_ADDR, + DISP_VAL_IF(start_driving & 0x100, + DISP_BITMASK_SS18) | + DISP_VAL_IF(start_driving & 0x080, + DISP_BITMASK_SS17) | + DISP_VAL_IF(start_driving & 0x040, + DISP_BITMASK_SS16) | + DISP_VAL_IF(start_driving & 0x020, + DISP_BITMASK_SS15) | + DISP_VAL_IF(start_driving & 0x010, + DISP_BITMASK_SS14) | + DISP_VAL_IF(start_driving & 0x008, + DISP_BITMASK_SS13) | + DISP_VAL_IF(start_driving & 0x004, + DISP_BITMASK_SS12) | + DISP_VAL_IF(start_driving & 0x002, + DISP_BITMASK_SS11) | + DISP_VAL_IF(start_driving & 0x001, DISP_BITMASK_SS10)); + + DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_2_ADDR, + DISP_VAL_IF(end_driving & 0x100, DISP_BITMASK_SE18) | + DISP_VAL_IF(end_driving & 0x080, DISP_BITMASK_SE17) | + DISP_VAL_IF(end_driving & 0x040, DISP_BITMASK_SE16) | + DISP_VAL_IF(end_driving & 0x020, DISP_BITMASK_SE15) | + DISP_VAL_IF(end_driving & 0x010, DISP_BITMASK_SE14) | + DISP_VAL_IF(end_driving & 0x008, DISP_BITMASK_SE13) | + DISP_VAL_IF(end_driving & 0x004, DISP_BITMASK_SE12) | + DISP_VAL_IF(end_driving & 0x002, DISP_BITMASK_SE11) | + DISP_VAL_IF(end_driving & 0x001, DISP_BITMASK_SE10)); +} + +static int tmd20qvga_disp_off(struct platform_device *pdev) +{ + if (!disp_initialized) + tmd20qvga_disp_init(pdev); + + if (display_on) { + if (tmd20qvga_lcd_rev == 2) { + DISP_WRITE_OUT(DISP_POFF_LN_SETTING_ADDR, 0x000A); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xFFEE); + WAIT_MSEC(40); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xF812); + WAIT_MSEC(40); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xE811); + WAIT_MSEC(40); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xC011); + WAIT_MSEC(40); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x4011); + WAIT_MSEC(20); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0010); + + } else { + DISP_WRITE_OUT(DISP_POFF_LN_SETTING_ADDR, 0x000F); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0BFE); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100); + WAIT_MSEC(40); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0BED); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100); + WAIT_MSEC(40); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x00CD); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100); + WAIT_MSEC(20); + DISP_WRITE_OUT(DISP_START_OSCILLATION_ADDR, 0x0); + } + + DISP_WRITE_OUT(DISP_MODE_SETTING_ADDR, 0x0004); + DISP_WRITE_OUT(DISP_MODE_SETTING_ADDR, 0x0000); + + display_on = FALSE; + } + + return 0; +} + +static int tmd20qvga_disp_on(struct platform_device *pdev) +{ + if (!disp_initialized) + tmd20qvga_disp_init(pdev); + + if (!display_on) { + /* Deep Stand-by -> Stand-by */ + DISP_CMD_OUT(DISP_START_OSCILLATION_ADDR); + WAIT_MSEC(1); + DISP_CMD_OUT(DISP_START_OSCILLATION_ADDR); + WAIT_MSEC(1); + DISP_CMD_OUT(DISP_START_OSCILLATION_ADDR); + WAIT_MSEC(1); + + /* OFF -> Deep Stan-By -> Stand-by */ + /* let's change the state from "Stand-by" to "Sleep" */ + DISP_WRITE_OUT(DISP_MODE_SETTING_ADDR, 0x0005); + WAIT_MSEC(1); + + /* Sleep -> Displaying */ + DISP_WRITE_OUT(DISP_START_OSCILLATION_ADDR, 0x0001); + DISP_WRITE_OUT(DISP_DRIVER_OUTPUT_CTL_ADDR, 0x0127); + DISP_WRITE_OUT(DISP_LCD_DRIVING_SIG_ADDR, 0x200); + /* fast write mode */ + DISP_WRITE_OUT(DISP_ENTRY_MODE_ADDR, 0x0130); + if (tmd20qvga_lcd_rev == 2) + DISP_WRITE_OUT(DISP_TMD_700_ADDR, 0x0003); + /* back porch = 14 + front porch = 2 --> 16 lines */ + if (tmd20qvga_lcd_rev == 2) { +#ifdef TMD20QVGA_LCD_18BPP + /* 256k color */ + DISP_WRITE_OUT(DISP_DISPLAY_CTL_1_ADDR, 0x0000); +#else + /* 65k color */ + DISP_WRITE_OUT(DISP_DISPLAY_CTL_1_ADDR, 0x4000); +#endif + DISP_WRITE_OUT(DISP_DISPLAY_CTL_2_ADDR, 0x0302); + } else { +#ifdef TMD20QVGA_LCD_18BPP + /* 256k color */ + DISP_WRITE_OUT(DISP_DISPLAY_CTL_1_ADDR, 0x0004); +#else + /* 65k color */ + DISP_WRITE_OUT(DISP_DISPLAY_CTL_1_ADDR, 0x4004); +#endif + DISP_WRITE_OUT(DISP_DISPLAY_CTL_2_ADDR, 0x020E); + } + /* 16 bit one transfer */ + if (tmd20qvga_lcd_rev == 2) { + DISP_WRITE_OUT(DISP_EXT_DISPLAY_CTL_1_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_FRAME_CYCLE_CTL_ADDR, 0x0010); + DISP_WRITE_OUT(DISP_LTPS_CTL_1_ADDR, 0x0302); + DISP_WRITE_OUT(DISP_LTPS_CTL_2_ADDR, 0x0102); + DISP_WRITE_OUT(DISP_LTPS_CTL_3_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_TMD_015_ADDR, 0x2000); + + DISP_WRITE_OUT(DISP_AMP_SETTING_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_1_ADDR, 0x0403); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_2_ADDR, 0x0304); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_3_ADDR, 0x0403); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_4_ADDR, 0x0303); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_5_ADDR, 0x0101); + DISP_WRITE_OUT(DISP_TMD_305_ADDR, 0); + + DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_1_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_2_ADDR, 0x013F); + + DISP_WRITE_OUT(DISP_POWER_CTL_3_ADDR, 0x077D); + + DISP_WRITE_OUT(DISP_POWER_CTL_4_ADDR, 0x0005); + DISP_WRITE_OUT(DISP_POWER_CTL_5_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_POWER_CTL_6_ADDR, 0x0015); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xC010); + WAIT_MSEC(1); + + DISP_WRITE_OUT(DISP_POWER_CTL_2_ADDR, 0x0001); + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0xFFFE); + WAIT_MSEC(60); + } else { + DISP_WRITE_OUT(DISP_EXT_DISPLAY_CTL_1_ADDR, 0x0001); + DISP_WRITE_OUT(DISP_FRAME_CYCLE_CTL_ADDR, 0x0010); + DISP_WRITE_OUT(DISP_LTPS_CTL_1_ADDR, 0x0301); + DISP_WRITE_OUT(DISP_LTPS_CTL_2_ADDR, 0x0001); + DISP_WRITE_OUT(DISP_LTPS_CTL_3_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_AMP_SETTING_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_1_ADDR, 0x0507); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_2_ADDR, 0x0405); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_3_ADDR, 0x0607); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_4_ADDR, 0x0502); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_5_ADDR, 0x0301); + DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_1_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_SCREEN_1_DRV_POS_2_ADDR, 0x013F); + DISP_WRITE_OUT(DISP_POWER_CTL_3_ADDR, 0x0795); + + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0102); + WAIT_MSEC(1); + + DISP_WRITE_OUT(DISP_POWER_CTL_4_ADDR, 0x0450); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0103); + WAIT_MSEC(1); + + DISP_WRITE_OUT(DISP_POWER_CTL_5_ADDR, 0x0008); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0104); + WAIT_MSEC(1); + + DISP_WRITE_OUT(DISP_POWER_CTL_6_ADDR, 0x0C00); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0105); + WAIT_MSEC(1); + + DISP_WRITE_OUT(DISP_POWER_CTL_7_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0106); + WAIT_MSEC(1); + + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0801); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100); + WAIT_MSEC(1); + + DISP_WRITE_OUT(DISP_POWER_CTL_2_ADDR, 0x001F); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0101); + WAIT_MSEC(60); + + DISP_WRITE_OUT(DISP_POWER_CTL_2_ADDR, 0x009F); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0101); + WAIT_MSEC(10); + + DISP_WRITE_OUT(DISP_HORZ_RAM_ADDR_POS_1_ADDR, 0x0010); + DISP_WRITE_OUT(DISP_HORZ_RAM_ADDR_POS_2_ADDR, 0x00FF); + DISP_WRITE_OUT(DISP_VERT_RAM_ADDR_POS_1_ADDR, 0x0000); + DISP_WRITE_OUT(DISP_VERT_RAM_ADDR_POS_2_ADDR, 0x013F); + /* RAM starts at address 0x10 */ + DISP_WRITE_OUT(DISP_RAM_ADDR_SET_1_ADDR, 0x0010); + DISP_WRITE_OUT(DISP_RAM_ADDR_SET_2_ADDR, 0x0000); + + /* lcd controller uses internal clock, not ext. vsync */ + DISP_CMD_OUT(DISP_CMD_RAMWR); + + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0881); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100); + WAIT_MSEC(40); + + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0BE1); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100); + WAIT_MSEC(40); + + DISP_WRITE_OUT(DISP_POWER_CTL_1_ADDR, 0x0BFF); + DISP_WRITE_OUT(DISP_POWER_SUPPLY_INTF_ADDR, 0x0100); + } + display_on = TRUE; + } + + return 0; +} + +static void tmd20qvga_disp_set_contrast(void) +{ +#if (defined(TMD20QVGA_LCD_18BPP)) + + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_1_ADDR, 0x0403); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_2_ADDR, 0x0302); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_3_ADDR, 0x0403); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_4_ADDR, 0x0303); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_5_ADDR, 0x0F07); + +#else + int newcontrast = 0x46; + + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_1_ADDR, 0x0403); + + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_2_ADDR, + DISP_VAL_IF(newcontrast & 0x0001, DISP_BITMASK_PKP20) | + DISP_VAL_IF(newcontrast & 0x0002, DISP_BITMASK_PKP21) | + DISP_VAL_IF(newcontrast & 0x0004, DISP_BITMASK_PKP22) | + DISP_VAL_IF(newcontrast & 0x0010, DISP_BITMASK_PKP30) | + DISP_VAL_IF(newcontrast & 0x0020, DISP_BITMASK_PKP31) | + DISP_VAL_IF(newcontrast & 0x0040, DISP_BITMASK_PKP32)); + + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_3_ADDR, + DISP_VAL_IF(newcontrast & 0x0010, DISP_BITMASK_PKP40) | + DISP_VAL_IF(newcontrast & 0x0020, DISP_BITMASK_PKP41) | + DISP_VAL_IF(newcontrast & 0x0040, DISP_BITMASK_PKP42) | + DISP_VAL_IF(newcontrast & 0x0001, DISP_BITMASK_PKP50) | + DISP_VAL_IF(newcontrast & 0x0002, DISP_BITMASK_PKP51) | + DISP_VAL_IF(newcontrast & 0x0004, DISP_BITMASK_PKP52)); + + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_4_ADDR, 0x0303); + DISP_WRITE_OUT(DISP_GAMMA_CONTROL_5_ADDR, 0x0F07); + +#endif /* defined(TMD20QVGA_LCD_18BPP) */ + +} /* End disp_set_contrast */ + +void tmd20qvga_disp_clear_screen_area + (word start_row, word end_row, word start_column, word end_column) { + int32 i; + + /* Clear the display screen */ + DISP_SET_RECT(start_row, end_row, start_column, end_column); + DISP_CMD_OUT(DISP_CMD_RAMWR); + i = (end_row - start_row + 1) * (end_column - start_column + 1); + for (; i > 0; i--) + DISP_DATA_OUT_16TO18BPP(0x0); +} + +static int __init tmd20qvga_probe(struct platform_device *pdev) +{ + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = tmd20qvga_probe, + .driver = { + .name = "ebi2_tmd_qvga", + }, +}; + +static struct msm_fb_panel_data tmd20qvga_panel_data = { + .on = tmd20qvga_disp_on, + .off = tmd20qvga_disp_off, + .set_rect = tmd20qvga_disp_set_rect, +}; + +static struct platform_device this_device = { + .name = "ebi2_tmd_qvga", + .id = 0, + .dev = { + .platform_data = &tmd20qvga_panel_data, + } +}; + +static int __init tmd20qvga_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + ret = platform_driver_register(&this_driver); + if (!ret) { + pinfo = &tmd20qvga_panel_data.panel_info; + pinfo->xres = 240; + pinfo->yres = 320; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = EBI2_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0x808000; +#ifdef TMD20QVGA_LCD_18BPP + pinfo->bpp = 18; +#else + pinfo->bpp = 16; +#endif + pinfo->fb_num = 2; + pinfo->lcd.vsync_enable = TRUE; + pinfo->lcd.refx100 = 6000; + pinfo->lcd.v_back_porch = 16; + pinfo->lcd.v_front_porch = 4; + pinfo->lcd.v_pulse_width = 0; + pinfo->lcd.hw_vsync_mode = FALSE; + pinfo->lcd.vsync_notifier_period = 0; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + } + + return ret; +} + +module_init(tmd20qvga_init); + diff --git a/drivers/video/msm/external_common.c b/drivers/video/msm/external_common.c new file mode 100644 index 0000000000000000000000000000000000000000..21d73740681c9cc91fd27c23e9f8167ab7c48481 --- /dev/null +++ b/drivers/video/msm/external_common.c @@ -0,0 +1,2103 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +/* #define DEBUG */ +#define DEV_DBG_PREFIX "EXT_COMMON: " + +/* The start of the data block collection within the CEA Extension Version 3 */ +#define DBC_START_OFFSET 4 + +#include "msm_fb.h" +#include "hdmi_msm.h" +#include "external_common.h" +#include "mhl_api.h" + +#include "mdp.h" + +struct external_common_state_type *external_common_state; +EXPORT_SYMBOL(external_common_state); +DEFINE_MUTEX(external_common_state_hpd_mutex); +EXPORT_SYMBOL(external_common_state_hpd_mutex); + + +static int atoi(const char *name) +{ + int val = 0; + + for (;; name++) { + switch (*name) { + case '0' ... '9': + val = 10*val+(*name-'0'); + break; + default: + return val; + } + } +} + +#ifdef DEBUG_EDID +/* + * Block 0 - 1920x1080p, 1360x768p + * Block 1 - 1280x720p, 1920x540i, 720x480p + */ +const char edid_blk0[0x100] = { +0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x4C, 0x2D, 0x03, 0x05, 0x00, +0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, 0x0A, 0xEE, +0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, 0xBD, 0xEF, 0x80, 0x71, +0x4F, 0x81, 0x00, 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0x95, 0x0F, 0xB3, 0x00, +0xA9, 0x40, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45, +0x00, 0xA0, 0x5A, 0x00, 0x00, 0x00, 0x1E, 0x66, 0x21, 0x50, 0xB0, 0x51, 0x00, +0x1B, 0x30, 0x40, 0x70, 0x36, 0x00, 0xA0, 0x5A, 0x00, 0x00, 0x00, 0x1E, 0x00, +0x00, 0x00, 0xFD, 0x00, 0x18, 0x4B, 0x1A, 0x51, 0x17, 0x00, 0x0A, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x53, 0x41, 0x4D, 0x53, +0x55, 0x4E, 0x47, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x8F}; + +const char edid_blk1[0x100] = { +0x02, 0x03, 0x1E, 0xF1, 0x46, 0x90, 0x04, 0x05, 0x03, 0x20, 0x22, 0x23, 0x09, +0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0xE2, 0x00, 0x0F, 0x67, 0x03, 0x0C, 0x00, +0x10, 0x00, 0xB8, 0x2D, 0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20, 0x6E, +0x28, 0x55, 0x00, 0xA0, 0x5A, 0x00, 0x00, 0x00, 0x1E, 0x01, 0x1D, 0x80, 0x18, +0x71, 0x1C, 0x16, 0x20, 0x58, 0x2C, 0x25, 0x00, 0xA0, 0x5A, 0x00, 0x00, 0x00, +0x9E, 0x8C, 0x0A, 0xD0, 0x8A, 0x20, 0xE0, 0x2D, 0x10, 0x10, 0x3E, 0x96, 0x00, +0xA0, 0x5A, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF}; +#endif /* DEBUG_EDID */ + +#define DMA_E_BASE 0xB0000 +void mdp_vid_quant_set(void) +{ + if ((external_common_state->video_resolution == \ + HDMI_VFRMT_720x480p60_4_3) || \ + (external_common_state->video_resolution == \ + HDMI_VFRMT_720x480p60_16_9)) { + MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x70, 0x00EB0010); + MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x74, 0x00EB0010); + MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x78, 0x00EB0010); + } else { + MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x70, 0x00FF0000); + MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x74, 0x00FF0000); + MDP_OUTP(MDP_BASE + DMA_E_BASE + 0x78, 0x00FF0000); + } +} + +const char *video_format_2string(uint32 format) +{ + switch (format) { + default: +#ifdef CONFIG_FB_MSM_HDMI_COMMON + case HDMI_VFRMT_640x480p60_4_3: return " 640x 480 p60 4/3"; + case HDMI_VFRMT_720x480p60_4_3: return " 720x 480 p60 4/3"; + case HDMI_VFRMT_720x480p60_16_9: return " 720x 480 p60 16/9"; + case HDMI_VFRMT_1280x720p60_16_9: return "1280x 720 p60 16/9"; + case HDMI_VFRMT_1920x1080i60_16_9: return "1920x1080 i60 16/9"; + case HDMI_VFRMT_1440x480i60_4_3: return "1440x 480 i60 4/3"; + case HDMI_VFRMT_1440x480i60_16_9: return "1440x 480 i60 16/9"; + case HDMI_VFRMT_1440x240p60_4_3: return "1440x 240 p60 4/3"; + case HDMI_VFRMT_1440x240p60_16_9: return "1440x 240 p60 16/9"; + case HDMI_VFRMT_2880x480i60_4_3: return "2880x 480 i60 4/3"; + case HDMI_VFRMT_2880x480i60_16_9: return "2880x 480 i60 16/9"; + case HDMI_VFRMT_2880x240p60_4_3: return "2880x 240 p60 4/3"; + case HDMI_VFRMT_2880x240p60_16_9: return "2880x 240 p60 16/9"; + case HDMI_VFRMT_1440x480p60_4_3: return "1440x 480 p60 4/3"; + case HDMI_VFRMT_1440x480p60_16_9: return "1440x 480 p60 16/9"; + case HDMI_VFRMT_1920x1080p60_16_9: return "1920x1080 p60 16/9"; + case HDMI_VFRMT_720x576p50_4_3: return " 720x 576 p50 4/3"; + case HDMI_VFRMT_720x576p50_16_9: return " 720x 576 p50 16/9"; + case HDMI_VFRMT_1280x720p50_16_9: return "1280x 720 p50 16/9"; + case HDMI_VFRMT_1920x1080i50_16_9: return "1920x1080 i50 16/9"; + case HDMI_VFRMT_1440x576i50_4_3: return "1440x 576 i50 4/3"; + case HDMI_VFRMT_1440x576i50_16_9: return "1440x 576 i50 16/9"; + case HDMI_VFRMT_1440x288p50_4_3: return "1440x 288 p50 4/3"; + case HDMI_VFRMT_1440x288p50_16_9: return "1440x 288 p50 16/9"; + case HDMI_VFRMT_2880x576i50_4_3: return "2880x 576 i50 4/3"; + case HDMI_VFRMT_2880x576i50_16_9: return "2880x 576 i50 16/9"; + case HDMI_VFRMT_2880x288p50_4_3: return "2880x 288 p50 4/3"; + case HDMI_VFRMT_2880x288p50_16_9: return "2880x 288 p50 16/9"; + case HDMI_VFRMT_1440x576p50_4_3: return "1440x 576 p50 4/3"; + case HDMI_VFRMT_1440x576p50_16_9: return "1440x 576 p50 16/9"; + case HDMI_VFRMT_1920x1080p50_16_9: return "1920x1080 p50 16/9"; + case HDMI_VFRMT_1920x1080p24_16_9: return "1920x1080 p24 16/9"; + case HDMI_VFRMT_1920x1080p25_16_9: return "1920x1080 p25 16/9"; + case HDMI_VFRMT_1920x1080p30_16_9: return "1920x1080 p30 16/9"; + case HDMI_VFRMT_2880x480p60_4_3: return "2880x 480 p60 4/3"; + case HDMI_VFRMT_2880x480p60_16_9: return "2880x 480 p60 16/9"; + case HDMI_VFRMT_2880x576p50_4_3: return "2880x 576 p50 4/3"; + case HDMI_VFRMT_2880x576p50_16_9: return "2880x 576 p50 16/9"; + case HDMI_VFRMT_1920x1250i50_16_9: return "1920x1250 i50 16/9"; + case HDMI_VFRMT_1920x1080i100_16_9:return "1920x1080 i100 16/9"; + case HDMI_VFRMT_1280x720p100_16_9: return "1280x 720 p100 16/9"; + case HDMI_VFRMT_720x576p100_4_3: return " 720x 576 p100 4/3"; + case HDMI_VFRMT_720x576p100_16_9: return " 720x 576 p100 16/9"; + case HDMI_VFRMT_1440x576i100_4_3: return "1440x 576 i100 4/3"; + case HDMI_VFRMT_1440x576i100_16_9: return "1440x 576 i100 16/9"; + case HDMI_VFRMT_1920x1080i120_16_9:return "1920x1080 i120 16/9"; + case HDMI_VFRMT_1280x720p120_16_9: return "1280x 720 p120 16/9"; + case HDMI_VFRMT_720x480p120_4_3: return " 720x 480 p120 4/3"; + case HDMI_VFRMT_720x480p120_16_9: return " 720x 480 p120 16/9"; + case HDMI_VFRMT_1440x480i120_4_3: return "1440x 480 i120 4/3"; + case HDMI_VFRMT_1440x480i120_16_9: return "1440x 480 i120 16/9"; + case HDMI_VFRMT_720x576p200_4_3: return " 720x 576 p200 4/3"; + case HDMI_VFRMT_720x576p200_16_9: return " 720x 576 p200 16/9"; + case HDMI_VFRMT_1440x576i200_4_3: return "1440x 576 i200 4/3"; + case HDMI_VFRMT_1440x576i200_16_9: return "1440x 576 i200 16/9"; + case HDMI_VFRMT_720x480p240_4_3: return " 720x 480 p240 4/3"; + case HDMI_VFRMT_720x480p240_16_9: return " 720x 480 p240 16/9"; + case HDMI_VFRMT_1440x480i240_4_3: return "1440x 480 i240 4/3"; + case HDMI_VFRMT_1440x480i240_16_9: return "1440x 480 i240 16/9"; +#elif defined(CONFIG_FB_MSM_TVOUT) + case TVOUT_VFRMT_NTSC_M_720x480i: return "NTSC_M_720x480i"; + case TVOUT_VFRMT_NTSC_J_720x480i: return "NTSC_J_720x480i"; + case TVOUT_VFRMT_PAL_BDGHIN_720x576i: return "PAL_BDGHIN_720x576i"; + case TVOUT_VFRMT_PAL_M_720x480i: return "PAL_M_720x480i"; + case TVOUT_VFRMT_PAL_N_720x480i: return "PAL_N_720x480i"; +#endif + + } +} +EXPORT_SYMBOL(video_format_2string); + +static ssize_t external_common_rda_video_mode_str(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%s\n", + video_format_2string(external_common_state->video_resolution)); + DEV_DBG("%s: '%s'\n", __func__, + video_format_2string(external_common_state->video_resolution)); + return ret; +} + +#ifdef CONFIG_FB_MSM_HDMI_COMMON +struct hdmi_disp_mode_timing_type + hdmi_common_supported_video_mode_lut[HDMI_VFRMT_MAX] = { + HDMI_SETTINGS_640x480p60_4_3, + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x240p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x240p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480i60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480i60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x240p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x240p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x288p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x288p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576i50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576i50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x288p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x288p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p24_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p25_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p30_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1250i50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i100_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p100_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p100_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p100_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i100_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i100_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i120_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p120_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p120_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p120_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i120_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i120_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p200_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p200_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i200_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i200_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_16_9), +}; +EXPORT_SYMBOL(hdmi_common_supported_video_mode_lut); + +struct hdmi_disp_mode_timing_type + hdmi_mhl_supported_video_mode_lut[HDMI_VFRMT_MAX] = { + HDMI_SETTINGS_640x480p60_4_3, + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p60_16_9), + HDMI_SETTINGS_1280x720p60_16_9, + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x240p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x240p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480i60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480i60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x240p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x240p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x288p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x288p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576i50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576i50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x288p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x288p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080p50_16_9), + HDMI_SETTINGS_1920x1080p24_16_9, + HDMI_SETTINGS_1920x1080p25_16_9, + HDMI_SETTINGS_1920x1080p30_16_9, + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480p60_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x480p60_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576p50_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_2880x576p50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1250i50_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i100_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p100_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p100_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p100_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i100_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i100_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1920x1080i120_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x720p120_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p120_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p120_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i120_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i120_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p200_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x576p200_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i200_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x576i200_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_4_3), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_16_9), +}; +EXPORT_SYMBOL(hdmi_mhl_supported_video_mode_lut); + +static ssize_t hdmi_common_rda_edid_modes(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + int i; + + buf[0] = 0; + if (external_common_state->disp_mode_list.num_of_elements) { + uint32 *video_mode = external_common_state->disp_mode_list + .disp_mode_list; + for (i = 0; i < external_common_state->disp_mode_list + .num_of_elements; ++i) { + if (ret > 0) + ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%d", + *video_mode++ + 1); + else + ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d", + *video_mode++ + 1); + } + } else + ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d", + external_common_state->video_resolution+1); + + DEV_DBG("%s: '%s'\n", __func__, buf); + ret += snprintf(buf+ret, PAGE_SIZE-ret, "\n"); + return ret; +} + +static ssize_t hdmi_common_rda_edid_physical_address(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->physical_address); + + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->physical_address); + return ret; +} + + +static ssize_t hdmi_common_rda_edid_scan_info(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d, %d, %d\n", + external_common_state->pt_scan_info, + external_common_state->it_scan_info, + external_common_state->ce_scan_info); + DEV_DBG("%s: '%s'\n", __func__, buf); + return ret; +} + +static ssize_t hdmi_common_wta_vendor_name(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint8 *s = (uint8 *) buf; + uint8 *d = external_common_state->spd_vendor_name; + ssize_t ret = strnlen(buf, PAGE_SIZE); + ret = (ret > 8) ? 8 : ret; + + memset(external_common_state->spd_vendor_name, 0, 8); + while (*s) { + if (*s & 0x60 && *s ^ 0x7f) { + *d = *s; + } else { + /* stop copying if control character found */ + break; + } + + if (++s > (uint8 *) (buf + ret)) + break; + + d++; + } + + DEV_DBG("%s: '%s'\n", __func__, + external_common_state->spd_vendor_name); + + return ret; +} + +static ssize_t hdmi_common_rda_vendor_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%s\n", + external_common_state->spd_vendor_name); + DEV_DBG("%s: '%s'\n", __func__, + external_common_state->spd_vendor_name); + + return ret; +} + +static ssize_t hdmi_common_wta_product_description(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint8 *s = (uint8 *) buf; + uint8 *d = external_common_state->spd_product_description; + ssize_t ret = strnlen(buf, PAGE_SIZE); + ret = (ret > 16) ? 16 : ret; + + memset(external_common_state->spd_product_description, 0, 16); + while (*s) { + if (*s & 0x60 && *s ^ 0x7f) { + *d = *s; + } else { + /* stop copying if control character found */ + break; + } + + if (++s > (uint8 *) (buf + ret)) + break; + + d++; + } + + DEV_DBG("%s: '%s'\n", __func__, + external_common_state->spd_product_description); + + return ret; +} + +static ssize_t hdmi_common_rda_product_description(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%s\n", + external_common_state->spd_product_description); + DEV_DBG("%s: '%s'\n", __func__, + external_common_state->spd_product_description); + + return ret; +} + +static ssize_t hdmi_common_rda_edid_3d_modes(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + int i; + char buff_3d[128]; + + buf[0] = 0; + if (external_common_state->disp_mode_list.num_of_elements) { + uint32 *video_mode = external_common_state->disp_mode_list + .disp_mode_list; + uint32 *video_3d_mode = external_common_state->disp_mode_list + .disp_3d_mode_list; + for (i = 0; i < external_common_state->disp_mode_list + .num_of_elements; ++i) { + video_3d_format_2string(*video_3d_mode++, buff_3d); + if (ret > 0) + ret += snprintf(buf+ret, PAGE_SIZE-ret, + ",%d=%s", + *video_mode++ + 1, buff_3d); + else + ret += snprintf(buf+ret, PAGE_SIZE-ret, + "%d=%s", + *video_mode++ + 1, buff_3d); + } + } else + ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d", + external_common_state->video_resolution+1); + + DEV_DBG("%s: '%s'\n", __func__, buf); + ret += snprintf(buf+ret, PAGE_SIZE-ret, "\n"); + return ret; +} + +static ssize_t hdmi_common_rda_hdcp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->hdcp_active); + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->hdcp_active); + return ret; +} + +static ssize_t hdmi_common_rda_hpd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + if (external_common_state->hpd_feature) { + ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->hpd_feature_on); + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->hpd_feature_on); + } else { + ret = snprintf(buf, PAGE_SIZE, "-1\n"); + DEV_DBG("%s: 'not supported'\n", __func__); + } + return ret; +} + +static ssize_t hdmi_common_wta_hpd(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + int hpd; + if (hdmi_prim_display) + hpd = 1; + else + hpd = atoi(buf); + + if (external_common_state->hpd_feature) { + if (hpd == 0 && external_common_state->hpd_feature_on) { + external_common_state->hpd_feature(0); + external_common_state->hpd_feature_on = 0; + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->hpd_feature_on); + } else if (hpd == 1 && !external_common_state->hpd_feature_on) { + external_common_state->hpd_feature(1); + external_common_state->hpd_feature_on = 1; + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->hpd_feature_on); + } else { + DEV_DBG("%s: '%d' (unchanged)\n", __func__, + external_common_state->hpd_feature_on); + } + } else { + DEV_DBG("%s: 'not supported'\n", __func__); + } + + return ret; +} + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT +/* + * This interface for CEC feature is defined to suit + * the current requirements. However, the actual functionality is + * added to accommodate different interfaces + */ +static ssize_t hdmi_msm_rda_cec(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* 0x028C CEC_CTRL */ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n", + (HDMI_INP(0x028C) & BIT(0))); + return ret; +} + +static ssize_t hdmi_msm_wta_cec(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + int cec = atoi(buf); + + if (cec != 0) { + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->cec_enabled = true; + hdmi_msm_state->cec_logical_addr = 4; + + /* flush CEC queue */ + hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start; + hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start; + hdmi_msm_state->cec_queue_full = false; + memset(hdmi_msm_state->cec_queue_rd, 0, + sizeof(struct hdmi_msm_cec_msg)*CEC_QUEUE_SIZE); + + mutex_unlock(&hdmi_msm_state_mutex); + hdmi_msm_cec_init(); + hdmi_msm_cec_write_logical_addr( + hdmi_msm_state->cec_logical_addr); + DEV_DBG("CEC enabled\n"); + } else { + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->cec_enabled = false; + hdmi_msm_state->cec_logical_addr = 15; + mutex_unlock(&hdmi_msm_state_mutex); + hdmi_msm_cec_write_logical_addr( + hdmi_msm_state->cec_logical_addr); + /* 0x028C CEC_CTRL */ + HDMI_OUTP(0x028C, 0); + DEV_DBG("CEC disabled\n"); + } + return ret; +} + +static ssize_t hdmi_msm_rda_cec_logical_addr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + mutex_lock(&hdmi_msm_state_mutex); + ret = snprintf(buf, PAGE_SIZE, "%d\n", + hdmi_msm_state->cec_logical_addr); + mutex_unlock(&hdmi_msm_state_mutex); + return ret; +} + +static ssize_t hdmi_msm_wta_cec_logical_addr(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + +#ifdef DRVR_ONLY_CECT_NO_DAEMON + /* + * Only for testing + */ + hdmi_msm_cec_one_touch_play(); + return 0; +#else + ssize_t ret = strnlen(buf, PAGE_SIZE); + int logical_addr = atoi(buf); + + if (logical_addr < 0 || logical_addr > 15) + return -EINVAL; + + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->cec_logical_addr = logical_addr; + mutex_unlock(&hdmi_msm_state_mutex); + + hdmi_msm_cec_write_logical_addr(logical_addr); + + return ret; +#endif +} + +static ssize_t hdmi_msm_rda_cec_frame(struct device *dev, + struct device_attribute *attr, char *buf) +{ + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->cec_queue_rd == hdmi_msm_state->cec_queue_wr + && !hdmi_msm_state->cec_queue_full) { + mutex_unlock(&hdmi_msm_state_mutex); + DEV_ERR("CEC message queue is empty\n"); + return -EBUSY; + } + memcpy(buf, hdmi_msm_state->cec_queue_rd++, + sizeof(struct hdmi_msm_cec_msg)); + hdmi_msm_state->cec_queue_full = false; + if (hdmi_msm_state->cec_queue_rd == CEC_QUEUE_END) + hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start; + mutex_unlock(&hdmi_msm_state_mutex); + + return sizeof(struct hdmi_msm_cec_msg); +} + +static ssize_t hdmi_msm_wta_cec_frame(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int i; + int retry = ((struct hdmi_msm_cec_msg *) buf)->retransmit; + + for (i = 0; i < RETRANSMIT_MAX_NUM; i++) { + hdmi_msm_cec_msg_send((struct hdmi_msm_cec_msg *) buf); + if (hdmi_msm_state->cec_frame_wr_status + & CEC_STATUS_WR_ERROR && retry--) { + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->fsm_reset_done) + retry++; + mutex_unlock(&hdmi_msm_state_mutex); + msleep(20); + } else + break; + } + + if (hdmi_msm_state->cec_frame_wr_status & CEC_STATUS_WR_DONE) + return sizeof(struct hdmi_msm_cec_msg); + else + return -EINVAL; +} +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + +static ssize_t hdmi_common_rda_3d_present(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->present_3d); + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->present_3d); + return ret; +} + +static ssize_t hdmi_common_rda_hdcp_present(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->present_hdcp); + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->present_hdcp); + return ret; +} +#endif + +#ifdef CONFIG_FB_MSM_HDMI_3D +static ssize_t hdmi_3d_rda_format_3d(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->format_3d); + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->format_3d); + return ret; +} + +static ssize_t hdmi_3d_wta_format_3d(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + int format_3d = atoi(buf); + + if (format_3d >= 0 && format_3d <= 2) { + if (format_3d != external_common_state->format_3d) { + external_common_state->format_3d = format_3d; + if (external_common_state->switch_3d) + external_common_state->switch_3d(format_3d); + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->format_3d); + } else { + DEV_DBG("%s: '%d' (unchanged)\n", __func__, + external_common_state->format_3d); + } + } else { + DEV_DBG("%s: '%d' (unknown)\n", __func__, format_3d); + } + + return ret; +} +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT +static DEVICE_ATTR(cec, S_IRUGO | S_IWUSR, + hdmi_msm_rda_cec, + hdmi_msm_wta_cec); + +static DEVICE_ATTR(cec_logical_addr, S_IRUGO | S_IWUSR, + hdmi_msm_rda_cec_logical_addr, + hdmi_msm_wta_cec_logical_addr); + +static DEVICE_ATTR(cec_rd_frame, S_IRUGO, + hdmi_msm_rda_cec_frame, NULL); + +static DEVICE_ATTR(cec_wr_frame, S_IWUSR, + NULL, hdmi_msm_wta_cec_frame); +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + + +static ssize_t external_common_rda_video_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->video_resolution+1); + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->video_resolution+1); + return ret; +} + +static ssize_t external_common_wta_video_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + uint32 video_mode; +#ifdef CONFIG_FB_MSM_HDMI_COMMON + const struct hdmi_disp_mode_timing_type *disp_mode; +#endif + mutex_lock(&external_common_state_hpd_mutex); + if (!external_common_state->hpd_state) { + mutex_unlock(&external_common_state_hpd_mutex); + DEV_INFO("%s: FAILED: display off or cable disconnected\n", + __func__); + return ret; + } + mutex_unlock(&external_common_state_hpd_mutex); + + video_mode = atoi(buf)-1; + DEV_INFO("%s: video_mode is %d\n", __func__, video_mode); + kobject_uevent(external_common_state->uevent_kobj, KOBJ_OFFLINE); +#ifdef CONFIG_FB_MSM_HDMI_COMMON + disp_mode = hdmi_common_get_supported_mode(video_mode); + if (!disp_mode) { + DEV_INFO("%s: FAILED: mode not supported (%d)\n", + __func__, video_mode); + return ret; + } + external_common_state->disp_mode_list.num_of_elements = 1; + external_common_state->disp_mode_list.disp_mode_list[0] = video_mode; +#elif defined(CONFIG_FB_MSM_TVOUT) + external_common_state->video_resolution = video_mode; +#endif + DEV_DBG("%s: 'mode=%d %s' successful (sending OFF/ONLINE)\n", __func__, + video_mode, video_format_2string(video_mode)); + kobject_uevent(external_common_state->uevent_kobj, KOBJ_ONLINE); + return ret; +} + +static ssize_t external_common_rda_connected(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + mutex_lock(&external_common_state_hpd_mutex); + ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->hpd_state); + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->hpd_state); + mutex_unlock(&external_common_state_hpd_mutex); + return ret; +} + +static ssize_t external_common_rda_hdmi_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + ret = snprintf(buf, PAGE_SIZE, "%d\n", + external_common_state->hdmi_sink); + + DEV_DBG("%s: '%d'\n", __func__, + external_common_state->hdmi_sink); + + return ret; +} + +static ssize_t hdmi_common_rda_hdmi_primary(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n", + hdmi_prim_display); + DEV_DBG("%s: '%d'\n", __func__, hdmi_prim_display); + return ret; +} + +static DEVICE_ATTR(video_mode, S_IRUGO | S_IWUGO, + external_common_rda_video_mode, external_common_wta_video_mode); +static DEVICE_ATTR(video_mode_str, S_IRUGO, external_common_rda_video_mode_str, + NULL); +static DEVICE_ATTR(connected, S_IRUGO, external_common_rda_connected, NULL); +static DEVICE_ATTR(hdmi_mode, S_IRUGO, external_common_rda_hdmi_mode, NULL); +#ifdef CONFIG_FB_MSM_HDMI_COMMON +static DEVICE_ATTR(edid_modes, S_IRUGO, hdmi_common_rda_edid_modes, NULL); +static DEVICE_ATTR(hpd, S_IRUGO | S_IWUGO, hdmi_common_rda_hpd, + hdmi_common_wta_hpd); +static DEVICE_ATTR(hdcp, S_IRUGO, hdmi_common_rda_hdcp, NULL); +static DEVICE_ATTR(pa, S_IRUGO, + hdmi_common_rda_edid_physical_address, NULL); +static DEVICE_ATTR(scan_info, S_IRUGO, + hdmi_common_rda_edid_scan_info, NULL); +static DEVICE_ATTR(vendor_name, S_IRUGO | S_IWUSR, hdmi_common_rda_vendor_name, + hdmi_common_wta_vendor_name); +static DEVICE_ATTR(product_description, S_IRUGO | S_IWUSR, + hdmi_common_rda_product_description, + hdmi_common_wta_product_description); +static DEVICE_ATTR(edid_3d_modes, S_IRUGO, + hdmi_common_rda_edid_3d_modes, NULL); +static DEVICE_ATTR(3d_present, S_IRUGO, hdmi_common_rda_3d_present, NULL); +static DEVICE_ATTR(hdcp_present, S_IRUGO, hdmi_common_rda_hdcp_present, NULL); +#endif +#ifdef CONFIG_FB_MSM_HDMI_3D +static DEVICE_ATTR(format_3d, S_IRUGO | S_IWUGO, hdmi_3d_rda_format_3d, + hdmi_3d_wta_format_3d); +#endif +static DEVICE_ATTR(hdmi_primary, S_IRUGO, hdmi_common_rda_hdmi_primary, NULL); + +static struct attribute *external_common_fs_attrs[] = { + &dev_attr_video_mode.attr, + &dev_attr_video_mode_str.attr, + &dev_attr_connected.attr, + &dev_attr_hdmi_mode.attr, +#ifdef CONFIG_FB_MSM_HDMI_COMMON + &dev_attr_edid_modes.attr, + &dev_attr_hdcp.attr, + &dev_attr_hpd.attr, + &dev_attr_pa.attr, + &dev_attr_scan_info.attr, + &dev_attr_vendor_name.attr, + &dev_attr_product_description.attr, + &dev_attr_edid_3d_modes.attr, + &dev_attr_3d_present.attr, + &dev_attr_hdcp_present.attr, +#endif +#ifdef CONFIG_FB_MSM_HDMI_3D + &dev_attr_format_3d.attr, +#endif +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + &dev_attr_cec.attr, + &dev_attr_cec_logical_addr.attr, + &dev_attr_cec_rd_frame.attr, + &dev_attr_cec_wr_frame.attr, +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + &dev_attr_hdmi_primary.attr, + NULL, +}; +static struct attribute_group external_common_fs_attr_group = { + .attrs = external_common_fs_attrs, +}; + +/* create external interface kobject and initialize */ +int external_common_state_create(struct platform_device *pdev) +{ + int rc; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + if (!mfd) { + DEV_ERR("%s: mfd not found\n", __func__); + return -ENODEV; + } + if (!mfd->fbi) { + DEV_ERR("%s: mfd->fbi not found\n", __func__); + return -ENODEV; + } + if (!mfd->fbi->dev) { + DEV_ERR("%s: mfd->fbi->dev not found\n", __func__); + return -ENODEV; + } + rc = sysfs_create_group(&mfd->fbi->dev->kobj, + &external_common_fs_attr_group); + if (rc) { + DEV_ERR("%s: sysfs group creation failed, rc=%d\n", __func__, + rc); + return rc; + } + external_common_state->uevent_kobj = &mfd->fbi->dev->kobj; + DEV_ERR("%s: sysfs group %p\n", __func__, + external_common_state->uevent_kobj); + + kobject_uevent(external_common_state->uevent_kobj, KOBJ_ADD); + DEV_DBG("%s: kobject_uevent(KOBJ_ADD)\n", __func__); + return 0; +} +EXPORT_SYMBOL(external_common_state_create); + +void external_common_state_remove(void) +{ + if (external_common_state->uevent_kobj) + sysfs_remove_group(external_common_state->uevent_kobj, + &external_common_fs_attr_group); + external_common_state->uevent_kobj = NULL; +} +EXPORT_SYMBOL(external_common_state_remove); + +#ifdef CONFIG_FB_MSM_HDMI_COMMON +/* The Logic ID for HDMI TX Core. Currently only support 1 HDMI TX Core. */ +struct hdmi_edid_video_mode_property_type { + uint32 video_code; + uint32 active_h; + uint32 active_v; + boolean interlaced; + uint32 total_h; + uint32 total_blank_h; + uint32 total_v; + uint32 total_blank_v; + /* Must divide by 1000 to get the frequency */ + uint32 freq_h; + /* Must divide by 1000 to get the frequency */ + uint32 freq_v; + /* Must divide by 1000 to get the frequency */ + uint32 pixel_freq; + /* Must divide by 1000 to get the frequency */ + uint32 refresh_rate; + boolean aspect_ratio_4_3; +}; + +/* LUT is sorted from lowest Active H to highest Active H - ease searching */ +static struct hdmi_edid_video_mode_property_type + hdmi_edid_disp_mode_lut[] = { + + /* All 640 H Active */ + {HDMI_VFRMT_640x480p60_4_3, 640, 480, FALSE, 800, 160, 525, 45, + 31465, 59940, 25175, 59940, TRUE}, + {HDMI_VFRMT_640x480p60_4_3, 640, 480, FALSE, 800, 160, 525, 45, + 31500, 60000, 25200, 60000, TRUE}, + + /* All 720 H Active */ + {HDMI_VFRMT_720x576p50_4_3, 720, 576, FALSE, 864, 144, 625, 49, + 31250, 50000, 27000, 50000, TRUE}, + {HDMI_VFRMT_720x480p60_4_3, 720, 480, FALSE, 858, 138, 525, 45, + 31465, 59940, 27000, 59940, TRUE}, + {HDMI_VFRMT_720x480p60_4_3, 720, 480, FALSE, 858, 138, 525, 45, + 31500, 60000, 27030, 60000, TRUE}, + {HDMI_VFRMT_720x576p100_4_3, 720, 576, FALSE, 864, 144, 625, 49, + 62500, 100000, 54000, 100000, TRUE}, + {HDMI_VFRMT_720x480p120_4_3, 720, 480, FALSE, 858, 138, 525, 45, + 62937, 119880, 54000, 119880, TRUE}, + {HDMI_VFRMT_720x480p120_4_3, 720, 480, FALSE, 858, 138, 525, 45, + 63000, 120000, 54054, 120000, TRUE}, + {HDMI_VFRMT_720x576p200_4_3, 720, 576, FALSE, 864, 144, 625, 49, + 125000, 200000, 108000, 200000, TRUE}, + {HDMI_VFRMT_720x480p240_4_3, 720, 480, FALSE, 858, 138, 525, 45, + 125874, 239760, 108000, 239000, TRUE}, + {HDMI_VFRMT_720x480p240_4_3, 720, 480, FALSE, 858, 138, 525, 45, + 126000, 240000, 108108, 240000, TRUE}, + + /* All 1280 H Active */ + {HDMI_VFRMT_1280x720p50_16_9, 1280, 720, FALSE, 1980, 700, 750, 30, + 37500, 50000, 74250, 50000, FALSE}, + {HDMI_VFRMT_1280x720p60_16_9, 1280, 720, FALSE, 1650, 370, 750, 30, + 44955, 59940, 74176, 59940, FALSE}, + {HDMI_VFRMT_1280x720p60_16_9, 1280, 720, FALSE, 1650, 370, 750, 30, + 45000, 60000, 74250, 60000, FALSE}, + {HDMI_VFRMT_1280x720p100_16_9, 1280, 720, FALSE, 1980, 700, 750, 30, + 75000, 100000, 148500, 100000, FALSE}, + {HDMI_VFRMT_1280x720p120_16_9, 1280, 720, FALSE, 1650, 370, 750, 30, + 89909, 119880, 148352, 119880, FALSE}, + {HDMI_VFRMT_1280x720p120_16_9, 1280, 720, FALSE, 1650, 370, 750, 30, + 90000, 120000, 148500, 120000, FALSE}, + + /* All 1440 H Active */ + {HDMI_VFRMT_1440x576i50_4_3, 1440, 576, TRUE, 1728, 288, 625, 24, + 15625, 50000, 27000, 50000, TRUE}, + {HDMI_VFRMT_720x288p50_4_3, 1440, 288, FALSE, 1728, 288, 312, 24, + 15625, 50080, 27000, 50000, TRUE}, + {HDMI_VFRMT_720x288p50_4_3, 1440, 288, FALSE, 1728, 288, 313, 25, + 15625, 49920, 27000, 50000, TRUE}, + {HDMI_VFRMT_720x288p50_4_3, 1440, 288, FALSE, 1728, 288, 314, 26, + 15625, 49761, 27000, 50000, TRUE}, + {HDMI_VFRMT_1440x576p50_4_3, 1440, 576, FALSE, 1728, 288, 625, 49, + 31250, 50000, 54000, 50000, TRUE}, + {HDMI_VFRMT_1440x480i60_4_3, 1440, 480, TRUE, 1716, 276, 525, 22, + 15734, 59940, 27000, 59940, TRUE}, + {HDMI_VFRMT_1440x240p60_4_3, 1440, 240, FALSE, 1716, 276, 262, 22, + 15734, 60054, 27000, 59940, TRUE}, + {HDMI_VFRMT_1440x240p60_4_3, 1440, 240, FALSE, 1716, 276, 263, 23, + 15734, 59826, 27000, 59940, TRUE}, + {HDMI_VFRMT_1440x480p60_4_3, 1440, 480, FALSE, 1716, 276, 525, 45, + 31469, 59940, 54000, 59940, TRUE}, + {HDMI_VFRMT_1440x480i60_4_3, 1440, 480, TRUE, 1716, 276, 525, 22, + 15750, 60000, 27027, 60000, TRUE}, + {HDMI_VFRMT_1440x240p60_4_3, 1440, 240, FALSE, 1716, 276, 262, 22, + 15750, 60115, 27027, 60000, TRUE}, + {HDMI_VFRMT_1440x240p60_4_3, 1440, 240, FALSE, 1716, 276, 263, 23, + 15750, 59886, 27027, 60000, TRUE}, + {HDMI_VFRMT_1440x480p60_4_3, 1440, 480, FALSE, 1716, 276, 525, 45, + 31500, 60000, 54054, 60000, TRUE}, + {HDMI_VFRMT_1440x576i100_4_3, 1440, 576, TRUE, 1728, 288, 625, 24, + 31250, 100000, 54000, 100000, TRUE}, + {HDMI_VFRMT_1440x480i120_4_3, 1440, 480, TRUE, 1716, 276, 525, 22, + 31469, 119880, 54000, 119880, TRUE}, + {HDMI_VFRMT_1440x480i120_4_3, 1440, 480, TRUE, 1716, 276, 525, 22, + 31500, 120000, 54054, 120000, TRUE}, + {HDMI_VFRMT_1440x576i200_4_3, 1440, 576, TRUE, 1728, 288, 625, 24, + 62500, 200000, 108000, 200000, TRUE}, + {HDMI_VFRMT_1440x480i240_4_3, 1440, 480, TRUE, 1716, 276, 525, 22, + 62937, 239760, 108000, 239000, TRUE}, + {HDMI_VFRMT_1440x480i240_4_3, 1440, 480, TRUE, 1716, 276, 525, 22, + 63000, 240000, 108108, 240000, TRUE}, + + /* All 1920 H Active */ + {HDMI_VFRMT_1920x1080p60_16_9, 1920, 1080, FALSE, 2200, 280, 1125, + 45, 67433, 59940, 148352, 59940, FALSE}, + {HDMI_VFRMT_1920x1080p60_16_9, 1920, 1080, TRUE, 2200, 280, 1125, + 45, 67500, 60000, 148500, 60000, FALSE}, + {HDMI_VFRMT_1920x1080p50_16_9, 1920, 1080, FALSE, 2640, 720, 1125, + 45, 56250, 50000, 148500, 50000, FALSE}, + {HDMI_VFRMT_1920x1080p24_16_9, 1920, 1080, FALSE, 2750, 830, 1125, + 45, 26973, 23976, 74176, 24000, FALSE}, + {HDMI_VFRMT_1920x1080p24_16_9, 1920, 1080, FALSE, 2750, 830, 1125, + 45, 27000, 24000, 74250, 24000, FALSE}, + {HDMI_VFRMT_1920x1080p25_16_9, 1920, 1080, FALSE, 2640, 720, 1125, + 45, 28125, 25000, 74250, 25000, FALSE}, + {HDMI_VFRMT_1920x1080p30_16_9, 1920, 1080, FALSE, 2200, 280, 1125, + 45, 33716, 29970, 74176, 30000, FALSE}, + {HDMI_VFRMT_1920x1080p30_16_9, 1920, 1080, FALSE, 2200, 280, 1125, + 45, 33750, 30000, 74250, 30000, FALSE}, + {HDMI_VFRMT_1920x1080i50_16_9, 1920, 1080, TRUE, 2304, 384, 1250, + 85, 31250, 50000, 72000, 50000, FALSE}, + {HDMI_VFRMT_1920x1080i60_16_9, 1920, 1080, TRUE, 2200, 280, 1125, + 22, 33716, 59940, 74176, 59940, FALSE}, + {HDMI_VFRMT_1920x1080i60_16_9, 1920, 1080, TRUE, 2200, 280, 1125, + 22, 33750, 60000, 74250, 60000, FALSE}, + {HDMI_VFRMT_1920x1080i100_16_9, 1920, 1080, TRUE, 2640, 720, 1125, + 22, 56250, 100000, 148500, 100000, FALSE}, + {HDMI_VFRMT_1920x1080i120_16_9, 1920, 1080, TRUE, 2200, 280, 1125, + 22, 67432, 119880, 148352, 119980, FALSE}, + {HDMI_VFRMT_1920x1080i120_16_9, 1920, 1080, TRUE, 2200, 280, 1125, + 22, 67500, 120000, 148500, 120000, FALSE}, + + /* All 2880 H Active */ + {HDMI_VFRMT_2880x576i50_4_3, 2880, 576, TRUE, 3456, 576, 625, 24, + 15625, 50000, 54000, 50000, TRUE}, + {HDMI_VFRMT_2880x288p50_4_3, 2880, 576, FALSE, 3456, 576, 312, 24, + 15625, 50080, 54000, 50000, TRUE}, + {HDMI_VFRMT_2880x288p50_4_3, 2880, 576, FALSE, 3456, 576, 313, 25, + 15625, 49920, 54000, 50000, TRUE}, + {HDMI_VFRMT_2880x288p50_4_3, 2880, 576, FALSE, 3456, 576, 314, 26, + 15625, 49761, 54000, 50000, TRUE}, + {HDMI_VFRMT_2880x576p50_4_3, 2880, 576, FALSE, 3456, 576, 625, 49, + 31250, 50000, 108000, 50000, TRUE}, + {HDMI_VFRMT_2880x480i60_4_3, 2880, 480, TRUE, 3432, 552, 525, 22, + 15734, 59940, 54000, 59940, TRUE}, + {HDMI_VFRMT_2880x240p60_4_3, 2880, 480, FALSE, 3432, 552, 262, 22, + 15734, 60054, 54000, 59940, TRUE}, + {HDMI_VFRMT_2880x240p60_4_3, 2880, 480, FALSE, 3432, 552, 263, 23, + 15734, 59940, 54000, 59940, TRUE}, + {HDMI_VFRMT_2880x480p60_4_3, 2880, 480, FALSE, 3432, 552, 525, 45, + 31469, 59940, 108000, 59940, TRUE}, + {HDMI_VFRMT_2880x480i60_4_3, 2880, 480, TRUE, 3432, 552, 525, 22, + 15750, 60000, 54054, 60000, TRUE}, + {HDMI_VFRMT_2880x240p60_4_3, 2880, 240, FALSE, 3432, 552, 262, 22, + 15750, 60115, 54054, 60000, TRUE}, + {HDMI_VFRMT_2880x240p60_4_3, 2880, 240, FALSE, 3432, 552, 262, 23, + 15750, 59886, 54054, 60000, TRUE}, + {HDMI_VFRMT_2880x480p60_4_3, 2880, 480, FALSE, 3432, 552, 525, 45, + 31500, 60000, 108108, 60000, TRUE}, +}; + +static const uint8 *hdmi_edid_find_block(const uint8 *in_buf, + uint32 start_offset, uint8 type, uint8 *len) +{ + /* the start of data block collection, start of Video Data Block */ + uint32 offset = start_offset; + uint32 end_dbc_offset = in_buf[2]; + + *len = 0; + + /*edid buffer 1, byte 2 being 4 means no non-DTD/Data block collection + present. + edid buffer 1, byte 2 being 0 menas no non-DTD/DATA block collection + present and no DTD data present.*/ + if ((end_dbc_offset == 0) || (end_dbc_offset == 4)) { + DEV_WARN("EDID: no DTD or non-DTD data present\n"); + return NULL; + } + while (offset < end_dbc_offset) { + uint8 block_len = in_buf[offset] & 0x1F; + if ((in_buf[offset] >> 5) == type) { + *len = block_len; + DEV_DBG("EDID: block=%d found @ %d with length=%d\n", + type, offset, block_len); + return in_buf+offset; + } + offset += 1 + block_len; + } + DEV_WARN("EDID: type=%d block not found in EDID block\n", type); + return NULL; +} + +static void hdmi_edid_extract_vendor_id(const uint8 *in_buf, + char *vendor_id) +{ + uint32 id_codes = ((uint32)in_buf[8] << 8) + in_buf[9]; + + vendor_id[0] = 'A' - 1 + ((id_codes >> 10) & 0x1F); + vendor_id[1] = 'A' - 1 + ((id_codes >> 5) & 0x1F); + vendor_id[2] = 'A' - 1 + (id_codes & 0x1F); + vendor_id[3] = 0; +} + +static uint32 hdmi_edid_extract_ieee_reg_id(const uint8 *in_buf) +{ + uint8 len; + const uint8 *vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3, + &len); + + if (vsd == NULL) + return 0; + + DEV_DBG("EDID: VSD PhyAddr=%04x, MaxTMDS=%dMHz\n", + ((uint32)vsd[4] << 8) + (uint32)vsd[5], (uint32)vsd[7] * 5); + external_common_state->physical_address = + ((uint16)vsd[4] << 8) + (uint16)vsd[5]; + return ((uint32)vsd[3] << 16) + ((uint32)vsd[2] << 8) + (uint32)vsd[1]; +} + +#define HDMI_VSDB_3D_DATA_OFFSET(vsd) \ + (!((vsd)[8] & BIT(7)) ? 9 : (!((vsd)[8] & BIT(6)) ? 11 : 13)) + +static void hdmi_edid_extract_3d_present(const uint8 *in_buf) +{ + uint8 len, offset; + const uint8 *vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3, + &len); + + external_common_state->present_3d = 0; + if (vsd == NULL || len < 9) { + DEV_DBG("EDID[3D]: block-id 3 not found or not long enough\n"); + return; + } + + offset = HDMI_VSDB_3D_DATA_OFFSET(vsd); + DEV_DBG("EDID: 3D present @ %d = %02x\n", offset, vsd[offset]); + if (vsd[offset] >> 7) { /* 3D format indication present */ + DEV_INFO("EDID: 3D present, 3D-len=%d\n", vsd[offset+1] & 0x1F); + external_common_state->present_3d = 1; + } +} + + +static void hdmi_edid_extract_latency_fields(const uint8 *in_buf) +{ + uint8 len; + const uint8 *vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3, + &len); + + if (vsd == NULL || len < 12 || !(vsd[8] & BIT(7))) { + external_common_state->video_latency = (uint16)-1; + external_common_state->audio_latency = (uint16)-1; + DEV_DBG("EDID: No audio/video latency present\n"); + } else { + external_common_state->video_latency = vsd[9]; + external_common_state->audio_latency = vsd[10]; + DEV_DBG("EDID: video-latency=%04x, audio-latency=%04x\n", + external_common_state->video_latency, + external_common_state->audio_latency); + } +} + +static void hdmi_edid_extract_speaker_allocation_data(const uint8 *in_buf) +{ + uint8 len; + const uint8 *sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4, + &len); + + if (sad == NULL) + return; + + external_common_state->speaker_allocation_block = sad[1]; + DEV_DBG("EDID: speaker allocation data SP byte = %08x %s%s%s%s%s%s%s\n", + sad[1], + (sad[1] & BIT(0)) ? "FL/FR," : "", + (sad[1] & BIT(1)) ? "LFE," : "", + (sad[1] & BIT(2)) ? "FC," : "", + (sad[1] & BIT(3)) ? "RL/RR," : "", + (sad[1] & BIT(4)) ? "RC," : "", + (sad[1] & BIT(5)) ? "FLC/FRC," : "", + (sad[1] & BIT(6)) ? "RLC/RRC," : ""); +} + +static void hdmi_edid_extract_audio_data_blocks(const uint8 *in_buf) +{ + uint8 len; + const uint8 *sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1, + &len); + uint32 *adb = external_common_state->audio_data_blocks; + + if (sad == NULL) + return; + + external_common_state->audio_data_block_cnt = 0; + while (len >= 3 && external_common_state->audio_data_block_cnt < 16) { + DEV_DBG("EDID: Audio Data Block=\n", + (sad[1] & 0x7)+1, sad[1] >> 3, sad[2], sad[3]); + *adb++ = (uint32)sad[1] + ((uint32)sad[2] << 8) + + ((uint32)sad[2] << 16); + ++external_common_state->audio_data_block_cnt; + len -= 3; + sad += 3; + } +} + +static void hdmi_edid_extract_extended_data_blocks(const uint8 *in_buf) +{ + uint8 len = 0; + uint32 start_offset = DBC_START_OFFSET; + + /* A Tage code of 7 identifies extended data blocks */ + uint8 const *etag = hdmi_edid_find_block(in_buf, start_offset, 7, &len); + + while (etag != NULL) { + /* The extended data block should at least be 2 bytes long */ + if (len < 2) { + DEV_DBG("EDID: Found an extended data block of length" + "less than 2 bytes. Ignoring ...\n"); + } else { + /* + * The second byte of the extended data block has the + * extended tag code + */ + switch (etag[1]) { + case 0: + /* Video Capability Data Block */ + DEV_DBG("EDID: VCDB=%02X %02X\n", etag[1], + etag[2]); + + /* + * Check if the sink specifies underscan + * support for: + * BIT 5: preferred video format + * BIT 3: IT video format + * BIT 1: CE video format + */ + external_common_state->pt_scan_info = (etag[2] & + (BIT(4) | BIT(5))) >> 4; + external_common_state->it_scan_info = (etag[2] & + (BIT(3) | BIT(2))) >> 2; + external_common_state->ce_scan_info = etag[2] & + (BIT(1) | BIT(0)); + DEV_DBG("EDID: Scan Information (pt|it|ce): " + "(%d|%d|%d)", + external_common_state->pt_scan_info, + external_common_state->it_scan_info, + external_common_state->ce_scan_info); + break; + default: + DEV_DBG("EDID: Extend Tag Code %d not" + "supported\n", etag[1]); + break; + } + } + + /* There could be more that one extended data block */ + start_offset = etag - in_buf + len + 1; + etag = hdmi_edid_find_block(in_buf, start_offset, 7, &len); + } +} + +static void hdmi_edid_detail_desc(const uint8 *data_buf, uint32 *disp_mode) +{ + boolean aspect_ratio_4_3 = FALSE; + boolean interlaced = FALSE; + uint32 active_h = 0; + uint32 active_v = 0; + uint32 blank_h = 0; + uint32 blank_v = 0; + uint32 ndx = 0; + uint32 max_num_of_elements = 0; + uint32 img_size_h = 0; + uint32 img_size_v = 0; + + /* See VESA Spec */ + /* EDID_TIMING_DESC_UPPER_H_NIBBLE[0x4]: Relative Offset to the EDID + * detailed timing descriptors - Upper 4 bit for each H active/blank + * field */ + /* EDID_TIMING_DESC_H_ACTIVE[0x2]: Relative Offset to the EDID detailed + * timing descriptors - H active */ + active_h = ((((uint32)data_buf[0x4] >> 0x4) & 0xF) << 8) + | data_buf[0x2]; + + /* EDID_TIMING_DESC_H_BLANK[0x3]: Relative Offset to the EDID detailed + * timing descriptors - H blank */ + blank_h = (((uint32)data_buf[0x4] & 0xF) << 8) + | data_buf[0x3]; + + /* EDID_TIMING_DESC_UPPER_V_NIBBLE[0x7]: Relative Offset to the EDID + * detailed timing descriptors - Upper 4 bit for each V active/blank + * field */ + /* EDID_TIMING_DESC_V_ACTIVE[0x5]: Relative Offset to the EDID detailed + * timing descriptors - V active */ + active_v = ((((uint32)data_buf[0x7] >> 0x4) & 0xF) << 8) + | data_buf[0x5]; + + /* EDID_TIMING_DESC_V_BLANK[0x6]: Relative Offset to the EDID detailed + * timing descriptors - V blank */ + blank_v = (((uint32)data_buf[0x7] & 0xF) << 8) + | data_buf[0x6]; + + /* EDID_TIMING_DESC_IMAGE_SIZE_UPPER_NIBBLE[0xE]: Relative Offset to the + * EDID detailed timing descriptors - Image Size upper nibble + * V and H */ + /* EDID_TIMING_DESC_H_IMAGE_SIZE[0xC]: Relative Offset to the EDID + * detailed timing descriptors - H image size */ + /* EDID_TIMING_DESC_V_IMAGE_SIZE[0xD]: Relative Offset to the EDID + * detailed timing descriptors - V image size */ + img_size_h = ((((uint32)data_buf[0xE] >> 0x4) & 0xF) << 8) + | data_buf[0xC]; + img_size_v = (((uint32)data_buf[0xE] & 0xF) << 8) + | data_buf[0xD]; + + /* + * aspect ratio as 4:3 if within specificed range , rathaer than being + * absolute value + */ + aspect_ratio_4_3 = (abs(img_size_h * 3 - img_size_v * 4) < 5) ? 1 : 0; + + max_num_of_elements = sizeof(hdmi_edid_disp_mode_lut) + / sizeof(*hdmi_edid_disp_mode_lut); + + /* EDID_TIMING_DESC_INTERLACE[0x11:7]: Relative Offset to the EDID + * detailed timing descriptors - Interlace flag */ + DEV_DBG("Interlaced mode byte data_buf[0x11]=[%x]\n", data_buf[0x11]); + /* + * CEA 861-D: interlaced bit is bit[7] of byte[0x11] + */ + interlaced = (data_buf[0x11] & 0x80) >> 7; + + DEV_DBG("%s: A[%ux%u] B[%ux%u] V[%ux%u] %s\n", __func__, + active_h, active_v, blank_h, blank_v, img_size_h, img_size_v, + interlaced ? "i" : "p"); + + *disp_mode = HDMI_VFRMT_FORCE_32BIT; + while (ndx < max_num_of_elements) { + const struct hdmi_edid_video_mode_property_type *edid = + hdmi_edid_disp_mode_lut+ndx; + + if ((interlaced == edid->interlaced) && + (active_h == edid->active_h) && + (blank_h == edid->total_blank_h) && + (blank_v == edid->total_blank_v) && + ((active_v == edid->active_v) || + (active_v == (edid->active_v + 1))) + ) { + if (edid->aspect_ratio_4_3 && !aspect_ratio_4_3) + /* Aspect ratio 16:9 */ + *disp_mode = edid->video_code + 1; + else + /* Aspect ratio 4:3 */ + *disp_mode = edid->video_code; + + DEV_DBG("%s: mode found:%d\n", __func__, *disp_mode); + break; + } + ++ndx; + } + if (ndx == max_num_of_elements) + DEV_INFO("%s: *no mode* found\n", __func__); +} + +static void add_supported_video_format( + struct hdmi_disp_mode_list_type *disp_mode_list, + uint32 video_format) +{ + const struct hdmi_disp_mode_timing_type *timing = + hdmi_common_get_supported_mode(video_format); + boolean supported = timing != NULL; + + if (video_format >= HDMI_VFRMT_MAX) + return; + + DEV_DBG("EDID: format: %d [%s], %s\n", + video_format, video_format_2string(video_format), + supported ? "Supported" : "Not-Supported"); + if (supported) { + if (mhl_is_connected()) { + const struct hdmi_disp_mode_timing_type *mhl_timing = + hdmi_mhl_get_supported_mode(video_format); + boolean mhl_supported = mhl_timing != NULL; + DEV_DBG("EDID: format: %d [%s], %s by MHL\n", + video_format, video_format_2string(video_format), + mhl_supported ? "Supported" : "Not-Supported"); + if (mhl_supported) + disp_mode_list->disp_mode_list[ + disp_mode_list->num_of_elements++] = video_format; + } else + disp_mode_list->disp_mode_list[ + disp_mode_list->num_of_elements++] = video_format; + } +} + +const char *single_video_3d_format_2string(uint32 format) +{ + switch (format) { + case TOP_AND_BOTTOM: return "TAB"; + case FRAME_PACKING: return "FP"; + case SIDE_BY_SIDE_HALF: return "SSH"; + } + return ""; +} + +ssize_t video_3d_format_2string(uint32 format, char *buf) +{ + ssize_t ret, len = 0; + ret = snprintf(buf, PAGE_SIZE, "%s", + single_video_3d_format_2string(format & FRAME_PACKING)); + len += ret; + + if (len && (format & TOP_AND_BOTTOM)) + ret = snprintf(buf + len, PAGE_SIZE, ":%s", + single_video_3d_format_2string( + format & TOP_AND_BOTTOM)); + else + ret = snprintf(buf + len, PAGE_SIZE, "%s", + single_video_3d_format_2string( + format & TOP_AND_BOTTOM)); + len += ret; + + if (len && (format & SIDE_BY_SIDE_HALF)) + ret = snprintf(buf + len, PAGE_SIZE, ":%s", + single_video_3d_format_2string( + format & SIDE_BY_SIDE_HALF)); + else + ret = snprintf(buf + len, PAGE_SIZE, "%s", + single_video_3d_format_2string( + format & SIDE_BY_SIDE_HALF)); + len += ret; + + return len; +} + +static void add_supported_3d_format( + struct hdmi_disp_mode_list_type *disp_mode_list, + uint32 video_format, + uint32 video_3d_format) +{ + char string[128]; + boolean added = FALSE; + int i; + for (i = 0; i < disp_mode_list->num_of_elements; ++i) { + if (disp_mode_list->disp_mode_list[i] == video_format) { + disp_mode_list->disp_3d_mode_list[i] |= + video_3d_format; + added = TRUE; + break; + } + } + video_3d_format_2string(video_3d_format, string); + DEV_DBG("EDID[3D]: format: %d [%s], %s %s\n", + video_format, video_format_2string(video_format), + string, added ? "added" : "NOT added"); +} + +static void hdmi_edid_get_display_vsd_3d_mode(const uint8 *data_buf, + struct hdmi_disp_mode_list_type *disp_mode_list, + uint32 num_og_cea_blocks) +{ + uint8 len, offset, present_multi_3d, hdmi_vic_len, hdmi_3d_len; + uint16 structure_all, structure_mask; + const uint8 *vsd = num_og_cea_blocks ? + hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET, + 3, &len) : NULL; + int i; + + offset = HDMI_VSDB_3D_DATA_OFFSET(vsd); + present_multi_3d = (vsd[offset] & 0x60) >> 5; + + offset += 1; + hdmi_vic_len = (vsd[offset] >> 5) & 0x7; + hdmi_3d_len = vsd[offset] & 0x1F; + DEV_DBG("EDID[3D]: HDMI_VIC_LEN = %d, HDMI_3D_LEN = %d\n", + hdmi_vic_len, hdmi_3d_len); + + offset += (hdmi_vic_len + 1); + if (present_multi_3d == 1 || present_multi_3d == 2) { + DEV_DBG("EDID[3D]: multi 3D present (%d)\n", present_multi_3d); + /* 3d_structure_all */ + structure_all = (vsd[offset] << 8) | vsd[offset + 1]; + offset += 2; + hdmi_3d_len -= 2; + if (present_multi_3d == 2) { + /* 3d_structure_mask */ + structure_mask = (vsd[offset] << 8) | vsd[offset + 1]; + offset += 2; + hdmi_3d_len -= 2; + } else + structure_mask = 0xffff; + + i = 0; + while (i < 16) { + if (i >= disp_mode_list->disp_multi_3d_mode_list_cnt) + break; + + if (!(structure_mask & BIT(i))) { + ++i; + continue; + } + + /* BIT0: FRAME PACKING */ + if (structure_all & BIT(0)) + add_supported_3d_format(disp_mode_list, + disp_mode_list-> + disp_multi_3d_mode_list[i], + FRAME_PACKING); + + /* BIT6: TOP AND BOTTOM */ + if (structure_all & BIT(6)) + add_supported_3d_format(disp_mode_list, + disp_mode_list-> + disp_multi_3d_mode_list[i], + TOP_AND_BOTTOM); + + /* BIT8: SIDE BY SIDE HALF */ + if (structure_all & BIT(8)) + add_supported_3d_format(disp_mode_list, + disp_mode_list-> + disp_multi_3d_mode_list[i], + SIDE_BY_SIDE_HALF); + + ++i; + } + } + + i = 0; + while (hdmi_3d_len > 0) { + DEV_DBG("EDID[3D]: 3D_Structure_%d @ %d: %02x\n", + i + 1, offset, vsd[offset]); + + if ((vsd[offset] >> 4) >= + disp_mode_list->disp_multi_3d_mode_list_cnt) { + if ((vsd[offset] & 0x0F) >= 8) { + offset += 1; + hdmi_3d_len -= 1; + DEV_DBG("EDID[3D]: 3D_Detail_%d @ %d: %02x\n", + i + 1, offset, vsd[offset]); + } + i += 1; + offset += 1; + hdmi_3d_len -= 1; + continue; + } + + switch (vsd[offset] & 0x0F) { + case 0: + /* 0000b: FRAME PACKING */ + add_supported_3d_format(disp_mode_list, + disp_mode_list->disp_multi_3d_mode_list + [vsd[offset] >> 4], + FRAME_PACKING); + break; + case 6: + /* 0110b: TOP AND BOTTOM */ + add_supported_3d_format(disp_mode_list, + disp_mode_list->disp_multi_3d_mode_list + [vsd[offset] >> 4], + TOP_AND_BOTTOM); + break; + case 8: + /* 1000b: SIDE BY SIDE HALF */ + add_supported_3d_format(disp_mode_list, + disp_mode_list->disp_multi_3d_mode_list + [vsd[offset] >> 4], + SIDE_BY_SIDE_HALF); + break; + } + if ((vsd[offset] & 0x0F) >= 8) { + offset += 1; + hdmi_3d_len -= 1; + DEV_DBG("EDID[3D]: 3D_Detail_%d @ %d: %02x\n", + i + 1, offset, vsd[offset]); + } + i += 1; + offset += 1; + hdmi_3d_len -= 1; + } +} + +static void hdmi_edid_get_display_mode(const uint8 *data_buf, + struct hdmi_disp_mode_list_type *disp_mode_list, + uint32 num_og_cea_blocks) +{ + uint8 i = 0; + uint32 video_format = HDMI_VFRMT_640x480p60_4_3; + boolean has480p = FALSE; + uint8 len; + const uint8 *edid_blk0 = &data_buf[0x0]; + const uint8 *edid_blk1 = &data_buf[0x80]; + const uint8 *svd = num_og_cea_blocks ? + hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET, + 2, &len) : NULL; + boolean has60hz_mode = FALSE; + boolean has50hz_mode = FALSE; + + + disp_mode_list->num_of_elements = 0; + disp_mode_list->disp_multi_3d_mode_list_cnt = 0; + if (svd != NULL) { + ++svd; + for (i = 0; i < len; ++i, ++svd) { + /* Subtract 1 because it is zero based in the driver, + * while the Video identification code is 1 based in the + * CEA_861D spec */ + video_format = (*svd & 0x7F) - 1; + add_supported_video_format(disp_mode_list, + video_format); + /* Make a note of the preferred video format */ + if (i == 0) { + external_common_state->preferred_video_format = + video_format; + } + if (i < 16) { + disp_mode_list->disp_multi_3d_mode_list[i] + = video_format; + disp_mode_list->disp_multi_3d_mode_list_cnt++; + } + + if (video_format <= HDMI_VFRMT_1920x1080p60_16_9 || + video_format == HDMI_VFRMT_2880x480p60_4_3 || + video_format == HDMI_VFRMT_2880x480p60_16_9) + has60hz_mode = TRUE; + + if ((video_format >= HDMI_VFRMT_720x576p50_4_3 && + video_format <= HDMI_VFRMT_1920x1080p50_16_9) || + video_format == HDMI_VFRMT_2880x576p50_4_3 || + video_format == HDMI_VFRMT_2880x576p50_16_9 || + video_format == HDMI_VFRMT_1920x1250i50_16_9) + has50hz_mode = TRUE; + if (video_format == HDMI_VFRMT_640x480p60_4_3) + has480p = TRUE; + } + } else if (!num_og_cea_blocks) { + /* Detailed timing descriptors */ + uint32 desc_offset = 0; + /* Maximum 4 timing descriptor in block 0 - No CEA + * extension in this case */ + /* EDID_FIRST_TIMING_DESC[0x36] - 1st detailed timing + * descriptor */ + /* EDID_DETAIL_TIMING_DESC_BLCK_SZ[0x12] - Each detailed timing + * descriptor has block size of 18 */ + while (4 > i && 0 != edid_blk0[0x36+desc_offset]) { + hdmi_edid_detail_desc(edid_blk0+0x36+desc_offset, + &video_format); + DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n", + __func__, __LINE__, + video_format_2string(video_format)); + add_supported_video_format(disp_mode_list, + video_format); + if (video_format == HDMI_VFRMT_640x480p60_4_3) + has480p = TRUE; + /* Make a note of the preferred video format */ + if (i == 0) { + external_common_state->preferred_video_format = + video_format; + } + desc_offset += 0x12; + ++i; + } + } else if (1 == num_og_cea_blocks) { + uint32 desc_offset = 0; + + /* + * Read from both block 0 and block 1 + * Read EDID block[0] as above + */ + while (4 > i && 0 != edid_blk0[0x36+desc_offset]) { + hdmi_edid_detail_desc(edid_blk0+0x36+desc_offset, + &video_format); + DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n", + __func__, __LINE__, + video_format_2string(video_format)); + add_supported_video_format(disp_mode_list, + video_format); + if (video_format == HDMI_VFRMT_640x480p60_4_3) + has480p = TRUE; + /* Make a note of the preferred video format */ + if (i == 0) { + external_common_state->preferred_video_format = + video_format; + } + desc_offset += 0x12; + ++i; + } + + /* Parse block 1 - CEA extension byte offset of first + * detailed timing generation - offset is relevant to + * the offset of block 1 */ + + /* EDID_CEA_EXTENSION_FIRST_DESC[0x82]: Offset to CEA + * extension first timing desc - indicate the offset of + * the first detailed timing descriptor */ + /* EDID_BLOCK_SIZE = 0x80 Each page size in the EDID ROM */ + desc_offset = edid_blk1[0x02]; + while (0 != edid_blk1[desc_offset]) { + hdmi_edid_detail_desc(edid_blk1+desc_offset, + &video_format); + DEV_DBG("[%s:%d] Block-1 Adding vid fmt = [%s]\n", + __func__, __LINE__, + video_format_2string(video_format)); + add_supported_video_format(disp_mode_list, + video_format); + if (video_format == HDMI_VFRMT_640x480p60_4_3) + has480p = TRUE; + /* Make a note of the preferred video format */ + if (i == 0) { + external_common_state->preferred_video_format = + video_format; + } + desc_offset += 0x12; + ++i; + } + } + + /* mandaroty 3d format */ + if (external_common_state->present_3d) { + if (has60hz_mode) { + add_supported_3d_format(disp_mode_list, + HDMI_VFRMT_1920x1080p24_16_9, + FRAME_PACKING | TOP_AND_BOTTOM); + add_supported_3d_format(disp_mode_list, + HDMI_VFRMT_1280x720p60_16_9, + FRAME_PACKING | TOP_AND_BOTTOM); + add_supported_3d_format(disp_mode_list, + HDMI_VFRMT_1920x1080i60_16_9, + SIDE_BY_SIDE_HALF); + } + if (has50hz_mode) { + add_supported_3d_format(disp_mode_list, + HDMI_VFRMT_1920x1080p24_16_9, + FRAME_PACKING | TOP_AND_BOTTOM); + add_supported_3d_format(disp_mode_list, + HDMI_VFRMT_1280x720p50_16_9, + FRAME_PACKING | TOP_AND_BOTTOM); + add_supported_3d_format(disp_mode_list, + HDMI_VFRMT_1920x1080i50_16_9, + SIDE_BY_SIDE_HALF); + } + + /* 3d format described in Vendor Specific Data */ + hdmi_edid_get_display_vsd_3d_mode(data_buf, disp_mode_list, + num_og_cea_blocks); + } + + if (!has480p) + /* Need to add default 640 by 480 timings, in case not described + * in the EDID structure. + * All DTV sink devices should support this mode */ + add_supported_video_format(disp_mode_list, + HDMI_VFRMT_640x480p60_4_3); +} + +static int hdmi_common_read_edid_block(int block, uint8 *edid_buf) +{ + uint32 ndx, check_sum, print_len; +#ifdef DEBUG + const u8 *b = edid_buf; +#endif + int status = external_common_state->read_edid_block(block, edid_buf); + if (status) + goto error; + + /* Calculate checksum */ + check_sum = 0; + for (ndx = 0; ndx < 0x80; ++ndx) + check_sum += edid_buf[ndx]; + + if (check_sum & 0xFF) { + DEV_ERR("%s: failed CHECKSUM (read:%x, expected:%x)\n", + __func__, (uint8)edid_buf[0x7F], (uint8)check_sum); +#ifdef DEBUG + for (ndx = 0; ndx < 0x100; ndx += 16) + DEV_DBG("EDID[%02x-%02x] %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x\n", ndx, ndx+15, + b[ndx+0], b[ndx+1], b[ndx+2], b[ndx+3], + b[ndx+4], b[ndx+5], b[ndx+6], b[ndx+7], + b[ndx+8], b[ndx+9], b[ndx+10], b[ndx+11], + b[ndx+12], b[ndx+13], b[ndx+14], b[ndx+15]); +#endif + status = -EPROTO; + goto error; + } + print_len = 0x80; + for (ndx = 0; ndx < print_len; ndx += 16) + DEV_DBG("EDID[%02x-%02x] %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x\n", ndx, ndx+15, + b[ndx+0], b[ndx+1], b[ndx+2], b[ndx+3], + b[ndx+4], b[ndx+5], b[ndx+6], b[ndx+7], + b[ndx+8], b[ndx+9], b[ndx+10], b[ndx+11], + b[ndx+12], b[ndx+13], b[ndx+14], b[ndx+15]); + + +error: + return status; +} + +static boolean check_edid_header(const uint8 *edid_buf) +{ + return (edid_buf[0] == 0x00) && (edid_buf[1] == 0xff) + && (edid_buf[2] == 0xff) && (edid_buf[3] == 0xff) + && (edid_buf[4] == 0xff) && (edid_buf[5] == 0xff) + && (edid_buf[6] == 0xff) && (edid_buf[7] == 0x00); +} + +int hdmi_common_read_edid(void) +{ + int status = 0; + uint32 cea_extension_ver = 0; + uint32 num_og_cea_blocks = 0; + uint32 ieee_reg_id = 0; + uint32 i = 1; + char vendor_id[5]; + /* EDID_BLOCK_SIZE[0x80] Each page size in the EDID ROM */ + uint8 edid_buf[0x80 * 4]; + + external_common_state->preferred_video_format = 0; + external_common_state->present_3d = 0; + memset(&external_common_state->disp_mode_list, 0, + sizeof(external_common_state->disp_mode_list)); + memset(edid_buf, 0, sizeof(edid_buf)); + + status = hdmi_common_read_edid_block(0, edid_buf); + if (status || !check_edid_header(edid_buf)) { + if (!status) + status = -EPROTO; + DEV_ERR("%s: edid read block(0) failed: %d " + "[%02x%02x%02x%02x%02x%02x%02x%02x]\n", __func__, + status, + edid_buf[0], edid_buf[1], edid_buf[2], edid_buf[3], + edid_buf[4], edid_buf[5], edid_buf[6], edid_buf[7]); + goto error; + } + hdmi_edid_extract_vendor_id(edid_buf, vendor_id); + + /* EDID_CEA_EXTENSION_FLAG[0x7E] - CEC extension byte */ + num_og_cea_blocks = edid_buf[0x7E]; + + DEV_DBG("[JSR] (%s): No. of CEA blocks is [%u]\n", __func__, + num_og_cea_blocks); + /* Find out any CEA extension blocks following block 0 */ + switch (num_og_cea_blocks) { + case 0: /* No CEA extension */ + external_common_state->hdmi_sink = false; + DEV_DBG("HDMI DVI mode: %s\n", + external_common_state->hdmi_sink ? "no" : "yes"); + break; + case 1: /* Read block 1 */ + status = hdmi_common_read_edid_block(1, &edid_buf[0x80]); + if (status) { + DEV_ERR("%s: ddc read block(1) failed: %d\n", __func__, + status); + goto error; + } + if (edid_buf[0x80] != 2) + num_og_cea_blocks = 0; + if (num_og_cea_blocks) { + ieee_reg_id = + hdmi_edid_extract_ieee_reg_id(edid_buf+0x80); + if (ieee_reg_id == 0x0c03) + external_common_state->hdmi_sink = TRUE ; + else + external_common_state->hdmi_sink = FALSE ; + hdmi_edid_extract_latency_fields(edid_buf+0x80); + hdmi_edid_extract_speaker_allocation_data( + edid_buf+0x80); + hdmi_edid_extract_audio_data_blocks(edid_buf+0x80); + hdmi_edid_extract_3d_present(edid_buf+0x80); + hdmi_edid_extract_extended_data_blocks(edid_buf+0x80); + } + break; + case 2: + case 3: + case 4: + for (i = 1; i <= num_og_cea_blocks; i++) { + if (!(i % 2)) { + status = hdmi_common_read_edid_block(i, + edid_buf+0x00); + if (status) { + DEV_ERR("%s: ddc read block(%d)" + "failed: %d\n", __func__, i, + status); + goto error; + } + } else { + status = hdmi_common_read_edid_block(i, + edid_buf+0x80); + if (status) { + DEV_ERR("%s: ddc read block(%d)" + "failed:%d\n", __func__, i, + status); + goto error; + } + } + } + break; + default: + DEV_ERR("%s: ddc read failed, not supported multi-blocks: %d\n", + __func__, num_og_cea_blocks); + status = -EPROTO; + goto error; + } + + if (num_og_cea_blocks) { + /* EDID_CEA_EXTENSION_VERSION[0x81]: Offset to CEA extension + * version number - v1,v2,v3 (v1 is seldom, v2 is obsolete, + * v3 most common) */ + cea_extension_ver = edid_buf[0x81]; + } + + /* EDID_VERSION[0x12] - EDID Version */ + /* EDID_REVISION[0x13] - EDID Revision */ + DEV_INFO("EDID (V=%d.%d, #CEABlocks=%d[V%d], ID=%s, IEEE=%04x, " + "EDID-Ext=0x%02x)\n", edid_buf[0x12], edid_buf[0x13], + num_og_cea_blocks, cea_extension_ver, vendor_id, ieee_reg_id, + edid_buf[0x80]); + + hdmi_edid_get_display_mode(edid_buf, + &external_common_state->disp_mode_list, num_og_cea_blocks); + + return 0; + +error: + external_common_state->disp_mode_list.num_of_elements = 1; + external_common_state->disp_mode_list.disp_mode_list[0] = + external_common_state->video_resolution; + return status; +} +EXPORT_SYMBOL(hdmi_common_read_edid); + +bool hdmi_common_get_video_format_from_drv_data(struct msm_fb_data_type *mfd) +{ + uint32 format; + struct fb_var_screeninfo *var = &mfd->fbi->var; + bool changed = TRUE; + + if (var->reserved[2]) { + format = var->reserved[2]-1; + DEV_DBG("reserved format is %d\n", format); + } else { + DEV_DBG("detecting resolution from %dx%d use var->reserved[3]" + " to specify mode", mfd->var_xres, mfd->var_yres); + switch (mfd->var_xres) { + default: + case 640: + format = HDMI_VFRMT_640x480p60_4_3; + break; + case 720: + format = (mfd->var_yres == 480) + ? HDMI_VFRMT_720x480p60_16_9 + : HDMI_VFRMT_720x576p50_16_9; + break; + case 1280: + format = HDMI_VFRMT_1280x720p60_16_9; + break; + case 1440: + format = (mfd->var_yres == 480) + ? HDMI_VFRMT_1440x480i60_16_9 + : HDMI_VFRMT_1440x576i50_16_9; + break; + case 1920: + format = HDMI_VFRMT_1920x1080p60_16_9; + break; + } + } + + changed = external_common_state->video_resolution != format; + if (external_common_state->video_resolution != format) + DEV_DBG("switching %s => %s", video_format_2string( + external_common_state->video_resolution), + video_format_2string(format)); + else + DEV_DBG("resolution %s", video_format_2string( + external_common_state->video_resolution)); + external_common_state->video_resolution = format; + return changed; +} +EXPORT_SYMBOL(hdmi_common_get_video_format_from_drv_data); + +const struct hdmi_disp_mode_timing_type *hdmi_common_get_mode(uint32 mode) +{ + if (mode >= HDMI_VFRMT_MAX) + return NULL; + + return &hdmi_common_supported_video_mode_lut[mode]; +} +EXPORT_SYMBOL(hdmi_common_get_mode); + +const struct hdmi_disp_mode_timing_type *hdmi_common_get_supported_mode( + uint32 mode) +{ + const struct hdmi_disp_mode_timing_type *ret + = hdmi_common_get_mode(mode); + + if (ret == NULL || !ret->supported) + return NULL; + return ret; +} +EXPORT_SYMBOL(hdmi_common_get_supported_mode); + +const struct hdmi_disp_mode_timing_type *hdmi_mhl_get_mode(uint32 mode) +{ + if (mode >= HDMI_VFRMT_MAX) + return NULL; + + return &hdmi_mhl_supported_video_mode_lut[mode]; +} +EXPORT_SYMBOL(hdmi_mhl_get_mode); + +const struct hdmi_disp_mode_timing_type *hdmi_mhl_get_supported_mode( + uint32 mode) +{ + const struct hdmi_disp_mode_timing_type *ret + = hdmi_mhl_get_mode(mode); + + if (ret == NULL || !ret->supported) + return NULL; + return ret; +} +EXPORT_SYMBOL(hdmi_mhl_get_supported_mode); + +void hdmi_common_init_panel_info(struct msm_panel_info *pinfo) +{ + const struct hdmi_disp_mode_timing_type *timing = + hdmi_common_get_supported_mode( + external_common_state->video_resolution); + + if (timing == NULL) + return; + + pinfo->xres = timing->active_h; + pinfo->yres = timing->active_v; + pinfo->clk_rate = timing->pixel_freq*1000; + pinfo->frame_rate = 60; + + pinfo->lcdc.h_back_porch = timing->back_porch_h; + pinfo->lcdc.h_front_porch = timing->front_porch_h; + pinfo->lcdc.h_pulse_width = timing->pulse_width_h; + pinfo->lcdc.v_back_porch = timing->back_porch_v; + pinfo->lcdc.v_front_porch = timing->front_porch_v; + pinfo->lcdc.v_pulse_width = timing->pulse_width_v; + + pinfo->type = DTV_PANEL; + pinfo->pdest = DISPLAY_2; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + if (hdmi_prim_display) + pinfo->fb_num = 2; + else + pinfo->fb_num = 1; + + /* blk */ + pinfo->lcdc.border_clr = 0; + /* blue */ + pinfo->lcdc.underflow_clr = 0xff; + pinfo->lcdc.hsync_skew = 0; +} +EXPORT_SYMBOL(hdmi_common_init_panel_info); +#endif diff --git a/drivers/video/msm/external_common.h b/drivers/video/msm/external_common.h new file mode 100644 index 0000000000000000000000000000000000000000..f27e50dec598d93a1c2c1a1ec6c4f18635a79ce2 --- /dev/null +++ b/drivers/video/msm/external_common.h @@ -0,0 +1,272 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __EXTERNAL_COMMON_H__ +#define __EXTERNAL_COMMON_H__ +#include + +#ifdef DEBUG +#ifndef DEV_DBG_PREFIX +#define DEV_DBG_PREFIX "EXT_INTERFACE: " +#endif +#define DEV_DBG(args...) pr_debug(DEV_DBG_PREFIX args) +#else +#define DEV_DBG(args...) (void)0 +#endif /* DEBUG */ +#define DEV_INFO(args...) dev_info(external_common_state->dev, args) +#define DEV_WARN(args...) dev_warn(external_common_state->dev, args) +#define DEV_ERR(args...) dev_err(external_common_state->dev, args) + +#ifdef CONFIG_FB_MSM_TVOUT +#define TVOUT_VFRMT_NTSC_M_720x480i 0 +#define TVOUT_VFRMT_NTSC_J_720x480i 1 +#define TVOUT_VFRMT_PAL_BDGHIN_720x576i 2 +#define TVOUT_VFRMT_PAL_M_720x480i 3 +#define TVOUT_VFRMT_PAL_N_720x480i 4 +#elif defined(CONFIG_FB_MSM_HDMI_COMMON) +/* all video formats defined by EIA CEA 861D */ +#define HDMI_VFRMT_640x480p60_4_3 0 +#define HDMI_VFRMT_720x480p60_4_3 1 +#define HDMI_VFRMT_720x480p60_16_9 2 +#define HDMI_VFRMT_1280x720p60_16_9 3 +#define HDMI_VFRMT_1920x1080i60_16_9 4 +#define HDMI_VFRMT_720x480i60_4_3 5 +#define HDMI_VFRMT_1440x480i60_4_3 HDMI_VFRMT_720x480i60_4_3 +#define HDMI_VFRMT_720x480i60_16_9 6 +#define HDMI_VFRMT_1440x480i60_16_9 HDMI_VFRMT_720x480i60_16_9 +#define HDMI_VFRMT_720x240p60_4_3 7 +#define HDMI_VFRMT_1440x240p60_4_3 HDMI_VFRMT_720x240p60_4_3 +#define HDMI_VFRMT_720x240p60_16_9 8 +#define HDMI_VFRMT_1440x240p60_16_9 HDMI_VFRMT_720x240p60_16_9 +#define HDMI_VFRMT_2880x480i60_4_3 9 +#define HDMI_VFRMT_2880x480i60_16_9 10 +#define HDMI_VFRMT_2880x240p60_4_3 11 +#define HDMI_VFRMT_2880x240p60_16_9 12 +#define HDMI_VFRMT_1440x480p60_4_3 13 +#define HDMI_VFRMT_1440x480p60_16_9 14 +#define HDMI_VFRMT_1920x1080p60_16_9 15 +#define HDMI_VFRMT_720x576p50_4_3 16 +#define HDMI_VFRMT_720x576p50_16_9 17 +#define HDMI_VFRMT_1280x720p50_16_9 18 +#define HDMI_VFRMT_1920x1080i50_16_9 19 +#define HDMI_VFRMT_720x576i50_4_3 20 +#define HDMI_VFRMT_1440x576i50_4_3 HDMI_VFRMT_720x576i50_4_3 +#define HDMI_VFRMT_720x576i50_16_9 21 +#define HDMI_VFRMT_1440x576i50_16_9 HDMI_VFRMT_720x576i50_16_9 +#define HDMI_VFRMT_720x288p50_4_3 22 +#define HDMI_VFRMT_1440x288p50_4_3 HDMI_VFRMT_720x288p50_4_3 +#define HDMI_VFRMT_720x288p50_16_9 23 +#define HDMI_VFRMT_1440x288p50_16_9 HDMI_VFRMT_720x288p50_16_9 +#define HDMI_VFRMT_2880x576i50_4_3 24 +#define HDMI_VFRMT_2880x576i50_16_9 25 +#define HDMI_VFRMT_2880x288p50_4_3 26 +#define HDMI_VFRMT_2880x288p50_16_9 27 +#define HDMI_VFRMT_1440x576p50_4_3 28 +#define HDMI_VFRMT_1440x576p50_16_9 29 +#define HDMI_VFRMT_1920x1080p50_16_9 30 +#define HDMI_VFRMT_1920x1080p24_16_9 31 +#define HDMI_VFRMT_1920x1080p25_16_9 32 +#define HDMI_VFRMT_1920x1080p30_16_9 33 +#define HDMI_VFRMT_2880x480p60_4_3 34 +#define HDMI_VFRMT_2880x480p60_16_9 35 +#define HDMI_VFRMT_2880x576p50_4_3 36 +#define HDMI_VFRMT_2880x576p50_16_9 37 +#define HDMI_VFRMT_1920x1250i50_16_9 38 +#define HDMI_VFRMT_1920x1080i100_16_9 39 +#define HDMI_VFRMT_1280x720p100_16_9 40 +#define HDMI_VFRMT_720x576p100_4_3 41 +#define HDMI_VFRMT_720x576p100_16_9 42 +#define HDMI_VFRMT_720x576i100_4_3 43 +#define HDMI_VFRMT_1440x576i100_4_3 HDMI_VFRMT_720x576i100_4_3 +#define HDMI_VFRMT_720x576i100_16_9 44 +#define HDMI_VFRMT_1440x576i100_16_9 HDMI_VFRMT_720x576i100_16_9 +#define HDMI_VFRMT_1920x1080i120_16_9 45 +#define HDMI_VFRMT_1280x720p120_16_9 46 +#define HDMI_VFRMT_720x480p120_4_3 47 +#define HDMI_VFRMT_720x480p120_16_9 48 +#define HDMI_VFRMT_720x480i120_4_3 49 +#define HDMI_VFRMT_1440x480i120_4_3 HDMI_VFRMT_720x480i120_4_3 +#define HDMI_VFRMT_720x480i120_16_9 50 +#define HDMI_VFRMT_1440x480i120_16_9 HDMI_VFRMT_720x480i120_16_9 +#define HDMI_VFRMT_720x576p200_4_3 51 +#define HDMI_VFRMT_720x576p200_16_9 52 +#define HDMI_VFRMT_720x576i200_4_3 53 +#define HDMI_VFRMT_1440x576i200_4_3 HDMI_VFRMT_720x576i200_4_3 +#define HDMI_VFRMT_720x576i200_16_9 54 +#define HDMI_VFRMT_1440x576i200_16_9 HDMI_VFRMT_720x576i200_16_9 +#define HDMI_VFRMT_720x480p240_4_3 55 +#define HDMI_VFRMT_720x480p240_16_9 56 +#define HDMI_VFRMT_720x480i240_4_3 57 +#define HDMI_VFRMT_1440x480i240_4_3 HDMI_VFRMT_720x480i240_4_3 +#define HDMI_VFRMT_720x480i240_16_9 58 +#define HDMI_VFRMT_1440x480i240_16_9 HDMI_VFRMT_720x480i240_16_9 +#define HDMI_VFRMT_MAX 59 +#define HDMI_VFRMT_FORCE_32BIT 0x7FFFFFFF + +struct hdmi_disp_mode_timing_type { + uint32 video_format; + uint32 active_h; + uint32 front_porch_h; + uint32 pulse_width_h; + uint32 back_porch_h; + boolean active_low_h; + uint32 active_v; + uint32 front_porch_v; + uint32 pulse_width_v; + uint32 back_porch_v; + boolean active_low_v; + /* Must divide by 1000 to get the actual frequency in MHZ */ + uint32 pixel_freq; + /* Must divide by 1000 to get the actual frequency in HZ */ + uint32 refresh_rate; + boolean interlaced; + boolean supported; +}; + +#define HDMI_SETTINGS_640x480p60_4_3 \ + {HDMI_VFRMT_640x480p60_4_3, 640, 16, 96, 48, TRUE, \ + 480, 10, 2, 33, TRUE, 25200, 60000, FALSE, TRUE} +#define HDMI_SETTINGS_720x480p60_4_3 \ + {HDMI_VFRMT_720x480p60_4_3, 720, 16, 62, 60, TRUE, \ + 480, 9, 6, 30, TRUE, 27030, 60000, FALSE, TRUE} +#define HDMI_SETTINGS_720x480p60_16_9 \ + {HDMI_VFRMT_720x480p60_16_9, 720, 16, 62, 60, TRUE, \ + 480, 9, 6, 30, TRUE, 27030, 60000, FALSE, TRUE} +#define HDMI_SETTINGS_1280x720p60_16_9 \ + {HDMI_VFRMT_1280x720p60_16_9, 1280, 110, 40, 220, FALSE, \ + 720, 5, 5, 20, FALSE, 74250, 60000, FALSE, TRUE} +#define HDMI_SETTINGS_1920x1080i60_16_9 \ + {HDMI_VFRMT_1920x1080i60_16_9, 1920, 88, 44, 148, FALSE, \ + 540, 2, 5, 5, FALSE, 74250, 60000, FALSE, TRUE} +#define HDMI_SETTINGS_1440x480i60_4_3 \ + {HDMI_VFRMT_1440x480i60_4_3, 1440, 38, 124, 114, TRUE, \ + 240, 4, 3, 15, TRUE, 27000, 60000, TRUE, TRUE} +#define HDMI_SETTINGS_1440x480i60_16_9 \ + {HDMI_VFRMT_1440x480i60_16_9, 1440, 38, 124, 114, TRUE, \ + 240, 4, 3, 15, TRUE, 27000, 60000, TRUE, TRUE} +#define HDMI_SETTINGS_1920x1080p60_16_9 \ + {HDMI_VFRMT_1920x1080p60_16_9, 1920, 88, 44, 148, FALSE, \ + 1080, 4, 5, 36, FALSE, 148500, 60000, FALSE, TRUE} +#define HDMI_SETTINGS_720x576p50_4_3 \ + {HDMI_VFRMT_720x576p50_4_3, 720, 12, 64, 68, TRUE, \ + 576, 5, 5, 39, TRUE, 27000, 50000, FALSE, TRUE} +#define HDMI_SETTINGS_720x576p50_16_9 \ + {HDMI_VFRMT_720x576p50_16_9, 720, 12, 64, 68, TRUE, \ + 576, 5, 5, 39, TRUE, 27000, 50000, FALSE, TRUE} +#define HDMI_SETTINGS_1280x720p50_16_9 \ + {HDMI_VFRMT_1280x720p50_16_9, 1280, 440, 40, 220, FALSE, \ + 720, 5, 5, 20, FALSE, 74250, 50000, FALSE, TRUE} +#define HDMI_SETTINGS_1440x576i50_4_3 \ + {HDMI_VFRMT_1440x576i50_4_3, 1440, 24, 126, 138, TRUE, \ + 288, 2, 3, 19, TRUE, 27000, 50000, TRUE, TRUE} +#define HDMI_SETTINGS_1440x576i50_16_9 \ + {HDMI_VFRMT_1440x576i50_16_9, 1440, 24, 126, 138, TRUE, \ + 288, 2, 3, 19, TRUE, 27000, 50000, TRUE, TRUE} +#define HDMI_SETTINGS_1920x1080p50_16_9 \ + {HDMI_VFRMT_1920x1080p50_16_9, 1920, 528, 44, 148, FALSE, \ + 1080, 4, 5, 36, FALSE, 148500, 50000, FALSE, TRUE} +#define HDMI_SETTINGS_1920x1080p24_16_9 \ + {HDMI_VFRMT_1920x1080p24_16_9, 1920, 638, 44, 148, FALSE, \ + 1080, 4, 5, 36, FALSE, 74250, 24000, FALSE, TRUE} +#define HDMI_SETTINGS_1920x1080p25_16_9 \ + {HDMI_VFRMT_1920x1080p25_16_9, 1920, 528, 44, 148, FALSE, \ + 1080, 4, 5, 36, FALSE, 74250, 25000, FALSE, TRUE} +#define HDMI_SETTINGS_1920x1080p30_16_9 \ + {HDMI_VFRMT_1920x1080p30_16_9, 1920, 88, 44, 148, FALSE, \ + 1080, 4, 5, 36, FALSE, 74250, 30000, FALSE, TRUE} + +/* A lookup table for all the supported display modes by the HDMI + * hardware and driver. Use HDMI_SETUP_LUT in the module init to + * setup the LUT with the supported modes. */ +extern struct hdmi_disp_mode_timing_type + hdmi_common_supported_video_mode_lut[HDMI_VFRMT_MAX]; + +/* Structure that encapsulates all the supported display modes by the HDMI sink + * device */ +struct hdmi_disp_mode_list_type { + uint32 disp_mode_list[HDMI_VFRMT_MAX]; +#define TOP_AND_BOTTOM 0x10 +#define FRAME_PACKING 0x20 +#define SIDE_BY_SIDE_HALF 0x40 + uint32 disp_3d_mode_list[HDMI_VFRMT_MAX]; + uint32 disp_multi_3d_mode_list[16]; + uint32 disp_multi_3d_mode_list_cnt; + uint32 num_of_elements; +}; +#endif + +struct external_common_state_type { + boolean hpd_state; + struct kobject *uevent_kobj; + uint32 video_resolution; + struct device *dev; + struct switch_dev sdev; +#ifdef CONFIG_FB_MSM_HDMI_3D + boolean format_3d; + void (*switch_3d)(boolean on); +#endif +#ifdef CONFIG_FB_MSM_HDMI_COMMON + boolean hdcp_active; + boolean hpd_feature_on; + boolean hdmi_sink; + struct hdmi_disp_mode_list_type disp_mode_list; + uint8 speaker_allocation_block; + uint16 video_latency, audio_latency; + uint8 audio_data_block_cnt; + uint16 physical_address; + uint32 preferred_video_format; + uint8 pt_scan_info; + uint8 it_scan_info; + uint8 ce_scan_info; + uint8 spd_vendor_name[8]; + uint8 spd_product_description[16]; + boolean present_3d; + boolean present_hdcp; + uint32 audio_data_blocks[16]; + int (*read_edid_block)(int block, uint8 *edid_buf); + int (*hpd_feature)(int on); +#endif +}; + +/* The external interface driver needs to initialize the common state. */ +extern struct external_common_state_type *external_common_state; +extern struct mutex external_common_state_hpd_mutex; +extern struct mutex hdmi_msm_state_mutex; + +#ifdef CONFIG_FB_MSM_HDMI_COMMON +#define VFRMT_NOT_SUPPORTED(VFRMT) \ + {VFRMT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FALSE} +#define HDMI_SETUP_LUT(MODE) do { \ + struct hdmi_disp_mode_timing_type mode \ + = HDMI_SETTINGS_ ## MODE; \ + hdmi_common_supported_video_mode_lut[mode.video_format] \ + = mode; \ + } while (0) + +int hdmi_common_read_edid(void); +const char *video_format_2string(uint32 format); +bool hdmi_common_get_video_format_from_drv_data(struct msm_fb_data_type *mfd); +const struct hdmi_disp_mode_timing_type *hdmi_common_get_mode(uint32 mode); +const struct hdmi_disp_mode_timing_type *hdmi_common_get_supported_mode( + uint32 mode); +const struct hdmi_disp_mode_timing_type *hdmi_mhl_get_mode(uint32 mode); +const struct hdmi_disp_mode_timing_type *hdmi_mhl_get_supported_mode( + uint32 mode); +void hdmi_common_init_panel_info(struct msm_panel_info *pinfo); + +ssize_t video_3d_format_2string(uint32 format, char *buf); +#endif + +int external_common_state_create(struct platform_device *pdev); +void external_common_state_remove(void); + +#endif /* __EXTERNAL_COMMON_H__ */ diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c new file mode 100644 index 0000000000000000000000000000000000000000..f02c24daf05031d86a9d850c1d1dcc2e03e49189 --- /dev/null +++ b/drivers/video/msm/hdmi_msm.c @@ -0,0 +1,4817 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* #define DEBUG */ +#define DEV_DBG_PREFIX "HDMI: " +/* #define REG_DUMP */ + +#define CEC_MSG_PRINT +#define TOGGLE_CEC_HARDWARE_FSM + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" +#include "hdmi_msm.h" + +/* Supported HDMI Audio channels */ +#define MSM_HDMI_AUDIO_CHANNEL_2 0 +#define MSM_HDMI_AUDIO_CHANNEL_4 1 +#define MSM_HDMI_AUDIO_CHANNEL_6 2 +#define MSM_HDMI_AUDIO_CHANNEL_8 3 +#define MSM_HDMI_AUDIO_CHANNEL_MAX 4 +#define MSM_HDMI_AUDIO_CHANNEL_FORCE_32BIT 0x7FFFFFFF + +/* Supported HDMI Audio sample rates */ +#define MSM_HDMI_SAMPLE_RATE_32KHZ 0 +#define MSM_HDMI_SAMPLE_RATE_44_1KHZ 1 +#define MSM_HDMI_SAMPLE_RATE_48KHZ 2 +#define MSM_HDMI_SAMPLE_RATE_88_2KHZ 3 +#define MSM_HDMI_SAMPLE_RATE_96KHZ 4 +#define MSM_HDMI_SAMPLE_RATE_176_4KHZ 5 +#define MSM_HDMI_SAMPLE_RATE_192KHZ 6 +#define MSM_HDMI_SAMPLE_RATE_MAX 7 +#define MSM_HDMI_SAMPLE_RATE_FORCE_32BIT 0x7FFFFFFF + +static int msm_hdmi_sample_rate = MSM_HDMI_SAMPLE_RATE_48KHZ; + +/* HDMI/HDCP Registers */ +#define HDCP_DDC_STATUS 0x0128 +#define HDCP_DDC_CTRL_0 0x0120 +#define HDCP_DDC_CTRL_1 0x0124 +#define HDMI_DDC_CTRL 0x020C + +struct workqueue_struct *hdmi_work_queue; +struct hdmi_msm_state_type *hdmi_msm_state; + +DEFINE_MUTEX(hdmi_msm_state_mutex); +EXPORT_SYMBOL(hdmi_msm_state_mutex); +static DEFINE_MUTEX(hdcp_auth_state_mutex); + +static void hdmi_msm_dump_regs(const char *prefix); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT +static void hdmi_msm_hdcp_enable(void); +#else +static inline void hdmi_msm_hdcp_enable(void) {} +#endif + +static void hdmi_msm_turn_on(void); +static int hdmi_msm_audio_off(void); +static int hdmi_msm_read_edid(void); +static void hdmi_msm_hpd_off(void); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + +static void hdmi_msm_cec_line_latch_detect(void); + +#ifdef TOGGLE_CEC_HARDWARE_FSM +static boolean msg_send_complete = TRUE; +static boolean msg_recv_complete = TRUE; +#endif + +#define HDMI_MSM_CEC_REFTIMER_REFTIMER_ENABLE BIT(16) +#define HDMI_MSM_CEC_REFTIMER_REFTIMER(___t) (((___t)&0xFFFF) << 0) + +#define HDMI_MSM_CEC_TIME_SIGNAL_FREE_TIME(___t) (((___t)&0x1FF) << 7) +#define HDMI_MSM_CEC_TIME_ENABLE BIT(0) + +#define HDMI_MSM_CEC_ADDR_LOGICAL_ADDR(___la) (((___la)&0xFF) << 0) + +#define HDMI_MSM_CEC_CTRL_LINE_OE BIT(9) +#define HDMI_MSM_CEC_CTRL_FRAME_SIZE(___sz) (((___sz)&0x1F) << 4) +#define HDMI_MSM_CEC_CTRL_SOFT_RESET BIT(2) +#define HDMI_MSM_CEC_CTRL_SEND_TRIG BIT(1) +#define HDMI_MSM_CEC_CTRL_ENABLE BIT(0) + +#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_MASK BIT(7) +#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_ACK BIT(6) +#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_INT BIT(6) +#define HDMI_MSM_CEC_INT_MONITOR_MASK BIT(5) +#define HDMI_MSM_CEC_INT_MONITOR_ACK BIT(4) +#define HDMI_MSM_CEC_INT_MONITOR_INT BIT(4) +#define HDMI_MSM_CEC_INT_FRAME_ERROR_MASK BIT(3) +#define HDMI_MSM_CEC_INT_FRAME_ERROR_ACK BIT(2) +#define HDMI_MSM_CEC_INT_FRAME_ERROR_INT BIT(2) +#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK BIT(1) +#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_ACK BIT(0) +#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_INT BIT(0) + +#define HDMI_MSM_CEC_FRAME_WR_SUCCESS(___st) (((___st)&0xB) ==\ + (HDMI_MSM_CEC_INT_FRAME_WR_DONE_INT |\ + HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK |\ + HDMI_MSM_CEC_INT_FRAME_ERROR_MASK)) + +#define HDMI_MSM_CEC_RETRANSMIT_NUM(___num) (((___num)&0xF) << 4) +#define HDMI_MSM_CEC_RETRANSMIT_ENABLE BIT(0) + +#define HDMI_MSM_CEC_WR_DATA_DATA(___d) (((___d)&0xFF) << 8) + + +void hdmi_msm_cec_init(void) +{ + /* 0x02A8 CEC_REFTIMER */ + HDMI_OUTP(0x02A8, + HDMI_MSM_CEC_REFTIMER_REFTIMER_ENABLE + | HDMI_MSM_CEC_REFTIMER_REFTIMER(27 * 50) + ); + + /* + * 0x02A0 CEC_ADDR + * Starting with a default address of 4 + */ + HDMI_OUTP(0x02A0, HDMI_MSM_CEC_ADDR_LOGICAL_ADDR(4)); + + hdmi_msm_state->first_monitor = 0; + hdmi_msm_state->fsm_reset_done = false; + + /* 0x029C CEC_INT */ + /* Enable CEC interrupts */ + HDMI_OUTP(0x029C, \ + HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK \ + | HDMI_MSM_CEC_INT_FRAME_ERROR_MASK \ + | HDMI_MSM_CEC_INT_MONITOR_MASK \ + | HDMI_MSM_CEC_INT_FRAME_RD_DONE_MASK); + + HDMI_OUTP(0x02B0, 0x7FF << 4 | 1); + + /* + * Slight adjustment to logic 1 low periods on read, + * CEC Test 8.2-3 was failing, 8 for the + * BIT_1_ERR_RANGE_HI = 8 => 750us, the test used 775us, + * so increased this to 9 which => 800us. + */ + /* + * CEC latch up issue - To fire monitor interrupt + * for every start of message + */ + HDMI_OUTP(0x02E0, 0x880000); + + /* + * Slight adjustment to logic 0 low period on write + */ + HDMI_OUTP(0x02DC, 0x8888A888); + + /* + * Enable Signal Free Time counter and set to 7 bit periods + */ + HDMI_OUTP(0x02A4, 0x1 | (7 * 0x30) << 7); + + /* 0x028C CEC_CTRL */ + HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE); +} + +void hdmi_msm_cec_write_logical_addr(int addr) +{ + /* 0x02A0 CEC_ADDR + * LOGICAL_ADDR 7:0 NUM + */ + HDMI_OUTP(0x02A0, addr & 0xFF); +} + +void hdmi_msm_dump_cec_msg(struct hdmi_msm_cec_msg *msg) +{ +#ifdef CEC_MSG_PRINT + int i; + DEV_DBG("sender_id : %d", msg->sender_id); + DEV_DBG("recvr_id : %d", msg->recvr_id); + if (msg->frame_size < 2) { + DEV_DBG("polling message"); + return; + } + DEV_DBG("opcode : %02x", msg->opcode); + for (i = 0; i < msg->frame_size - 2; i++) + DEV_DBG("operand(%2d) : %02x", i + 1, msg->operand[i]); +#endif /* CEC_MSG_PRINT */ +} + +void hdmi_msm_cec_msg_send(struct hdmi_msm_cec_msg *msg) +{ + int i; + uint32 timeout_count = 1; + int retry = 10; + + boolean frameType = (msg->recvr_id == 15 ? BIT(0) : 0); + + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->fsm_reset_done = false; + mutex_unlock(&hdmi_msm_state_mutex); +#ifdef TOGGLE_CEC_HARDWARE_FSM + msg_send_complete = FALSE; +#endif + + INIT_COMPLETION(hdmi_msm_state->cec_frame_wr_done); + hdmi_msm_state->cec_frame_wr_status = 0; + + /* 0x0294 HDMI_MSM_CEC_RETRANSMIT */ + HDMI_OUTP(0x0294, +#ifdef DRVR_ONLY_CECT_NO_DAEMON + HDMI_MSM_CEC_RETRANSMIT_NUM(msg->retransmit) + | (msg->retransmit > 0) ? HDMI_MSM_CEC_RETRANSMIT_ENABLE : 0); +#else + HDMI_MSM_CEC_RETRANSMIT_NUM(0) | + HDMI_MSM_CEC_RETRANSMIT_ENABLE); +#endif + + /* 0x028C CEC_CTRL */ + HDMI_OUTP(0x028C, 0x1 | msg->frame_size << 4); + + /* 0x0290 CEC_WR_DATA */ + + /* header block */ + HDMI_OUTP(0x0290, + HDMI_MSM_CEC_WR_DATA_DATA(msg->sender_id << 4 | msg->recvr_id) + | frameType); + + /* data block 0 : opcode */ + HDMI_OUTP(0x0290, + HDMI_MSM_CEC_WR_DATA_DATA(msg->frame_size < 2 ? 0 : msg->opcode) + | frameType); + + /* data block 1-14 : operand 0-13 */ + for (i = 0; i < msg->frame_size - 1; i++) + HDMI_OUTP(0x0290, + HDMI_MSM_CEC_WR_DATA_DATA(msg->operand[i]) + | (msg->recvr_id == 15 ? BIT(0) : 0)); + + for (; i < 14; i++) + HDMI_OUTP(0x0290, + HDMI_MSM_CEC_WR_DATA_DATA(0) + | (msg->recvr_id == 15 ? BIT(0) : 0)); + + while ((HDMI_INP(0x0298) & 1) && retry--) { + DEV_DBG("CEC line is busy(%d)\n", retry); + schedule(); + } + + /* 0x028C CEC_CTRL */ + HDMI_OUTP(0x028C, + HDMI_MSM_CEC_CTRL_LINE_OE + | HDMI_MSM_CEC_CTRL_FRAME_SIZE(msg->frame_size) + | HDMI_MSM_CEC_CTRL_SEND_TRIG + | HDMI_MSM_CEC_CTRL_ENABLE); + + timeout_count = wait_for_completion_interruptible_timeout( + &hdmi_msm_state->cec_frame_wr_done, HZ); + + if (!timeout_count) { + hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_TMOUT; + DEV_ERR("%s: timedout", __func__); + hdmi_msm_dump_cec_msg(msg); + } else { + DEV_DBG("CEC write frame done (frame len=%d)", + msg->frame_size); + hdmi_msm_dump_cec_msg(msg); + } + +#ifdef TOGGLE_CEC_HARDWARE_FSM + if (!msg_recv_complete) { + /* Toggle CEC hardware FSM */ + HDMI_OUTP(0x028C, 0x0); + HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE); + msg_recv_complete = TRUE; + } + msg_send_complete = TRUE; +#else + HDMI_OUTP(0x028C, 0x0); + HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE); +#endif +} + +void hdmi_msm_cec_line_latch_detect(void) +{ + /* + * CECT 9-5-1 + * The timer period needs to be changed to appropriate value + */ + /* + * Timedout without RD_DONE, WR_DONE or ERR_INT + * Toggle CEC hardware FSM + */ + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->first_monitor == 1) { + DEV_WARN("CEC line is probably latched up - CECT 9-5-1"); + if (!msg_recv_complete) + hdmi_msm_state->fsm_reset_done = true; + HDMI_OUTP(0x028C, 0x0); + HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE); + hdmi_msm_state->first_monitor = 0; + } + mutex_unlock(&hdmi_msm_state_mutex); +} + +void hdmi_msm_cec_msg_recv(void) +{ + uint32 data; + int i; +#ifdef DRVR_ONLY_CECT_NO_DAEMON + struct hdmi_msm_cec_msg temp_msg; +#endif + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->cec_queue_wr == hdmi_msm_state->cec_queue_rd + && hdmi_msm_state->cec_queue_full) { + mutex_unlock(&hdmi_msm_state_mutex); + DEV_ERR("CEC message queue is overflowing\n"); +#ifdef DRVR_ONLY_CECT_NO_DAEMON + /* + * Without CEC daemon: + * Compliance tests fail once the queue gets filled up. + * so reset the pointers to the start of the queue. + */ + hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start; + hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start; + hdmi_msm_state->cec_queue_full = false; +#else + return; +#endif + } + if (hdmi_msm_state->cec_queue_wr == NULL) { + DEV_ERR("%s: wp is NULL\n", __func__); + return; + } + mutex_unlock(&hdmi_msm_state_mutex); + + /* 0x02AC CEC_RD_DATA */ + data = HDMI_INP(0x02AC); + + hdmi_msm_state->cec_queue_wr->sender_id = (data & 0xF0) >> 4; + hdmi_msm_state->cec_queue_wr->recvr_id = (data & 0x0F); + hdmi_msm_state->cec_queue_wr->frame_size = (data & 0x1F00) >> 8; + DEV_DBG("Recvd init=[%u] dest=[%u] size=[%u]\n", + hdmi_msm_state->cec_queue_wr->sender_id, + hdmi_msm_state->cec_queue_wr->recvr_id, + hdmi_msm_state->cec_queue_wr->frame_size); + + if (hdmi_msm_state->cec_queue_wr->frame_size < 1) { + DEV_ERR("%s: invalid message (frame length = %d)", + __func__, hdmi_msm_state->cec_queue_wr->frame_size); + return; + } else if (hdmi_msm_state->cec_queue_wr->frame_size == 1) { + DEV_DBG("%s: polling message (dest[%x] <- init[%x])", + __func__, + hdmi_msm_state->cec_queue_wr->recvr_id, + hdmi_msm_state->cec_queue_wr->sender_id); + return; + } + + /* data block 0 : opcode */ + data = HDMI_INP(0x02AC); + hdmi_msm_state->cec_queue_wr->opcode = data & 0xFF; + + /* data block 1-14 : operand 0-13 */ + for (i = 0; i < hdmi_msm_state->cec_queue_wr->frame_size - 2; i++) { + data = HDMI_INP(0x02AC); + hdmi_msm_state->cec_queue_wr->operand[i] = data & 0xFF; + } + + for (; i < 14; i++) + hdmi_msm_state->cec_queue_wr->operand[i] = 0; + + DEV_DBG("CEC read frame done\n"); + DEV_DBG("=======================================\n"); + hdmi_msm_dump_cec_msg(hdmi_msm_state->cec_queue_wr); + DEV_DBG("=======================================\n"); + +#ifdef DRVR_ONLY_CECT_NO_DAEMON + switch (hdmi_msm_state->cec_queue_wr->opcode) { + case 0x64: + /* Set OSD String */ + DEV_INFO("Recvd OSD Str=[%x]\n",\ + hdmi_msm_state->cec_queue_wr->operand[3]); + break; + case 0x83: + /* Give Phy Addr */ + DEV_INFO("Recvd a Give Phy Addr cmd\n"); + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + /* Setup a frame for sending out phy addr */ + temp_msg.sender_id = 0x4; + + /* Broadcast */ + temp_msg.recvr_id = 0xf; + temp_msg.opcode = 0x84; + i = 0; + temp_msg.operand[i++] = 0x10; + temp_msg.operand[i++] = 0x00; + temp_msg.operand[i++] = 0x04; + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + break; + case 0xFF: + /* Abort */ + DEV_INFO("Recvd an abort cmd 0xFF\n"); + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id; + i = 0; + + /*feature abort */ + temp_msg.opcode = 0x00; + temp_msg.operand[i++] = + hdmi_msm_state->cec_queue_wr->opcode; + + /*reason for abort = "Refused" */ + temp_msg.operand[i++] = 0x04; + temp_msg.frame_size = i + 2; + hdmi_msm_dump_cec_msg(&temp_msg); + hdmi_msm_cec_msg_send(&temp_msg); + break; + case 0x046: + /* Give OSD name */ + DEV_INFO("Recvd cmd 0x046\n"); + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id; + i = 0; + + /* OSD Name */ + temp_msg.opcode = 0x47; + + /* Display control byte */ + temp_msg.operand[i++] = 0x00; + temp_msg.operand[i++] = 'H'; + temp_msg.operand[i++] = 'e'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'o'; + temp_msg.operand[i++] = ' '; + temp_msg.operand[i++] = 'W'; + temp_msg.operand[i++] = 'o'; + temp_msg.operand[i++] = 'r'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'd'; + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + break; + case 0x08F: + /* Give Device Power status */ + DEV_INFO("Recvd a Power status message\n"); + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id; + i = 0; + + /* OSD String */ + temp_msg.opcode = 0x90; + temp_msg.operand[i++] = 'H'; + temp_msg.operand[i++] = 'e'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'o'; + temp_msg.operand[i++] = ' '; + temp_msg.operand[i++] = 'W'; + temp_msg.operand[i++] = 'o'; + temp_msg.operand[i++] = 'r'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'd'; + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + break; + case 0x080: + /* Routing Change cmd */ + case 0x086: + /* Set Stream Path */ + DEV_INFO("Recvd Set Stream\n"); + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + + /*Broadcast this message*/ + temp_msg.recvr_id = 0xf; + i = 0; + temp_msg.opcode = 0x82; /* Active Source */ + temp_msg.operand[i++] = 0x10; + temp_msg.operand[i++] = 0x00; + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + + /* + * sending message + */ + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id; + i = 0; + /* opcode for Image View On */ + temp_msg.opcode = 0x04; + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + break; + case 0x44: + /* User Control Pressed */ + DEV_INFO("User Control Pressed\n"); + break; + case 0x45: + /* User Control Released */ + DEV_INFO("User Control Released\n"); + break; + default: + DEV_INFO("Recvd an unknown cmd = [%u]\n", + hdmi_msm_state->cec_queue_wr->opcode); +#ifdef __SEND_ABORT__ + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id; + i = 0; + /* opcode for feature abort */ + temp_msg.opcode = 0x00; + temp_msg.operand[i++] = + hdmi_msm_state->cec_queue_wr->opcode; + /*reason for abort = "Unrecognized opcode" */ + temp_msg.operand[i++] = 0x00; + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + break; +#else + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id; + i = 0; + /* OSD String */ + temp_msg.opcode = 0x64; + temp_msg.operand[i++] = 0x0; + temp_msg.operand[i++] = 'H'; + temp_msg.operand[i++] = 'e'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'o'; + temp_msg.operand[i++] = ' '; + temp_msg.operand[i++] = 'W'; + temp_msg.operand[i++] = 'o'; + temp_msg.operand[i++] = 'r'; + temp_msg.operand[i++] = 'l'; + temp_msg.operand[i++] = 'd'; + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + break; +#endif /* __SEND_ABORT__ */ + } + +#endif /* DRVR_ONLY_CECT_NO_DAEMON */ + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->cec_queue_wr++; + if (hdmi_msm_state->cec_queue_wr == CEC_QUEUE_END) + hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start; + if (hdmi_msm_state->cec_queue_wr == hdmi_msm_state->cec_queue_rd) + hdmi_msm_state->cec_queue_full = true; + mutex_unlock(&hdmi_msm_state_mutex); + DEV_DBG("Exiting %s()\n", __func__); +} + +void hdmi_msm_cec_one_touch_play(void) +{ + struct hdmi_msm_cec_msg temp_msg; + uint32 i = 0; + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + /* + * Broadcast this message + */ + temp_msg.recvr_id = 0xf; + i = 0; + /* Active Source */ + temp_msg.opcode = 0x82; + temp_msg.operand[i++] = 0x10; + temp_msg.operand[i++] = 0x00; + /*temp_msg.operand[i++] = 0x04;*/ + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + /* + * sending message + */ + memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg)); + temp_msg.sender_id = 0x4; + temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id; + i = 0; + /* Image View On */ + temp_msg.opcode = 0x04; + temp_msg.frame_size = i + 2; + hdmi_msm_cec_msg_send(&temp_msg); + +} +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + +uint32 hdmi_msm_get_io_base(void) +{ + return (uint32)MSM_HDMI_BASE; +} +EXPORT_SYMBOL(hdmi_msm_get_io_base); + +/* Table indicating the video format supported by the HDMI TX Core v1.0 */ +/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */ +static void hdmi_msm_setup_video_mode_lut(void) +{ + HDMI_SETUP_LUT(640x480p60_4_3); + HDMI_SETUP_LUT(720x480p60_4_3); + HDMI_SETUP_LUT(720x480p60_16_9); + HDMI_SETUP_LUT(1280x720p60_16_9); + HDMI_SETUP_LUT(1920x1080i60_16_9); + HDMI_SETUP_LUT(1440x480i60_4_3); + HDMI_SETUP_LUT(1440x480i60_16_9); + HDMI_SETUP_LUT(1920x1080p60_16_9); + HDMI_SETUP_LUT(720x576p50_4_3); + HDMI_SETUP_LUT(720x576p50_16_9); + HDMI_SETUP_LUT(1280x720p50_16_9); + HDMI_SETUP_LUT(1440x576i50_4_3); + HDMI_SETUP_LUT(1440x576i50_16_9); + HDMI_SETUP_LUT(1920x1080p50_16_9); + HDMI_SETUP_LUT(1920x1080p24_16_9); + HDMI_SETUP_LUT(1920x1080p25_16_9); + HDMI_SETUP_LUT(1920x1080p30_16_9); +} + +#ifdef PORT_DEBUG +const char *hdmi_msm_name(uint32 offset) +{ + switch (offset) { + case 0x0000: return "CTRL"; + case 0x0020: return "AUDIO_PKT_CTRL1"; + case 0x0024: return "ACR_PKT_CTRL"; + case 0x0028: return "VBI_PKT_CTRL"; + case 0x002C: return "INFOFRAME_CTRL0"; +#ifdef CONFIG_FB_MSM_HDMI_3D + case 0x0034: return "GEN_PKT_CTRL"; +#endif + case 0x003C: return "ACP"; + case 0x0040: return "GC"; + case 0x0044: return "AUDIO_PKT_CTRL2"; + case 0x0048: return "ISRC1_0"; + case 0x004C: return "ISRC1_1"; + case 0x0050: return "ISRC1_2"; + case 0x0054: return "ISRC1_3"; + case 0x0058: return "ISRC1_4"; + case 0x005C: return "ISRC2_0"; + case 0x0060: return "ISRC2_1"; + case 0x0064: return "ISRC2_2"; + case 0x0068: return "ISRC2_3"; + case 0x006C: return "AVI_INFO0"; + case 0x0070: return "AVI_INFO1"; + case 0x0074: return "AVI_INFO2"; + case 0x0078: return "AVI_INFO3"; +#ifdef CONFIG_FB_MSM_HDMI_3D + case 0x0084: return "GENERIC0_HDR"; + case 0x0088: return "GENERIC0_0"; + case 0x008C: return "GENERIC0_1"; +#endif + case 0x00C4: return "ACR_32_0"; + case 0x00C8: return "ACR_32_1"; + case 0x00CC: return "ACR_44_0"; + case 0x00D0: return "ACR_44_1"; + case 0x00D4: return "ACR_48_0"; + case 0x00D8: return "ACR_48_1"; + case 0x00E4: return "AUDIO_INFO0"; + case 0x00E8: return "AUDIO_INFO1"; +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + case 0x0110: return "HDCP_CTRL"; + case 0x0114: return "HDCP_DEBUG_CTRL"; + case 0x0118: return "HDCP_INT_CTRL"; + case 0x011C: return "HDCP_LINK0_STATUS"; + case 0x012C: return "HDCP_ENTROPY_CTRL0"; + case 0x0130: return "HDCP_RESET"; + case 0x0134: return "HDCP_RCVPORT_DATA0"; + case 0x0138: return "HDCP_RCVPORT_DATA1"; + case 0x013C: return "HDCP_RCVPORT_DATA2"; + case 0x0144: return "HDCP_RCVPORT_DATA3"; + case 0x0148: return "HDCP_RCVPORT_DATA4"; + case 0x014C: return "HDCP_RCVPORT_DATA5"; + case 0x0150: return "HDCP_RCVPORT_DATA6"; + case 0x0168: return "HDCP_RCVPORT_DATA12"; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + case 0x01D0: return "AUDIO_CFG"; + case 0x0208: return "USEC_REFTIMER"; + case 0x020C: return "DDC_CTRL"; + case 0x0214: return "DDC_INT_CTRL"; + case 0x0218: return "DDC_SW_STATUS"; + case 0x021C: return "DDC_HW_STATUS"; + case 0x0220: return "DDC_SPEED"; + case 0x0224: return "DDC_SETUP"; + case 0x0228: return "DDC_TRANS0"; + case 0x022C: return "DDC_TRANS1"; + case 0x0238: return "DDC_DATA"; + case 0x0250: return "HPD_INT_STATUS"; + case 0x0254: return "HPD_INT_CTRL"; + case 0x0258: return "HPD_CTRL"; +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + case 0x025C: return "HDCP_ENTROPY_CTRL1"; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + case 0x027C: return "DDC_REF"; +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + case 0x0284: return "HDCP_SW_UPPER_AKSV"; + case 0x0288: return "HDCP_SW_LOWER_AKSV"; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + case 0x02B4: return "ACTIVE_H"; + case 0x02B8: return "ACTIVE_V"; + case 0x02BC: return "ACTIVE_V_F2"; + case 0x02C0: return "TOTAL"; + case 0x02C4: return "V_TOTAL_F2"; + case 0x02C8: return "FRAME_CTRL"; + case 0x02CC: return "AUD_INT"; + case 0x0300: return "PHY_REG0"; + case 0x0304: return "PHY_REG1"; + case 0x0308: return "PHY_REG2"; + case 0x030C: return "PHY_REG3"; + case 0x0310: return "PHY_REG4"; + case 0x0314: return "PHY_REG5"; + case 0x0318: return "PHY_REG6"; + case 0x031C: return "PHY_REG7"; + case 0x0320: return "PHY_REG8"; + case 0x0324: return "PHY_REG9"; + case 0x0328: return "PHY_REG10"; + case 0x032C: return "PHY_REG11"; + case 0x0330: return "PHY_REG12"; + default: return "???"; + } +} + +void hdmi_outp(uint32 offset, uint32 value) +{ + uint32 in_val; + + outpdw(MSM_HDMI_BASE+offset, value); + in_val = inpdw(MSM_HDMI_BASE+offset); + DEV_DBG("HDMI[%04x] => %08x [%08x] %s\n", + offset, value, in_val, hdmi_msm_name(offset)); +} + +uint32 hdmi_inp(uint32 offset) +{ + uint32 value = inpdw(MSM_HDMI_BASE+offset); + DEV_DBG("HDMI[%04x] <= %08x %s\n", + offset, value, hdmi_msm_name(offset)); + return value; +} +#endif /* DEBUG */ + +static void hdmi_msm_turn_on(void); +static int hdmi_msm_audio_off(void); +static int hdmi_msm_read_edid(void); +static void hdmi_msm_hpd_off(void); + +static void hdmi_msm_hpd_state_work(struct work_struct *work) +{ + boolean hpd_state; + char *envp[2]; + + if (!hdmi_msm_state || !hdmi_msm_state->hpd_initialized || + !MSM_HDMI_BASE) { + DEV_DBG("%s: ignored, probe failed\n", __func__); + return; + } + + DEV_DBG("%s:Got interrupt\n", __func__); + /* HPD_INT_STATUS[0x0250] */ + hpd_state = (HDMI_INP(0x0250) & 0x2) >> 1; + mutex_lock(&external_common_state_hpd_mutex); + mutex_lock(&hdmi_msm_state_mutex); + if ((external_common_state->hpd_state != hpd_state) || (hdmi_msm_state-> + hpd_prev_state != external_common_state->hpd_state)) { + external_common_state->hpd_state = hpd_state; + hdmi_msm_state->hpd_prev_state = + external_common_state->hpd_state; + DEV_DBG("%s: state not stable yet, wait again (%d|%d|%d)\n", + __func__, hdmi_msm_state->hpd_prev_state, + external_common_state->hpd_state, hpd_state); + mutex_unlock(&external_common_state_hpd_mutex); + hdmi_msm_state->hpd_stable = 0; + mutex_unlock(&hdmi_msm_state_mutex); + mod_timer(&hdmi_msm_state->hpd_state_timer, jiffies + HZ/2); + return; + } + mutex_unlock(&external_common_state_hpd_mutex); + + if (hdmi_msm_state->hpd_stable++) { + mutex_unlock(&hdmi_msm_state_mutex); + DEV_DBG("%s: no more timer, depending for IRQ now\n", + __func__); + return; + } + + hdmi_msm_state->hpd_stable = 1; + DEV_INFO("HDMI HPD: event detected\n"); + + if (!hdmi_msm_state->hpd_cable_chg_detected) { + mutex_unlock(&hdmi_msm_state_mutex); + if (hpd_state) { + if (!external_common_state-> + disp_mode_list.num_of_elements) + hdmi_msm_read_edid(); + hdmi_msm_turn_on(); + } + } else { + hdmi_msm_state->hpd_cable_chg_detected = FALSE; + mutex_unlock(&hdmi_msm_state_mutex); + /* QDSP OFF preceding the HPD event notification */ + envp[0] = "HDCP_STATE=FAIL"; + envp[1] = NULL; + DEV_INFO("HDMI HPD: QDSP OFF\n"); + kobject_uevent_env(external_common_state->uevent_kobj, + KOBJ_CHANGE, envp); + switch_set_state(&external_common_state->sdev, 0); + DEV_INFO("Hdmi state switch to %d: %s\n", + external_common_state->sdev.state, __func__); + if (hpd_state) { + hdmi_msm_read_edid(); +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + hdmi_msm_state->reauth = FALSE ; +#endif + /* Build EDID table */ + hdmi_msm_turn_on(); + DEV_INFO("HDMI HPD: sense CONNECTED: send ONLINE\n"); + kobject_uevent(external_common_state->uevent_kobj, + KOBJ_ONLINE); + hdmi_msm_hdcp_enable(); +#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + /* Send Audio for HDMI Compliance Cases*/ + envp[0] = "HDCP_STATE=PASS"; + envp[1] = NULL; + DEV_INFO("HDMI HPD: sense : send HDCP_PASS\n"); + kobject_uevent_env(external_common_state->uevent_kobj, + KOBJ_CHANGE, envp); + switch_set_state(&external_common_state->sdev, 1); + DEV_INFO("Hdmi state switch to %d: %s\n", + external_common_state->sdev.state, __func__); +#endif + } else { + DEV_INFO("HDMI HPD: sense DISCONNECTED: send OFFLINE\n" + ); + kobject_uevent(external_common_state->uevent_kobj, + KOBJ_OFFLINE); + switch_set_state(&external_common_state->sdev, 0); + DEV_INFO("Hdmi state switch to %d: %s\n", + external_common_state->sdev.state, __func__); + } + } + + /* HPD_INT_CTRL[0x0254] + * 31:10 Reserved + * 9 RCV_PLUGIN_DET_MASK receiver plug in interrupt mask. + * When programmed to 1, + * RCV_PLUGIN_DET_INT will toggle + * the interrupt line + * 8:6 Reserved + * 5 RX_INT_EN Panel RX interrupt enable + * 0: Disable + * 1: Enable + * 4 RX_INT_ACK WRITE ONLY. Panel RX interrupt + * ack + * 3 Reserved + * 2 INT_EN Panel interrupt control + * 0: Disable + * 1: Enable + * 1 INT_POLARITY Panel interrupt polarity + * 0: generate interrupt on disconnect + * 1: generate interrupt on connect + * 0 INT_ACK WRITE ONLY. Panel interrupt ack */ + /* Set IRQ for HPD */ + HDMI_OUTP(0x0254, 4 | (hpd_state ? 0 : 2)); +} + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT +static void hdmi_msm_cec_latch_work(struct work_struct *work) +{ + hdmi_msm_cec_line_latch_detect(); +} +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT +static void hdcp_deauthenticate(void); +static void hdmi_msm_hdcp_reauth_work(struct work_struct *work) +{ + + /* Don't process recursive actions */ + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->hdcp_activating) { + mutex_unlock(&hdmi_msm_state_mutex); + return; + } + mutex_unlock(&hdmi_msm_state_mutex); + + /* + * Reauth=>deauth, hdcp_auth + * hdcp_auth=>turn_on() which calls + * HDMI Core reset without informing the Audio QDSP + * this can do bad things to video playback on the HDTV + * Therefore, as surprising as it may sound do reauth + * only if the device is HDCP-capable + */ + if (external_common_state->present_hdcp) { + hdcp_deauthenticate(); + mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2); + } +} + +static void hdmi_msm_hdcp_work(struct work_struct *work) +{ + + /* Only re-enable if cable still connected */ + mutex_lock(&external_common_state_hpd_mutex); + if (external_common_state->hpd_state && + !(hdmi_msm_state->full_auth_done)) { + mutex_unlock(&external_common_state_hpd_mutex); + hdmi_msm_state->reauth = TRUE; + hdmi_msm_turn_on(); + } else + mutex_unlock(&external_common_state_hpd_mutex); +} +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +static irqreturn_t hdmi_msm_isr(int irq, void *dev_id) +{ + uint32 hpd_int_status; + uint32 hpd_int_ctrl; +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + uint32 cec_intr_status; +#endif + uint32 ddc_int_ctrl; + uint32 audio_int_val; +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + uint32 hdcp_int_val; + char *envp[2]; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + static uint32 fifo_urun_int_occurred; + static uint32 sample_drop_int_occurred; + const uint32 occurrence_limit = 5; + + if (!hdmi_msm_state || !hdmi_msm_state->hpd_initialized || + !MSM_HDMI_BASE) { + DEV_DBG("ISR ignored, probe failed\n"); + return IRQ_HANDLED; + } + + /* Process HPD Interrupt */ + /* HDMI_HPD_INT_STATUS[0x0250] */ + hpd_int_status = HDMI_INP_ND(0x0250); + /* HDMI_HPD_INT_CTRL[0x0254] */ + hpd_int_ctrl = HDMI_INP_ND(0x0254); + if ((hpd_int_ctrl & (1 << 2)) && (hpd_int_status & (1 << 0))) { + boolean cable_detected = (hpd_int_status & 2) >> 1; + + /* HDMI_HPD_INT_CTRL[0x0254] */ + /* Clear all interrupts, timer will turn IRQ back on + * Leaving the bit[2] on, else core goes off + * on getting HPD during power off + */ + HDMI_OUTP(0x0254, (1 << 2) | (1 << 0)); + + DEV_DBG("%s: HPD IRQ, Ctrl=%04x, State=%04x\n", __func__, + hpd_int_ctrl, hpd_int_status); + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->hpd_cable_chg_detected = TRUE; + + /* ensure 2 readouts */ + hdmi_msm_state->hpd_prev_state = cable_detected ? 0 : 1; + external_common_state->hpd_state = cable_detected ? 1 : 0; + hdmi_msm_state->hpd_stable = 0; + mod_timer(&hdmi_msm_state->hpd_state_timer, jiffies + HZ/2); + mutex_unlock(&hdmi_msm_state_mutex); + /* + * HDCP Compliance 1A-01: + * The Quantum Data Box 882 triggers two consecutive + * HPD events very close to each other as a part of this + * test which can trigger two parallel HDCP auth threads + * if HDCP authentication is going on and we get ISR + * then stop the authentication , rather than + * reauthenticating it again + */ + if (!(hdmi_msm_state->full_auth_done)) { + DEV_DBG("%s getting hpd while authenticating\n",\ + __func__); + mutex_lock(&hdcp_auth_state_mutex); + hdmi_msm_state->hpd_during_auth = TRUE; + mutex_unlock(&hdcp_auth_state_mutex); + } + return IRQ_HANDLED; + } + + /* Process DDC Interrupts */ + /* HDMI_DDC_INT_CTRL[0x0214] */ + ddc_int_ctrl = HDMI_INP_ND(0x0214); + if ((ddc_int_ctrl & (1 << 2)) && (ddc_int_ctrl & (1 << 0))) { + /* SW_DONE INT occured, clr it */ + HDMI_OUTP_ND(0x0214, ddc_int_ctrl | (1 << 1)); + complete(&hdmi_msm_state->ddc_sw_done); + return IRQ_HANDLED; + } + + /* FIFO Underrun Int is enabled */ + /* HDMI_AUD_INT[0x02CC] + * [3] AUD_SAM_DROP_MASK [R/W] + * [2] AUD_SAM_DROP_ACK [W], AUD_SAM_DROP_INT [R] + * [1] AUD_FIFO_URUN_MASK [R/W] + * [0] AUD_FIFO_URUN_ACK [W], AUD_FIFO_URUN_INT [R] */ + audio_int_val = HDMI_INP_ND(0x02CC); + if ((audio_int_val & (1 << 1)) && (audio_int_val & (1 << 0))) { + /* FIFO Underrun occured, clr it */ + HDMI_OUTP(0x02CC, audio_int_val | (1 << 0)); + + ++fifo_urun_int_occurred; + DEV_INFO("HDMI AUD_FIFO_URUN: %d\n", fifo_urun_int_occurred); + + if (fifo_urun_int_occurred >= occurrence_limit) { + HDMI_OUTP(0x02CC, HDMI_INP(0x02CC) & ~(1 << 1)); + DEV_INFO("HDMI AUD_FIFO_URUN: INT has been disabled " + "by the ISR after %d occurences...\n", + fifo_urun_int_occurred); + } + return IRQ_HANDLED; + } + + /* Audio Sample Drop int is enabled */ + if ((audio_int_val & (1 << 3)) && (audio_int_val & (1 << 2))) { + /* Audio Sample Drop occured, clr it */ + HDMI_OUTP(0x02CC, audio_int_val | (1 << 2)); + DEV_DBG("%s: AUD_SAM_DROP", __func__); + + ++sample_drop_int_occurred; + if (sample_drop_int_occurred >= occurrence_limit) { + HDMI_OUTP(0x02CC, HDMI_INP(0x02CC) & ~(1 << 3)); + DEV_INFO("HDMI AUD_SAM_DROP: INT has been disabled " + "by the ISR after %d occurences...\n", + sample_drop_int_occurred); + } + return IRQ_HANDLED; + } + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + /* HDCP_INT_CTRL[0x0118] + * [0] AUTH_SUCCESS_INT [R] HDCP Authentication Success + * interrupt status + * [1] AUTH_SUCCESS_ACK [W] Acknowledge bit for HDCP + * Authentication Success bit - write 1 to clear + * [2] AUTH_SUCCESS_MASK [R/W] Mask bit for HDCP Authentication + * Success interrupt - set to 1 to enable interrupt */ + hdcp_int_val = HDMI_INP_ND(0x0118); + if ((hdcp_int_val & (1 << 2)) && (hdcp_int_val & (1 << 0))) { + /* AUTH_SUCCESS_INT */ + HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 1)) & ~(1 << 0)); + DEV_INFO("HDCP: AUTH_SUCCESS_INT received\n"); + complete_all(&hdmi_msm_state->hdcp_success_done); + return IRQ_HANDLED; + } + /* [4] AUTH_FAIL_INT [R] HDCP Authentication Lost + * interrupt Status + * [5] AUTH_FAIL_ACK [W] Acknowledge bit for HDCP + * Authentication Lost bit - write 1 to clear + * [6] AUTH_FAIL_MASK [R/W] Mask bit fo HDCP Authentication + * Lost interrupt set to 1 to enable interrupt + * [7] AUTH_FAIL_INFO_ACK [W] Acknowledge bit for HDCP + * Authentication Failure Info field - write 1 to clear */ + if ((hdcp_int_val & (1 << 6)) && (hdcp_int_val & (1 << 4))) { + /* AUTH_FAIL_INT */ + /* Clear and Disable */ + HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 5)) + & ~((1 << 6) | (1 << 4))); + DEV_INFO("HDCP: AUTH_FAIL_INT received, LINK0_STATUS=0x%08x\n", + HDMI_INP_ND(0x011C)); + if (hdmi_msm_state->full_auth_done) { + envp[0] = "HDCP_STATE=FAIL"; + envp[1] = NULL; + DEV_INFO("HDMI HPD:QDSP OFF\n"); + kobject_uevent_env(external_common_state->uevent_kobj, + KOBJ_CHANGE, envp); + switch_set_state(&external_common_state->sdev, 0); + DEV_INFO("Hdmi state switch to %d: %s\n", + external_common_state->sdev.state, __func__); + mutex_lock(&hdcp_auth_state_mutex); + hdmi_msm_state->full_auth_done = FALSE; + mutex_unlock(&hdcp_auth_state_mutex); + /* Calling reauth only when authentication + * is sucessful or else we always go into + * the reauth loop + */ + queue_work(hdmi_work_queue, + &hdmi_msm_state->hdcp_reauth_work); + } + mutex_lock(&hdcp_auth_state_mutex); + /* This flag prevents other threads from re-authenticating + * after we've just authenticated (i.e., finished part3) + */ + hdmi_msm_state->full_auth_done = FALSE; + + mutex_unlock(&hdcp_auth_state_mutex); + DEV_DBG("calling reauthenticate from %s HDCP FAIL INT ", + __func__); + + /* Clear AUTH_FAIL_INFO as well */ + HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 7))); + return IRQ_HANDLED; + } + /* [8] DDC_XFER_REQ_INT [R] HDCP DDC Transfer Request + * interrupt status + * [9] DDC_XFER_REQ_ACK [W] Acknowledge bit for HDCP DDC + * Transfer Request bit - write 1 to clear + * [10] DDC_XFER_REQ_MASK [R/W] Mask bit for HDCP DDC Transfer + * Request interrupt - set to 1 to enable interrupt */ + if ((hdcp_int_val & (1 << 10)) && (hdcp_int_val & (1 << 8))) { + /* DDC_XFER_REQ_INT */ + HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 9)) & ~(1 << 8)); + if (!(hdcp_int_val & (1 << 12))) + return IRQ_HANDLED; + } + /* [12] DDC_XFER_DONE_INT [R] HDCP DDC Transfer done interrupt + * status + * [13] DDC_XFER_DONE_ACK [W] Acknowledge bit for HDCP DDC + * Transfer done bit - write 1 to clear + * [14] DDC_XFER_DONE_MASK [R/W] Mask bit for HDCP DDC Transfer + * done interrupt - set to 1 to enable interrupt */ + if ((hdcp_int_val & (1 << 14)) && (hdcp_int_val & (1 << 12))) { + /* DDC_XFER_DONE_INT */ + HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 13)) & ~(1 << 12)); + DEV_INFO("HDCP: DDC_XFER_DONE received\n"); + return IRQ_HANDLED; + } +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + /* Process CEC Interrupt */ + /* HDMI_MSM_CEC_INT[0x029C] */ + cec_intr_status = HDMI_INP_ND(0x029C); + + DEV_DBG("cec interrupt status is [%u]\n", cec_intr_status); + + if (HDMI_MSM_CEC_FRAME_WR_SUCCESS(cec_intr_status)) { + DEV_DBG("CEC_IRQ_FRAME_WR_DONE\n"); + HDMI_OUTP(0x029C, cec_intr_status | + HDMI_MSM_CEC_INT_FRAME_WR_DONE_ACK); + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_DONE; + hdmi_msm_state->first_monitor = 0; + del_timer(&hdmi_msm_state->cec_read_timer); + mutex_unlock(&hdmi_msm_state_mutex); + complete(&hdmi_msm_state->cec_frame_wr_done); + return IRQ_HANDLED; + } + if ((cec_intr_status & (1 << 2)) && (cec_intr_status & (1 << 3))) { + DEV_DBG("CEC_IRQ_FRAME_ERROR\n"); +#ifdef TOGGLE_CEC_HARDWARE_FSM + /* Toggle CEC hardware FSM */ + HDMI_OUTP(0x028C, 0x0); + HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE); +#endif + HDMI_OUTP(0x029C, cec_intr_status); + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->first_monitor = 0; + del_timer(&hdmi_msm_state->cec_read_timer); + hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_ERROR; + mutex_unlock(&hdmi_msm_state_mutex); + complete(&hdmi_msm_state->cec_frame_wr_done); + return IRQ_HANDLED; + } + + if ((cec_intr_status & (1 << 4)) && (cec_intr_status & (1 << 5))) { + DEV_DBG("CEC_IRQ_MONITOR\n"); + HDMI_OUTP(0x029C, cec_intr_status | + HDMI_MSM_CEC_INT_MONITOR_ACK); + + /* + * CECT 9-5-1 + * On the first occassion start a timer + * for few hundred ms, if it expires then + * reset the CEC block else go on with + * frame transactions as usual. + * Below adds hdmi_msm_cec_msg_recv() as an + * item into the work queue instead of running in + * interrupt context + */ + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->first_monitor == 0) { + /* This timer might have to be changed + * worst case theoritical = + * 16 bytes * 8 * 2.7msec = 346 msec + */ + mod_timer(&hdmi_msm_state->cec_read_timer, + jiffies + HZ/2); + hdmi_msm_state->first_monitor = 1; + } + mutex_unlock(&hdmi_msm_state_mutex); + return IRQ_HANDLED; + } + + if ((cec_intr_status & (1 << 6)) && (cec_intr_status & (1 << 7))) { + DEV_DBG("CEC_IRQ_FRAME_RD_DONE\n"); + + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->first_monitor = 0; + del_timer(&hdmi_msm_state->cec_read_timer); + mutex_unlock(&hdmi_msm_state_mutex); + HDMI_OUTP(0x029C, cec_intr_status | + HDMI_MSM_CEC_INT_FRAME_RD_DONE_ACK); + hdmi_msm_cec_msg_recv(); + +#ifdef TOGGLE_CEC_HARDWARE_FSM + if (!msg_send_complete) + msg_recv_complete = FALSE; + else { + /* Toggle CEC hardware FSM */ + HDMI_OUTP(0x028C, 0x0); + HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE); + } +#else + HDMI_OUTP(0x028C, 0x0); + HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE); +#endif + + return IRQ_HANDLED; + } +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + + DEV_DBG("%s: HPD, ddc_int_ctrl=%04x, " + "aud_int=%04x, cec_intr_status=%04x\n", __func__, hpd_int_ctrl, + hpd_int_status, ddc_int_ctrl, audio_int_val, + HDMI_INP_ND(0x029C)); + + return IRQ_HANDLED; +} + +static int check_hdmi_features(void) +{ + /* RAW_FEAT_CONFIG_ROW0_LSB */ + uint32 val = inpdw(QFPROM_BASE + 0x0238); + /* HDMI_DISABLE */ + boolean hdmi_disabled = (val & 0x00200000) >> 21; + /* HDCP_DISABLE */ + boolean hdcp_disabled = (val & 0x00400000) >> 22; + + DEV_DBG("Features \n", val, + hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON"); + if (hdmi_disabled) { + DEV_ERR("ERROR: HDMI disabled\n"); + return -ENODEV; + } + + if (hdcp_disabled) + DEV_WARN("WARNING: HDCP disabled\n"); + + return 0; +} + +static boolean hdmi_msm_has_hdcp(void) +{ + /* RAW_FEAT_CONFIG_ROW0_LSB, HDCP_DISABLE */ + return (inpdw(QFPROM_BASE + 0x0238) & 0x00400000) ? FALSE : TRUE; +} + +static boolean hdmi_msm_is_power_on(void) +{ + /* HDMI_CTRL, ENABLE */ + return (HDMI_INP_ND(0x0000) & 0x00000001) ? TRUE : FALSE; +} + +/* 1.2.1.2.1 DVI Operation + * HDMI compliance requires the HDMI core to support DVI as well. The + * HDMI core also supports DVI. In DVI operation there are no preambles + * and guardbands transmitted. THe TMDS encoding of video data remains + * the same as HDMI. There are no VBI or audio packets transmitted. In + * order to enable DVI mode in HDMI core, HDMI_DVI_SEL field of + * HDMI_CTRL register needs to be programmed to 0. */ +static boolean hdmi_msm_is_dvi_mode(void) +{ + /* HDMI_CTRL, HDMI_DVI_SEL */ + return (HDMI_INP_ND(0x0000) & 0x00000002) ? FALSE : TRUE; +} + +void hdmi_msm_set_mode(boolean power_on) +{ + uint32 reg_val = 0; + if (power_on) { + /* ENABLE */ + reg_val |= 0x00000001; /* Enable the block */ + if (external_common_state->hdmi_sink == 0) { + /* HDMI_DVI_SEL */ + reg_val |= 0x00000002; + if (external_common_state->present_hdcp) + /* HDMI Encryption */ + reg_val |= 0x00000004; + /* HDMI_CTRL */ + HDMI_OUTP(0x0000, reg_val); + /* HDMI_DVI_SEL */ + reg_val &= ~0x00000002; + } else { + if (external_common_state->present_hdcp) + /* HDMI_Encryption_ON */ + reg_val |= 0x00000006; + else + reg_val |= 0x00000002; + } + } else + reg_val = 0x00000002; + + /* HDMI_CTRL */ + HDMI_OUTP(0x0000, reg_val); + DEV_DBG("HDMI Core: %s\n", power_on ? "Enable" : "Disable"); +} + +static void msm_hdmi_init_ddc(void) +{ + /* 0x0220 HDMI_DDC_SPEED + [31:16] PRESCALE prescale = (m * xtal_frequency) / + (desired_i2c_speed), where m is multiply + factor, default: m = 1 + [1:0] THRESHOLD Select threshold to use to determine whether value + sampled on SDA is a 1 or 0. Specified in terms of the ratio + between the number of sampled ones and the total number of times + SDA is sampled. + * 0x0: >0 + * 0x1: 1/4 of total samples + * 0x2: 1/2 of total samples + * 0x3: 3/4 of total samples */ + /* Configure the Pre-Scale multiplier + * Configure the Threshold */ + HDMI_OUTP_ND(0x0220, (10 << 16) | (2 << 0)); + + /* + * 0x0224 HDMI_DDC_SETUP + * Setting 31:24 bits : Time units to wait before timeout + * when clock is being stalled by external sink device + */ + HDMI_OUTP_ND(0x0224, 0xff000000); + + /* 0x027C HDMI_DDC_REF + [6] REFTIMER_ENABLE Enable the timer + * 0: Disable + * 1: Enable + [15:0] REFTIMER Value to set the register in order to generate + DDC strobe. This register counts on HDCP application clock */ + /* Enable reference timer + * 27 micro-seconds */ + HDMI_OUTP_ND(0x027C, (1 << 16) | (27 << 0)); +} + +static int hdmi_msm_ddc_clear_irq(const char *what) +{ + const uint32 time_out = 0xFFFF; + uint32 time_out_count, reg_val; + + /* clear pending and enable interrupt */ + time_out_count = time_out; + do { + --time_out_count; + /* HDMI_DDC_INT_CTRL[0x0214] + [2] SW_DONE_MK Mask bit for SW_DONE_INT. Set to 1 to enable + interrupt. + [1] SW_DONE_ACK WRITE ONLY. Acknowledge bit for SW_DONE_INT. + Write 1 to clear interrupt. + [0] SW_DONE_INT READ ONLY. SW_DONE interrupt status */ + /* Clear and Enable DDC interrupt */ + /* Write */ + HDMI_OUTP_ND(0x0214, (1 << 2) | (1 << 1)); + /* Read back */ + reg_val = HDMI_INP_ND(0x0214); + } while ((reg_val & 0x1) && time_out_count); + if (!time_out_count) { + DEV_ERR("%s[%s]: timedout\n", __func__, what); + return -ETIMEDOUT; + } + + return 0; +} + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT +static int hdmi_msm_ddc_write(uint32 dev_addr, uint32 offset, + const uint8 *data_buf, uint32 data_len, const char *what) +{ + uint32 reg_val, ndx; + int status = 0, retry = 10; + uint32 time_out_count; + + if (NULL == data_buf) { + status = -EINVAL; + DEV_ERR("%s[%s]: invalid input paramter\n", __func__, what); + goto error; + } + +again: + status = hdmi_msm_ddc_clear_irq(what); + if (status) + goto error; + + /* Ensure Device Address has LSB set to 0 to indicate Slave addr read */ + dev_addr &= 0xFE; + + /* 0x0238 HDMI_DDC_DATA + [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to + 1 while writing HDMI_DDC_DATA. + [23:16] INDEX Use to set index into DDC buffer for next read or + current write, or to read index of current read or next write. + Writable only when INDEX_WRITE=1. + [15:8] DATA Use to fill or read the DDC buffer + [0] DATA_RW Select whether buffer access will be a read or write. + For writes, address auto-increments on write to HDMI_DDC_DATA. + For reads, address autoincrements on reads to HDMI_DDC_DATA. + * 0: Write + * 1: Read */ + + /* 1. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #1 + * DATA_RW = 0x1 (write) + * DATA = linkAddress (primary link address and writing) + * INDEX = 0x0 (initial offset into buffer) + * INDEX_WRITE = 0x1 (setting initial offset) */ + HDMI_OUTP_ND(0x0238, (0x1UL << 31) | (dev_addr << 8)); + + /* 2. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #2 + * DATA_RW = 0x0 (write) + * DATA = offsetAddress + * INDEX = 0x0 + * INDEX_WRITE = 0x0 (auto-increment by hardware) */ + HDMI_OUTP_ND(0x0238, offset << 8); + + /* 3. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #3 + * DATA_RW = 0x0 (write) + * DATA = data_buf[ndx] + * INDEX = 0x0 + * INDEX_WRITE = 0x0 (auto-increment by hardware) */ + for (ndx = 0; ndx < data_len; ++ndx) + HDMI_OUTP_ND(0x0238, ((uint32)data_buf[ndx]) << 8); + + /* Data setup is complete, now setup the transaction characteristics */ + + /* 0x0228 HDMI_DDC_TRANS0 + [23:16] CNT0 Byte count for first transaction (excluding the first + byte, which is usually the address). + [13] STOP0 Determines whether a stop bit will be sent after the first + transaction + * 0: NO STOP + * 1: STOP + [12] START0 Determines whether a start bit will be sent before the + first transaction + * 0: NO START + * 1: START + [8] STOP_ON_NACK0 Determines whether the current transfer will stop + if a NACK is received during the first transaction (current + transaction always stops). + * 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION + * 1: STOP ALL TRANSACTIONS, SEND STOP BIT + [0] RW0 Read/write indicator for first transaction - set to 0 for + write, 1 for read. This bit only controls HDMI_DDC behaviour - + the R/W bit in the transaction is programmed into the DDC buffer + as the LSB of the address byte. + * 0: WRITE + * 1: READ */ + + /* 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in + order to handle characteristics of portion #1 and portion #2 + * RW0 = 0x0 (write) + * START0 = 0x1 (insert START bit) + * STOP0 = 0x0 (do NOT insert STOP bit) + * CNT0 = 0x1 (single byte transaction excluding address) */ + HDMI_OUTP_ND(0x0228, (1 << 12) | (1 << 16)); + + /* 0x022C HDMI_DDC_TRANS1 + [23:16] CNT1 Byte count for second transaction (excluding the first + byte, which is usually the address). + [13] STOP1 Determines whether a stop bit will be sent after the second + transaction + * 0: NO STOP + * 1: STOP + [12] START1 Determines whether a start bit will be sent before the + second transaction + * 0: NO START + * 1: START + [8] STOP_ON_NACK1 Determines whether the current transfer will stop if + a NACK is received during the second transaction (current + transaction always stops). + * 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION + * 1: STOP ALL TRANSACTIONS, SEND STOP BIT + [0] RW1 Read/write indicator for second transaction - set to 0 for + write, 1 for read. This bit only controls HDMI_DDC behaviour - + the R/W bit in the transaction is programmed into the DDC buffer + as the LSB of the address byte. + * 0: WRITE + * 1: READ */ + + /* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in + order to handle characteristics of portion #3 + * RW1 = 0x1 (read) + * START1 = 0x1 (insert START bit) + * STOP1 = 0x1 (insert STOP bit) + * CNT1 = data_len (0xN (write N bytes of data)) + * Byte count for second transition (excluding the first + * Byte which is usually the address) */ + HDMI_OUTP_ND(0x022C, (1 << 13) | ((data_len-1) << 16)); + + /* Trigger the I2C transfer */ + /* 0x020C HDMI_DDC_CTRL + [21:20] TRANSACTION_CNT + Number of transactions to be done in current transfer. + * 0x0: transaction0 only + * 0x1: transaction0, transaction1 + * 0x2: transaction0, transaction1, transaction2 + * 0x3: transaction0, transaction1, transaction2, transaction3 + [3] SW_STATUS_RESET + Write 1 to reset HDMI_DDC_SW_STATUS flags, will reset SW_DONE, + ABORTED, TIMEOUT, SW_INTERRUPTED, BUFFER_OVERFLOW, + STOPPED_ON_NACK, NACK0, NACK1, NACK2, NACK3 + [2] SEND_RESET Set to 1 to send reset sequence (9 clocks with no + data) at start of transfer. This sequence is sent after GO is + written to 1, before the first transaction only. + [1] SOFT_RESET Write 1 to reset DDC controller + [0] GO WRITE ONLY. Write 1 to start DDC transfer. */ + + /* 6. Write to HDMI_I2C_CONTROL to kick off the hardware. + * Note that NOTHING has been transmitted on the DDC lines up to this + * point. + * TRANSACTION_CNT = 0x1 (execute transaction0 followed by + * transaction1) + * GO = 0x1 (kicks off hardware) */ + INIT_COMPLETION(hdmi_msm_state->ddc_sw_done); + HDMI_OUTP_ND(0x020C, (1 << 0) | (1 << 20)); + + time_out_count = wait_for_completion_interruptible_timeout( + &hdmi_msm_state->ddc_sw_done, HZ/2); + HDMI_OUTP_ND(0x0214, 0x2); + if (!time_out_count) { + if (retry-- > 0) { + DEV_INFO("%s[%s]: failed timout, retry=%d\n", __func__, + what, retry); + goto again; + } + status = -ETIMEDOUT; + DEV_ERR("%s[%s]: timedout, DDC SW Status=%08x, HW " + "Status=%08x, Int Ctrl=%08x\n", __func__, what, + HDMI_INP_ND(0x0218), HDMI_INP_ND(0x021C), + HDMI_INP_ND(0x0214)); + goto error; + } + + /* Read DDC status */ + reg_val = HDMI_INP_ND(0x0218); + reg_val &= 0x00001000 | 0x00002000 | 0x00004000 | 0x00008000; + + /* Check if any NACK occurred */ + if (reg_val) { + if (retry > 1) + HDMI_OUTP_ND(0x020C, BIT(3)); /* SW_STATUS_RESET */ + else + HDMI_OUTP_ND(0x020C, BIT(1)); /* SOFT_RESET */ + if (retry-- > 0) { + DEV_DBG("%s[%s]: failed NACK=%08x, retry=%d\n", + __func__, what, reg_val, retry); + msleep(100); + goto again; + } + status = -EIO; + DEV_ERR("%s[%s]: failed NACK: %08x\n", __func__, what, reg_val); + goto error; + } + + DEV_DBG("%s[%s] success\n", __func__, what); + +error: + return status; +} +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +static int hdmi_msm_ddc_read_retry(uint32 dev_addr, uint32 offset, + uint8 *data_buf, uint32 data_len, uint32 request_len, int retry, + const char *what) +{ + uint32 reg_val, ndx; + int status = 0; + uint32 time_out_count; + int log_retry_fail = retry != 1; + + if (NULL == data_buf) { + status = -EINVAL; + DEV_ERR("%s: invalid input paramter\n", __func__); + goto error; + } + +again: + status = hdmi_msm_ddc_clear_irq(what); + if (status) + goto error; + + /* Ensure Device Address has LSB set to 0 to indicate Slave addr read */ + dev_addr &= 0xFE; + + /* 0x0238 HDMI_DDC_DATA + [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to + 1 while writing HDMI_DDC_DATA. + [23:16] INDEX Use to set index into DDC buffer for next read or + current write, or to read index of current read or next write. + Writable only when INDEX_WRITE=1. + [15:8] DATA Use to fill or read the DDC buffer + [0] DATA_RW Select whether buffer access will be a read or write. + For writes, address auto-increments on write to HDMI_DDC_DATA. + For reads, address autoincrements on reads to HDMI_DDC_DATA. + * 0: Write + * 1: Read */ + + /* 1. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #1 + * DATA_RW = 0x0 (write) + * DATA = linkAddress (primary link address and writing) + * INDEX = 0x0 (initial offset into buffer) + * INDEX_WRITE = 0x1 (setting initial offset) */ + HDMI_OUTP_ND(0x0238, (0x1UL << 31) | (dev_addr << 8)); + + /* 2. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #2 + * DATA_RW = 0x0 (write) + * DATA = offsetAddress + * INDEX = 0x0 + * INDEX_WRITE = 0x0 (auto-increment by hardware) */ + HDMI_OUTP_ND(0x0238, offset << 8); + + /* 3. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #3 + * DATA_RW = 0x0 (write) + * DATA = linkAddress + 1 (primary link address 0x74 and reading) + * INDEX = 0x0 + * INDEX_WRITE = 0x0 (auto-increment by hardware) */ + HDMI_OUTP_ND(0x0238, (dev_addr | 1) << 8); + + /* Data setup is complete, now setup the transaction characteristics */ + + /* 0x0228 HDMI_DDC_TRANS0 + [23:16] CNT0 Byte count for first transaction (excluding the first + byte, which is usually the address). + [13] STOP0 Determines whether a stop bit will be sent after the first + transaction + * 0: NO STOP + * 1: STOP + [12] START0 Determines whether a start bit will be sent before the + first transaction + * 0: NO START + * 1: START + [8] STOP_ON_NACK0 Determines whether the current transfer will stop + if a NACK is received during the first transaction (current + transaction always stops). + * 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION + * 1: STOP ALL TRANSACTIONS, SEND STOP BIT + [0] RW0 Read/write indicator for first transaction - set to 0 for + write, 1 for read. This bit only controls HDMI_DDC behaviour - + the R/W bit in the transaction is programmed into the DDC buffer + as the LSB of the address byte. + * 0: WRITE + * 1: READ */ + + /* 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in + order to handle characteristics of portion #1 and portion #2 + * RW0 = 0x0 (write) + * START0 = 0x1 (insert START bit) + * STOP0 = 0x0 (do NOT insert STOP bit) + * CNT0 = 0x1 (single byte transaction excluding address) */ + HDMI_OUTP_ND(0x0228, (1 << 12) | (1 << 16)); + + /* 0x022C HDMI_DDC_TRANS1 + [23:16] CNT1 Byte count for second transaction (excluding the first + byte, which is usually the address). + [13] STOP1 Determines whether a stop bit will be sent after the second + transaction + * 0: NO STOP + * 1: STOP + [12] START1 Determines whether a start bit will be sent before the + second transaction + * 0: NO START + * 1: START + [8] STOP_ON_NACK1 Determines whether the current transfer will stop if + a NACK is received during the second transaction (current + transaction always stops). + * 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION + * 1: STOP ALL TRANSACTIONS, SEND STOP BIT + [0] RW1 Read/write indicator for second transaction - set to 0 for + write, 1 for read. This bit only controls HDMI_DDC behaviour - + the R/W bit in the transaction is programmed into the DDC buffer + as the LSB of the address byte. + * 0: WRITE + * 1: READ */ + + /* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in + order to handle characteristics of portion #3 + * RW1 = 0x1 (read) + * START1 = 0x1 (insert START bit) + * STOP1 = 0x1 (insert STOP bit) + * CNT1 = data_len (it's 128 (0x80) for a blk read) */ + HDMI_OUTP_ND(0x022C, 1 | (1 << 12) | (1 << 13) | (request_len << 16)); + + /* Trigger the I2C transfer */ + /* 0x020C HDMI_DDC_CTRL + [21:20] TRANSACTION_CNT + Number of transactions to be done in current transfer. + * 0x0: transaction0 only + * 0x1: transaction0, transaction1 + * 0x2: transaction0, transaction1, transaction2 + * 0x3: transaction0, transaction1, transaction2, transaction3 + [3] SW_STATUS_RESET + Write 1 to reset HDMI_DDC_SW_STATUS flags, will reset SW_DONE, + ABORTED, TIMEOUT, SW_INTERRUPTED, BUFFER_OVERFLOW, + STOPPED_ON_NACK, NACK0, NACK1, NACK2, NACK3 + [2] SEND_RESET Set to 1 to send reset sequence (9 clocks with no + data) at start of transfer. This sequence is sent after GO is + written to 1, before the first transaction only. + [1] SOFT_RESET Write 1 to reset DDC controller + [0] GO WRITE ONLY. Write 1 to start DDC transfer. */ + + /* 6. Write to HDMI_I2C_CONTROL to kick off the hardware. + * Note that NOTHING has been transmitted on the DDC lines up to this + * point. + * TRANSACTION_CNT = 0x1 (execute transaction0 followed by + * transaction1) + * SEND_RESET = Set to 1 to send reset sequence + * GO = 0x1 (kicks off hardware) */ + INIT_COMPLETION(hdmi_msm_state->ddc_sw_done); + HDMI_OUTP_ND(0x020C, (1 << 0) | (1 << 20)); + + time_out_count = wait_for_completion_interruptible_timeout( + &hdmi_msm_state->ddc_sw_done, HZ/2); + HDMI_OUTP_ND(0x0214, 0x2); + if (!time_out_count) { + if (retry-- > 0) { + DEV_INFO("%s: failed timout, retry=%d\n", __func__, + retry); + goto again; + } + status = -ETIMEDOUT; + DEV_ERR("%s: timedout(7), DDC SW Status=%08x, HW " + "Status=%08x, Int Ctrl=%08x\n", __func__, + HDMI_INP(0x0218), HDMI_INP(0x021C), HDMI_INP(0x0214)); + goto error; + } + + /* Read DDC status */ + reg_val = HDMI_INP_ND(0x0218); + reg_val &= 0x00001000 | 0x00002000 | 0x00004000 | 0x00008000; + + /* Check if any NACK occurred */ + if (reg_val) { + HDMI_OUTP_ND(0x020C, BIT(3)); /* SW_STATUS_RESET */ + if (retry == 1) + HDMI_OUTP_ND(0x020C, BIT(1)); /* SOFT_RESET */ + if (retry-- > 0) { + DEV_DBG("%s(%s): failed NACK=0x%08x, retry=%d, " + "dev-addr=0x%02x, offset=0x%02x, " + "length=%d\n", __func__, what, + reg_val, retry, dev_addr, + offset, data_len); + goto again; + } + status = -EIO; + if (log_retry_fail) + DEV_ERR("%s(%s): failed NACK=0x%08x, dev-addr=0x%02x, " + "offset=0x%02x, length=%d\n", __func__, what, + reg_val, dev_addr, offset, data_len); + goto error; + } + + /* 0x0238 HDMI_DDC_DATA + [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to 1 + while writing HDMI_DDC_DATA. + [23:16] INDEX Use to set index into DDC buffer for next read or + current write, or to read index of current read or next write. + Writable only when INDEX_WRITE=1. + [15:8] DATA Use to fill or read the DDC buffer + [0] DATA_RW Select whether buffer access will be a read or write. + For writes, address auto-increments on write to HDMI_DDC_DATA. + For reads, address autoincrements on reads to HDMI_DDC_DATA. + * 0: Write + * 1: Read */ + + /* 8. ALL data is now available and waiting in the DDC buffer. + * Read HDMI_I2C_DATA with the following fields set + * RW = 0x1 (read) + * DATA = BCAPS (this is field where data is pulled from) + * INDEX = 0x3 (where the data has been placed in buffer by hardware) + * INDEX_WRITE = 0x1 (explicitly define offset) */ + /* Write this data to DDC buffer */ + HDMI_OUTP_ND(0x0238, 0x1 | (3 << 16) | (1 << 31)); + + /* Discard first byte */ + HDMI_INP_ND(0x0238); + for (ndx = 0; ndx < data_len; ++ndx) { + reg_val = HDMI_INP_ND(0x0238); + data_buf[ndx] = (uint8) ((reg_val & 0x0000FF00) >> 8); + } + + DEV_DBG("%s[%s] success\n", __func__, what); + +error: + return status; +} + +static int hdmi_msm_ddc_read_edid_seg(uint32 dev_addr, uint32 offset, + uint8 *data_buf, uint32 data_len, uint32 request_len, int retry, + const char *what) +{ + uint32 reg_val, ndx; + int status = 0; + uint32 time_out_count; + int log_retry_fail = retry != 1; + int seg_addr = 0x60, seg_num = 0x01; + + if (NULL == data_buf) { + status = -EINVAL; + DEV_ERR("%s: invalid input paramter\n", __func__); + goto error; + } + +again: + status = hdmi_msm_ddc_clear_irq(what); + if (status) + goto error; + + /* Ensure Device Address has LSB set to 0 to indicate Slave addr read */ + dev_addr &= 0xFE; + + /* 0x0238 HDMI_DDC_DATA + [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to + 1 while writing HDMI_DDC_DATA. + [23:16] INDEX Use to set index into DDC buffer for next read or + current write, or to read index of current read or next write. + Writable only when INDEX_WRITE=1. + [15:8] DATA Use to fill or read the DDC buffer + [0] DATA_RW Select whether buffer access will be a read or write. + For writes, address auto-increments on write to HDMI_DDC_DATA. + For reads, address autoincrements on reads to HDMI_DDC_DATA. + * 0: Write + * 1: Read */ + + /* 1. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #1 + * DATA_RW = 0x0 (write) + * DATA = linkAddress (primary link address and writing) + * INDEX = 0x0 (initial offset into buffer) + * INDEX_WRITE = 0x1 (setting initial offset) */ + HDMI_OUTP_ND(0x0238, (0x1UL << 31) | (seg_addr << 8)); + + /* 2. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #2 + * DATA_RW = 0x0 (write) + * DATA = offsetAddress + * INDEX = 0x0 + * INDEX_WRITE = 0x0 (auto-increment by hardware) */ + HDMI_OUTP_ND(0x0238, seg_num << 8); + + /* 3. Write to HDMI_I2C_DATA with the following fields set in order to + * handle portion #3 + * DATA_RW = 0x0 (write) + * DATA = linkAddress + 1 (primary link address 0x74 and reading) + * INDEX = 0x0 + * INDEX_WRITE = 0x0 (auto-increment by hardware) */ + HDMI_OUTP_ND(0x0238, dev_addr << 8); + HDMI_OUTP_ND(0x0238, offset << 8); + HDMI_OUTP_ND(0x0238, (dev_addr | 1) << 8); + + /* Data setup is complete, now setup the transaction characteristics */ + + /* 0x0228 HDMI_DDC_TRANS0 + [23:16] CNT0 Byte count for first transaction (excluding the first + byte, which is usually the address). + [13] STOP0 Determines whether a stop bit will be sent after the first + transaction + * 0: NO STOP + * 1: STOP + [12] START0 Determines whether a start bit will be sent before the + first transaction + * 0: NO START + * 1: START + [8] STOP_ON_NACK0 Determines whether the current transfer will stop + if a NACK is received during the first transaction (current + transaction always stops). + * 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION + * 1: STOP ALL TRANSACTIONS, SEND STOP BIT + [0] RW0 Read/write indicator for first transaction - set to 0 for + write, 1 for read. This bit only controls HDMI_DDC behaviour - + the R/W bit in the transaction is programmed into the DDC buffer + as the LSB of the address byte. + * 0: WRITE + * 1: READ */ + + /* 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in + order to handle characteristics of portion #1 and portion #2 + * RW0 = 0x0 (write) + * START0 = 0x1 (insert START bit) + * STOP0 = 0x0 (do NOT insert STOP bit) + * CNT0 = 0x1 (single byte transaction excluding address) */ + HDMI_OUTP_ND(0x0228, (1 << 12) | (1 << 16)); + + /* 0x022C HDMI_DDC_TRANS1 + [23:16] CNT1 Byte count for second transaction (excluding the first + byte, which is usually the address). + [13] STOP1 Determines whether a stop bit will be sent after the second + transaction + * 0: NO STOP + * 1: STOP + [12] START1 Determines whether a start bit will be sent before the + second transaction + * 0: NO START + * 1: START + [8] STOP_ON_NACK1 Determines whether the current transfer will stop if + a NACK is received during the second transaction (current + transaction always stops). + * 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION + * 1: STOP ALL TRANSACTIONS, SEND STOP BIT + [0] RW1 Read/write indicator for second transaction - set to 0 for + write, 1 for read. This bit only controls HDMI_DDC behaviour - + the R/W bit in the transaction is programmed into the DDC buffer + as the LSB of the address byte. + * 0: WRITE + * 1: READ */ + + /* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in + order to handle characteristics of portion #3 + * RW1 = 0x1 (read) + * START1 = 0x1 (insert START bit) + * STOP1 = 0x1 (insert STOP bit) + * CNT1 = data_len (it's 128 (0x80) for a blk read) */ + HDMI_OUTP_ND(0x022C, (1 << 12) | (1 << 16)); + + /* 0x022C HDMI_DDC_TRANS2 + [23:16] CNT1 Byte count for second transaction (excluding the first + byte, which is usually the address). + [13] STOP1 Determines whether a stop bit will be sent after the second + transaction + * 0: NO STOP + * 1: STOP + [12] START1 Determines whether a start bit will be sent before the + second transaction + * 0: NO START + * 1: START + [8] STOP_ON_NACK1 Determines whether the current transfer will stop if + a NACK is received during the second transaction (current + transaction always stops). + * 0: STOP CURRENT TRANSACTION, GO TO NEXT TRANSACTION + * 1: STOP ALL TRANSACTIONS, SEND STOP BIT + [0] RW1 Read/write indicator for second transaction - set to 0 for + write, 1 for read. This bit only controls HDMI_DDC behaviour - + the R/W bit in the transaction is programmed into the DDC buffer + as the LSB of the address byte. + * 0: WRITE + * 1: READ */ + + /* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in + order to handle characteristics of portion #3 + * RW1 = 0x1 (read) + * START1 = 0x1 (insert START bit) + * STOP1 = 0x1 (insert STOP bit) + * CNT1 = data_len (it's 128 (0x80) for a blk read) */ + HDMI_OUTP_ND(0x0230, 1 | (1 << 12) | (1 << 13) | (request_len << 16)); + + /* Trigger the I2C transfer */ + /* 0x020C HDMI_DDC_CTRL + [21:20] TRANSACTION_CNT + Number of transactions to be done in current transfer. + * 0x0: transaction0 only + * 0x1: transaction0, transaction1 + * 0x2: transaction0, transaction1, transaction2 + * 0x3: transaction0, transaction1, transaction2, transaction3 + [3] SW_STATUS_RESET + Write 1 to reset HDMI_DDC_SW_STATUS flags, will reset SW_DONE, + ABORTED, TIMEOUT, SW_INTERRUPTED, BUFFER_OVERFLOW, + STOPPED_ON_NACK, NACK0, NACK1, NACK2, NACK3 + [2] SEND_RESET Set to 1 to send reset sequence (9 clocks with no + data) at start of transfer. This sequence is sent after GO is + written to 1, before the first transaction only. + [1] SOFT_RESET Write 1 to reset DDC controller + [0] GO WRITE ONLY. Write 1 to start DDC transfer. */ + + /* 6. Write to HDMI_I2C_CONTROL to kick off the hardware. + * Note that NOTHING has been transmitted on the DDC lines up to this + * point. + * TRANSACTION_CNT = 0x2 (execute transaction0 followed by + * transaction1) + * GO = 0x1 (kicks off hardware) */ + INIT_COMPLETION(hdmi_msm_state->ddc_sw_done); + HDMI_OUTP_ND(0x020C, (1 << 0) | (2 << 20)); + + time_out_count = wait_for_completion_interruptible_timeout( + &hdmi_msm_state->ddc_sw_done, HZ/2); + HDMI_OUTP_ND(0x0214, 0x2); + if (!time_out_count) { + if (retry-- > 0) { + DEV_INFO("%s: failed timout, retry=%d\n", __func__, + retry); + goto again; + } + status = -ETIMEDOUT; + DEV_ERR("%s: timedout(7), DDC SW Status=%08x, HW " + "Status=%08x, Int Ctrl=%08x\n", __func__, + HDMI_INP(0x0218), HDMI_INP(0x021C), HDMI_INP(0x0214)); + goto error; + } + + /* Read DDC status */ + reg_val = HDMI_INP_ND(0x0218); + reg_val &= 0x00001000 | 0x00002000 | 0x00004000 | 0x00008000; + + /* Check if any NACK occurred */ + if (reg_val) { + HDMI_OUTP_ND(0x020C, BIT(3)); /* SW_STATUS_RESET */ + if (retry == 1) + HDMI_OUTP_ND(0x020C, BIT(1)); /* SOFT_RESET */ + if (retry-- > 0) { + DEV_DBG("%s(%s): failed NACK=0x%08x, retry=%d, " + "dev-addr=0x%02x, offset=0x%02x, " + "length=%d\n", __func__, what, + reg_val, retry, dev_addr, + offset, data_len); + goto again; + } + status = -EIO; + if (log_retry_fail) + DEV_ERR("%s(%s): failed NACK=0x%08x, dev-addr=0x%02x, " + "offset=0x%02x, length=%d\n", __func__, what, + reg_val, dev_addr, offset, data_len); + goto error; + } + + /* 0x0238 HDMI_DDC_DATA + [31] INDEX_WRITE WRITE ONLY. To write index field, set this bit to 1 + while writing HDMI_DDC_DATA. + [23:16] INDEX Use to set index into DDC buffer for next read or + current write, or to read index of current read or next write. + Writable only when INDEX_WRITE=1. + [15:8] DATA Use to fill or read the DDC buffer + [0] DATA_RW Select whether buffer access will be a read or write. + For writes, address auto-increments on write to HDMI_DDC_DATA. + For reads, address autoincrements on reads to HDMI_DDC_DATA. + * 0: Write + * 1: Read */ + + /* 8. ALL data is now available and waiting in the DDC buffer. + * Read HDMI_I2C_DATA with the following fields set + * RW = 0x1 (read) + * DATA = BCAPS (this is field where data is pulled from) + * INDEX = 0x5 (where the data has been placed in buffer by hardware) + * INDEX_WRITE = 0x1 (explicitly define offset) */ + /* Write this data to DDC buffer */ + HDMI_OUTP_ND(0x0238, 0x1 | (5 << 16) | (1 << 31)); + + /* Discard first byte */ + HDMI_INP_ND(0x0238); + + for (ndx = 0; ndx < data_len; ++ndx) { + reg_val = HDMI_INP_ND(0x0238); + data_buf[ndx] = (uint8) ((reg_val & 0x0000FF00) >> 8); + } + + DEV_DBG("%s[%s] success\n", __func__, what); + +error: + return status; +} + + +static int hdmi_msm_ddc_read(uint32 dev_addr, uint32 offset, uint8 *data_buf, + uint32 data_len, int retry, const char *what, boolean no_align) +{ + int ret = hdmi_msm_ddc_read_retry(dev_addr, offset, data_buf, data_len, + data_len, retry, what); + if (!ret) + return 0; + if (no_align) { + return hdmi_msm_ddc_read_retry(dev_addr, offset, data_buf, + data_len, data_len, retry, what); + } else { + return hdmi_msm_ddc_read_retry(dev_addr, offset, data_buf, + data_len, 32 * ((data_len + 31) / 32), retry, what); + } +} + + +static int hdmi_msm_read_edid_block(int block, uint8 *edid_buf) +{ + int i, rc = 0; + int block_size = 0x80; + + do { + DEV_DBG("EDID: reading block(%d) with block-size=%d\n", + block, block_size); + for (i = 0; i < 0x80; i += block_size) { + /*Read EDID twice with 32bit alighnment too */ + if (block < 2) { + rc = hdmi_msm_ddc_read(0xA0, block*0x80 + i, + edid_buf+i, block_size, 1, + "EDID", FALSE); + } else { + rc = hdmi_msm_ddc_read_edid_seg(0xA0, + block*0x80 + i, edid_buf+i, block_size, + block_size, 1, "EDID"); + } + if (rc) + break; + } + + block_size /= 2; + } while (rc && (block_size >= 16)); + + return rc; +} + +static int hdmi_msm_read_edid(void) +{ + int status; + + msm_hdmi_init_ddc(); + /* Looks like we need to turn on HDMI engine before any + * DDC transaction */ + if (!hdmi_msm_is_power_on()) { + DEV_ERR("%s: failed: HDMI power is off", __func__); + status = -ENXIO; + goto error; + } + + external_common_state->read_edid_block = hdmi_msm_read_edid_block; + status = hdmi_common_read_edid(); + if (!status) + DEV_DBG("EDID: successfully read\n"); + +error: + return status; +} + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT +static void hdcp_auth_info(uint32 auth_info) +{ + switch (auth_info) { + case 0: + DEV_INFO("%s: None", __func__); + break; + case 1: + DEV_INFO("%s: Software Disabled Authentication", __func__); + break; + case 2: + DEV_INFO("%s: An Written", __func__); + break; + case 3: + DEV_INFO("%s: Invalid Aksv", __func__); + break; + case 4: + DEV_INFO("%s: Invalid Bksv", __func__); + break; + case 5: + DEV_INFO("%s: RI Mismatch (including RO)", __func__); + break; + case 6: + DEV_INFO("%s: consecutive Pj Mismatches", __func__); + break; + case 7: + DEV_INFO("%s: HPD Disconnect", __func__); + break; + case 8: + default: + DEV_INFO("%s: Reserved", __func__); + break; + } +} + +static void hdcp_key_state(uint32 key_state) +{ + switch (key_state) { + case 0: + DEV_WARN("%s: No HDCP Keys", __func__); + break; + case 1: + DEV_WARN("%s: Not Checked", __func__); + break; + case 2: + DEV_DBG("%s: Checking", __func__); + break; + case 3: + DEV_DBG("%s: HDCP Keys Valid", __func__); + break; + case 4: + DEV_WARN("%s: AKSV not valid", __func__); + break; + case 5: + DEV_WARN("%s: Checksum Mismatch", __func__); + break; + case 6: + DEV_DBG("%s: Production AKSV" + "with ENABLE_USER_DEFINED_AN=1", __func__); + break; + case 7: + default: + DEV_INFO("%s: Reserved", __func__); + break; + } +} + +static int hdmi_msm_count_one(uint8 *array, uint8 len) +{ + int i, j, count = 0; + for (i = 0; i < len; i++) + for (j = 0; j < 8; j++) + count += (((array[i] >> j) & 0x1) ? 1 : 0); + return count; +} + +static void hdcp_deauthenticate(void) +{ + int hdcp_link_status = HDMI_INP(0x011C); + + /* Disable HDCP interrupts */ + HDMI_OUTP(0x0118, 0x0); + + external_common_state->hdcp_active = FALSE; + /* 0x0130 HDCP_RESET + [0] LINK0_DEAUTHENTICATE */ + HDMI_OUTP(0x0130, 0x1); + + /* 0x0110 HDCP_CTRL + [8] ENCRYPTION_ENABLE + [0] ENABLE */ + /* encryption_enable = 0 | hdcp block enable = 1 */ + HDMI_OUTP(0x0110, 0x0); + + if (hdcp_link_status & 0x00000004) + hdcp_auth_info((hdcp_link_status & 0x000000F0) >> 4); +} + +static void check_and_clear_HDCP_DDC_Failure(void) +{ + int hdcp_ddc_ctrl1_reg; + int hdcp_ddc_status; + int failure; + int nack0; + + /* + * Check for any DDC transfer failures + * 0x0128 HDCP_DDC_STATUS + * [16] FAILED Indicates that the last HDCP HW DDC transer + * failed. This occurs when a transfer is + * attempted with HDCP DDC disabled + * (HDCP_DDC_DISABLE=1) or the number of retries + * match HDCP_DDC_RETRY_CNT + * + * [14] NACK0 Indicates that the last HDCP HW DDC transfer + * was aborted due to a NACK on the first + * transaction - cleared by writing 0 to GO bit + */ + hdcp_ddc_status = HDMI_INP(HDCP_DDC_STATUS); + failure = (hdcp_ddc_status >> 16) & 0x1; + nack0 = (hdcp_ddc_status >> 14) & 0x1; + DEV_DBG("%s: On Entry: HDCP_DDC_STATUS = 0x%x, FAILURE = %d," + "NACK0 = %d\n", __func__ , hdcp_ddc_status, failure, nack0); + + if (failure == 0x1) { + /* + * Indicates that the last HDCP HW DDC transfer failed. + * This occurs when a transfer is attempted with HDCP DDC + * disabled (HDCP_DDC_DISABLE=1) or the number of retries + * matches HDCP_DDC_RETRY_CNT. + * Failure occured, let's clear it. + */ + DEV_INFO("%s: DDC failure detected. HDCP_DDC_STATUS=0x%08x\n", + __func__, hdcp_ddc_status); + /* + * First, Disable DDC + * 0x0120 HDCP_DDC_CTRL_0 + * [0] DDC_DISABLE Determines whether HDCP Ri and Pj reads + * are done unassisted by hardware or by + * software via HDMI_DDC (HDCP provides + * interrupts to request software + * transfers) + * 0 : Use Hardware DDC + * 1 : Use Software DDC + */ + HDMI_OUTP(HDCP_DDC_CTRL_0, 0x1); + + /* + * ACK the Failure to Clear it + * 0x0124 HDCP_DDC_CTRL_1 + * [0] DDC_FAILED_ACK Write 1 to clear + * HDCP_STATUS.HDCP_DDC_FAILED + */ + hdcp_ddc_ctrl1_reg = HDMI_INP(HDCP_DDC_CTRL_1); + HDMI_OUTP(HDCP_DDC_CTRL_1, hdcp_ddc_ctrl1_reg | 0x1); + + /* Check if the FAILURE got Cleared */ + hdcp_ddc_status = HDMI_INP(HDCP_DDC_STATUS); + hdcp_ddc_status = (hdcp_ddc_status >> 16) & 0x1; + if (hdcp_ddc_status == 0x0) { + DEV_INFO("%s: HDCP DDC Failure has been cleared\n", + __func__); + } else { + DEV_WARN("%s: Error: HDCP DDC Failure DID NOT get" + "cleared\n", __func__); + } + + /* Re-Enable HDCP DDC */ + HDMI_OUTP(HDCP_DDC_CTRL_0, 0x0); + } + + if (nack0 == 0x1) { + /* + * 0x020C HDMI_DDC_CTRL + * [3] SW_STATUS_RESET Write 1 to reset HDMI_DDC_SW_STATUS + * flags, will reset SW_DONE, ABORTED, + * TIMEOUT, SW_INTERRUPTED, + * BUFFER_OVERFLOW, STOPPED_ON_NACK, NACK0, + * NACK1, NACK2, NACK3 + */ + HDMI_OUTP_ND(HDMI_DDC_CTRL, + HDMI_INP(HDMI_DDC_CTRL) | (0x1 << 3)); + msleep(20); + HDMI_OUTP_ND(HDMI_DDC_CTRL, + HDMI_INP(HDMI_DDC_CTRL) & ~(0x1 << 3)); + } + + hdcp_ddc_status = HDMI_INP(HDCP_DDC_STATUS); + + failure = (hdcp_ddc_status >> 16) & 0x1; + nack0 = (hdcp_ddc_status >> 14) & 0x1; + DEV_DBG("%s: On Exit: HDCP_DDC_STATUS = 0x%x, FAILURE = %d," + "NACK0 = %d\n", __func__ , hdcp_ddc_status, failure, nack0); +} + + +static int hdcp_authentication_part1(void) +{ + int ret = 0; + boolean is_match; + boolean is_part1_done = FALSE; + uint32 timeout_count; + uint8 bcaps; + uint8 aksv[5]; + uint32 qfprom_aksv_0, qfprom_aksv_1, link0_aksv_0, link0_aksv_1; + uint8 bksv[5]; + uint32 link0_bksv_0, link0_bksv_1; + uint8 an[8]; + uint32 link0_an_0, link0_an_1; + uint32 hpd_int_status, hpd_int_ctrl; + + + static uint8 buf[0xFF]; + memset(buf, 0, sizeof(buf)); + + if (!is_part1_done) { + is_part1_done = TRUE; + + /* Fetch aksv from QFprom, this info should be public. */ + qfprom_aksv_0 = inpdw(QFPROM_BASE + 0x000060D8); + qfprom_aksv_1 = inpdw(QFPROM_BASE + 0x000060DC); + + /* copy an and aksv to byte arrays for transmission */ + aksv[0] = qfprom_aksv_0 & 0xFF; + aksv[1] = (qfprom_aksv_0 >> 8) & 0xFF; + aksv[2] = (qfprom_aksv_0 >> 16) & 0xFF; + aksv[3] = (qfprom_aksv_0 >> 24) & 0xFF; + aksv[4] = qfprom_aksv_1 & 0xFF; + /* check there are 20 ones in AKSV */ + if (hdmi_msm_count_one(aksv, 5) != 20) { + DEV_ERR("HDCP: AKSV read from QFPROM doesn't have " + "20 1's and 20 0's, FAIL (AKSV=%02x%08x)\n", + qfprom_aksv_1, qfprom_aksv_0); + ret = -EINVAL; + goto error; + } + DEV_DBG("HDCP: AKSV=%02x%08x\n", qfprom_aksv_1, qfprom_aksv_0); + + /* 0x0288 HDCP_SW_LOWER_AKSV + [31:0] LOWER_AKSV */ + /* 0x0284 HDCP_SW_UPPER_AKSV + [7:0] UPPER_AKSV */ + + /* This is the lower 32 bits of the SW + * injected AKSV value(AKSV[31:0]) read + * from the EFUSE. It is needed for HDCP + * authentication and must be written + * before enabling HDCP. */ + HDMI_OUTP(0x0288, qfprom_aksv_0); + HDMI_OUTP(0x0284, qfprom_aksv_1); + + msm_hdmi_init_ddc(); + + /* read Bcaps at 0x40 in HDCP Port */ + ret = hdmi_msm_ddc_read(0x74, 0x40, &bcaps, 1, 5, "Bcaps", + TRUE); + if (ret) { + DEV_ERR("%s(%d): Read Bcaps failed", __func__, + __LINE__); + goto error; + } + DEV_DBG("HDCP: Bcaps=%02x\n", bcaps); + + /* HDCP setup prior to HDCP enabled */ + + /* 0x0148 HDCP_RCVPORT_DATA4 + [15:8] LINK0_AINFO + [7:0] LINK0_AKSV_1 */ + /* LINK0_AINFO = 0x2 FEATURE 1.1 on. + * = 0x0 FEATURE 1.1 off*/ + HDMI_OUTP(0x0148, 0x0); + + /* 0x012C HDCP_ENTROPY_CTRL0 + [31:0] BITS_OF_INFLUENCE_0 */ + /* 0x025C HDCP_ENTROPY_CTRL1 + [31:0] BITS_OF_INFLUENCE_1 */ + HDMI_OUTP(0x012C, 0xB1FFB0FF); + HDMI_OUTP(0x025C, 0xF00DFACE); + + /* 0x0114 HDCP_DEBUG_CTRL + [2] DEBUG_RNG_CIPHER + else default 0 */ + HDMI_OUTP(0x0114, HDMI_INP(0x0114) & 0xFFFFFFFB); + + /* 0x0110 HDCP_CTRL + [8] ENCRYPTION_ENABLE + [0] ENABLE */ + /* encryption_enable | enable */ + HDMI_OUTP(0x0110, (1 << 8) | (1 << 0)); + + /* + * Check to see if a HDCP DDC Failure is indicated in + * HDCP_DDC_STATUS. If yes, clear it. + */ + check_and_clear_HDCP_DDC_Failure(); + + /* 0x0118 HDCP_INT_CTRL + * [2] AUTH_SUCCESS_MASK [R/W] Mask bit for\ + * HDCP Authentication + * Success interrupt - set to 1 to enable interrupt + * + * [6] AUTH_FAIL_MASK [R/W] Mask bit for HDCP + * Authentication + * Lost interrupt set to 1 to enable interrupt + * + * [7] AUTH_FAIL_INFO_ACK [W] Acknwledge bit for HDCP + * Auth Failure Info field - write 1 to clear + * + * [10] DDC_XFER_REQ_MASK [R/W] Mask bit for HDCP\ + * DDC Transfer + * Request interrupt - set to 1 to enable interrupt + * + * [14] DDC_XFER_DONE_MASK [R/W] Mask bit for HDCP\ + * DDC Transfer + * done interrupt - set to 1 to enable interrupt */ + /* enable all HDCP ints */ + HDMI_OUTP(0x0118, (1 << 2) | (1 << 6) | (1 << 7)); + + /* 0x011C HDCP_LINK0_STATUS + [8] AN_0_READY + [9] AN_1_READY */ + /* wait for an0 and an1 ready bits to be set in LINK0_STATUS */ + + mutex_lock(&hdcp_auth_state_mutex); + timeout_count = 100; + while (((HDMI_INP_ND(0x011C) & (0x3 << 8)) != (0x3 << 8)) + && timeout_count--) + msleep(20); + if (!timeout_count) { + ret = -ETIMEDOUT; + DEV_ERR("%s(%d): timedout, An0=%d, An1=%d\n", + __func__, __LINE__, + (HDMI_INP_ND(0x011C) & BIT(8)) >> 8, + (HDMI_INP_ND(0x011C) & BIT(9)) >> 9); + mutex_unlock(&hdcp_auth_state_mutex); + goto error; + } + + /* 0x0168 HDCP_RCVPORT_DATA12 + [23:8] BSTATUS + [7:0] BCAPS */ + HDMI_OUTP(0x0168, bcaps); + + /* 0x014C HDCP_RCVPORT_DATA5 + [31:0] LINK0_AN_0 */ + /* read an0 calculation */ + link0_an_0 = HDMI_INP(0x014C); + + /* 0x0150 HDCP_RCVPORT_DATA6 + [31:0] LINK0_AN_1 */ + /* read an1 calculation */ + link0_an_1 = HDMI_INP(0x0150); + mutex_unlock(&hdcp_auth_state_mutex); + + /* three bits 28..30 */ + hdcp_key_state((HDMI_INP(0x011C) >> 28) & 0x7); + + /* 0x0144 HDCP_RCVPORT_DATA3 + [31:0] LINK0_AKSV_0 public key + 0x0148 HDCP_RCVPORT_DATA4 + [15:8] LINK0_AINFO + [7:0] LINK0_AKSV_1 public key */ + link0_aksv_0 = HDMI_INP(0x0144); + link0_aksv_1 = HDMI_INP(0x0148); + + /* copy an and aksv to byte arrays for transmission */ + aksv[0] = link0_aksv_0 & 0xFF; + aksv[1] = (link0_aksv_0 >> 8) & 0xFF; + aksv[2] = (link0_aksv_0 >> 16) & 0xFF; + aksv[3] = (link0_aksv_0 >> 24) & 0xFF; + aksv[4] = link0_aksv_1 & 0xFF; + + an[0] = link0_an_0 & 0xFF; + an[1] = (link0_an_0 >> 8) & 0xFF; + an[2] = (link0_an_0 >> 16) & 0xFF; + an[3] = (link0_an_0 >> 24) & 0xFF; + an[4] = link0_an_1 & 0xFF; + an[5] = (link0_an_1 >> 8) & 0xFF; + an[6] = (link0_an_1 >> 16) & 0xFF; + an[7] = (link0_an_1 >> 24) & 0xFF; + + /* Write An 8 bytes to offset 0x18 */ + ret = hdmi_msm_ddc_write(0x74, 0x18, an, 8, "An"); + if (ret) { + DEV_ERR("%s(%d): Write An failed", __func__, __LINE__); + goto error; + } + + /* Write Aksv 5 bytes to offset 0x10 */ + ret = hdmi_msm_ddc_write(0x74, 0x10, aksv, 5, "Aksv"); + if (ret) { + DEV_ERR("%s(%d): Write Aksv failed", __func__, + __LINE__); + goto error; + } + DEV_DBG("HDCP: Link0-AKSV=%02x%08x\n", + link0_aksv_1 & 0xFF, link0_aksv_0); + + /* Read Bksv 5 bytes at 0x00 in HDCP port */ + ret = hdmi_msm_ddc_read(0x74, 0x00, bksv, 5, 5, "Bksv", TRUE); + if (ret) { + DEV_ERR("%s(%d): Read BKSV failed", __func__, __LINE__); + goto error; + } + /* check there are 20 ones in BKSV */ + if (hdmi_msm_count_one(bksv, 5) != 20) { + DEV_ERR("HDCP: BKSV read from Sink doesn't have " + "20 1's and 20 0's, FAIL (BKSV=" + "%02x%02x%02x%02x%02x)\n", + bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]); + ret = -EINVAL; + goto error; + } + + link0_bksv_0 = bksv[3]; + link0_bksv_0 = (link0_bksv_0 << 8) | bksv[2]; + link0_bksv_0 = (link0_bksv_0 << 8) | bksv[1]; + link0_bksv_0 = (link0_bksv_0 << 8) | bksv[0]; + link0_bksv_1 = bksv[4]; + DEV_DBG("HDCP: BKSV=%02x%08x\n", link0_bksv_1, link0_bksv_0); + + /* 0x0134 HDCP_RCVPORT_DATA0 + [31:0] LINK0_BKSV_0 */ + HDMI_OUTP(0x0134, link0_bksv_0); + /* 0x0138 HDCP_RCVPORT_DATA1 + [31:0] LINK0_BKSV_1 */ + HDMI_OUTP(0x0138, link0_bksv_1); + DEV_DBG("HDCP: Link0-BKSV=%02x%08x\n", link0_bksv_1, + link0_bksv_0); + + /* HDMI_HPD_INT_STATUS[0x0250] */ + hpd_int_status = HDMI_INP_ND(0x0250); + /* HDMI_HPD_INT_CTRL[0x0254] */ + hpd_int_ctrl = HDMI_INP_ND(0x0254); + DEV_DBG("[SR-DEUG]: HPD_INTR_CTRL=[%u] HPD_INTR_STATUS=[%u] " + "before reading R0'\n", hpd_int_ctrl, hpd_int_status); + + /* + * HDCP Compliace Test case 1B-01: + * Wait here until all the ksv bytes have been + * read from the KSV FIFO register. + */ + msleep(125); + + /* Reading R0' 2 bytes at offset 0x08 */ + ret = hdmi_msm_ddc_read(0x74, 0x08, buf, 2, 5, "RO'", TRUE); + if (ret) { + DEV_ERR("%s(%d): Read RO's failed", __func__, + __LINE__); + goto error; + } + + DEV_DBG("HDCP: R0'=%02x%02x\n", buf[1], buf[0]); + INIT_COMPLETION(hdmi_msm_state->hdcp_success_done); + /* 0x013C HDCP_RCVPORT_DATA2_0 + [15:0] LINK0_RI */ + HDMI_OUTP(0x013C, (((uint32)buf[1]) << 8) | buf[0]); + + timeout_count = wait_for_completion_interruptible_timeout( + &hdmi_msm_state->hdcp_success_done, HZ*2); + + if (!timeout_count) { + ret = -ETIMEDOUT; + is_match = HDMI_INP(0x011C) & BIT(12); + DEV_ERR("%s(%d): timedout, Link0=<%s>\n", __func__, + __LINE__, + is_match ? "RI_MATCH" : "No RI Match INTR in time"); + if (!is_match) + goto error; + } + + /* 0x011C HDCP_LINK0_STATUS + [12] RI_MATCHES [0] MISMATCH, [1] MATCH + [0] AUTH_SUCCESS */ + /* Checking for RI, R0 Match */ + /* RI_MATCHES */ + if ((HDMI_INP(0x011C) & BIT(12)) != BIT(12)) { + ret = -EINVAL; + DEV_ERR("%s: HDCP_LINK0_STATUS[RI_MATCHES]: MISMATCH\n", + __func__); + goto error; + } + + DEV_INFO("HDCP: authentication part I, successful\n"); + is_part1_done = FALSE; + return 0; +error: + DEV_ERR("[%s]: HDCP Reauthentication\n", __func__); + is_part1_done = FALSE; + return ret; + } else { + return 1; + } +} + +static int hdmi_msm_transfer_v_h(void) +{ + /* Read V'.HO 4 Byte at offset 0x20 */ + char what[20]; + int ret; + uint8 buf[4]; + + snprintf(what, sizeof(what), "V' H0"); + ret = hdmi_msm_ddc_read(0x74, 0x20, buf, 4, 5, what, TRUE); + if (ret) { + DEV_ERR("%s: Read %s failed", __func__, what); + return ret; + } + DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ", + buf[0] , buf[1] , buf[2] , buf[3]); + + /* 0x0154 HDCP_RCVPORT_DATA7 + [31:0] V_HO */ + HDMI_OUTP(0x0154 , + (buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); + + snprintf(what, sizeof(what), "V' H1"); + ret = hdmi_msm_ddc_read(0x74, 0x24, buf, 4, 5, what, TRUE); + if (ret) { + DEV_ERR("%s: Read %s failed", __func__, what); + return ret; + } + DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ", + buf[0] , buf[1] , buf[2] , buf[3]); + + /* 0x0158 HDCP_RCVPORT_ DATA8 + [31:0] V_H1 */ + HDMI_OUTP(0x0158, + (buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); + + + snprintf(what, sizeof(what), "V' H2"); + ret = hdmi_msm_ddc_read(0x74, 0x28, buf, 4, 5, what, TRUE); + if (ret) { + DEV_ERR("%s: Read %s failed", __func__, what); + return ret; + } + DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ", + buf[0] , buf[1] , buf[2] , buf[3]); + + /* 0x015c HDCP_RCVPORT_DATA9 + [31:0] V_H2 */ + HDMI_OUTP(0x015c , + (buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); + + snprintf(what, sizeof(what), "V' H3"); + ret = hdmi_msm_ddc_read(0x74, 0x2c, buf, 4, 5, what, TRUE); + if (ret) { + DEV_ERR("%s: Read %s failed", __func__, what); + return ret; + } + DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ", + buf[0] , buf[1] , buf[2] , buf[3]); + + /* 0x0160 HDCP_RCVPORT_DATA10 + [31:0] V_H3 */ + HDMI_OUTP(0x0160, + (buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); + + snprintf(what, sizeof(what), "V' H4"); + ret = hdmi_msm_ddc_read(0x74, 0x30, buf, 4, 5, what, TRUE); + if (ret) { + DEV_ERR("%s: Read %s failed", __func__, what); + return ret; + } + DEV_DBG("buf[0]= %x , buf[1] = %x , buf[2] = %x , buf[3] = %x\n ", + buf[0] , buf[1] , buf[2] , buf[3]); + /* 0x0164 HDCP_RCVPORT_DATA11 + [31:0] V_H4 */ + HDMI_OUTP(0x0164, + (buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); + + return 0; +} + +static int hdcp_authentication_part2(void) +{ + int ret = 0; + uint32 timeout_count; + int i = 0; + int cnt = 0; + uint bstatus; + uint8 bcaps; + uint32 down_stream_devices; + uint32 ksv_bytes; + + static uint8 buf[0xFF]; + static uint8 kvs_fifo[5 * 127]; + + boolean max_devs_exceeded = 0; + boolean max_cascade_exceeded = 0; + + boolean ksv_done = FALSE; + + memset(buf, 0, sizeof(buf)); + memset(kvs_fifo, 0, sizeof(kvs_fifo)); + + /* wait until READY bit is set in bcaps */ + timeout_count = 50; + do { + timeout_count--; + /* read bcaps 1 Byte at offset 0x40 */ + ret = hdmi_msm_ddc_read(0x74, 0x40, &bcaps, 1, 1, + "Bcaps", FALSE); + if (ret) { + DEV_ERR("%s(%d): Read Bcaps failed", __func__, + __LINE__); + goto error; + } + msleep(100); + } while ((0 == (bcaps & 0x20)) && timeout_count); /* READY (Bit 5) */ + if (!timeout_count) { + ret = -ETIMEDOUT; + DEV_ERR("%s:timedout(1)", __func__); + goto error; + } + + /* read bstatus 2 bytes at offset 0x41 */ + + ret = hdmi_msm_ddc_read(0x74, 0x41, buf, 2, 5, "Bstatus", FALSE); + if (ret) { + DEV_ERR("%s(%d): Read Bstatus failed", __func__, __LINE__); + goto error; + } + bstatus = buf[1]; + bstatus = (bstatus << 8) | buf[0]; + /* 0x0168 DCP_RCVPORT_DATA12 + [7:0] BCAPS + [23:8 BSTATUS */ + HDMI_OUTP(0x0168, bcaps | (bstatus << 8)); + /* BSTATUS [6:0] DEVICE_COUNT Number of HDMI device attached to repeater + * - see HDCP spec */ + down_stream_devices = bstatus & 0x7F; + + if (down_stream_devices == 0x0) { + /* There isn't any devices attaced to the Repeater */ + DEV_ERR("%s: there isn't any devices attached to the " + "Repeater\n", __func__); + ret = -EINVAL; + goto error; + } + + /* + * HDCP Compliance 1B-05: + * Check if no. of devices connected to repeater + * exceed max_devices_connected from bit 7 of Bstatus. + */ + max_devs_exceeded = (bstatus & 0x80) >> 7; + if (max_devs_exceeded == 0x01) { + DEV_ERR("%s: Number of devs connected to repeater " + "exceeds max_devs\n", __func__); + ret = -EINVAL; + goto hdcp_error; + } + + /* + * HDCP Compliance 1B-06: + * Check if no. of cascade connected to repeater + * exceed max_cascade_connected from bit 11 of Bstatus. + */ + max_cascade_exceeded = (bstatus & 0x800) >> 11; + if (max_cascade_exceeded == 0x01) { + DEV_ERR("%s: Number of cascade connected to repeater " + "exceeds max_cascade\n", __func__); + ret = -EINVAL; + goto hdcp_error; + } + + /* Read KSV FIFO over DDC + * Key Slection vector FIFO + * Used to pull downstream KSVs from HDCP Repeaters. + * All bytes (DEVICE_COUNT * 5) must be read in a single, + * auto incrementing access. + * All bytes read as 0x00 for HDCP Receivers that are not + * HDCP Repeaters (REPEATER == 0). */ + ksv_bytes = 5 * down_stream_devices; + /* Reading KSV FIFO / KSV FIFO */ + ksv_done = FALSE; + + ret = hdmi_msm_ddc_read(0x74, 0x43, kvs_fifo, ksv_bytes, 5, + "KSV FIFO", TRUE); + do { + if (ret) { + DEV_ERR("%s(%d): Read KSV FIFO failed", + __func__, __LINE__); + /* + * HDCP Compliace Test case 1B-01: + * Wait here until all the ksv bytes have been + * read from the KSV FIFO register. + */ + msleep(25); + } else { + ksv_done = TRUE; + } + cnt++; + } while (!ksv_done && cnt != 20); + + if (ksv_done == FALSE) + goto error; + + ret = hdmi_msm_transfer_v_h(); + if (ret) + goto error; + + /* Next: Write KSV FIFO to HDCP_SHA_DATA. + * This is done 1 byte at time starting with the LSB. + * On the very last byte write, + * the HDCP_SHA_DATA_DONE bit[0] + */ + + /* 0x023C HDCP_SHA_CTRL + [0] RESET [0] Enable, [1] Reset + [4] SELECT [0] DIGA_HDCP, [1] DIGB_HDCP */ + /* reset SHA engine */ + HDMI_OUTP(0x023C, 1); + /* enable SHA engine, SEL=DIGA_HDCP */ + HDMI_OUTP(0x023C, 0); + + for (i = 0; i < ksv_bytes - 1; i++) { + /* Write KSV byte and do not set DONE bit[0] */ + HDMI_OUTP_ND(0x0244, kvs_fifo[i] << 16); + + /* Once 64 bytes have been written, we need to poll for + * HDCP_SHA_BLOCK_DONE before writing any further + */ + if (i && !((i+1)%64)) { + timeout_count = 100; + while (!(HDMI_INP_ND(0x0240) & 0x1) + && (--timeout_count)) { + DEV_DBG("HDCP Auth Part II: Waiting for the " + "computation of the current 64 byte to " + "complete. HDCP_SHA_STATUS=%08x. " + "timeout_count=%d\n", + HDMI_INP_ND(0x0240), timeout_count); + msleep(20); + } + if (!timeout_count) { + ret = -ETIMEDOUT; + DEV_ERR("%s(%d): timedout", __func__, __LINE__); + goto error; + } + } + + } + + /* Write l to DONE bit[0] */ + HDMI_OUTP_ND(0x0244, (kvs_fifo[ksv_bytes - 1] << 16) | 0x1); + + /* 0x0240 HDCP_SHA_STATUS + [4] COMP_DONE */ + /* Now wait for HDCP_SHA_COMP_DONE */ + timeout_count = 100; + while ((0x10 != (HDMI_INP_ND(0x0240) & 0xFFFFFF10)) && --timeout_count) + msleep(20); + + if (!timeout_count) { + ret = -ETIMEDOUT; + DEV_ERR("%s(%d): timedout", __func__, __LINE__); + goto error; + } + + /* 0x011C HDCP_LINK0_STATUS + [20] V_MATCHES */ + timeout_count = 100; + while (((HDMI_INP_ND(0x011C) & (1 << 20)) != (1 << 20)) + && --timeout_count) { + msleep(20); + } + + if (!timeout_count) { + ret = -ETIMEDOUT; + DEV_ERR("%s(%d): timedout", __func__, __LINE__); + goto error; + } + + DEV_INFO("HDCP: authentication part II, successful\n"); + +hdcp_error: +error: + return ret; +} + +static int hdcp_authentication_part3(uint32 found_repeater) +{ + int ret = 0; + int poll = 3000; + while (poll) { + /* 0x011C HDCP_LINK0_STATUS + [30:28] KEYS_STATE = 3 = "Valid" + [24] RO_COMPUTATION_DONE [0] Not Done, [1] Done + [20] V_MATCHES [0] Mismtach, [1] Match + [12] RI_MATCHES [0] Mismatch, [1] Match + [0] AUTH_SUCCESS */ + if (HDMI_INP_ND(0x011C) != (0x31001001 | + (found_repeater << 20))) { + DEV_ERR("HDCP: autentication part III, FAILED, " + "Link Status=%08x\n", HDMI_INP(0x011C)); + ret = -EINVAL; + goto error; + } + poll--; + } + + DEV_INFO("HDCP: authentication part III, successful\n"); + +error: + return ret; +} + +static void hdmi_msm_hdcp_enable(void) +{ + int ret = 0; + uint8 bcaps; + uint32 found_repeater = 0x0; + char *envp[2]; + + if (!hdmi_msm_has_hdcp()) { + switch_set_state(&external_common_state->sdev, 1); + DEV_INFO("Hdmi state switch to %d: %s\n", + external_common_state->sdev.state, __func__); + return; + } + + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->hdcp_activating = TRUE; + mutex_unlock(&hdmi_msm_state_mutex); + + fill_black_screen(); + + mutex_lock(&hdcp_auth_state_mutex); + /* + * Initialize this to zero here to make + * sure HPD has not happened yet + */ + hdmi_msm_state->hpd_during_auth = FALSE; + /* This flag prevents other threads from re-authenticating + * after we've just authenticated (i.e., finished part3) + * We probably need to protect this in a mutex lock */ + hdmi_msm_state->full_auth_done = FALSE; + mutex_unlock(&hdcp_auth_state_mutex); + + /* PART I Authentication*/ + ret = hdcp_authentication_part1(); + if (ret) + goto error; + + /* PART II Authentication*/ + /* read Bcaps at 0x40 in HDCP Port */ + ret = hdmi_msm_ddc_read(0x74, 0x40, &bcaps, 1, 5, "Bcaps", FALSE); + if (ret) { + DEV_ERR("%s(%d): Read Bcaps failed\n", __func__, __LINE__); + goto error; + } + DEV_DBG("HDCP: Bcaps=0x%02x (%s)\n", bcaps, + (bcaps & BIT(6)) ? "repeater" : "no repeater"); + + /* if REPEATER (Bit 6), perform Part2 Authentication */ + if (bcaps & BIT(6)) { + found_repeater = 0x1; + ret = hdcp_authentication_part2(); + if (ret) + goto error; + } else + DEV_INFO("HDCP: authentication part II skipped, no repeater\n"); + + /* PART III Authentication*/ + ret = hdcp_authentication_part3(found_repeater); + if (ret) + goto error; + + unfill_black_screen(); + + external_common_state->hdcp_active = TRUE; + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->hdcp_activating = FALSE; + mutex_unlock(&hdmi_msm_state_mutex); + + mutex_lock(&hdcp_auth_state_mutex); + /* + * This flag prevents other threads from re-authenticating + * after we've just authenticated (i.e., finished part3) + */ + hdmi_msm_state->full_auth_done = TRUE; + mutex_unlock(&hdcp_auth_state_mutex); + + if (!hdmi_msm_is_dvi_mode()) { + DEV_INFO("HDMI HPD: sense : send HDCP_PASS\n"); + envp[0] = "HDCP_STATE=PASS"; + envp[1] = NULL; + kobject_uevent_env(external_common_state->uevent_kobj, + KOBJ_CHANGE, envp); + } + switch_set_state(&external_common_state->sdev, 1); + DEV_INFO("Hdmi state switch to %d: %s\n", + external_common_state->sdev.state, __func__); + return; + +error: + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->hdcp_activating = FALSE; + mutex_unlock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->hpd_during_auth) { + DEV_WARN("Calling Deauthentication: HPD occured during " + "authentication from [%s]\n", __func__); + hdcp_deauthenticate(); + mutex_lock(&hdcp_auth_state_mutex); + hdmi_msm_state->hpd_during_auth = FALSE; + mutex_unlock(&hdcp_auth_state_mutex); + } else { + DEV_WARN("[DEV_DBG]: Calling reauth from [%s]\n", __func__); + if (hdmi_msm_state->panel_power_on) + queue_work(hdmi_work_queue, + &hdmi_msm_state->hdcp_reauth_work); + } + switch_set_state(&external_common_state->sdev, 0); + DEV_INFO("Hdmi state switch to %d: %s\n", + external_common_state->sdev.state, __func__); +} +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +static void hdmi_msm_video_setup(int video_format) +{ + uint32 total_v = 0; + uint32 total_h = 0; + uint32 start_h = 0; + uint32 end_h = 0; + uint32 start_v = 0; + uint32 end_v = 0; + const struct hdmi_disp_mode_timing_type *timing = + hdmi_common_get_supported_mode(video_format); + + /* timing register setup */ + if (timing == NULL) { + DEV_ERR("video format not supported: %d\n", video_format); + return; + } + + /* Hsync Total and Vsync Total */ + total_h = timing->active_h + timing->front_porch_h + + timing->back_porch_h + timing->pulse_width_h - 1; + total_v = timing->active_v + timing->front_porch_v + + timing->back_porch_v + timing->pulse_width_v - 1; + /* 0x02C0 HDMI_TOTAL + [27:16] V_TOTAL Vertical Total + [11:0] H_TOTAL Horizontal Total */ + HDMI_OUTP(0x02C0, ((total_v << 16) & 0x0FFF0000) + | ((total_h << 0) & 0x00000FFF)); + + /* Hsync Start and Hsync End */ + start_h = timing->back_porch_h + timing->pulse_width_h; + end_h = (total_h + 1) - timing->front_porch_h; + /* 0x02B4 HDMI_ACTIVE_H + [27:16] END Horizontal end + [11:0] START Horizontal start */ + HDMI_OUTP(0x02B4, ((end_h << 16) & 0x0FFF0000) + | ((start_h << 0) & 0x00000FFF)); + + start_v = timing->back_porch_v + timing->pulse_width_v - 1; + end_v = total_v - timing->front_porch_v; + /* 0x02B8 HDMI_ACTIVE_V + [27:16] END Vertical end + [11:0] START Vertical start */ + HDMI_OUTP(0x02B8, ((end_v << 16) & 0x0FFF0000) + | ((start_v << 0) & 0x00000FFF)); + + if (timing->interlaced) { + /* 0x02C4 HDMI_V_TOTAL_F2 + [11:0] V_TOTAL_F2 Vertical total for field2 */ + HDMI_OUTP(0x02C4, ((total_v + 1) << 0) & 0x00000FFF); + + /* 0x02BC HDMI_ACTIVE_V_F2 + [27:16] END_F2 Vertical end for field2 + [11:0] START_F2 Vertical start for Field2 */ + HDMI_OUTP(0x02BC, + (((start_v + 1) << 0) & 0x00000FFF) + | (((end_v + 1) << 16) & 0x0FFF0000)); + } else { + /* HDMI_V_TOTAL_F2 */ + HDMI_OUTP(0x02C4, 0); + /* HDMI_ACTIVE_V_F2 */ + HDMI_OUTP(0x02BC, 0); + } + + hdmi_frame_ctrl_cfg(timing); +} + +struct hdmi_msm_audio_acr { + uint32 n; /* N parameter for clock regeneration */ + uint32 cts; /* CTS parameter for clock regeneration */ +}; + +struct hdmi_msm_audio_arcs { + uint32 pclk; + struct hdmi_msm_audio_acr lut[MSM_HDMI_SAMPLE_RATE_MAX]; +}; + +#define HDMI_MSM_AUDIO_ARCS(pclk, ...) { pclk, __VA_ARGS__ } + +/* Audio constants lookup table for hdmi_msm_audio_acr_setup */ +/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */ +static const struct hdmi_msm_audio_arcs hdmi_msm_audio_acr_lut[] = { + /* 25.200MHz */ + HDMI_MSM_AUDIO_ARCS(25200, { + {4096, 25200}, {6272, 28000}, {6144, 25200}, {12544, 28000}, + {12288, 25200}, {25088, 28000}, {24576, 25200} }), + /* 27.000MHz */ + HDMI_MSM_AUDIO_ARCS(27000, { + {4096, 27000}, {6272, 30000}, {6144, 27000}, {12544, 30000}, + {12288, 27000}, {25088, 30000}, {24576, 27000} }), + /* 27.027MHz */ + HDMI_MSM_AUDIO_ARCS(27030, { + {4096, 27027}, {6272, 30030}, {6144, 27027}, {12544, 30030}, + {12288, 27027}, {25088, 30030}, {24576, 27027} }), + /* 74.250MHz */ + HDMI_MSM_AUDIO_ARCS(74250, { + {4096, 74250}, {6272, 82500}, {6144, 74250}, {12544, 82500}, + {12288, 74250}, {25088, 82500}, {24576, 74250} }), + /* 148.500MHz */ + HDMI_MSM_AUDIO_ARCS(148500, { + {4096, 148500}, {6272, 165000}, {6144, 148500}, {12544, 165000}, + {12288, 148500}, {25088, 165000}, {24576, 148500} }), +}; + +static void hdmi_msm_audio_acr_setup(boolean enabled, int video_format, + int audio_sample_rate, int num_of_channels) +{ + /* Read first before writing */ + /* HDMI_ACR_PKT_CTRL[0x0024] */ + uint32 acr_pck_ctrl_reg = HDMI_INP(0x0024); + + if (enabled) { + const struct hdmi_disp_mode_timing_type *timing = + hdmi_common_get_supported_mode(video_format); + const struct hdmi_msm_audio_arcs *audio_arc = + &hdmi_msm_audio_acr_lut[0]; + const int lut_size = sizeof(hdmi_msm_audio_acr_lut) + /sizeof(*hdmi_msm_audio_acr_lut); + uint32 i, n, cts, layout, multiplier, aud_pck_ctrl_2_reg; + + if (timing == NULL) { + DEV_WARN("%s: video format %d not supported\n", + __func__, video_format); + return; + } + + for (i = 0; i < lut_size; + audio_arc = &hdmi_msm_audio_acr_lut[++i]) { + if (audio_arc->pclk == timing->pixel_freq) + break; + } + if (i >= lut_size) { + DEV_WARN("%s: pixel clock %d not supported\n", __func__, + timing->pixel_freq); + return; + } + + n = audio_arc->lut[audio_sample_rate].n; + cts = audio_arc->lut[audio_sample_rate].cts; + layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1; + + if ((MSM_HDMI_SAMPLE_RATE_192KHZ == audio_sample_rate) || + (MSM_HDMI_SAMPLE_RATE_176_4KHZ == audio_sample_rate)) { + multiplier = 4; + n >>= 2; /* divide N by 4 and use multiplier */ + } else if ((MSM_HDMI_SAMPLE_RATE_96KHZ == audio_sample_rate) || + (MSM_HDMI_SAMPLE_RATE_88_2KHZ == audio_sample_rate)) { + multiplier = 2; + n >>= 1; /* divide N by 2 and use multiplier */ + } else { + multiplier = 1; + } + DEV_DBG("%s: n=%u, cts=%u, layout=%u\n", __func__, n, cts, + layout); + + /* AUDIO_PRIORITY | SOURCE */ + acr_pck_ctrl_reg |= 0x80000100; + /* N_MULTIPLE(multiplier) */ + acr_pck_ctrl_reg |= (multiplier & 7) << 16; + + if ((MSM_HDMI_SAMPLE_RATE_48KHZ == audio_sample_rate) || + (MSM_HDMI_SAMPLE_RATE_96KHZ == audio_sample_rate) || + (MSM_HDMI_SAMPLE_RATE_192KHZ == audio_sample_rate)) { + /* SELECT(3) */ + acr_pck_ctrl_reg |= 3 << 4; + /* CTS_48 */ + cts <<= 12; + + /* CTS: need to determine how many fractional bits */ + /* HDMI_ACR_48_0 */ + HDMI_OUTP(0x00D4, cts); + /* N */ + /* HDMI_ACR_48_1 */ + HDMI_OUTP(0x00D8, n); + } else if ((MSM_HDMI_SAMPLE_RATE_44_1KHZ == audio_sample_rate) + || (MSM_HDMI_SAMPLE_RATE_88_2KHZ == + audio_sample_rate) + || (MSM_HDMI_SAMPLE_RATE_176_4KHZ == + audio_sample_rate)) { + /* SELECT(2) */ + acr_pck_ctrl_reg |= 2 << 4; + /* CTS_44 */ + cts <<= 12; + + /* CTS: need to determine how many fractional bits */ + /* HDMI_ACR_44_0 */ + HDMI_OUTP(0x00CC, cts); + /* N */ + /* HDMI_ACR_44_1 */ + HDMI_OUTP(0x00D0, n); + } else { /* default to 32k */ + /* SELECT(1) */ + acr_pck_ctrl_reg |= 1 << 4; + /* CTS_32 */ + cts <<= 12; + + /* CTS: need to determine how many fractional bits */ + /* HDMI_ACR_32_0 */ + HDMI_OUTP(0x00C4, cts); + /* N */ + /* HDMI_ACR_32_1 */ + HDMI_OUTP(0x00C8, n); + } + /* Payload layout depends on number of audio channels */ + /* LAYOUT_SEL(layout) */ + aud_pck_ctrl_2_reg = 1 | (layout << 1); + /* override | layout */ + /* HDMI_AUDIO_PKT_CTRL2[0x00044] */ + HDMI_OUTP(0x00044, aud_pck_ctrl_2_reg); + + /* SEND | CONT */ + acr_pck_ctrl_reg |= 0x00000003; + } else { + /* ~(SEND | CONT) */ + acr_pck_ctrl_reg &= ~0x00000003; + } + /* HDMI_ACR_PKT_CTRL[0x0024] */ + HDMI_OUTP(0x0024, acr_pck_ctrl_reg); +} + +static void hdmi_msm_outpdw_chk(uint32 offset, uint32 data) +{ + uint32 check, i = 0; + +#ifdef DEBUG + HDMI_OUTP(offset, data); +#endif + do { + outpdw(MSM_HDMI_BASE+offset, data); + check = inpdw(MSM_HDMI_BASE+offset); + } while (check != data && i++ < 10); + + if (check != data) + DEV_ERR("%s: failed addr=%08x, data=%x, check=%x", + __func__, offset, data, check); +} + +static void hdmi_msm_rmw32or(uint32 offset, uint32 data) +{ + uint32 reg_data; + reg_data = inpdw(MSM_HDMI_BASE+offset); + reg_data = inpdw(MSM_HDMI_BASE+offset); + hdmi_msm_outpdw_chk(offset, reg_data | data); +} + + +#define HDMI_AUDIO_CFG 0x01D0 +#define HDMI_AUDIO_ENGINE_ENABLE 1 +#define HDMI_AUDIO_FIFO_MASK 0x000000F0 +#define HDMI_AUDIO_FIFO_WATERMARK_SHIFT 4 +#define HDMI_AUDIO_FIFO_MAX_WATER_MARK 8 + + +int hdmi_audio_enable(bool on , u32 fifo_water_mark) +{ + u32 hdmi_audio_config; + + hdmi_audio_config = HDMI_INP(HDMI_AUDIO_CFG); + + if (on) { + + if (fifo_water_mark > HDMI_AUDIO_FIFO_MAX_WATER_MARK) { + pr_err("%s : HDMI audio fifo water mark can not be more" + " than %u\n", __func__, + HDMI_AUDIO_FIFO_MAX_WATER_MARK); + return -EINVAL; + } + + /* + * Enable HDMI Audio engine. + * MUST be enabled after Audio DMA is enabled. + */ + hdmi_audio_config &= ~(HDMI_AUDIO_FIFO_MASK); + + hdmi_audio_config |= (HDMI_AUDIO_ENGINE_ENABLE | + (fifo_water_mark << HDMI_AUDIO_FIFO_WATERMARK_SHIFT)); + + } else + hdmi_audio_config &= ~(HDMI_AUDIO_ENGINE_ENABLE); + + HDMI_OUTP(HDMI_AUDIO_CFG, hdmi_audio_config); + + mb(); + pr_info("%s :HDMI_AUDIO_CFG 0x%08x\n", __func__, + HDMI_INP(HDMI_AUDIO_CFG)); + + return 0; +} +EXPORT_SYMBOL(hdmi_audio_enable); + +#define HDMI_AUDIO_PKT_CTRL 0x0020 +#define HDMI_AUDIO_SAMPLE_SEND_ENABLE 1 + +int hdmi_audio_packet_enable(bool on) +{ + u32 hdmi_audio_pkt_ctrl; + hdmi_audio_pkt_ctrl = HDMI_INP(HDMI_AUDIO_PKT_CTRL); + + if (on) + hdmi_audio_pkt_ctrl |= HDMI_AUDIO_SAMPLE_SEND_ENABLE; + else + hdmi_audio_pkt_ctrl &= ~(HDMI_AUDIO_SAMPLE_SEND_ENABLE); + + HDMI_OUTP(HDMI_AUDIO_PKT_CTRL, hdmi_audio_pkt_ctrl); + + mb(); + pr_info("%s : HDMI_AUDIO_PKT_CTRL 0x%08x\n", __func__, + HDMI_INP(HDMI_AUDIO_PKT_CTRL)); + return 0; +} +EXPORT_SYMBOL(hdmi_audio_packet_enable); + + +/* TO-DO: return -EINVAL when num_of_channels and channel_allocation + * does not match CEA 861-D spec. +*/ +int hdmi_msm_audio_info_setup(bool enabled, u32 num_of_channels, + u32 channel_allocation, u32 level_shift, bool down_mix) +{ + uint32 channel_count = 1; /* Default to 2 channels + -> See Table 17 in CEA-D spec */ + uint32 check_sum, audio_info_0_reg, audio_info_1_reg; + uint32 audio_info_ctrl_reg; + u32 aud_pck_ctrl_2_reg; + u32 layout; + + layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1; + aud_pck_ctrl_2_reg = 1 | (layout << 1); + HDMI_OUTP(0x00044, aud_pck_ctrl_2_reg); + + /* Please see table 20 Audio InfoFrame in HDMI spec + FL = front left + FC = front Center + FR = front right + FLC = front left center + FRC = front right center + RL = rear left + RC = rear center + RR = rear right + RLC = rear left center + RRC = rear right center + LFE = low frequency effect + */ + + /* Read first then write because it is bundled with other controls */ + /* HDMI_INFOFRAME_CTRL0[0x002C] */ + audio_info_ctrl_reg = HDMI_INP(0x002C); + + if (enabled) { + switch (num_of_channels) { + case MSM_HDMI_AUDIO_CHANNEL_2: + channel_allocation = 0; /* Default to FR,FL */ + break; + case MSM_HDMI_AUDIO_CHANNEL_4: + channel_count = 3; + /* FC,LFE,FR,FL */ + channel_allocation = 0x3; + break; + case MSM_HDMI_AUDIO_CHANNEL_6: + channel_count = 5; + /* RR,RL,FC,LFE,FR,FL */ + channel_allocation = 0xB; + break; + case MSM_HDMI_AUDIO_CHANNEL_8: + channel_count = 7; + /* FRC,FLC,RR,RL,FC,LFE,FR,FL */ + channel_allocation = 0x1f; + break; + default: + pr_err("%s(): Unsupported num_of_channels = %u\n", + __func__, num_of_channels); + return -EINVAL; + break; + } + + /* Program the Channel-Speaker allocation */ + audio_info_1_reg = 0; + /* CA(channel_allocation) */ + audio_info_1_reg |= channel_allocation & 0xff; + /* Program the Level shifter */ + /* LSV(level_shift) */ + audio_info_1_reg |= (level_shift << 11) & 0x00007800; + /* Program the Down-mix Inhibit Flag */ + /* DM_INH(down_mix) */ + audio_info_1_reg |= (down_mix << 15) & 0x00008000; + + /* HDMI_AUDIO_INFO1[0x00E8] */ + HDMI_OUTP(0x00E8, audio_info_1_reg); + + /* Calculate CheckSum + Sum of all the bytes in the Audio Info Packet bytes + (See table 8.4 in HDMI spec) */ + check_sum = 0; + /* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_TYPE[0x84] */ + check_sum += 0x84; + /* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_VERSION[0x01] */ + check_sum += 1; + /* HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH[0x0A] */ + check_sum += 0x0A; + check_sum += channel_count; + check_sum += channel_allocation; + /* See Table 8.5 in HDMI spec */ + check_sum += (level_shift & 0xF) << 3 | (down_mix & 0x1) << 7; + check_sum &= 0xFF; + check_sum = (uint8) (256 - check_sum); + + audio_info_0_reg = 0; + /* CHECKSUM(check_sum) */ + audio_info_0_reg |= check_sum & 0xff; + /* CC(channel_count) */ + audio_info_0_reg |= (channel_count << 8) & 0x00000700; + + /* HDMI_AUDIO_INFO0[0x00E4] */ + HDMI_OUTP(0x00E4, audio_info_0_reg); + + /* Set these flags */ + /* AUDIO_INFO_UPDATE | AUDIO_INFO_SOURCE | AUDIO_INFO_CONT + | AUDIO_INFO_SEND */ + audio_info_ctrl_reg |= 0x000000F0; + } else { + /* Clear these flags */ + /* ~(AUDIO_INFO_UPDATE | AUDIO_INFO_SOURCE | AUDIO_INFO_CONT + | AUDIO_INFO_SEND) */ + audio_info_ctrl_reg &= ~0x000000F0; + } + /* HDMI_INFOFRAME_CTRL0[0x002C] */ + HDMI_OUTP(0x002C, audio_info_ctrl_reg); + + + hdmi_msm_dump_regs("HDMI-AUDIO-ON: "); + + return 0; + +} +EXPORT_SYMBOL(hdmi_msm_audio_info_setup); + +static void hdmi_msm_en_gc_packet(boolean av_mute_is_requested) +{ + /* HDMI_GC[0x0040] */ + HDMI_OUTP(0x0040, av_mute_is_requested ? 1 : 0); + + /* GC packet enable (every frame) */ + /* HDMI_VBI_PKT_CTRL[0x0028] */ + hdmi_msm_rmw32or(0x0028, 3 << 4); +} + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_ISRC_ACP_SUPPORT +static void hdmi_msm_en_isrc_packet(boolean isrc_is_continued) +{ + static const char isrc_psuedo_data[] = + "ISRC1:0123456789isrc2=ABCDEFGHIJ"; + const uint32 * isrc_data = (const uint32 *) isrc_psuedo_data; + + /* ISRC_STATUS =0b010 | ISRC_CONTINUE | ISRC_VALID */ + /* HDMI_ISRC1_0[0x00048] */ + HDMI_OUTP(0x00048, 2 | (isrc_is_continued ? 1 : 0) << 6 | 0 << 7); + + /* HDMI_ISRC1_1[0x004C] */ + HDMI_OUTP(0x004C, *isrc_data++); + /* HDMI_ISRC1_2[0x0050] */ + HDMI_OUTP(0x0050, *isrc_data++); + /* HDMI_ISRC1_3[0x0054] */ + HDMI_OUTP(0x0054, *isrc_data++); + /* HDMI_ISRC1_4[0x0058] */ + HDMI_OUTP(0x0058, *isrc_data++); + + /* HDMI_ISRC2_0[0x005C] */ + HDMI_OUTP(0x005C, *isrc_data++); + /* HDMI_ISRC2_1[0x0060] */ + HDMI_OUTP(0x0060, *isrc_data++); + /* HDMI_ISRC2_2[0x0064] */ + HDMI_OUTP(0x0064, *isrc_data++); + /* HDMI_ISRC2_3[0x0068] */ + HDMI_OUTP(0x0068, *isrc_data); + + /* HDMI_VBI_PKT_CTRL[0x0028] */ + /* ISRC Send + Continuous */ + hdmi_msm_rmw32or(0x0028, 3 << 8); +} +#else +static void hdmi_msm_en_isrc_packet(boolean isrc_is_continued) +{ + /* + * Until end-to-end support for various audio packets + */ +} +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_ISRC_ACP_SUPPORT +static void hdmi_msm_en_acp_packet(uint32 byte1) +{ + /* HDMI_ACP[0x003C] */ + HDMI_OUTP(0x003C, 2 | 1 << 8 | byte1 << 16); + + /* HDMI_VBI_PKT_CTRL[0x0028] */ + /* ACP send, s/w source */ + hdmi_msm_rmw32or(0x0028, 3 << 12); +} +#else +static void hdmi_msm_en_acp_packet(uint32 byte1) +{ + /* + * Until end-to-end support for various audio packets + */ +} +#endif + +int hdmi_msm_audio_get_sample_rate(void) +{ + return msm_hdmi_sample_rate; +} +EXPORT_SYMBOL(hdmi_msm_audio_get_sample_rate); + +void hdmi_msm_audio_sample_rate_reset(int rate) +{ + msm_hdmi_sample_rate = rate; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + if (hdmi_msm_has_hdcp()) + hdcp_deauthenticate(); + else +#endif + hdmi_msm_turn_on(); +} +EXPORT_SYMBOL(hdmi_msm_audio_sample_rate_reset); + +static void hdmi_msm_audio_setup(void) +{ + const int channels = MSM_HDMI_AUDIO_CHANNEL_2; + + /* (0) for clr_avmute, (1) for set_avmute */ + hdmi_msm_en_gc_packet(0); + /* (0) for isrc1 only, (1) for isrc1 and isrc2 */ + hdmi_msm_en_isrc_packet(1); + /* arbitrary bit pattern for byte1 */ + hdmi_msm_en_acp_packet(0x5a); + DEV_DBG("Not setting ACP, ISRC1, ISRC2 packets\n"); + hdmi_msm_audio_acr_setup(TRUE, + external_common_state->video_resolution, + msm_hdmi_sample_rate, channels); + hdmi_msm_audio_info_setup(TRUE, channels, 0, 0, FALSE); + + /* Turn on Audio FIFO and SAM DROP ISR */ + HDMI_OUTP(0x02CC, HDMI_INP(0x02CC) | BIT(1) | BIT(3)); + DEV_INFO("HDMI Audio: Enabled\n"); +} + +static int hdmi_msm_audio_off(void) +{ + uint32 audio_pkt_ctrl, audio_cfg; + /* Number of wait iterations */ + int i = 10; + audio_pkt_ctrl = HDMI_INP_ND(0x0020); + audio_cfg = HDMI_INP_ND(0x01D0); + + /* Checking BIT[0] of AUDIO PACKET CONTROL and */ + /* AUDIO CONFIGURATION register */ + while (((audio_pkt_ctrl & 0x00000001) || (audio_cfg & 0x00000001)) + && (i--)) { + audio_pkt_ctrl = HDMI_INP_ND(0x0020); + audio_cfg = HDMI_INP_ND(0x01D0); + DEV_DBG("%d times :: HDMI AUDIO PACKET is %08x and " + "AUDIO CFG is %08x", i, audio_pkt_ctrl, audio_cfg); + msleep(100); + if (!i) { + DEV_ERR("%s:failed to set BIT[0] AUDIO PACKET" + "CONTROL or AUDIO CONFIGURATION REGISTER\n", + __func__); + return -ETIMEDOUT; + } + } + hdmi_msm_audio_info_setup(FALSE, 0, 0, 0, FALSE); + hdmi_msm_audio_acr_setup(FALSE, 0, 0, 0); + DEV_INFO("HDMI Audio: Disabled\n"); + return 0; +} + + +static uint8 hdmi_msm_avi_iframe_lut[][16] = { +/* 480p60 480i60 576p50 576i50 720p60 720p50 1080p60 1080i60 1080p50 + 1080i50 1080p24 1080p30 1080p25 640x480p 480p60_16_9 576p50_4_3 */ + {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, /*00*/ + {0x18, 0x18, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x18, 0x28, 0x18}, /*01*/ + {0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x88, 0x00, 0x04}, /*02*/ + {0x02, 0x06, 0x11, 0x15, 0x04, 0x13, 0x10, 0x05, 0x1F, + 0x14, 0x20, 0x22, 0x21, 0x01, 0x03, 0x11}, /*03*/ + {0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*04*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*05*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*06*/ + {0xE1, 0xE1, 0x41, 0x41, 0xD1, 0xd1, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0xe1, 0xE1, 0x41}, /*07*/ + {0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x01, 0x01, 0x02}, /*08*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*09*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*10*/ + {0xD1, 0xD1, 0xD1, 0xD1, 0x01, 0x01, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0xD1, 0xD1}, /*11*/ + {0x02, 0x02, 0x02, 0x02, 0x05, 0x05, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x02, 0x02, 0x02} /*12*/ +}; + +static void hdmi_msm_avi_info_frame(void) +{ + /* two header + length + 13 data */ + uint8 aviInfoFrame[16]; + uint8 checksum; + uint32 sum; + uint32 regVal; + int i; + int mode = 0; + boolean use_ce_scan_info = TRUE; + + switch (external_common_state->video_resolution) { + case HDMI_VFRMT_720x480p60_4_3: + mode = 0; + break; + case HDMI_VFRMT_720x480i60_16_9: + mode = 1; + break; + case HDMI_VFRMT_720x576p50_16_9: + mode = 2; + break; + case HDMI_VFRMT_720x576i50_16_9: + mode = 3; + break; + case HDMI_VFRMT_1280x720p60_16_9: + mode = 4; + break; + case HDMI_VFRMT_1280x720p50_16_9: + mode = 5; + break; + case HDMI_VFRMT_1920x1080p60_16_9: + mode = 6; + break; + case HDMI_VFRMT_1920x1080i60_16_9: + mode = 7; + break; + case HDMI_VFRMT_1920x1080p50_16_9: + mode = 8; + break; + case HDMI_VFRMT_1920x1080i50_16_9: + mode = 9; + break; + case HDMI_VFRMT_1920x1080p24_16_9: + mode = 10; + break; + case HDMI_VFRMT_1920x1080p30_16_9: + mode = 11; + break; + case HDMI_VFRMT_1920x1080p25_16_9: + mode = 12; + break; + case HDMI_VFRMT_640x480p60_4_3: + mode = 13; + break; + case HDMI_VFRMT_720x480p60_16_9: + mode = 14; + break; + case HDMI_VFRMT_720x576p50_4_3: + mode = 15; + break; + default: + DEV_INFO("%s: mode %d not supported\n", __func__, + external_common_state->video_resolution); + return; + } + + /* InfoFrame Type = 82 */ + aviInfoFrame[0] = 0x82; + /* Version = 2 */ + aviInfoFrame[1] = 2; + /* Length of AVI InfoFrame = 13 */ + aviInfoFrame[2] = 13; + + /* Data Byte 01: 0 Y1 Y0 A0 B1 B0 S1 S0 */ + aviInfoFrame[3] = hdmi_msm_avi_iframe_lut[0][mode]; + + /* + * If the sink specified support for both underscan/overscan + * then, by default, set the underscan bit. + * Only checking underscan support for preferred format and cea formats + */ + if ((external_common_state->video_resolution == + external_common_state->preferred_video_format)) { + use_ce_scan_info = FALSE; + switch (external_common_state->pt_scan_info) { + case 0: + /* + * Need to use the info specified for the corresponding + * IT or CE format + */ + DEV_DBG("%s: No underscan information specified for the" + " preferred video format\n", __func__); + use_ce_scan_info = TRUE; + break; + case 3: + DEV_DBG("%s: Setting underscan bit for the preferred" + " video format\n", __func__); + aviInfoFrame[3] |= 0x02; + break; + default: + DEV_DBG("%s: Underscan information not set for the" + " preferred video format\n", __func__); + break; + } + } + + if (use_ce_scan_info) { + if (3 == external_common_state->ce_scan_info) { + DEV_DBG("%s: Setting underscan bit for the CE video" + " format\n", __func__); + aviInfoFrame[3] |= 0x02; + } else { + DEV_DBG("%s: Not setting underscan bit for the CE video" + " format\n", __func__); + } + } + + /* Data Byte 02: C1 C0 M1 M0 R3 R2 R1 R0 */ + aviInfoFrame[4] = hdmi_msm_avi_iframe_lut[1][mode]; + /* Data Byte 03: ITC EC2 EC1 EC0 Q1 Q0 SC1 SC0 */ + aviInfoFrame[5] = hdmi_msm_avi_iframe_lut[2][mode]; + /* Data Byte 04: 0 VIC6 VIC5 VIC4 VIC3 VIC2 VIC1 VIC0 */ + aviInfoFrame[6] = hdmi_msm_avi_iframe_lut[3][mode]; + /* Data Byte 05: 0 0 0 0 PR3 PR2 PR1 PR0 */ + aviInfoFrame[7] = hdmi_msm_avi_iframe_lut[4][mode]; + /* Data Byte 06: LSB Line No of End of Top Bar */ + aviInfoFrame[8] = hdmi_msm_avi_iframe_lut[5][mode]; + /* Data Byte 07: MSB Line No of End of Top Bar */ + aviInfoFrame[9] = hdmi_msm_avi_iframe_lut[6][mode]; + /* Data Byte 08: LSB Line No of Start of Bottom Bar */ + aviInfoFrame[10] = hdmi_msm_avi_iframe_lut[7][mode]; + /* Data Byte 09: MSB Line No of Start of Bottom Bar */ + aviInfoFrame[11] = hdmi_msm_avi_iframe_lut[8][mode]; + /* Data Byte 10: LSB Pixel Number of End of Left Bar */ + aviInfoFrame[12] = hdmi_msm_avi_iframe_lut[9][mode]; + /* Data Byte 11: MSB Pixel Number of End of Left Bar */ + aviInfoFrame[13] = hdmi_msm_avi_iframe_lut[10][mode]; + /* Data Byte 12: LSB Pixel Number of Start of Right Bar */ + aviInfoFrame[14] = hdmi_msm_avi_iframe_lut[11][mode]; + /* Data Byte 13: MSB Pixel Number of Start of Right Bar */ + aviInfoFrame[15] = hdmi_msm_avi_iframe_lut[12][mode]; + + sum = 0; + for (i = 0; i < 16; i++) + sum += aviInfoFrame[i]; + sum &= 0xFF; + sum = 256 - sum; + checksum = (uint8) sum; + + regVal = aviInfoFrame[5]; + regVal = regVal << 8 | aviInfoFrame[4]; + regVal = regVal << 8 | aviInfoFrame[3]; + regVal = regVal << 8 | checksum; + HDMI_OUTP(0x006C, regVal); + + regVal = aviInfoFrame[9]; + regVal = regVal << 8 | aviInfoFrame[8]; + regVal = regVal << 8 | aviInfoFrame[7]; + regVal = regVal << 8 | aviInfoFrame[6]; + HDMI_OUTP(0x0070, regVal); + + regVal = aviInfoFrame[13]; + regVal = regVal << 8 | aviInfoFrame[12]; + regVal = regVal << 8 | aviInfoFrame[11]; + regVal = regVal << 8 | aviInfoFrame[10]; + HDMI_OUTP(0x0074, regVal); + + regVal = aviInfoFrame[1]; + regVal = regVal << 16 | aviInfoFrame[15]; + regVal = regVal << 8 | aviInfoFrame[14]; + HDMI_OUTP(0x0078, regVal); + + /* INFOFRAME_CTRL0[0x002C] */ + /* 0x3 for AVI InfFrame enable (every frame) */ + HDMI_OUTP(0x002C, HDMI_INP(0x002C) | 0x00000003L); +} + +#ifdef CONFIG_FB_MSM_HDMI_3D +static void hdmi_msm_vendor_infoframe_packetsetup(void) +{ + uint32 packet_header = 0; + uint32 check_sum = 0; + uint32 packet_payload = 0; + + if (!external_common_state->format_3d) { + HDMI_OUTP(0x0034, 0); + return; + } + + /* 0x0084 GENERIC0_HDR + * HB0 7:0 NUM + * HB1 15:8 NUM + * HB2 23:16 NUM */ + /* Setup Packet header and payload */ + /* 0x81 VS_INFO_FRAME_ID + 0x01 VS_INFO_FRAME_VERSION + 0x1B VS_INFO_FRAME_PAYLOAD_LENGTH */ + packet_header = 0x81 | (0x01 << 8) | (0x1B << 16); + HDMI_OUTP(0x0084, packet_header); + + check_sum = packet_header & 0xff; + check_sum += (packet_header >> 8) & 0xff; + check_sum += (packet_header >> 16) & 0xff; + + /* 0x008C GENERIC0_1 + * BYTE4 7:0 NUM + * BYTE5 15:8 NUM + * BYTE6 23:16 NUM + * BYTE7 31:24 NUM */ + /* 0x02 VS_INFO_FRAME_3D_PRESENT */ + packet_payload = 0x02 << 5; + switch (external_common_state->format_3d) { + case 1: + /* 0b1000 VIDEO_3D_FORMAT_SIDE_BY_SIDE_HALF */ + packet_payload |= (0x08 << 8) << 4; + break; + case 2: + /* 0b0110 VIDEO_3D_FORMAT_TOP_AND_BOTTOM_HALF */ + packet_payload |= (0x06 << 8) << 4; + break; + } + HDMI_OUTP(0x008C, packet_payload); + + check_sum += packet_payload & 0xff; + check_sum += (packet_payload >> 8) & 0xff; + + #define IEEE_REGISTRATION_ID 0xC03 + /* Next 3 bytes are IEEE Registration Identifcation */ + /* 0x0088 GENERIC0_0 + * BYTE0 7:0 NUM (checksum) + * BYTE1 15:8 NUM + * BYTE2 23:16 NUM + * BYTE3 31:24 NUM */ + check_sum += IEEE_REGISTRATION_ID & 0xff; + check_sum += (IEEE_REGISTRATION_ID >> 8) & 0xff; + check_sum += (IEEE_REGISTRATION_ID >> 16) & 0xff; + + HDMI_OUTP(0x0088, (0x100 - (0xff & check_sum)) + | ((IEEE_REGISTRATION_ID & 0xff) << 8) + | (((IEEE_REGISTRATION_ID >> 8) & 0xff) << 16) + | (((IEEE_REGISTRATION_ID >> 16) & 0xff) << 24)); + + /* 0x0034 GEN_PKT_CTRL + * GENERIC0_SEND 0 0 = Disable Generic0 Packet Transmission + * 1 = Enable Generic0 Packet Transmission + * GENERIC0_CONT 1 0 = Send Generic0 Packet on next frame only + * 1 = Send Generic0 Packet on every frame + * GENERIC0_UPDATE 2 NUM + * GENERIC1_SEND 4 0 = Disable Generic1 Packet Transmission + * 1 = Enable Generic1 Packet Transmission + * GENERIC1_CONT 5 0 = Send Generic1 Packet on next frame only + * 1 = Send Generic1 Packet on every frame + * GENERIC0_LINE 21:16 NUM + * GENERIC1_LINE 29:24 NUM + */ + /* GENERIC0_LINE | GENERIC0_UPDATE | GENERIC0_CONT | GENERIC0_SEND + * Setup HDMI TX generic packet control + * Enable this packet to transmit every frame + * Enable this packet to transmit every frame + * Enable HDMI TX engine to transmit Generic packet 0 */ + HDMI_OUTP(0x0034, (1 << 16) | (1 << 2) | BIT(1) | BIT(0)); +} + +static void hdmi_msm_switch_3d(boolean on) +{ + mutex_lock(&external_common_state_hpd_mutex); + if (external_common_state->hpd_state) + hdmi_msm_vendor_infoframe_packetsetup(); + mutex_unlock(&external_common_state_hpd_mutex); +} +#endif + +#define IFRAME_CHECKSUM_32(d) \ + ((d & 0xff) + ((d >> 8) & 0xff) + \ + ((d >> 16) & 0xff) + ((d >> 24) & 0xff)) + +static void hdmi_msm_spd_infoframe_packetsetup(void) +{ + uint32 packet_header = 0; + uint32 check_sum = 0; + uint32 packet_payload = 0; + uint32 packet_control = 0; + + uint8 *vendor_name = external_common_state->spd_vendor_name; + uint8 *product_description = + external_common_state->spd_product_description; + + /* 0x00A4 GENERIC1_HDR + * HB0 7:0 NUM + * HB1 15:8 NUM + * HB2 23:16 NUM */ + /* Setup Packet header and payload */ + /* 0x83 InfoFrame Type Code + 0x01 InfoFrame Version Number + 0x19 Length of Source Product Description InfoFrame + */ + packet_header = 0x83 | (0x01 << 8) | (0x19 << 16); + HDMI_OUTP(0x00A4, packet_header); + check_sum += IFRAME_CHECKSUM_32(packet_header); + + /* Vendor Name (7bit ASCII code) */ + /* 0x00A8 GENERIC1_0 + * BYTE0 7:0 CheckSum + * BYTE1 15:8 VENDOR_NAME[0] + * BYTE2 23:16 VENDOR_NAME[1] + * BYTE3 31:24 VENDOR_NAME[2] */ + packet_payload = ((vendor_name[0] & 0x7f) << 8) + | ((vendor_name[1] & 0x7f) << 16) + | ((vendor_name[2] & 0x7f) << 24); + check_sum += IFRAME_CHECKSUM_32(packet_payload); + packet_payload |= ((0x100 - (0xff & check_sum)) & 0xff); + HDMI_OUTP(0x00A8, packet_payload); + + /* 0x00AC GENERIC1_1 + * BYTE4 7:0 VENDOR_NAME[3] + * BYTE5 15:8 VENDOR_NAME[4] + * BYTE6 23:16 VENDOR_NAME[5] + * BYTE7 31:24 VENDOR_NAME[6] */ + packet_payload = (vendor_name[3] & 0x7f) + | ((vendor_name[4] & 0x7f) << 8) + | ((vendor_name[5] & 0x7f) << 16) + | ((vendor_name[6] & 0x7f) << 24); + HDMI_OUTP(0x00AC, packet_payload); + check_sum += IFRAME_CHECKSUM_32(packet_payload); + + /* Product Description (7-bit ASCII code) */ + /* 0x00B0 GENERIC1_2 + * BYTE8 7:0 VENDOR_NAME[7] + * BYTE9 15:8 PRODUCT_NAME[ 0] + * BYTE10 23:16 PRODUCT_NAME[ 1] + * BYTE11 31:24 PRODUCT_NAME[ 2] */ + packet_payload = (vendor_name[7] & 0x7f) + | ((product_description[0] & 0x7f) << 8) + | ((product_description[1] & 0x7f) << 16) + | ((product_description[2] & 0x7f) << 24); + HDMI_OUTP(0x00B0, packet_payload); + check_sum += IFRAME_CHECKSUM_32(packet_payload); + + /* 0x00B4 GENERIC1_3 + * BYTE12 7:0 PRODUCT_NAME[ 3] + * BYTE13 15:8 PRODUCT_NAME[ 4] + * BYTE14 23:16 PRODUCT_NAME[ 5] + * BYTE15 31:24 PRODUCT_NAME[ 6] */ + packet_payload = (product_description[3] & 0x7f) + | ((product_description[4] & 0x7f) << 8) + | ((product_description[5] & 0x7f) << 16) + | ((product_description[6] & 0x7f) << 24); + HDMI_OUTP(0x00B4, packet_payload); + check_sum += IFRAME_CHECKSUM_32(packet_payload); + + /* 0x00B8 GENERIC1_4 + * BYTE16 7:0 PRODUCT_NAME[ 7] + * BYTE17 15:8 PRODUCT_NAME[ 8] + * BYTE18 23:16 PRODUCT_NAME[ 9] + * BYTE19 31:24 PRODUCT_NAME[10] */ + packet_payload = (product_description[7] & 0x7f) + | ((product_description[8] & 0x7f) << 8) + | ((product_description[9] & 0x7f) << 16) + | ((product_description[10] & 0x7f) << 24); + HDMI_OUTP(0x00B8, packet_payload); + check_sum += IFRAME_CHECKSUM_32(packet_payload); + + /* 0x00BC GENERIC1_5 + * BYTE20 7:0 PRODUCT_NAME[11] + * BYTE21 15:8 PRODUCT_NAME[12] + * BYTE22 23:16 PRODUCT_NAME[13] + * BYTE23 31:24 PRODUCT_NAME[14] */ + packet_payload = (product_description[11] & 0x7f) + | ((product_description[12] & 0x7f) << 8) + | ((product_description[13] & 0x7f) << 16) + | ((product_description[14] & 0x7f) << 24); + HDMI_OUTP(0x00BC, packet_payload); + check_sum += IFRAME_CHECKSUM_32(packet_payload); + + /* 0x00C0 GENERIC1_6 + * BYTE24 7:0 PRODUCT_NAME[15] + * BYTE25 15:8 Source Device Information + * BYTE26 23:16 NUM + * BYTE27 31:24 NUM */ + /* Source Device Information + * 00h unknown + * 01h Digital STB + * 02h DVD + * 03h D-VHS + * 04h HDD Video + * 05h DVC + * 06h DSC + * 07h Video CD + * 08h Game + * 09h PC general */ + packet_payload = (product_description[15] & 0x7f) | 0x00 << 8; + HDMI_OUTP(0x00C0, packet_payload); + check_sum += IFRAME_CHECKSUM_32(packet_payload); + + /* GENERIC1_LINE | GENERIC1_CONT | GENERIC1_SEND + * Setup HDMI TX generic packet control + * Enable this packet to transmit every frame + * Enable HDMI TX engine to transmit Generic packet 1 */ + packet_control = HDMI_INP_ND(0x0034); + packet_control |= ((0x1 << 24) | (1 << 5) | (1 << 4)); + HDMI_OUTP(0x0034, packet_control); +} + +int hdmi_msm_clk(int on) +{ + int rc; + + DEV_DBG("HDMI Clk: %s\n", on ? "Enable" : "Disable"); + if (on) { + rc = clk_prepare_enable(hdmi_msm_state->hdmi_app_clk); + if (rc) { + DEV_ERR("'hdmi_app_clk' clock enable failed, rc=%d\n", + rc); + return rc; + } + + rc = clk_prepare_enable(hdmi_msm_state->hdmi_m_pclk); + if (rc) { + DEV_ERR("'hdmi_m_pclk' clock enable failed, rc=%d\n", + rc); + return rc; + } + + rc = clk_prepare_enable(hdmi_msm_state->hdmi_s_pclk); + if (rc) { + DEV_ERR("'hdmi_s_pclk' clock enable failed, rc=%d\n", + rc); + return rc; + } + } else { + clk_disable_unprepare(hdmi_msm_state->hdmi_app_clk); + clk_disable_unprepare(hdmi_msm_state->hdmi_m_pclk); + clk_disable_unprepare(hdmi_msm_state->hdmi_s_pclk); + } + + return 0; +} + +static void hdmi_msm_turn_on(void) +{ + uint32 hpd_ctrl; + uint32 audio_pkt_ctrl, audio_cfg; + /* + * Number of wait iterations for QDSP to disable Audio Engine + * before resetting HDMI core + */ + int i = 10; + audio_pkt_ctrl = HDMI_INP_ND(0x0020); + audio_cfg = HDMI_INP_ND(0x01D0); + + /* + * Checking BIT[0] of AUDIO PACKET CONTROL and + * AUDIO CONFIGURATION register + */ + while (((audio_pkt_ctrl & 0x00000001) || (audio_cfg & 0x00000001)) + && (i--)) { + audio_pkt_ctrl = HDMI_INP_ND(0x0020); + audio_cfg = HDMI_INP_ND(0x01D0); + DEV_DBG("%d times :: HDMI AUDIO PACKET is %08x and " + "AUDIO CFG is %08x", i, audio_pkt_ctrl, audio_cfg); + msleep(20); + } + + mutex_lock(&hdcp_auth_state_mutex); + hdmi_msm_reset_core(); + mutex_unlock(&hdcp_auth_state_mutex); + + hdmi_msm_init_phy(external_common_state->video_resolution); + /* HDMI_USEC_REFTIMER[0x0208] */ + HDMI_OUTP(0x0208, 0x0001001B); + + hdmi_msm_set_mode(TRUE); + + hdmi_msm_video_setup(external_common_state->video_resolution); + if (!hdmi_msm_is_dvi_mode()) + hdmi_msm_audio_setup(); + hdmi_msm_avi_info_frame(); +#ifdef CONFIG_FB_MSM_HDMI_3D + hdmi_msm_vendor_infoframe_packetsetup(); +#endif + hdmi_msm_spd_infoframe_packetsetup(); + + /* set timeout to 4.1ms (max) for hardware debounce */ + hpd_ctrl = (HDMI_INP(0x0258) & ~0xFFF) | 0xFFF; + + /* Toggle HPD circuit to trigger HPD sense */ + HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl); + HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl); + + /* Setup HPD IRQ */ + HDMI_OUTP(0x0254, 4 | (external_common_state->hpd_state ? 0 : 2)); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + if (hdmi_msm_state->reauth) { + hdmi_msm_hdcp_enable(); + hdmi_msm_state->reauth = FALSE ; + } +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + /* re-initialize CEC if enabled */ + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->cec_enabled == true) { + hdmi_msm_cec_init(); + hdmi_msm_cec_write_logical_addr( + hdmi_msm_state->cec_logical_addr); + } + mutex_unlock(&hdmi_msm_state_mutex); +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + DEV_INFO("HDMI Core: Initialized\n"); +} + +static void hdmi_msm_hpd_state_timer(unsigned long data) +{ + queue_work(hdmi_work_queue, &hdmi_msm_state->hpd_state_work); +} + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT +static void hdmi_msm_hdcp_timer(unsigned long data) +{ + queue_work(hdmi_work_queue, &hdmi_msm_state->hdcp_work); +} +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT +static void hdmi_msm_cec_read_timer_func(unsigned long data) +{ + queue_work(hdmi_work_queue, &hdmi_msm_state->cec_latch_detect_work); +} +#endif + +static void hdmi_msm_hpd_read_work(struct work_struct *work) +{ + uint32 hpd_ctrl; + + clk_prepare_enable(hdmi_msm_state->hdmi_app_clk); + hdmi_msm_state->pd->core_power(1, 1); + hdmi_msm_state->pd->enable_5v(1); + hdmi_msm_set_mode(FALSE); + hdmi_msm_init_phy(external_common_state->video_resolution); + /* HDMI_USEC_REFTIMER[0x0208] */ + HDMI_OUTP(0x0208, 0x0001001B); + hpd_ctrl = (HDMI_INP(0x0258) & ~0xFFF) | 0xFFF; + + /* Toggle HPD circuit to trigger HPD sense */ + HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl); + HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl); + + hdmi_msm_set_mode(TRUE); + msleep(1000); + external_common_state->hpd_state = (HDMI_INP(0x0250) & 0x2) >> 1; + if (external_common_state->hpd_state) { + hdmi_msm_read_edid(); + DEV_DBG("%s: sense CONNECTED: send ONLINE\n", __func__); + kobject_uevent(external_common_state->uevent_kobj, + KOBJ_ONLINE); + } + hdmi_msm_hpd_off(); + hdmi_msm_set_mode(FALSE); + hdmi_msm_state->pd->core_power(0, 1); + hdmi_msm_state->pd->enable_5v(0); + clk_disable_unprepare(hdmi_msm_state->hdmi_app_clk); +} + +static void hdmi_msm_hpd_off(void) +{ + if (!hdmi_msm_state->hpd_initialized) { + DEV_DBG("%s: HPD is already OFF, returning\n", __func__); + return; + } + + DEV_DBG("%s: (timer, clk, 5V, core, IRQ off)\n", __func__); + del_timer(&hdmi_msm_state->hpd_state_timer); + disable_irq(hdmi_msm_state->irq); + + hdmi_msm_set_mode(FALSE); + hdmi_msm_state->hpd_initialized = FALSE; + hdmi_msm_powerdown_phy(); + hdmi_msm_state->pd->cec_power(0); + hdmi_msm_state->pd->enable_5v(0); + hdmi_msm_state->pd->core_power(0, 1); + hdmi_msm_clk(0); + hdmi_msm_state->hpd_initialized = FALSE; +} + +static void hdmi_msm_dump_regs(const char *prefix) +{ +#ifdef REG_DUMP + print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 32, 4, + (void *)MSM_HDMI_BASE, 0x0334, false); +#endif +} + +static int hdmi_msm_hpd_on(bool trigger_handler) +{ + static int phy_reset_done; + uint32 hpd_ctrl; + + if (hdmi_msm_state->hpd_initialized) { + DEV_DBG("%s: HPD is already ON, returning\n", __func__); + return 0; + } + + hdmi_msm_clk(1); + hdmi_msm_state->pd->core_power(1, 1); + hdmi_msm_state->pd->enable_5v(1); + hdmi_msm_state->pd->cec_power(1); + hdmi_msm_dump_regs("HDMI-INIT: "); + hdmi_msm_set_mode(FALSE); + + if (!phy_reset_done) { + hdmi_phy_reset(); + phy_reset_done = 1; + } + + /* HDMI_USEC_REFTIMER[0x0208] */ + HDMI_OUTP(0x0208, 0x0001001B); + + /* Check HPD State */ + enable_irq(hdmi_msm_state->irq); + + /* set timeout to 4.1ms (max) for hardware debounce */ + hpd_ctrl = (HDMI_INP(0x0258) & ~0xFFF) | 0xFFF; + + /* Toggle HPD circuit to trigger HPD sense */ + HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl); + HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl); + + DEV_DBG("%s: (clk, 5V, core, IRQ on) \n", __func__, + trigger_handler ? "true" : "false"); + + if (trigger_handler) { + /* Set HPD state machine: ensure at least 2 readouts */ + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->hpd_stable = 0; + hdmi_msm_state->hpd_prev_state = TRUE; + mutex_lock(&external_common_state_hpd_mutex); + external_common_state->hpd_state = FALSE; + mutex_unlock(&external_common_state_hpd_mutex); + hdmi_msm_state->hpd_cable_chg_detected = TRUE; + mutex_unlock(&hdmi_msm_state_mutex); + mod_timer(&hdmi_msm_state->hpd_state_timer, + jiffies + HZ/2); + } + + hdmi_msm_state->hpd_initialized = TRUE; + + hdmi_msm_set_mode(TRUE); + + return 0; +} + +static int hdmi_msm_power_ctrl(boolean enable) +{ + if (!external_common_state->hpd_feature_on) + return 0; + + if (enable) + hdmi_msm_hpd_on(true); + else + hdmi_msm_hpd_off(); + + return 0; +} + +static int hdmi_msm_power_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + bool changed; + + if (!hdmi_msm_state || !hdmi_msm_state->hdmi_app_clk || !MSM_HDMI_BASE) + return -ENODEV; + + DEV_INFO("power: ON (%dx%d %d)\n", mfd->var_xres, mfd->var_yres, + mfd->var_pixclock); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->hdcp_activating) { + hdmi_msm_state->panel_power_on = TRUE; + DEV_INFO("HDCP: activating, returning\n"); + } + mutex_unlock(&hdmi_msm_state_mutex); +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + + changed = hdmi_common_get_video_format_from_drv_data(mfd); + if (!external_common_state->hpd_feature_on || mfd->ref_cnt) { + int rc = hdmi_msm_hpd_on(true); + DEV_INFO("HPD: panel power without 'hpd' feature on\n"); + if (rc) { + DEV_WARN("HPD: activation failed: rc=%d\n", rc); + return rc; + } + } + hdmi_msm_audio_info_setup(TRUE, 0, 0, 0, FALSE); + + mutex_lock(&external_common_state_hpd_mutex); + hdmi_msm_state->panel_power_on = TRUE; + if ((external_common_state->hpd_state && !hdmi_msm_is_power_on()) + || changed) { + mutex_unlock(&external_common_state_hpd_mutex); + hdmi_msm_turn_on(); + } else + mutex_unlock(&external_common_state_hpd_mutex); + + hdmi_msm_dump_regs("HDMI-ON: "); + + DEV_INFO("power=%s DVI= %s\n", + hdmi_msm_is_power_on() ? "ON" : "OFF" , + hdmi_msm_is_dvi_mode() ? "ON" : "OFF"); + return 0; +} + +/* Note that power-off will also be called when the cable-remove event is + * processed on the user-space and as a result the framebuffer is powered + * down. However, we are still required to be able to detect a cable-insert + * event; so for now leave the HDMI engine running; so that the HPD IRQ is + * still being processed. + */ +static int hdmi_msm_power_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + if (!hdmi_msm_state->hdmi_app_clk) + return -ENODEV; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + mutex_lock(&hdmi_msm_state_mutex); + if (hdmi_msm_state->hdcp_activating) { + hdmi_msm_state->panel_power_on = FALSE; + mutex_unlock(&hdmi_msm_state_mutex); + DEV_INFO("HDCP: activating, returning\n"); + return 0; + } + mutex_unlock(&hdmi_msm_state_mutex); +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + + DEV_INFO("power: OFF (audio off, Reset Core)\n"); + hdmi_msm_audio_off(); +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + hdcp_deauthenticate(); +#endif + hdmi_msm_hpd_off(); + hdmi_msm_powerdown_phy(); + hdmi_msm_dump_regs("HDMI-OFF: "); + hdmi_msm_hpd_on(true); + + mutex_lock(&external_common_state_hpd_mutex); + if (!external_common_state->hpd_feature_on || mfd->ref_cnt) + hdmi_msm_hpd_off(); + mutex_unlock(&external_common_state_hpd_mutex); + + hdmi_msm_state->panel_power_on = FALSE; + return 0; +} + +static int hdmi_msm_probe(struct platform_device *pdev) +{ + int rc; + struct platform_device *fb_dev; + + if (!hdmi_msm_state) { + pr_err("%s: hdmi_msm_state is NULL\n", __func__); + return -ENOMEM; + } + + external_common_state->dev = &pdev->dev; + DEV_DBG("probe\n"); + if (pdev->id == 0) { + struct resource *res; + + #define GET_RES(name, mode) do { \ + res = platform_get_resource_byname(pdev, mode, name); \ + if (!res) { \ + DEV_ERR("'" name "' resource not found\n"); \ + rc = -ENODEV; \ + goto error; \ + } \ + } while (0) + + #define IO_REMAP(var, name) do { \ + GET_RES(name, IORESOURCE_MEM); \ + var = ioremap(res->start, resource_size(res)); \ + if (!var) { \ + DEV_ERR("'" name "' ioremap failed\n"); \ + rc = -ENOMEM; \ + goto error; \ + } \ + } while (0) + + #define GET_IRQ(var, name) do { \ + GET_RES(name, IORESOURCE_IRQ); \ + var = res->start; \ + } while (0) + + IO_REMAP(hdmi_msm_state->qfprom_io, "hdmi_msm_qfprom_addr"); + hdmi_msm_state->hdmi_io = MSM_HDMI_BASE; + GET_IRQ(hdmi_msm_state->irq, "hdmi_msm_irq"); + + hdmi_msm_state->pd = pdev->dev.platform_data; + + #undef GET_RES + #undef IO_REMAP + #undef GET_IRQ + return 0; + } + + hdmi_msm_state->hdmi_app_clk = clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(hdmi_msm_state->hdmi_app_clk)) { + DEV_ERR("'core_clk' clk not found\n"); + rc = IS_ERR(hdmi_msm_state->hdmi_app_clk); + goto error; + } + + hdmi_msm_state->hdmi_m_pclk = clk_get(&pdev->dev, "master_iface_clk"); + if (IS_ERR(hdmi_msm_state->hdmi_m_pclk)) { + DEV_ERR("'master_iface_clk' clk not found\n"); + rc = IS_ERR(hdmi_msm_state->hdmi_m_pclk); + goto error; + } + + hdmi_msm_state->hdmi_s_pclk = clk_get(&pdev->dev, "slave_iface_clk"); + if (IS_ERR(hdmi_msm_state->hdmi_s_pclk)) { + DEV_ERR("'slave_iface_clk' clk not found\n"); + rc = IS_ERR(hdmi_msm_state->hdmi_s_pclk); + goto error; + } + + rc = check_hdmi_features(); + if (rc) { + DEV_ERR("Init FAILED: check_hdmi_features rc=%d\n", rc); + goto error; + } + + if (!hdmi_msm_state->pd->core_power) { + DEV_ERR("Init FAILED: core_power function missing\n"); + rc = -ENODEV; + goto error; + } + if (!hdmi_msm_state->pd->enable_5v) { + DEV_ERR("Init FAILED: enable_5v function missing\n"); + rc = -ENODEV; + goto error; + } + + if (!hdmi_msm_state->pd->cec_power) { + DEV_ERR("Init FAILED: cec_power function missing\n"); + rc = -ENODEV; + goto error; + } + + rc = request_threaded_irq(hdmi_msm_state->irq, NULL, &hdmi_msm_isr, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "hdmi_msm_isr", NULL); + if (rc) { + DEV_ERR("Init FAILED: IRQ request, rc=%d\n", rc); + goto error; + } + disable_irq(hdmi_msm_state->irq); + + init_timer(&hdmi_msm_state->hpd_state_timer); + hdmi_msm_state->hpd_state_timer.function = + hdmi_msm_hpd_state_timer; + hdmi_msm_state->hpd_state_timer.data = (uint32)NULL; + + hdmi_msm_state->hpd_state_timer.expires = 0xffffffffL; + add_timer(&hdmi_msm_state->hpd_state_timer); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + init_timer(&hdmi_msm_state->hdcp_timer); + hdmi_msm_state->hdcp_timer.function = + hdmi_msm_hdcp_timer; + hdmi_msm_state->hdcp_timer.data = (uint32)NULL; + + hdmi_msm_state->hdcp_timer.expires = 0xffffffffL; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + init_timer(&hdmi_msm_state->cec_read_timer); + hdmi_msm_state->cec_read_timer.function = + hdmi_msm_cec_read_timer_func; + hdmi_msm_state->cec_read_timer.data = (uint32)NULL; + + hdmi_msm_state->cec_read_timer.expires = 0xffffffffL; + #endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + + fb_dev = msm_fb_add_device(pdev); + if (fb_dev) { + rc = external_common_state_create(fb_dev); + if (rc) { + DEV_ERR("Init FAILED: hdmi_msm_state_create, rc=%d\n", + rc); + goto error; + } + } else + DEV_ERR("Init FAILED: failed to add fb device\n"); + + DEV_INFO("HDMI HPD: ON\n"); + + rc = hdmi_msm_hpd_on(true); + if (rc) + goto error; + + if (hdmi_msm_has_hdcp()) { + /* Don't Set Encryption in case of non HDCP builds */ + external_common_state->present_hdcp = FALSE; +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + external_common_state->present_hdcp = TRUE; +#endif + } else { + external_common_state->present_hdcp = FALSE; +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + /* + * If the device is not hdcp capable do + * not start hdcp timer. + */ + del_timer(&hdmi_msm_state->hdcp_timer); +#endif + } + + queue_work(hdmi_work_queue, &hdmi_msm_state->hpd_read_work); + + /* Initialize hdmi node and register with switch driver */ + if (hdmi_prim_display) + external_common_state->sdev.name = "hdmi_as_primary"; + else + external_common_state->sdev.name = "hdmi"; + if (switch_dev_register(&external_common_state->sdev) < 0) + DEV_ERR("Hdmi switch registration failed\n"); + + return 0; + +error: + if (hdmi_msm_state->qfprom_io) + iounmap(hdmi_msm_state->qfprom_io); + hdmi_msm_state->qfprom_io = NULL; + + if (hdmi_msm_state->hdmi_io) + iounmap(hdmi_msm_state->hdmi_io); + hdmi_msm_state->hdmi_io = NULL; + + external_common_state_remove(); + + if (hdmi_msm_state->hdmi_app_clk) + clk_put(hdmi_msm_state->hdmi_app_clk); + if (hdmi_msm_state->hdmi_m_pclk) + clk_put(hdmi_msm_state->hdmi_m_pclk); + if (hdmi_msm_state->hdmi_s_pclk) + clk_put(hdmi_msm_state->hdmi_s_pclk); + + hdmi_msm_state->hdmi_app_clk = NULL; + hdmi_msm_state->hdmi_m_pclk = NULL; + hdmi_msm_state->hdmi_s_pclk = NULL; + + return rc; +} + +static int hdmi_msm_remove(struct platform_device *pdev) +{ + DEV_INFO("HDMI device: remove\n"); + + DEV_INFO("HDMI HPD: OFF\n"); + + /* Unregister hdmi node from switch driver */ + switch_dev_unregister(&external_common_state->sdev); + + hdmi_msm_hpd_off(); + free_irq(hdmi_msm_state->irq, NULL); + + if (hdmi_msm_state->qfprom_io) + iounmap(hdmi_msm_state->qfprom_io); + hdmi_msm_state->qfprom_io = NULL; + + if (hdmi_msm_state->hdmi_io) + iounmap(hdmi_msm_state->hdmi_io); + hdmi_msm_state->hdmi_io = NULL; + + external_common_state_remove(); + + if (hdmi_msm_state->hdmi_app_clk) + clk_put(hdmi_msm_state->hdmi_app_clk); + if (hdmi_msm_state->hdmi_m_pclk) + clk_put(hdmi_msm_state->hdmi_m_pclk); + if (hdmi_msm_state->hdmi_s_pclk) + clk_put(hdmi_msm_state->hdmi_s_pclk); + + hdmi_msm_state->hdmi_app_clk = NULL; + hdmi_msm_state->hdmi_m_pclk = NULL; + hdmi_msm_state->hdmi_s_pclk = NULL; + + kfree(hdmi_msm_state); + hdmi_msm_state = NULL; + + return 0; +} + +static int hdmi_msm_hpd_feature(int on) +{ + int rc = 0; + + DEV_INFO("%s: %d\n", __func__, on); + if (on) { + rc = hdmi_msm_hpd_on(true); + } else { + hdmi_msm_hpd_off(); + /* Set HDMI switch node to 0 on HPD feature disable */ + switch_set_state(&external_common_state->sdev, 0); + } + + return rc; +} + +static struct platform_driver this_driver = { + .probe = hdmi_msm_probe, + .remove = hdmi_msm_remove, + .driver.name = "hdmi_msm", +}; + +static struct msm_fb_panel_data hdmi_msm_panel_data = { + .on = hdmi_msm_power_on, + .off = hdmi_msm_power_off, + .power_ctrl = hdmi_msm_power_ctrl, +}; + +static struct platform_device this_device = { + .name = "hdmi_msm", + .id = 1, + .dev.platform_data = &hdmi_msm_panel_data, +}; + +static int __init hdmi_msm_init(void) +{ + int rc; + + if (msm_fb_detect_client("hdmi_msm")) + return 0; + +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY + hdmi_prim_display = 1; +#endif + + hdmi_msm_setup_video_mode_lut(); + hdmi_msm_state = kzalloc(sizeof(*hdmi_msm_state), GFP_KERNEL); + if (!hdmi_msm_state) { + pr_err("hdmi_msm_init FAILED: out of memory\n"); + rc = -ENOMEM; + goto init_exit; + } + + external_common_state = &hdmi_msm_state->common; + external_common_state->video_resolution = HDMI_VFRMT_1920x1080p60_16_9; +#ifdef CONFIG_FB_MSM_HDMI_3D + external_common_state->switch_3d = hdmi_msm_switch_3d; +#endif + memset(external_common_state->spd_vendor_name, 0, + sizeof(external_common_state->spd_vendor_name)); + memset(external_common_state->spd_product_description, 0, + sizeof(external_common_state->spd_product_description)); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + hdmi_msm_state->cec_queue_start = + kzalloc(sizeof(struct hdmi_msm_cec_msg)*CEC_QUEUE_SIZE, + GFP_KERNEL); + if (!hdmi_msm_state->cec_queue_start) { + pr_err("hdmi_msm_init FAILED: CEC queue out of memory\n"); + rc = -ENOMEM; + goto init_exit; + } + + hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start; + hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start; + hdmi_msm_state->cec_queue_full = false; +#endif + + /* + * Create your work queue + * allocs and returns ptr + */ + hdmi_work_queue = create_workqueue("hdmi_hdcp"); + external_common_state->hpd_feature = hdmi_msm_hpd_feature; + + rc = platform_driver_register(&this_driver); + if (rc) { + pr_err("hdmi_msm_init FAILED: platform_driver_register rc=%d\n", + rc); + goto init_exit; + } + + hdmi_common_init_panel_info(&hdmi_msm_panel_data.panel_info); + init_completion(&hdmi_msm_state->ddc_sw_done); + INIT_WORK(&hdmi_msm_state->hpd_state_work, hdmi_msm_hpd_state_work); + INIT_WORK(&hdmi_msm_state->hpd_read_work, hdmi_msm_hpd_read_work); +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + init_completion(&hdmi_msm_state->hdcp_success_done); + INIT_WORK(&hdmi_msm_state->hdcp_reauth_work, hdmi_msm_hdcp_reauth_work); + INIT_WORK(&hdmi_msm_state->hdcp_work, hdmi_msm_hdcp_work); +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + INIT_WORK(&hdmi_msm_state->cec_latch_detect_work, + hdmi_msm_cec_latch_work); + init_completion(&hdmi_msm_state->cec_frame_wr_done); + init_completion(&hdmi_msm_state->cec_line_latch_wait); +#endif + + rc = platform_device_register(&this_device); + if (rc) { + pr_err("hdmi_msm_init FAILED: platform_device_register rc=%d\n", + rc); + platform_driver_unregister(&this_driver); + goto init_exit; + } + + pr_debug("%s: success:" +#ifdef DEBUG + " DEBUG" +#else + " RELEASE" +#endif + " AUDIO EDID HPD HDCP" +#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + ":0" +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + " DVI" +#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_DVI_SUPPORT + ":0" +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_DVI_SUPPORT */ + "\n", __func__); + + return 0; + +init_exit: + kfree(hdmi_msm_state); + hdmi_msm_state = NULL; + + return rc; +} + +static void __exit hdmi_msm_exit(void) +{ + platform_device_unregister(&this_device); + platform_driver_unregister(&this_driver); +} + +module_init(hdmi_msm_init); +module_exit(hdmi_msm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.3"); +MODULE_AUTHOR("Qualcomm Innovation Center, Inc."); +MODULE_DESCRIPTION("HDMI MSM TX driver"); diff --git a/drivers/video/msm/hdmi_msm.h b/drivers/video/msm/hdmi_msm.h new file mode 100644 index 0000000000000000000000000000000000000000..489dce8fcdc3a977587d3c345b8d38c549b2f0e7 --- /dev/null +++ b/drivers/video/msm/hdmi_msm.h @@ -0,0 +1,138 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __HDMI_MSM_H__ +#define __HDMI_MSM_H__ + +#include +#include "external_common.h" +/* #define PORT_DEBUG */ + +#ifdef PORT_DEBUG +const char *hdmi_msm_name(uint32 offset); +void hdmi_outp(uint32 offset, uint32 value); +uint32 hdmi_inp(uint32 offset); + +#define HDMI_OUTP_ND(offset, value) outpdw(MSM_HDMI_BASE+(offset), (value)) +#define HDMI_OUTP(offset, value) hdmi_outp((offset), (value)) +#define HDMI_INP_ND(offset) inpdw(MSM_HDMI_BASE+(offset)) +#define HDMI_INP(offset) hdmi_inp((offset)) +#else +#define HDMI_OUTP_ND(offset, value) outpdw(MSM_HDMI_BASE+(offset), (value)) +#define HDMI_OUTP(offset, value) outpdw(MSM_HDMI_BASE+(offset), (value)) +#define HDMI_INP_ND(offset) inpdw(MSM_HDMI_BASE+(offset)) +#define HDMI_INP(offset) inpdw(MSM_HDMI_BASE+(offset)) +#endif + + +/* + * Ref. HDMI 1.4a + * Supplement-1 CEC Section 6, 7 + */ +struct hdmi_msm_cec_msg { + uint8 sender_id; + uint8 recvr_id; + uint8 opcode; + uint8 operand[15]; + uint8 frame_size; + uint8 retransmit; +}; + +#define QFPROM_BASE ((uint32)hdmi_msm_state->qfprom_io) +#define HDMI_BASE ((uint32)hdmi_msm_state->hdmi_io) + +struct hdmi_msm_state_type { + boolean panel_power_on; + boolean hpd_initialized; +#ifdef CONFIG_SUSPEND + boolean pm_suspended; +#endif + int hpd_stable; + boolean hpd_prev_state; + boolean hpd_cable_chg_detected; + boolean full_auth_done; + boolean hpd_during_auth; + struct work_struct hpd_state_work, hpd_read_work; + struct timer_list hpd_state_timer; + struct completion ddc_sw_done; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + boolean hdcp_activating; + boolean reauth ; + struct work_struct hdcp_reauth_work, hdcp_work; + struct completion hdcp_success_done; + struct timer_list hdcp_timer; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT + boolean cec_enabled; + unsigned int first_monitor; + int cec_logical_addr; + struct completion cec_frame_wr_done; + struct timer_list cec_read_timer; +#define CEC_STATUS_WR_ERROR 0x0001 +#define CEC_STATUS_WR_DONE 0x0002 +#define CEC_STATUS_WR_TMOUT 0x0004 + uint32 cec_frame_wr_status; + + struct hdmi_msm_cec_msg *cec_queue_start; + struct hdmi_msm_cec_msg *cec_queue_wr; + struct hdmi_msm_cec_msg *cec_queue_rd; + boolean cec_queue_full; + boolean fsm_reset_done; + + /* + * CECT 9-5-1 + */ + struct completion cec_line_latch_wait; + struct work_struct cec_latch_detect_work; + +#define CEC_QUEUE_SIZE 16 +#define CEC_QUEUE_END (hdmi_msm_state->cec_queue_start + CEC_QUEUE_SIZE) +#define RETRANSMIT_MAX_NUM 5 +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + + int irq; + struct msm_hdmi_platform_data *pd; + struct clk *hdmi_app_clk; + struct clk *hdmi_m_pclk; + struct clk *hdmi_s_pclk; + void __iomem *qfprom_io; + void __iomem *hdmi_io; + + struct external_common_state_type common; +}; + +extern struct hdmi_msm_state_type *hdmi_msm_state; + +uint32 hdmi_msm_get_io_base(void); + +#ifdef CONFIG_FB_MSM_HDMI_COMMON +void hdmi_msm_set_mode(boolean power_on); +int hdmi_msm_clk(int on); +void hdmi_phy_reset(void); +void hdmi_msm_reset_core(void); +void hdmi_msm_init_phy(int video_format); +void hdmi_msm_powerdown_phy(void); +void hdmi_frame_ctrl_cfg(const struct hdmi_disp_mode_timing_type *timing); +void hdmi_msm_phy_status_poll(void); +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT +void hdmi_msm_cec_init(void); +void hdmi_msm_cec_write_logical_addr(int addr); +void hdmi_msm_cec_msg_recv(void); +void hdmi_msm_cec_one_touch_play(void); +void hdmi_msm_cec_msg_send(struct hdmi_msm_cec_msg *msg); +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ + +#endif /* __HDMI_MSM_H__ */ diff --git a/drivers/video/msm/hdmi_sii9022.c b/drivers/video/msm/hdmi_sii9022.c new file mode 100644 index 0000000000000000000000000000000000000000..c1b5e03caf94823eae57a9ff252ab79ed1808f56 --- /dev/null +++ b/drivers/video/msm/hdmi_sii9022.c @@ -0,0 +1,245 @@ +/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "msm_fb.h" + +#define DEVICE_NAME "sii9022" +#define SII9022_DEVICE_ID 0xB0 + +struct sii9022_i2c_addr_data{ + u8 addr; + u8 data; +}; + +/* video mode data */ +static u8 video_mode_data[] = { + 0x00, + 0xF9, 0x1C, 0x70, 0x17, 0x72, 0x06, 0xEE, 0x02, +}; + +static u8 avi_io_format[] = { + 0x09, + 0x00, 0x00, +}; + +/* power state */ +static struct sii9022_i2c_addr_data regset0[] = { + { 0x60, 0x04 }, + { 0x63, 0x00 }, + { 0x1E, 0x00 }, +}; + +static u8 video_infoframe[] = { + 0x0C, + 0xF0, 0x00, 0x68, 0x00, 0x04, 0x00, 0x19, 0x00, + 0xE9, 0x02, 0x04, 0x01, 0x04, 0x06, +}; + +/* configure audio */ +static struct sii9022_i2c_addr_data regset1[] = { + { 0x26, 0x90 }, + { 0x20, 0x90 }, + { 0x1F, 0x80 }, + { 0x26, 0x80 }, + { 0x24, 0x02 }, + { 0x25, 0x0B }, + { 0xBC, 0x02 }, + { 0xBD, 0x24 }, + { 0xBE, 0x02 }, +}; + +/* enable audio */ +static u8 misc_infoframe[] = { + 0xBF, + 0xC2, 0x84, 0x01, 0x0A, 0x6F, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +/* set HDMI, active */ +static struct sii9022_i2c_addr_data regset2[] = { + { 0x1A, 0x01 }, + { 0x3D, 0x00 }, +}; + +static int send_i2c_data(struct i2c_client *client, + struct sii9022_i2c_addr_data *regset, + int size) +{ + int i; + int rc = 0; + + for (i = 0; i < size; i++) { + rc = i2c_smbus_write_byte_data( + client, + regset[i].addr, regset[i].data); + if (rc) + break; + } + return rc; +} + +static int hdmi_sii_enable(struct i2c_client *client) +{ + int rc; + int retries = 10; + int count; + + rc = i2c_smbus_write_byte_data(client, 0xC7, 0x00); + if (rc) + goto enable_exit; + + do { + msleep(1); + rc = i2c_smbus_read_byte_data(client, 0x1B); + } while ((rc != SII9022_DEVICE_ID) && retries--); + + if (rc != SII9022_DEVICE_ID) + return -ENODEV; + + rc = i2c_smbus_write_byte_data(client, 0x1A, 0x11); + if (rc) + goto enable_exit; + + count = ARRAY_SIZE(video_mode_data); + rc = i2c_master_send(client, video_mode_data, count); + if (rc != count) { + rc = -EIO; + goto enable_exit; + } + + rc = i2c_smbus_write_byte_data(client, 0x08, 0x20); + if (rc) + goto enable_exit; + count = ARRAY_SIZE(avi_io_format); + rc = i2c_master_send(client, avi_io_format, count); + if (rc != count) { + rc = -EIO; + goto enable_exit; + } + + rc = send_i2c_data(client, regset0, ARRAY_SIZE(regset0)); + if (rc) + goto enable_exit; + + count = ARRAY_SIZE(video_infoframe); + rc = i2c_master_send(client, video_infoframe, count); + if (rc != count) { + rc = -EIO; + goto enable_exit; + } + + rc = send_i2c_data(client, regset1, ARRAY_SIZE(regset1)); + if (rc) + goto enable_exit; + + count = ARRAY_SIZE(misc_infoframe); + rc = i2c_master_send(client, misc_infoframe, count); + if (rc != count) { + rc = -EIO; + goto enable_exit; + } + + rc = send_i2c_data(client, regset2, ARRAY_SIZE(regset2)); + if (rc) + goto enable_exit; + + return 0; +enable_exit: + printk(KERN_ERR "%s: exited rc=%d\n", __func__, rc); + return rc; +} + +static const struct i2c_device_id hmdi_sii_id[] = { + { DEVICE_NAME, 0 }, + { } +}; + +static int hdmi_sii_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) + return -ENODEV; + rc = hdmi_sii_enable(client); + return rc; +} + + +static struct i2c_driver hdmi_sii_i2c_driver = { + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + }, + .probe = hdmi_sii_probe, + .remove = __exit_p(hdmi_sii_remove), + .id_table = hmdi_sii_id, +}; + +static int __init hdmi_sii_init(void) +{ + int ret; + struct msm_panel_info pinfo; + + if (msm_fb_detect_client("hdmi_sii9022")) + return 0; + + pinfo.xres = 1280; + pinfo.yres = 720; + MSM_FB_SINGLE_MODE_PANEL(&pinfo); + pinfo.type = HDMI_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 18; + pinfo.fb_num = 2; + pinfo.clk_rate = 74250000; + + pinfo.lcdc.h_back_porch = 124; + pinfo.lcdc.h_front_porch = 110; + pinfo.lcdc.h_pulse_width = 136; + pinfo.lcdc.v_back_porch = 19; + pinfo.lcdc.v_front_porch = 5; + pinfo.lcdc.v_pulse_width = 6; + pinfo.lcdc.border_clr = 0; + pinfo.lcdc.underflow_clr = 0xff; + pinfo.lcdc.hsync_skew = 0; + + ret = lcdc_device_register(&pinfo); + if (ret) { + printk(KERN_ERR "%s: failed to register device\n", __func__); + goto init_exit; + } + + ret = i2c_add_driver(&hdmi_sii_i2c_driver); + if (ret) + printk(KERN_ERR "%s: failed to add i2c driver\n", __func__); + +init_exit: + return ret; +} + +static void __exit hdmi_sii_exit(void) +{ + i2c_del_driver(&hdmi_sii_i2c_driver); +} + +module_init(hdmi_sii_init); +module_exit(hdmi_sii_exit); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Qualcomm Innovation Center, Inc."); +MODULE_DESCRIPTION("SiI9022 HDMI driver"); +MODULE_ALIAS("platform:hdmi-sii9022"); diff --git a/drivers/video/msm/lcdc.c b/drivers/video/msm/lcdc.c new file mode 100644 index 0000000000000000000000000000000000000000..7156e4cdda388f185813cf055ce843479690ff0c --- /dev/null +++ b/drivers/video/msm/lcdc.c @@ -0,0 +1,289 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" + +static int lcdc_probe(struct platform_device *pdev); +static int lcdc_remove(struct platform_device *pdev); + +static int lcdc_off(struct platform_device *pdev); +static int lcdc_on(struct platform_device *pdev); + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; + +static struct clk *pixel_mdp_clk; /* drives the lcdc block in mdp */ +static struct clk *pixel_lcdc_clk; /* drives the lcdc interface */ + +static struct platform_driver lcdc_driver = { + .probe = lcdc_probe, + .remove = lcdc_remove, + .suspend = NULL, + .resume = NULL, + .shutdown = NULL, + .driver = { + .name = "lcdc", + }, +}; + +static struct lcdc_platform_data *lcdc_pdata; + +static int lcdc_off(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + ret = panel_next_off(pdev); + + clk_disable_unprepare(pixel_mdp_clk); + clk_disable_unprepare(pixel_lcdc_clk); + + if (lcdc_pdata && lcdc_pdata->lcdc_power_save) + lcdc_pdata->lcdc_power_save(0); + + if (lcdc_pdata && lcdc_pdata->lcdc_gpio_config) + ret = lcdc_pdata->lcdc_gpio_config(0); + +#ifndef CONFIG_MSM_BUS_SCALING + if (mfd->ebi1_clk) { + if (mdp_rev == MDP_REV_303) { + if (clk_set_rate(mfd->ebi1_clk, 0)) + pr_err("%s: ebi1_lcdc_clk set rate failed\n", + __func__); + } + clk_disable_unprepare(mfd->ebi1_clk); + } +#else + mdp_bus_scale_update_request(0); +#endif + + return ret; +} + +static int lcdc_on(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + unsigned long panel_pixclock_freq = 0; +#ifndef CONFIG_MSM_BUS_SCALING + unsigned long pm_qos_rate; +#endif + mfd = platform_get_drvdata(pdev); + + if (lcdc_pdata && lcdc_pdata->lcdc_get_clk) + panel_pixclock_freq = lcdc_pdata->lcdc_get_clk(); + + if (!panel_pixclock_freq) + panel_pixclock_freq = mfd->fbi->var.pixclock; +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(2); +#else + if (panel_pixclock_freq > 65000000) + /* pm_qos_rate should be in Khz */ + pm_qos_rate = panel_pixclock_freq / 1000 ; + else + pm_qos_rate = 65000; + + if (mfd->ebi1_clk) { + if (mdp_rev == MDP_REV_303) { + if (clk_set_rate(mfd->ebi1_clk, 65000000)) + pr_err("%s: ebi1_lcdc_clk set rate failed\n", + __func__); + } else { + clk_set_rate(mfd->ebi1_clk, pm_qos_rate * 1000); + } + clk_prepare_enable(mfd->ebi1_clk); + } + +#endif + mfd = platform_get_drvdata(pdev); + + mfd->fbi->var.pixclock = clk_round_rate(pixel_mdp_clk, + mfd->fbi->var.pixclock); + ret = clk_set_rate(pixel_mdp_clk, mfd->fbi->var.pixclock); + if (ret) { + pr_err("%s: Can't set MDP LCDC pixel clock to rate %u\n", + __func__, mfd->fbi->var.pixclock); + goto out; + } + + clk_prepare_enable(pixel_mdp_clk); + clk_prepare_enable(pixel_lcdc_clk); + + if (lcdc_pdata && lcdc_pdata->lcdc_power_save) + lcdc_pdata->lcdc_power_save(1); + if (lcdc_pdata && lcdc_pdata->lcdc_gpio_config) + ret = lcdc_pdata->lcdc_gpio_config(1); + + ret = panel_next_on(pdev); + +out: + return ret; +} + +static int lcdc_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct fb_info *fbi; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc; + struct clk *ebi1_clk = NULL; + + if (pdev->id == 0) { + lcdc_pdata = pdev->dev.platform_data; + pixel_mdp_clk = clk_get(&pdev->dev, "mdp_clk"); + if (IS_ERR(pixel_mdp_clk)) { + pr_err("Couldnt find pixel_mdp_clk\n"); + return -EINVAL; + } + + pixel_lcdc_clk = clk_get(&pdev->dev, "lcdc_clk"); + if (IS_ERR(pixel_lcdc_clk)) { + pr_err("Couldnt find pixel_lcdc_clk\n"); + return -EINVAL; + } + +#ifndef CONFIG_MSM_BUS_SCALING + ebi1_clk = clk_get(&pdev->dev, "mem_clk"); + if (IS_ERR(ebi1_clk)) + return PTR_ERR(ebi1_clk); +#endif + + return 0; + } + + mfd = platform_get_drvdata(pdev); + mfd->ebi1_clk = ebi1_clk; + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + + /* + * link to the latest pdev + */ + mfd->pdev = mdp_dev; + mfd->dest = DISPLAY_LCDC; + + /* + * alloc panel device data + */ + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + pr_err("lcdc_probe: platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + /* + * data chain + */ + pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data; + pdata->on = lcdc_on; + pdata->off = lcdc_off; + pdata->next = pdev; + + /* + * get/set panel specific fb info + */ + mfd->panel_info = pdata->panel_info; + + if (mfd->index == 0) + mfd->fb_imgType = MSMFB_DEFAULT_TYPE; + else + mfd->fb_imgType = MDP_RGB_565; + + fbi = mfd->fbi; + fbi->var.pixclock = clk_round_rate(pixel_mdp_clk, + mfd->panel_info.clk_rate); + fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch; + fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch; + fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch; + fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch; + fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width; + fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width; + + /* + * set driver data + */ + platform_set_drvdata(mdp_dev, mfd); + /* + * register in mdp driver + */ + rc = platform_device_add(mdp_dev); + if (rc) + goto lcdc_probe_err; + + pdev_list[pdev_list_cnt++] = pdev; + + return 0; + +lcdc_probe_err: + platform_device_put(mdp_dev); + return rc; +} + +static int lcdc_remove(struct platform_device *pdev) +{ +#ifndef CONFIG_MSM_BUS_SCALING + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + clk_put(mfd->ebi1_clk); +#endif + return 0; +} + +static int lcdc_register_driver(void) +{ + return platform_driver_register(&lcdc_driver); +} + +static int __init lcdc_driver_init(void) +{ + + return lcdc_register_driver(); +} + +module_init(lcdc_driver_init); diff --git a/drivers/video/msm/lcdc_auo_wvga.c b/drivers/video/msm/lcdc_auo_wvga.c new file mode 100644 index 0000000000000000000000000000000000000000..f346c459c3b5cfdfbc5b8792c97cd42e2e07e2c7 --- /dev/null +++ b/drivers/video/msm/lcdc_auo_wvga.c @@ -0,0 +1,411 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#ifdef CONFIG_SPI_QUP +#include +#else +#include +#endif +#include "msm_fb.h" + +#define MAX_BACKLIGHT_LEVEL 15 +#define PANEL_CMD_BACKLIGHT_LEVEL 0x6A18 +#define PANEL_CMD_FORMAT 0x3A00 +#define PANEL_CMD_RGBCTRL 0x3B00 +#define PANEL_CMD_BCTRL 0x5300 +#define PANEL_CMD_PWM_EN 0x6A17 + +#define PANEL_CMD_SLEEP_OUT 0x1100 +#define PANEL_CMD_DISP_ON 0x2900 +#define PANEL_CMD_DISP_OFF 0x2800 +#define PANEL_CMD_SLEEP_IN 0x1000 + +#define LCDC_AUO_PANEL_NAME "lcdc_auo_wvga" + +#ifdef CONFIG_SPI_QUP +#define LCDC_AUO_SPI_DEVICE_NAME "lcdc_auo_nt35582" +static struct spi_device *lcdc_spi_client; +#else +static int spi_cs; +static int spi_sclk; +static int spi_mosi; +#endif + +struct auo_state_type { + boolean display_on; + int bl_level; +}; + + +static struct auo_state_type auo_state = { .bl_level = 10 }; +static struct msm_panel_common_pdata *lcdc_auo_pdata; + +#ifndef CONFIG_SPI_QUP +static void auo_spi_write_byte(u8 data) +{ + uint32 bit; + int bnum; + + bnum = 8; /* 8 data bits */ + bit = 0x80; + while (bnum--) { + gpio_set_value(spi_sclk, 0); /* clk low */ + gpio_set_value(spi_mosi, (data & bit) ? 1 : 0); + udelay(1); + gpio_set_value(spi_sclk, 1); /* clk high */ + udelay(1); + bit >>= 1; + } + gpio_set_value(spi_mosi, 0); +} + +static void auo_spi_read_byte(u16 cmd_16, u8 *data) +{ + int bnum; + u8 cmd_hi = (u8)(cmd_16 >> 8); + u8 cmd_low = (u8)(cmd_16); + + /* Chip Select - low */ + gpio_set_value(spi_cs, 0); + udelay(2); + + /* command byte first */ + auo_spi_write_byte(0x20); + udelay(2); + auo_spi_write_byte(cmd_hi); + udelay(2); + auo_spi_write_byte(0x00); + udelay(2); + auo_spi_write_byte(cmd_low); + udelay(2); + auo_spi_write_byte(0xc0); + udelay(2); + + gpio_direction_input(spi_mosi); + + /* followed by data bytes */ + bnum = 1 * 8; /* number of bits */ + *data = 0; + while (bnum) { + gpio_set_value(spi_sclk, 0); /* clk low */ + udelay(1); + *data <<= 1; + *data |= gpio_get_value(spi_mosi) ? 1 : 0; + gpio_set_value(spi_sclk, 1); /* clk high */ + udelay(1); + --bnum; + if ((bnum % 8) == 0) + ++data; + } + + gpio_direction_output(spi_mosi, 0); + + /* Chip Select - high */ + udelay(2); + gpio_set_value(spi_cs, 1); +} +#endif + +static int auo_serigo(u8 *input_data, int input_len) +{ +#ifdef CONFIG_SPI_QUP + int rc; + struct spi_message m; + struct spi_transfer t; + + if (!lcdc_spi_client) { + pr_err("%s lcdc_spi_client is NULL\n", __func__); + return -EINVAL; + } + + memset(&t, 0, sizeof t); + + t.tx_buf = input_data; + t.len = input_len; + t.bits_per_word = 16; + + spi_setup(lcdc_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + rc = spi_sync(lcdc_spi_client, &m); + + return rc; +#else + int i; + + /* Chip Select - low */ + gpio_set_value(spi_cs, 0); + udelay(2); + + for (i = 0; i < input_len; ++i) { + auo_spi_write_byte(input_data[i]); + udelay(2); + } + + /* Chip Select - high */ + gpio_set_value(spi_cs, 1); + + return 0; +#endif +} + +#ifndef CONFIG_SPI_QUP +static void auo_spi_init(void) +{ + spi_sclk = *(lcdc_auo_pdata->gpio_num); + spi_cs = *(lcdc_auo_pdata->gpio_num + 1); + spi_mosi = *(lcdc_auo_pdata->gpio_num + 2); + + /* Set the output so that we don't disturb the slave device */ + gpio_set_value(spi_sclk, 1); + gpio_set_value(spi_mosi, 0); + + /* Set the Chip Select deasserted (active low) */ + gpio_set_value(spi_cs, 1); +} +#endif + +static struct work_struct disp_on_delayed_work; +static void auo_write_cmd(u16 cmd) +{ + u8 local_data[4]; + + local_data[0] = 0x20; + local_data[1] = (u8)(cmd >> 8); + local_data[2] = 0; + local_data[3] = (u8)cmd; + auo_serigo(local_data, 4); +} +static void auo_write_cmd_1param(u16 cmd, u8 para1) +{ + u8 local_data[6]; + + local_data[0] = 0x20; + local_data[1] = (u8)(cmd >> 8); + local_data[2] = 0; + local_data[3] = (u8)cmd; + local_data[4] = 0x40; + local_data[5] = para1; + auo_serigo(local_data, 6); +} +static void lcdc_auo_set_backlight(struct msm_fb_data_type *mfd) +{ + int bl_level; + + bl_level = mfd->bl_level; + if (auo_state.display_on) { + auo_write_cmd_1param(PANEL_CMD_BACKLIGHT_LEVEL, + bl_level * 255 / MAX_BACKLIGHT_LEVEL); + auo_state.bl_level = bl_level; + } + +} +static void auo_disp_on_delayed_work(struct work_struct *work_ptr) +{ + /* 0x1100: Sleep Out */ + auo_write_cmd(PANEL_CMD_SLEEP_OUT); + + msleep(180); + + /* SET_PIXEL_FORMAT: Set how many bits per pixel are used (3A00h)*/ + auo_write_cmd_1param(PANEL_CMD_FORMAT, 0x66); /* 18 bits */ + + /* RGBCTRL: RGB Interface Signal Control (3B00h) */ + auo_write_cmd_1param(PANEL_CMD_RGBCTRL, 0x2B); + + /* Display ON command */ + auo_write_cmd(PANEL_CMD_DISP_ON); + msleep(20); + + /*Backlight on */ + auo_write_cmd_1param(PANEL_CMD_BCTRL, 0x24); /*BCTRL, BL */ + auo_write_cmd_1param(PANEL_CMD_PWM_EN, 0x01); /*Enable PWM Level */ + + msleep(20); +} + +static void auo_disp_on(void) +{ + if (!auo_state.display_on) { + INIT_WORK(&disp_on_delayed_work, auo_disp_on_delayed_work); +#ifdef CONFIG_SPI_QUP + if (lcdc_spi_client) +#endif + schedule_work(&disp_on_delayed_work); + auo_state.display_on = TRUE; + } +} + +static int lcdc_auo_panel_on(struct platform_device *pdev) +{ + pr_info("%s\n", __func__); + if (!auo_state.display_on) { +#ifndef CONFIG_SPI_QUP + lcdc_auo_pdata->panel_config_gpio(1); + auo_spi_init(); +#endif + auo_disp_on(); + } + return 0; +} + +static int lcdc_auo_panel_off(struct platform_device *pdev) +{ + pr_info("%s\n", __func__); + if (auo_state.display_on) { + /* 0x2800: Display Off */ + auo_write_cmd(PANEL_CMD_DISP_OFF); + msleep(120); + /* 0x1000: Sleep In */ + auo_write_cmd(PANEL_CMD_SLEEP_IN); + msleep(120); + + auo_state.display_on = FALSE; + } + return 0; +} + +static int auo_probe(struct platform_device *pdev) +{ + pr_info("%s: id=%d\n", __func__, pdev->id); + if (pdev->id == 0) { + lcdc_auo_pdata = pdev->dev.platform_data; + return 0; + } + + msm_fb_add_device(pdev); + + return 0; +} + +#ifdef CONFIG_SPI_QUP +static int lcdc_auo_spi_probe(struct spi_device *spi) +{ + pr_info("%s\n", __func__); + lcdc_spi_client = spi; + lcdc_spi_client->bits_per_word = 32; + if (auo_state.display_on) + schedule_work(&disp_on_delayed_work); + return 0; +} +static int lcdc_auo_spi_remove(struct spi_device *spi) +{ + lcdc_spi_client = NULL; + return 0; +} +static struct spi_driver lcdc_auo_spi_driver = { + .driver.name = LCDC_AUO_SPI_DEVICE_NAME, + .driver.owner = THIS_MODULE, + .probe = lcdc_auo_spi_probe, + .remove = lcdc_auo_spi_remove, +}; +#endif + +static struct platform_driver this_driver = { + .probe = auo_probe, + .driver.name = LCDC_AUO_PANEL_NAME, +}; + +static struct msm_fb_panel_data auo_panel_data = { + .on = lcdc_auo_panel_on, + .off = lcdc_auo_panel_off, + .set_backlight = lcdc_auo_set_backlight, +}; + +static struct platform_device this_device = { + .name = LCDC_AUO_PANEL_NAME, + .id = 1, + .dev.platform_data = &auo_panel_data, +}; + +static int __init lcdc_auo_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + if (msm_fb_detect_client(LCDC_AUO_PANEL_NAME)) { + pr_err("%s: detect failed\n", __func__); + return 0; + } + + ret = platform_driver_register(&this_driver); + if (ret) { + pr_err("%s: driver register failed, rc=%d\n", __func__, ret); + return ret; + } + + pinfo = &auo_panel_data.panel_info; + pinfo->xres = 480; + pinfo->yres = 800; + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + pinfo->clk_rate = 25600000; + pinfo->bl_max = MAX_BACKLIGHT_LEVEL; + pinfo->bl_min = 1; + + pinfo->lcdc.h_back_porch = 16-2; /* HBP-HLW */ + pinfo->lcdc.h_front_porch = 16; + pinfo->lcdc.h_pulse_width = 2; + + pinfo->lcdc.v_back_porch = 3-2; /* VBP-VLW */ + pinfo->lcdc.v_front_porch = 28; + pinfo->lcdc.v_pulse_width = 2; + + pinfo->lcdc.border_clr = 0; + pinfo->lcdc.underflow_clr = 0xff; + pinfo->lcdc.hsync_skew = 0; + + ret = platform_device_register(&this_device); + if (ret) { + pr_err("%s: device register failed, rc=%d\n", __func__, ret); + goto fail_driver; + } +#ifdef CONFIG_SPI_QUP + ret = spi_register_driver(&lcdc_auo_spi_driver); + + if (ret) { + pr_err("%s: spi register failed: rc=%d\n", __func__, ret); + goto fail_device; + } + pr_info("%s: SUCCESS (SPI)\n", __func__); +#else + pr_info("%s: SUCCESS (BitBang)\n", __func__); +#endif + return ret; + +#ifdef CONFIG_SPI_QUP +fail_device: + platform_device_unregister(&this_device); +#endif +fail_driver: + platform_driver_unregister(&this_driver); + + return ret; +} + +module_init(lcdc_auo_panel_init); +static void __exit lcdc_auo_panel_exit(void) +{ + pr_info("%s\n", __func__); + platform_device_unregister(&this_device); + platform_driver_unregister(&this_driver); +#ifdef CONFIG_SPI_QUP + spi_unregister_driver(&lcdc_auo_spi_driver); +#endif +} +module_exit(lcdc_auo_panel_exit); diff --git a/drivers/video/msm/lcdc_chimei_wxga.c b/drivers/video/msm/lcdc_chimei_wxga.c new file mode 100644 index 0000000000000000000000000000000000000000..a29a38bea682e25071b70ab15b5d011527d1f8a6 --- /dev/null +++ b/drivers/video/msm/lcdc_chimei_wxga.c @@ -0,0 +1,231 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#ifdef CONFIG_PMIC8058_PWM +#include +#include +#endif +#include +#include "msm_fb.h" + + + + +static struct pwm_device *bl_pwm; + +#define PWM_FREQ_HZ 210 +#define PWM_PERIOD_USEC (USEC_PER_SEC / PWM_FREQ_HZ) +#define PWM_DUTY_LEVEL (PWM_PERIOD_USEC / PWM_LEVEL) +#define PWM_LEVEL 15 + +static struct msm_panel_common_pdata *cm_pdata; +static struct platform_device *cm_fbpdev; +static int led_pwm; /* pm8058 gpio 24, channel 0 */ +static int led_en; /* pm8058 gpio 1 */ +static int lvds_pwr_down; /* msm gpio 30 */ +static int chimei_bl_level = 1; + + +static void lcdc_chimei_set_backlight(int level) +{ + int ret; + + if (bl_pwm) { + ret = pwm_config(bl_pwm, PWM_DUTY_LEVEL * level, + PWM_PERIOD_USEC); + if (ret) { + pr_err("%s: pwm_config on pwm failed %d\n", + __func__, ret); + return; + } + + ret = pwm_enable(bl_pwm); + if (ret) { + pr_err("%s: pwm_enable on pwm failed %d\n", + __func__, ret); + return; + } + } + + chimei_bl_level = level; +} + +static int lcdc_chimei_panel_on(struct platform_device *pdev) +{ + int ret; + + /* panel powered on here */ + + ret = gpio_request(lvds_pwr_down, "lvds_pwr_down"); + if (ret == 0) { + /* output, pull high to enable */ + gpio_direction_output(lvds_pwr_down, 1); + } else { + pr_err("%s: lvds_pwr_down=%d, gpio_request failed\n", + __func__, lvds_pwr_down); + } + + msleep(200); + /* power on led pwm power >= 200 ms */ + + if (chimei_bl_level == 0) + chimei_bl_level = 1; + lcdc_chimei_set_backlight(chimei_bl_level); + + msleep(10); + + ret = gpio_request(led_en, "led_en"); + if (ret == 0) { + /* output, pull high */ + gpio_direction_output(led_en, 1); + } else { + pr_err("%s: led_en=%d, gpio_request failed\n", + __func__, led_en); + } + return ret; +} + +static int lcdc_chimei_panel_off(struct platform_device *pdev) +{ + /* pull low to disable */ + gpio_set_value_cansleep(led_en, 0); + gpio_free(led_en); + + msleep(10); + + lcdc_chimei_set_backlight(0); + + msleep(200); + /* power off led pwm power >= 200 ms */ + + /* pull low to shut down lvds */ + gpio_set_value_cansleep(lvds_pwr_down, 0); + gpio_free(lvds_pwr_down); + + /* panel power off here */ + + return 0; +} + +static void lcdc_chimei_panel_backlight(struct msm_fb_data_type *mfd) +{ + lcdc_chimei_set_backlight(mfd->bl_level); +} + +static int chimei_probe(struct platform_device *pdev) +{ + int rc = 0; + + if (pdev->id == 0) { + cm_pdata = pdev->dev.platform_data; + if (cm_pdata == NULL) { + pr_err("%s: no PWM gpio specified\n", __func__); + return 0; + } + led_pwm = cm_pdata->gpio_num[0]; + led_en = cm_pdata->gpio_num[1]; + lvds_pwr_down = cm_pdata->gpio_num[2]; + pr_info("%s: led_pwm=%d led_en=%d lvds_pwr_down=%d\n", + __func__, led_pwm, led_en, lvds_pwr_down); + return 0; + } + + if (cm_pdata == NULL) + return -ENODEV; + + bl_pwm = pwm_request(led_pwm, "backlight"); + if (bl_pwm == NULL || IS_ERR(bl_pwm)) { + pr_err("%s pwm_request() failed\n", __func__); + bl_pwm = NULL; + } + + cm_fbpdev = msm_fb_add_device(pdev); + if (!cm_fbpdev) { + dev_err(&pdev->dev, "failed to add msm_fb device\n"); + rc = -ENODEV; + goto probe_exit; + } + +probe_exit: + return rc; +} + +static struct platform_driver this_driver = { + .probe = chimei_probe, + .driver = { + .name = "lcdc_chimei_lvds_wxga", + }, +}; + +static struct msm_fb_panel_data chimei_panel_data = { + .on = lcdc_chimei_panel_on, + .off = lcdc_chimei_panel_off, + .set_backlight = lcdc_chimei_panel_backlight, +}; + +static struct platform_device this_device = { + .name = "lcdc_chimei_lvds_wxga", + .id = 1, + .dev = { + .platform_data = &chimei_panel_data, + } +}; + +static int __init lcdc_chimei_lvds_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + if (msm_fb_detect_client("lcdc_chimei_wxga")) + return 0; + + ret = platform_driver_register(&this_driver); + if (ret) + return ret; + + pinfo = &chimei_panel_data.panel_info; + pinfo->xres = 1366; + pinfo->yres = 768; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + pinfo->clk_rate = 69300000; + pinfo->bl_max = PWM_LEVEL; + pinfo->bl_min = 1; + + /* + * this panel is operated by de, + * vsycn and hsync are ignored + */ + pinfo->lcdc.h_back_porch = 108; + pinfo->lcdc.h_front_porch = 0; + pinfo->lcdc.h_pulse_width = 1; + pinfo->lcdc.v_back_porch = 0; + pinfo->lcdc.v_front_porch = 16; + pinfo->lcdc.v_pulse_width = 1; + pinfo->lcdc.border_clr = 0; + pinfo->lcdc.underflow_clr = 0xff; + pinfo->lcdc.hsync_skew = 0; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + + return ret; +} + +module_init(lcdc_chimei_lvds_panel_init); diff --git a/drivers/video/msm/lcdc_external.c b/drivers/video/msm/lcdc_external.c new file mode 100644 index 0000000000000000000000000000000000000000..b699610f51077fda4568a25d8506c69d7a345658 --- /dev/null +++ b/drivers/video/msm/lcdc_external.c @@ -0,0 +1,51 @@ +/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" + +static int __init lcdc_external_init(void) +{ + int ret; + struct msm_panel_info pinfo; + + if (msm_fb_detect_client("lcdc_external")) + return 0; + + pinfo.xres = 1280; + pinfo.yres = 720; + MSM_FB_SINGLE_MODE_PANEL(&pinfo); + pinfo.type = LCDC_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.fb_num = 2; + pinfo.clk_rate = 74250000; + + pinfo.lcdc.h_back_porch = 124; + pinfo.lcdc.h_front_porch = 110; + pinfo.lcdc.h_pulse_width = 136; + pinfo.lcdc.v_back_porch = 19; + pinfo.lcdc.v_front_porch = 5; + pinfo.lcdc.v_pulse_width = 6; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + + ret = lcdc_device_register(&pinfo); + if (ret) + printk(KERN_ERR "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(lcdc_external_init); diff --git a/drivers/video/msm/lcdc_gordon.c b/drivers/video/msm/lcdc_gordon.c new file mode 100644 index 0000000000000000000000000000000000000000..1b6d5f0446372399cf6fdc0c5293f02e5a0dc729 --- /dev/null +++ b/drivers/video/msm/lcdc_gordon.c @@ -0,0 +1,457 @@ +/* Copyright (c) 2009-2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "msm_fb.h" + +/* registers */ +#define GORDON_REG_NOP 0x00 +#define GORDON_REG_IMGCTL1 0x10 +#define GORDON_REG_IMGCTL2 0x11 +#define GORDON_REG_IMGSET1 0x12 +#define GORDON_REG_IMGSET2 0x13 +#define GORDON_REG_IVBP1 0x14 +#define GORDON_REG_IHBP1 0x15 +#define GORDON_REG_IVNUM1 0x16 +#define GORDON_REG_IHNUM1 0x17 +#define GORDON_REG_IVBP2 0x18 +#define GORDON_REG_IHBP2 0x19 +#define GORDON_REG_IVNUM2 0x1A +#define GORDON_REG_IHNUM2 0x1B +#define GORDON_REG_LCDIFCTL1 0x30 +#define GORDON_REG_VALTRAN 0x31 +#define GORDON_REG_AVCTL 0x33 +#define GORDON_REG_LCDIFCTL2 0x34 +#define GORDON_REG_LCDIFCTL3 0x35 +#define GORDON_REG_LCDIFSET1 0x36 +#define GORDON_REG_PCCTL 0x3C +#define GORDON_REG_TPARAM1 0x40 +#define GORDON_REG_TLCDIF1 0x41 +#define GORDON_REG_TSSPB_ST1 0x42 +#define GORDON_REG_TSSPB_ED1 0x43 +#define GORDON_REG_TSCK_ST1 0x44 +#define GORDON_REG_TSCK_WD1 0x45 +#define GORDON_REG_TGSPB_VST1 0x46 +#define GORDON_REG_TGSPB_VED1 0x47 +#define GORDON_REG_TGSPB_CH1 0x48 +#define GORDON_REG_TGCK_ST1 0x49 +#define GORDON_REG_TGCK_ED1 0x4A +#define GORDON_REG_TPCTL_ST1 0x4B +#define GORDON_REG_TPCTL_ED1 0x4C +#define GORDON_REG_TPCHG_ED1 0x4D +#define GORDON_REG_TCOM_CH1 0x4E +#define GORDON_REG_THBP1 0x4F +#define GORDON_REG_TPHCTL1 0x50 +#define GORDON_REG_EVPH1 0x51 +#define GORDON_REG_EVPL1 0x52 +#define GORDON_REG_EVNH1 0x53 +#define GORDON_REG_EVNL1 0x54 +#define GORDON_REG_TBIAS1 0x55 +#define GORDON_REG_TPARAM2 0x56 +#define GORDON_REG_TLCDIF2 0x57 +#define GORDON_REG_TSSPB_ST2 0x58 +#define GORDON_REG_TSSPB_ED2 0x59 +#define GORDON_REG_TSCK_ST2 0x5A +#define GORDON_REG_TSCK_WD2 0x5B +#define GORDON_REG_TGSPB_VST2 0x5C +#define GORDON_REG_TGSPB_VED2 0x5D +#define GORDON_REG_TGSPB_CH2 0x5E +#define GORDON_REG_TGCK_ST2 0x5F +#define GORDON_REG_TGCK_ED2 0x60 +#define GORDON_REG_TPCTL_ST2 0x61 +#define GORDON_REG_TPCTL_ED2 0x62 +#define GORDON_REG_TPCHG_ED2 0x63 +#define GORDON_REG_TCOM_CH2 0x64 +#define GORDON_REG_THBP2 0x65 +#define GORDON_REG_TPHCTL2 0x66 +#define GORDON_REG_POWCTL 0x80 + +static int lcdc_gordon_panel_off(struct platform_device *pdev); + +static int spi_cs; +static int spi_sclk; +static int spi_sdo; +static int spi_sdi; +static int spi_dac; +static int bl_level; +static unsigned char bit_shift[8] = { (1 << 7), /* MSB */ + (1 << 6), + (1 << 5), + (1 << 4), + (1 << 3), + (1 << 2), + (1 << 1), + (1 << 0) /* LSB */ +}; + +struct gordon_state_type{ + boolean disp_initialized; + boolean display_on; + boolean disp_powered_up; +}; + +static struct gordon_state_type gordon_state = { 0 }; +static struct msm_panel_common_pdata *lcdc_gordon_pdata; + +static void serigo(uint16 reg, uint8 data) +{ + unsigned int tx_val = ((0x00FF & reg) << 8) | data; + unsigned char i, val = 0; + + /* Enable the Chip Select */ + gpio_set_value(spi_cs, 1); + udelay(33); + + /* Transmit it in two parts, Higher Byte first, then Lower Byte */ + val = (unsigned char)((tx_val & 0xFF00) >> 8); + + /* Clock should be Low before entering ! */ + for (i = 0; i < 8; i++) { + /* #1: Drive the Data (High or Low) */ + if (val & bit_shift[i]) + gpio_set_value(spi_sdi, 1); + else + gpio_set_value(spi_sdi, 0); + + /* #2: Drive the Clk High and then Low */ + udelay(33); + gpio_set_value(spi_sclk, 1); + udelay(33); + gpio_set_value(spi_sclk, 0); + } + + /* Idle state of SDO (MOSI) is Low */ + gpio_set_value(spi_sdi, 0); + /* ..then Lower Byte */ + val = (uint8) (tx_val & 0x00FF); + /* Before we enter here the Clock should be Low ! */ + + for (i = 0; i < 8; i++) { + /* #1: Drive the Data (High or Low) */ + if (val & bit_shift[i]) + gpio_set_value(spi_sdi, 1); + else + gpio_set_value(spi_sdi, 0); + + /* #2: Drive the Clk High and then Low */ + udelay(33); + + gpio_set_value(spi_sclk, 1); + udelay(33); + gpio_set_value(spi_sclk, 0); + } + + /* Idle state of SDO (MOSI) is Low */ + gpio_set_value(spi_sdi, 0); + + /* Now Disable the Chip Select */ + udelay(33); + gpio_set_value(spi_cs, 0); +} + +static void spi_init(void) +{ + /* Setting the Default GPIO's */ + spi_sclk = *(lcdc_gordon_pdata->gpio_num); + spi_cs = *(lcdc_gordon_pdata->gpio_num + 1); + spi_sdi = *(lcdc_gordon_pdata->gpio_num + 2); + spi_sdo = *(lcdc_gordon_pdata->gpio_num + 3); + + /* Set the output so that we dont disturb the slave device */ + gpio_set_value(spi_sclk, 0); + gpio_set_value(spi_sdi, 0); + + /* Set the Chip Select De-asserted */ + gpio_set_value(spi_cs, 0); + +} + +static void gordon_disp_powerup(void) +{ + if (!gordon_state.disp_powered_up && !gordon_state.display_on) { + /* Reset the hardware first */ + /* Include DAC power up implementation here */ + gordon_state.disp_powered_up = TRUE; + } +} + +static void gordon_init(void) +{ + /* Image interface settings */ + serigo(GORDON_REG_IMGCTL2, 0x00); + serigo(GORDON_REG_IMGSET1, 0x00); + + /* Exchange the RGB signal for J510(Softbank mobile) */ + serigo(GORDON_REG_IMGSET2, 0x12); + serigo(GORDON_REG_LCDIFSET1, 0x00); + + /* Pre-charge settings */ + serigo(GORDON_REG_PCCTL, 0x09); + serigo(GORDON_REG_LCDIFCTL2, 0x7B); + + mdelay(1); +} + +static void gordon_disp_on(void) +{ + if (gordon_state.disp_powered_up && !gordon_state.display_on) { + gordon_init(); + mdelay(20); + /* gordon_dispmode setting */ + serigo(GORDON_REG_TPARAM1, 0x30); + serigo(GORDON_REG_TLCDIF1, 0x00); + serigo(GORDON_REG_TSSPB_ST1, 0x8B); + serigo(GORDON_REG_TSSPB_ED1, 0x93); + serigo(GORDON_REG_TSCK_ST1, 0x88); + serigo(GORDON_REG_TSCK_WD1, 0x00); + serigo(GORDON_REG_TGSPB_VST1, 0x01); + serigo(GORDON_REG_TGSPB_VED1, 0x02); + serigo(GORDON_REG_TGSPB_CH1, 0x5E); + serigo(GORDON_REG_TGCK_ST1, 0x80); + serigo(GORDON_REG_TGCK_ED1, 0x3C); + serigo(GORDON_REG_TPCTL_ST1, 0x50); + serigo(GORDON_REG_TPCTL_ED1, 0x74); + serigo(GORDON_REG_TPCHG_ED1, 0x78); + serigo(GORDON_REG_TCOM_CH1, 0x50); + serigo(GORDON_REG_THBP1, 0x84); + serigo(GORDON_REG_TPHCTL1, 0x00); + serigo(GORDON_REG_EVPH1, 0x70); + serigo(GORDON_REG_EVPL1, 0x64); + serigo(GORDON_REG_EVNH1, 0x56); + serigo(GORDON_REG_EVNL1, 0x48); + serigo(GORDON_REG_TBIAS1, 0x88); + + /* QVGA settings */ + serigo(GORDON_REG_TPARAM2, 0x28); + serigo(GORDON_REG_TLCDIF2, 0x14); + serigo(GORDON_REG_TSSPB_ST2, 0x49); + serigo(GORDON_REG_TSSPB_ED2, 0x4B); + serigo(GORDON_REG_TSCK_ST2, 0x4A); + serigo(GORDON_REG_TSCK_WD2, 0x02); + serigo(GORDON_REG_TGSPB_VST2, 0x02); + serigo(GORDON_REG_TGSPB_VED2, 0x03); + serigo(GORDON_REG_TGSPB_CH2, 0x2F); + serigo(GORDON_REG_TGCK_ST2, 0x40); + serigo(GORDON_REG_TGCK_ED2, 0x1E); + serigo(GORDON_REG_TPCTL_ST2, 0x2C); + serigo(GORDON_REG_TPCTL_ED2, 0x3A); + serigo(GORDON_REG_TPCHG_ED2, 0x3C); + serigo(GORDON_REG_TCOM_CH2, 0x28); + serigo(GORDON_REG_THBP2, 0x4D); + serigo(GORDON_REG_TPHCTL2, 0x1A); + + /* VGA settings */ + serigo(GORDON_REG_IVBP1, 0x02); + serigo(GORDON_REG_IHBP1, 0x90); + serigo(GORDON_REG_IVNUM1, 0xA0); + serigo(GORDON_REG_IHNUM1, 0x78); + + /* QVGA settings */ + serigo(GORDON_REG_IVBP2, 0x02); + serigo(GORDON_REG_IHBP2, 0x48); + serigo(GORDON_REG_IVNUM2, 0x50); + serigo(GORDON_REG_IHNUM2, 0x3C); + + /* Gordon Charge pump settings and ON */ + serigo(GORDON_REG_POWCTL, 0x03); + mdelay(15); + serigo(GORDON_REG_POWCTL, 0x07); + mdelay(15); + + serigo(GORDON_REG_POWCTL, 0x0F); + mdelay(15); + + serigo(GORDON_REG_AVCTL, 0x03); + mdelay(15); + + serigo(GORDON_REG_POWCTL, 0x1F); + mdelay(15); + + serigo(GORDON_REG_POWCTL, 0x5F); + mdelay(15); + + serigo(GORDON_REG_POWCTL, 0x7F); + mdelay(15); + + serigo(GORDON_REG_LCDIFCTL1, 0x02); + mdelay(15); + + serigo(GORDON_REG_IMGCTL1, 0x00); + mdelay(15); + + serigo(GORDON_REG_LCDIFCTL3, 0x00); + mdelay(15); + + serigo(GORDON_REG_VALTRAN, 0x01); + mdelay(15); + + serigo(GORDON_REG_LCDIFCTL1, 0x03); + mdelay(1); + gordon_state.display_on = TRUE; + } +} + +static int lcdc_gordon_panel_on(struct platform_device *pdev) +{ + if (!gordon_state.disp_initialized) { + /* Configure reset GPIO that drives DAC */ + lcdc_gordon_pdata->panel_config_gpio(1); + spi_dac = *(lcdc_gordon_pdata->gpio_num + 4); + gpio_set_value(spi_dac, 0); + udelay(15); + gpio_set_value(spi_dac, 1); + spi_init(); /* LCD needs SPI */ + gordon_disp_powerup(); + gordon_disp_on(); + if (bl_level <= 1) { + /* keep back light OFF */ + serigo(GORDON_REG_LCDIFCTL2, 0x0B); + udelay(15); + serigo(GORDON_REG_VALTRAN, 0x01); + } else { + /* keep back light ON */ + serigo(GORDON_REG_LCDIFCTL2, 0x7B); + udelay(15); + serigo(GORDON_REG_VALTRAN, 0x01); + } + gordon_state.disp_initialized = TRUE; + } + return 0; +} + +static int lcdc_gordon_panel_off(struct platform_device *pdev) +{ + if (gordon_state.disp_powered_up && gordon_state.display_on) { + serigo(GORDON_REG_LCDIFCTL2, 0x7B); + serigo(GORDON_REG_VALTRAN, 0x01); + serigo(GORDON_REG_LCDIFCTL1, 0x02); + serigo(GORDON_REG_LCDIFCTL3, 0x01); + mdelay(20); + serigo(GORDON_REG_VALTRAN, 0x01); + serigo(GORDON_REG_IMGCTL1, 0x01); + serigo(GORDON_REG_LCDIFCTL1, 0x00); + mdelay(20); + + serigo(GORDON_REG_POWCTL, 0x1F); + mdelay(40); + + serigo(GORDON_REG_POWCTL, 0x07); + mdelay(40); + + serigo(GORDON_REG_POWCTL, 0x03); + mdelay(40); + + serigo(GORDON_REG_POWCTL, 0x00); + mdelay(40); + lcdc_gordon_pdata->panel_config_gpio(0); + gordon_state.display_on = FALSE; + gordon_state.disp_initialized = FALSE; + } + return 0; +} + +static void lcdc_gordon_set_backlight(struct msm_fb_data_type *mfd) +{ + bl_level = mfd->bl_level; + + if (gordon_state.disp_initialized) { + if (bl_level <= 1) { + /* keep back light OFF */ + serigo(GORDON_REG_LCDIFCTL2, 0x0B); + udelay(15); + serigo(GORDON_REG_VALTRAN, 0x01); + } else { + /* keep back light ON */ + serigo(GORDON_REG_LCDIFCTL2, 0x7B); + udelay(15); + serigo(GORDON_REG_VALTRAN, 0x01); + } + } +} + +static int gordon_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + lcdc_gordon_pdata = pdev->dev.platform_data; + return 0; + } + msm_fb_add_device(pdev); + return 0; +} + +static struct platform_driver this_driver = { + .probe = gordon_probe, + .driver = { + .name = "lcdc_gordon_vga", + }, +}; + +static struct msm_fb_panel_data gordon_panel_data = { + .on = lcdc_gordon_panel_on, + .off = lcdc_gordon_panel_off, + .set_backlight = lcdc_gordon_set_backlight, +}; + +static struct platform_device this_device = { + .name = "lcdc_gordon_vga", + .id = 1, + .dev = { + .platform_data = &gordon_panel_data, + } +}; + +static int __init lcdc_gordon_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + +#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM + if (msm_fb_detect_client("lcdc_gordon_vga")) + return 0; +#endif + ret = platform_driver_register(&this_driver); + if (ret) + return ret; + + pinfo = &gordon_panel_data.panel_info; + pinfo->xres = 480; + pinfo->yres = 640; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + pinfo->fb_num = 2; + pinfo->clk_rate = 24500000; + pinfo->bl_max = 4; + pinfo->bl_min = 1; + + pinfo->lcdc.h_back_porch = 84; + pinfo->lcdc.h_front_porch = 33; + pinfo->lcdc.h_pulse_width = 60; + pinfo->lcdc.v_back_porch = 0; + pinfo->lcdc.v_front_porch = 2; + pinfo->lcdc.v_pulse_width = 2; + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + + return ret; +} + +module_init(lcdc_gordon_panel_init); diff --git a/drivers/video/msm/lcdc_nt35582_wvga.c b/drivers/video/msm/lcdc_nt35582_wvga.c new file mode 100644 index 0000000000000000000000000000000000000000..db7fd34dc1d9eb66c43d9806644dba1291d159e8 --- /dev/null +++ b/drivers/video/msm/lcdc_nt35582_wvga.c @@ -0,0 +1,472 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#ifdef CONFIG_SPI_QUP +#include +#endif +#include +#include +#include "msm_fb.h" + +#define LCDC_NT35582_PANEL_NAME "lcdc_nt35582_wvga" + +#define WRITE_FIRST_TRANS 0x20 +#define WRITE_SECOND_TRANS 0x00 +#define WRITE_THIRD_TRANS 0x40 +#define READ_FIRST_TRANS 0x20 +#define READ_SECOND_TRANS 0x00 +#define READ_THIRD_TRANS 0xC0 + +#ifdef CONFIG_SPI_QUP +#define LCDC_NT35582_SPI_DEVICE_NAME "lcdc_nt35582_spi" +static struct spi_device *spi_client; +#endif + +struct nt35582_state_type { + boolean display_on; + int bl_level; +}; + +static struct nt35582_state_type nt35582_state = { 0 }; +static int gpio_backlight_en; +static struct msm_panel_common_pdata *lcdc_nt35582_pdata; + +static int spi_write_2bytes(struct spi_device *spi, + unsigned char reg_high_addr, unsigned char reg_low_addr) +{ + char tx_buf[4]; + int rc; + struct spi_message m; + struct spi_transfer t; + + memset(&t, 0, sizeof t); + t.tx_buf = tx_buf; + + spi_setup(spi); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + tx_buf[0] = WRITE_FIRST_TRANS; + tx_buf[1] = reg_high_addr; + tx_buf[2] = WRITE_SECOND_TRANS; + tx_buf[3] = reg_low_addr; + t.rx_buf = NULL; + t.len = 4; + t.bits_per_word = 16; + rc = spi_sync(spi, &m); + if (rc) + pr_err("write spi command failed!\n"); + + return rc; +} + +static int spi_write_3bytes(struct spi_device *spi, unsigned char reg_high_addr, + unsigned char reg_low_addr, unsigned char write_data) +{ + char tx_buf[6]; + int rc; + struct spi_message m; + struct spi_transfer t; + + memset(&t, 0, sizeof t); + t.tx_buf = tx_buf; + + spi_setup(spi); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + tx_buf[0] = WRITE_FIRST_TRANS; + tx_buf[1] = reg_high_addr; + tx_buf[2] = WRITE_SECOND_TRANS; + tx_buf[3] = reg_low_addr; + tx_buf[4] = WRITE_THIRD_TRANS; + tx_buf[5] = write_data; + t.rx_buf = NULL; + t.len = 6; + t.bits_per_word = 16; + rc = spi_sync(spi, &m); + + if (rc) + pr_err("write spi command failed!\n"); + + return rc; +} + +static int spi_read_bytes(struct spi_device *spi, unsigned char reg_high_addr, + unsigned char reg_low_addr, unsigned char *read_value) +{ + char tx_buf[6]; + char rx_buf[6]; + int rc; + struct spi_message m; + struct spi_transfer t; + + memset(&t, 0, sizeof t); + t.tx_buf = tx_buf; + + spi_setup(spi); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + tx_buf[0] = READ_FIRST_TRANS; + tx_buf[1] = reg_high_addr; + tx_buf[2] = READ_SECOND_TRANS; + tx_buf[3] = reg_low_addr; + tx_buf[4] = READ_THIRD_TRANS; + tx_buf[5] = 0x00; + + t.rx_buf = rx_buf; + t.len = 6; + t.bits_per_word = 16; + rc = spi_sync(spi, &m); + + if (rc) + pr_err("write spi command failed!\n"); + else + *read_value = rx_buf[5]; + + return rc; +} + +static void nt35582_disp_on(void) +{ + uint32 panel_id1 = 0, panel_id2 = 0; + + if (!nt35582_state.display_on) { + + /* GVDD setting */ + spi_write_3bytes(spi_client, 0xC0, 0x00, 0xC0); + spi_write_3bytes(spi_client, 0xC0, 0x01, 0x00); + spi_write_3bytes(spi_client, 0xC0, 0x02, 0xC0); + spi_write_3bytes(spi_client, 0xC0, 0x03, 0x00); + /* Power setting */ + spi_write_3bytes(spi_client, 0xC1, 0x00, 0x40); + spi_write_3bytes(spi_client, 0xC2, 0x00, 0x21); + spi_write_3bytes(spi_client, 0xC2, 0x02, 0x02); + + /* Gamma setting */ + spi_write_3bytes(spi_client, 0xE0, 0x00, 0x0E); + spi_write_3bytes(spi_client, 0xE0, 0x01, 0x54); + spi_write_3bytes(spi_client, 0xE0, 0x02, 0x63); + spi_write_3bytes(spi_client, 0xE0, 0x03, 0x76); + spi_write_3bytes(spi_client, 0xE0, 0x04, 0x1F); + spi_write_3bytes(spi_client, 0xE0, 0x05, 0x31); + spi_write_3bytes(spi_client, 0xE0, 0x06, 0x62); + spi_write_3bytes(spi_client, 0xE0, 0x07, 0x78); + spi_write_3bytes(spi_client, 0xE0, 0x08, 0x1F); + spi_write_3bytes(spi_client, 0xE0, 0x09, 0x25); + spi_write_3bytes(spi_client, 0xE0, 0x0A, 0xB3); + spi_write_3bytes(spi_client, 0xE0, 0x0B, 0x17); + spi_write_3bytes(spi_client, 0xE0, 0x0C, 0x38); + spi_write_3bytes(spi_client, 0xE0, 0x0D, 0x5A); + spi_write_3bytes(spi_client, 0xE0, 0x0E, 0xA2); + spi_write_3bytes(spi_client, 0xE0, 0x0F, 0xA2); + spi_write_3bytes(spi_client, 0xE0, 0x10, 0x24); + spi_write_3bytes(spi_client, 0xE0, 0x11, 0x57); + + spi_write_3bytes(spi_client, 0xE1, 0x00, 0x0E); + spi_write_3bytes(spi_client, 0xE1, 0x01, 0x54); + spi_write_3bytes(spi_client, 0xE1, 0x02, 0x63); + spi_write_3bytes(spi_client, 0xE1, 0x03, 0x76); + spi_write_3bytes(spi_client, 0xE1, 0x04, 0x1F); + spi_write_3bytes(spi_client, 0xE1, 0x05, 0x31); + spi_write_3bytes(spi_client, 0xE1, 0x06, 0X62); + spi_write_3bytes(spi_client, 0xE1, 0x07, 0x78); + spi_write_3bytes(spi_client, 0xE1, 0x08, 0x1F); + spi_write_3bytes(spi_client, 0xE1, 0x09, 0x25); + spi_write_3bytes(spi_client, 0xE1, 0x0A, 0xB3); + spi_write_3bytes(spi_client, 0xE1, 0x0B, 0x17); + spi_write_3bytes(spi_client, 0xE1, 0x0C, 0x38); + spi_write_3bytes(spi_client, 0xE1, 0x0D, 0x5A); + spi_write_3bytes(spi_client, 0xE1, 0x0E, 0xA2); + spi_write_3bytes(spi_client, 0xE1, 0x0F, 0xA2); + spi_write_3bytes(spi_client, 0xE1, 0x10, 0x24); + spi_write_3bytes(spi_client, 0xE1, 0x11, 0x57); + + spi_write_3bytes(spi_client, 0xE2, 0x00, 0x0E); + spi_write_3bytes(spi_client, 0xE2, 0x01, 0x54); + spi_write_3bytes(spi_client, 0xE2, 0x02, 0x63); + spi_write_3bytes(spi_client, 0xE2, 0x03, 0x76); + spi_write_3bytes(spi_client, 0xE2, 0x04, 0x1F); + spi_write_3bytes(spi_client, 0xE2, 0x05, 0x31); + spi_write_3bytes(spi_client, 0xE2, 0x06, 0x62); + spi_write_3bytes(spi_client, 0xE2, 0x07, 0x78); + spi_write_3bytes(spi_client, 0xE2, 0x08, 0x1F); + spi_write_3bytes(spi_client, 0xE2, 0x09, 0x25); + spi_write_3bytes(spi_client, 0xE2, 0x0A, 0xB3); + spi_write_3bytes(spi_client, 0xE2, 0x0B, 0x17); + spi_write_3bytes(spi_client, 0xE2, 0x0C, 0x38); + spi_write_3bytes(spi_client, 0xE2, 0x0D, 0x5A); + spi_write_3bytes(spi_client, 0xE2, 0x0E, 0xA2); + spi_write_3bytes(spi_client, 0xE2, 0x0F, 0xA2); + spi_write_3bytes(spi_client, 0xE2, 0x10, 0x24); + spi_write_3bytes(spi_client, 0xE2, 0x11, 0x57); + + spi_write_3bytes(spi_client, 0xE3, 0x00, 0x0E); + spi_write_3bytes(spi_client, 0xE3, 0x01, 0x54); + spi_write_3bytes(spi_client, 0xE3, 0x02, 0x63); + spi_write_3bytes(spi_client, 0xE3, 0x03, 0x76); + spi_write_3bytes(spi_client, 0xE3, 0x04, 0x1F); + spi_write_3bytes(spi_client, 0xE3, 0x05, 0x31); + spi_write_3bytes(spi_client, 0xE3, 0x06, 0x62); + spi_write_3bytes(spi_client, 0xE3, 0x07, 0x78); + spi_write_3bytes(spi_client, 0xE3, 0x08, 0x1F); + spi_write_3bytes(spi_client, 0xE3, 0x09, 0x25); + spi_write_3bytes(spi_client, 0xE3, 0x0A, 0xB3); + spi_write_3bytes(spi_client, 0xE3, 0x0B, 0x17); + spi_write_3bytes(spi_client, 0xE3, 0x0C, 0x38); + spi_write_3bytes(spi_client, 0xE3, 0x0D, 0x5A); + spi_write_3bytes(spi_client, 0xE3, 0x0E, 0xA2); + spi_write_3bytes(spi_client, 0xE3, 0x0F, 0xA2); + spi_write_3bytes(spi_client, 0xE3, 0x10, 0x24); + spi_write_3bytes(spi_client, 0xE3, 0x11, 0x57); + + spi_write_3bytes(spi_client, 0xE4, 0x00, 0x48); + spi_write_3bytes(spi_client, 0xE4, 0x01, 0x6B); + spi_write_3bytes(spi_client, 0xE4, 0x02, 0x84); + spi_write_3bytes(spi_client, 0xE4, 0x03, 0x9B); + spi_write_3bytes(spi_client, 0xE4, 0x04, 0x1F); + spi_write_3bytes(spi_client, 0xE4, 0x05, 0x31); + spi_write_3bytes(spi_client, 0xE4, 0x06, 0x62); + spi_write_3bytes(spi_client, 0xE4, 0x07, 0x78); + spi_write_3bytes(spi_client, 0xE4, 0x08, 0x1F); + spi_write_3bytes(spi_client, 0xE4, 0x09, 0x25); + spi_write_3bytes(spi_client, 0xE4, 0x0A, 0xB3); + spi_write_3bytes(spi_client, 0xE4, 0x0B, 0x17); + spi_write_3bytes(spi_client, 0xE4, 0x0C, 0x38); + spi_write_3bytes(spi_client, 0xE4, 0x0D, 0x5A); + spi_write_3bytes(spi_client, 0xE4, 0x0E, 0xA2); + spi_write_3bytes(spi_client, 0xE4, 0x0F, 0xA2); + spi_write_3bytes(spi_client, 0xE4, 0x10, 0x24); + spi_write_3bytes(spi_client, 0xE4, 0x11, 0x57); + + spi_write_3bytes(spi_client, 0xE5, 0x00, 0x48); + spi_write_3bytes(spi_client, 0xE5, 0x01, 0x6B); + spi_write_3bytes(spi_client, 0xE5, 0x02, 0x84); + spi_write_3bytes(spi_client, 0xE5, 0x03, 0x9B); + spi_write_3bytes(spi_client, 0xE5, 0x04, 0x1F); + spi_write_3bytes(spi_client, 0xE5, 0x05, 0x31); + spi_write_3bytes(spi_client, 0xE5, 0x06, 0x62); + spi_write_3bytes(spi_client, 0xE5, 0x07, 0x78); + spi_write_3bytes(spi_client, 0xE5, 0x08, 0x1F); + spi_write_3bytes(spi_client, 0xE5, 0x09, 0x25); + spi_write_3bytes(spi_client, 0xE5, 0x0A, 0xB3); + spi_write_3bytes(spi_client, 0xE5, 0x0B, 0x17); + spi_write_3bytes(spi_client, 0xE5, 0x0C, 0x38); + spi_write_3bytes(spi_client, 0xE5, 0x0D, 0x5A); + spi_write_3bytes(spi_client, 0xE5, 0x0E, 0xA2); + spi_write_3bytes(spi_client, 0xE5, 0x0F, 0xA2); + spi_write_3bytes(spi_client, 0xE5, 0x10, 0x24); + spi_write_3bytes(spi_client, 0xE5, 0x11, 0x57); + + /* Data format setting */ + spi_write_3bytes(spi_client, 0x3A, 0x00, 0x70); + + /* Reverse PCLK signal of LCM to meet Qualcomm's platform */ + spi_write_3bytes(spi_client, 0x3B, 0x00, 0x2B); + + /* Scan direstion setting */ + spi_write_3bytes(spi_client, 0x36, 0x00, 0x00); + + /* Sleep out */ + spi_write_2bytes(spi_client, 0x11, 0x00); + + msleep(120); + + /* Display on */ + spi_write_2bytes(spi_client, 0x29, 0x00); + + pr_info("%s: LCM SPI display on CMD finished...\n", __func__); + + msleep(200); + + nt35582_state.display_on = TRUE; + } + + /* Test to read RDDID. It should be 0x0055h and 0x0082h */ + spi_read_bytes(spi_client, 0x10, 0x80, (unsigned char *)&panel_id1); + spi_read_bytes(spi_client, 0x11, 0x80, (unsigned char *)&panel_id2); + + pr_info(KERN_INFO "nt35582_disp_on: LCM_ID=[0x%x, 0x%x]\n", + panel_id1, panel_id2); +} + +static int lcdc_nt35582_panel_on(struct platform_device *pdev) +{ + nt35582_disp_on(); + return 0; +} + +static int lcdc_nt35582_panel_off(struct platform_device *pdev) +{ + nt35582_state.display_on = FALSE; + return 0; +} + +static void lcdc_nt35582_set_backlight(struct msm_fb_data_type *mfd) +{ + int bl_level; + int i = 0, step = 0; + + bl_level = mfd->bl_level; + if (bl_level == nt35582_state.bl_level) + return; + else + nt35582_state.bl_level = bl_level; + + if (bl_level == 0) { + gpio_set_value_cansleep(gpio_backlight_en, 0); + return; + } + + /* Level:0~31 mapping to step 32~1 */ + step = 32 - bl_level; + for (i = 0; i < step; i++) { + gpio_set_value_cansleep(gpio_backlight_en, 0); + ndelay(5); + gpio_set_value_cansleep(gpio_backlight_en, 1); + ndelay(5); + } +} + +static int nt35582_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + lcdc_nt35582_pdata = pdev->dev.platform_data; + return 0; + } + + gpio_backlight_en = *(lcdc_nt35582_pdata->gpio_num); + + msm_fb_add_device(pdev); + return 0; +} + +#ifdef CONFIG_SPI_QUP +static int lcdc_nt35582_spi_probe(struct spi_device *spi) +{ + spi_client = spi; + spi_client->bits_per_word = 16; + spi_client->chip_select = 0; + spi_client->max_speed_hz = 1100000; + spi_client->mode = SPI_MODE_0; + spi_setup(spi_client); + + return 0; +} + +static int lcdc_nt35582_spi_remove(struct spi_device *spi) +{ + spi_client = NULL; + return 0; +} + +static struct spi_driver lcdc_nt35582_spi_driver = { + .driver = { + .name = LCDC_NT35582_SPI_DEVICE_NAME, + .owner = THIS_MODULE, + }, + .probe = lcdc_nt35582_spi_probe, + .remove = lcdc_nt35582_spi_remove, +}; +#endif +static struct platform_driver this_driver = { + .probe = nt35582_probe, + .driver = { + .name = LCDC_NT35582_PANEL_NAME, + }, +}; + +static struct msm_fb_panel_data nt35582_panel_data = { + .on = lcdc_nt35582_panel_on, + .off = lcdc_nt35582_panel_off, + .set_backlight = lcdc_nt35582_set_backlight, +}; + +static struct platform_device this_device = { + .name = LCDC_NT35582_PANEL_NAME, + .id = 1, + .dev = { + .platform_data = &nt35582_panel_data, + } +}; + +static int __init lcdc_nt35582_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + +#ifdef CONFIG_FB_MSM_LCDC_AUTO_DETECT + if (msm_fb_detect_client(LCDC_NT35582_PANEL_NAME)) { + pr_err("detect failed\n"); + return 0; + } +#endif + ret = platform_driver_register(&this_driver); + if (ret) { + pr_err("Fails to platform_driver_register...\n"); + return ret; + } + + pinfo = &nt35582_panel_data.panel_info; + pinfo->xres = 480; + pinfo->yres = 800; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + pinfo->fb_num = 2; + pinfo->clk_rate = 25600000; + pinfo->bl_max = 31; + pinfo->bl_min = 1; + + pinfo->lcdc.h_back_porch = 10; /* hsw = 8 + hbp=184 */ + pinfo->lcdc.h_front_porch = 10; + pinfo->lcdc.h_pulse_width = 2; + pinfo->lcdc.v_back_porch = 4; /* vsw=1 + vbp = 2 */ + pinfo->lcdc.v_front_porch = 10; + pinfo->lcdc.v_pulse_width = 2; + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + + ret = platform_device_register(&this_device); + if (ret) { + pr_err("not able to register the device\n"); + goto fail_driver; + } +#ifdef CONFIG_SPI_QUP + ret = spi_register_driver(&lcdc_nt35582_spi_driver); + + if (ret) { + pr_err("not able to register spi\n"); + goto fail_device; + } +#endif + return ret; + +#ifdef CONFIG_SPI_QUP +fail_device: + platform_device_unregister(&this_device); +#endif +fail_driver: + platform_driver_unregister(&this_driver); + return ret; +} + +device_initcall(lcdc_nt35582_panel_init); diff --git a/drivers/video/msm/lcdc_panel.c b/drivers/video/msm/lcdc_panel.c new file mode 100644 index 0000000000000000000000000000000000000000..16d6855e1b2f87d775fc83e34017e83959765c16 --- /dev/null +++ b/drivers/video/msm/lcdc_panel.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" + +static int lcdc_panel_on(struct platform_device *pdev) +{ + return 0; +} + +static int lcdc_panel_off(struct platform_device *pdev) +{ + return 0; +} + +static int lcdc_panel_probe(struct platform_device *pdev) +{ + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = lcdc_panel_probe, + .driver = { + .name = "lcdc_panel", + }, +}; + +static struct msm_fb_panel_data lcdc_panel_data = { + .on = lcdc_panel_on, + .off = lcdc_panel_off, +}; + +static int lcdc_dev_id; + +int lcdc_device_register(struct msm_panel_info *pinfo) +{ + struct platform_device *pdev = NULL; + int ret; + + pdev = platform_device_alloc("lcdc_panel", ++lcdc_dev_id); + if (!pdev) + return -ENOMEM; + + lcdc_panel_data.panel_info = *pinfo; + ret = platform_device_add_data(pdev, &lcdc_panel_data, + sizeof(lcdc_panel_data)); + if (ret) { + printk(KERN_ERR + "%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + printk(KERN_ERR + "%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static int __init lcdc_panel_init(void) +{ + return platform_driver_register(&this_driver); +} + +module_init(lcdc_panel_init); diff --git a/drivers/video/msm/lcdc_prism.c b/drivers/video/msm/lcdc_prism.c new file mode 100644 index 0000000000000000000000000000000000000000..fb040ca76890ccc16b3a6263b51bb29052640c86 --- /dev/null +++ b/drivers/video/msm/lcdc_prism.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" + +#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM +#include "mddihosti.h" +#endif + +static int __init lcdc_prism_init(void) +{ + int ret; + struct msm_panel_info pinfo; + +#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM + ret = msm_fb_detect_client("lcdc_prism_wvga"); + if (ret == -ENODEV) + return 0; + + if (ret && (mddi_get_client_id() != 0)) + return 0; +#endif + + pinfo.xres = 800; + pinfo.yres = 480; + MSM_FB_SINGLE_MODE_PANEL(&pinfo); + pinfo.type = LCDC_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.fb_num = 2; + pinfo.clk_rate = 30720000; + + pinfo.lcdc.h_back_porch = 21; + pinfo.lcdc.h_front_porch = 81; + pinfo.lcdc.h_pulse_width = 60; + pinfo.lcdc.v_back_porch = 18; + pinfo.lcdc.v_front_porch = 27; + pinfo.lcdc.v_pulse_width = 2; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + + ret = lcdc_device_register(&pinfo); + if (ret) + printk(KERN_ERR "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(lcdc_prism_init); diff --git a/drivers/video/msm/lcdc_samsung_oled_pt.c b/drivers/video/msm/lcdc_samsung_oled_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..00656ebe70c8907c2f14fb026305af1f41c1e6f4 --- /dev/null +++ b/drivers/video/msm/lcdc_samsung_oled_pt.c @@ -0,0 +1,588 @@ +/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#ifdef CONFIG_SPI_QUP +#include +#else +#include +#endif +#include "msm_fb.h" + +#define DEBUG +/* #define SYSFS_DEBUG_CMD */ + +#ifdef CONFIG_SPI_QUP +#define LCDC_SAMSUNG_SPI_DEVICE_NAME "lcdc_samsung_ams367pe02" +static struct spi_device *lcdc_spi_client; +#else +static int spi_cs; +static int spi_sclk; +static int spi_mosi; +#endif + +struct samsung_state_type { + boolean disp_initialized; + boolean display_on; + boolean disp_powered_up; + int brightness; +}; + +struct samsung_spi_data { + u8 addr; + u8 len; + u8 data[22]; +}; + +static struct samsung_spi_data panel_sequence[] = { + { .addr = 0xf8, .len = 14, .data = { 0x01, 0x27, 0x27, 0x07, 0x07, + 0x54, 0x9f, 0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00 } }, +}; +static struct samsung_spi_data display_sequence[] = { + { .addr = 0xf2, .len = 5, .data = { 0x02, 0x03, 0x1c, 0x10, 0x10 } }, + { .addr = 0xf7, .len = 3, .data = { 0x00, 0x00, 0x30 } }, +}; + +/* lum=300 cd/m2 */ +static struct samsung_spi_data gamma_sequence_300[] = { + { .addr = 0xfa, .len = 22, .data = { 0x02, 0x18, 0x08, 0x24, 0x7d, 0x77, + 0x5b, 0xbe, 0xc1, 0xb1, 0xb3, 0xb7, 0xa6, 0xc3, 0xc5, 0xb9, 0x00, 0xb3, + 0x00, 0xaf, 0x00, 0xe8 } }, + { .addr = 0xFA, .len = 1, .data = { 0x03 } }, +}; +/* lum = 180 cd/m2*/ +static struct samsung_spi_data gamma_sequence_180[] = { + { .addr = 0xfa, .len = 22, .data = { 0x02, 0x18, 0x08, 0x24, 0x83, 0x78, + 0x60, 0xc5, 0xc6, 0xb8, 0xba, 0xbe, 0xad, 0xcb, 0xcd, 0xc2, 0x00, 0x92, + 0x00, 0x8e, 0x00, 0xbc } }, + { .addr = 0xFA, .len = 1, .data = { 0x03 } }, +}; +/* lum = 80 cd/m2*/ +static struct samsung_spi_data gamma_sequence_80[] = { + { .addr = 0xfa, .len = 22, .data = { 0x02, 0x18, 0x08, 0x24, 0x94, 0x73, + 0x6c, 0xcb, 0xca, 0xbe, 0xc4, 0xc7, 0xb8, 0xd3, 0xd5, 0xcb, 0x00, 0x6d, + 0x00, 0x69, 0x00, 0x8b } }, + { .addr = 0xFA, .len = 1, .data = { 0x03 } }, +}; + +static struct samsung_spi_data etc_sequence[] = { + { .addr = 0xF6, .len = 3, .data = { 0x00, 0x8e, 0x07 } }, + { .addr = 0xB3, .len = 1, .data = { 0x0C } }, +}; + +static struct samsung_state_type samsung_state = { .brightness = 180 }; +static struct msm_panel_common_pdata *lcdc_samsung_pdata; + +#ifndef CONFIG_SPI_QUP +static void samsung_spi_write_byte(boolean dc, u8 data) +{ + uint32 bit; + int bnum; + + gpio_set_value(spi_sclk, 0); + gpio_set_value(spi_mosi, dc ? 1 : 0); + udelay(1); /* at least 20 ns */ + gpio_set_value(spi_sclk, 1); /* clk high */ + udelay(1); /* at least 20 ns */ + + bnum = 8; /* 8 data bits */ + bit = 0x80; + while (bnum--) { + gpio_set_value(spi_sclk, 0); /* clk low */ + gpio_set_value(spi_mosi, (data & bit) ? 1 : 0); + udelay(1); + gpio_set_value(spi_sclk, 1); /* clk high */ + udelay(1); + bit >>= 1; + } + gpio_set_value(spi_mosi, 0); + +} + +static void samsung_spi_read_bytes(u8 cmd, u8 *data, int num) +{ + int bnum; + + /* Chip Select - low */ + gpio_set_value(spi_cs, 0); + udelay(2); + + /* command byte first */ + samsung_spi_write_byte(0, cmd); + udelay(2); + + gpio_direction_input(spi_mosi); + + if (num > 1) { + /* extra dummy clock */ + gpio_set_value(spi_sclk, 0); + udelay(1); + gpio_set_value(spi_sclk, 1); + udelay(1); + } + + /* followed by data bytes */ + bnum = num * 8; /* number of bits */ + *data = 0; + while (bnum) { + gpio_set_value(spi_sclk, 0); /* clk low */ + udelay(1); + *data <<= 1; + *data |= gpio_get_value(spi_mosi) ? 1 : 0; + gpio_set_value(spi_sclk, 1); /* clk high */ + udelay(1); + --bnum; + if ((bnum % 8) == 0) + ++data; + } + + gpio_direction_output(spi_mosi, 0); + + /* Chip Select - high */ + udelay(2); + gpio_set_value(spi_cs, 1); +} +#endif + +#ifdef DEBUG +static const char *byte_to_binary(const u8 *buf, int len) +{ + static char b[32*8+1]; + char *p = b; + int i, z; + + for (i = 0; i < len; ++i) { + u8 val = *buf++; + for (z = 1 << 7; z > 0; z >>= 1) + *p++ = (val & z) ? '1' : '0'; + } + *p = 0; + + return b; +} +#endif + +#define BIT_OFFSET (bit_size % 8) +#define ADD_BIT(val) do { \ + tx_buf[bit_size / 8] |= \ + (u8)((val ? 1 : 0) << (7 - BIT_OFFSET)); \ + ++bit_size; \ + } while (0) + +#define ADD_BYTE(data) do { \ + tx_buf[bit_size / 8] |= (u8)(data >> BIT_OFFSET); \ + bit_size += 8; \ + if (BIT_OFFSET != 0) \ + tx_buf[bit_size / 8] |= (u8)(data << (8 - BIT_OFFSET));\ + } while (0) + +static int samsung_serigo(struct samsung_spi_data data) +{ +#ifdef CONFIG_SPI_QUP + char tx_buf[32]; + int bit_size = 0, i, rc; + struct spi_message m; + struct spi_transfer t; + + if (!lcdc_spi_client) { + pr_err("%s lcdc_spi_client is NULL\n", __func__); + return -EINVAL; + } + + memset(&t, 0, sizeof t); + memset(tx_buf, 0, sizeof tx_buf); + t.tx_buf = tx_buf; + spi_setup(lcdc_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + ADD_BIT(FALSE); + ADD_BYTE(data.addr); + for (i = 0; i < data.len; ++i) { + ADD_BIT(TRUE); + ADD_BYTE(data.data[i]); + } + + /* add padding bits so we round to next byte */ + t.len = (bit_size+7) / 8; + if (t.len <= 4) + t.bits_per_word = bit_size; + + rc = spi_sync(lcdc_spi_client, &m); +#ifdef DEBUG + pr_info("%s: addr=0x%02x, #args=%d[%d] [%s], rc=%d\n", + __func__, data.addr, t.len, t.bits_per_word, + byte_to_binary(tx_buf, t.len), rc); +#endif + return rc; +#else + int i; + + /* Chip Select - low */ + gpio_set_value(spi_cs, 0); + udelay(2); + + samsung_spi_write_byte(FALSE, data.addr); + udelay(2); + + for (i = 0; i < data.len; ++i) { + samsung_spi_write_byte(TRUE, data.data[i]); + udelay(2); + } + + /* Chip Select - high */ + gpio_set_value(spi_cs, 1); +#ifdef DEBUG + pr_info("%s: cmd=0x%02x, #args=%d\n", __func__, data.addr, data.len); +#endif + return 0; +#endif +} + +static int samsung_write_cmd(u8 cmd) +{ +#ifdef CONFIG_SPI_QUP + char tx_buf[2]; + int bit_size = 0, rc; + struct spi_message m; + struct spi_transfer t; + + if (!lcdc_spi_client) { + pr_err("%s lcdc_spi_client is NULL\n", __func__); + return -EINVAL; + } + + memset(&t, 0, sizeof t); + memset(tx_buf, 0, sizeof tx_buf); + t.tx_buf = tx_buf; + spi_setup(lcdc_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + ADD_BIT(FALSE); + ADD_BYTE(cmd); + + t.len = 2; + t.bits_per_word = 9; + + rc = spi_sync(lcdc_spi_client, &m); +#ifdef DEBUG + pr_info("%s: addr=0x%02x, #args=%d[%d] [%s], rc=%d\n", + __func__, cmd, t.len, t.bits_per_word, + byte_to_binary(tx_buf, t.len), rc); +#endif + return rc; +#else + /* Chip Select - low */ + gpio_set_value(spi_cs, 0); + udelay(2); + + samsung_spi_write_byte(FALSE, cmd); + + /* Chip Select - high */ + udelay(2); + gpio_set_value(spi_cs, 1); +#ifdef DEBUG + pr_info("%s: cmd=0x%02x\n", __func__, cmd); +#endif + return 0; +#endif +} + +static int samsung_serigo_list(struct samsung_spi_data *data, int count) +{ + int i, rc; + for (i = 0; i < count; ++i, ++data) { + rc = samsung_serigo(*data); + if (rc) + return rc; + msleep(10); + } + return 0; +} + +#ifndef CONFIG_SPI_QUP +static void samsung_spi_init(void) +{ + spi_sclk = *(lcdc_samsung_pdata->gpio_num); + spi_cs = *(lcdc_samsung_pdata->gpio_num + 1); + spi_mosi = *(lcdc_samsung_pdata->gpio_num + 2); + + /* Set the output so that we don't disturb the slave device */ + gpio_set_value(spi_sclk, 1); + gpio_set_value(spi_mosi, 0); + + /* Set the Chip Select deasserted (active low) */ + gpio_set_value(spi_cs, 1); +} +#endif + +static void samsung_disp_powerup(void) +{ + if (!samsung_state.disp_powered_up && !samsung_state.display_on) + samsung_state.disp_powered_up = TRUE; +} + +static struct work_struct disp_on_delayed_work; +static void samsung_disp_on_delayed_work(struct work_struct *work_ptr) +{ + /* 0x01: Software Reset */ + samsung_write_cmd(0x01); + msleep(120); + + msleep(300); + samsung_serigo_list(panel_sequence, + sizeof(panel_sequence)/sizeof(*panel_sequence)); + samsung_serigo_list(display_sequence, + sizeof(display_sequence)/sizeof(*display_sequence)); + + switch (samsung_state.brightness) { + case 300: + samsung_serigo_list(gamma_sequence_300, + sizeof(gamma_sequence_300)/sizeof(*gamma_sequence_300)); + break; + case 180: + default: + samsung_serigo_list(gamma_sequence_180, + sizeof(gamma_sequence_180)/sizeof(*gamma_sequence_180)); + break; + case 80: + samsung_serigo_list(gamma_sequence_80, + sizeof(gamma_sequence_80)/sizeof(*gamma_sequence_80)); + break; + } + + samsung_serigo_list(etc_sequence, + sizeof(etc_sequence)/sizeof(*etc_sequence)); + + /* 0x11: Sleep Out */ + samsung_write_cmd(0x11); + msleep(120); + /* 0x13: Normal Mode On */ + samsung_write_cmd(0x13); + +#ifndef CONFIG_SPI_QUP + { + u8 data; + + msleep(120); + /* 0x0A: Read Display Power Mode */ + samsung_spi_read_bytes(0x0A, &data, 1); + pr_info("%s: power=[%s]\n", __func__, + byte_to_binary(&data, 1)); + + msleep(120); + /* 0x0C: Read Display Pixel Format */ + samsung_spi_read_bytes(0x0C, &data, 1); + pr_info("%s: pixel-format=[%s]\n", __func__, + byte_to_binary(&data, 1)); + } +#endif + msleep(120); + /* 0x29: Display On */ + samsung_write_cmd(0x29); +} + +static void samsung_disp_on(void) +{ + if (samsung_state.disp_powered_up && !samsung_state.display_on) { + INIT_WORK(&disp_on_delayed_work, samsung_disp_on_delayed_work); + schedule_work(&disp_on_delayed_work); + + samsung_state.display_on = TRUE; + } +} + +static int lcdc_samsung_panel_on(struct platform_device *pdev) +{ + pr_info("%s\n", __func__); + if (!samsung_state.disp_initialized) { +#ifndef CONFIG_SPI_QUP + lcdc_samsung_pdata->panel_config_gpio(1); + samsung_spi_init(); +#endif + samsung_disp_powerup(); + samsung_disp_on(); + samsung_state.disp_initialized = TRUE; + } + return 0; +} + +static int lcdc_samsung_panel_off(struct platform_device *pdev) +{ + pr_info("%s\n", __func__); + if (samsung_state.disp_powered_up && samsung_state.display_on) { + /* 0x10: Sleep In */ + samsung_write_cmd(0x10); + msleep(120); + + samsung_state.display_on = FALSE; + samsung_state.disp_initialized = FALSE; + } + return 0; +} + +#ifdef SYSFS_DEBUG_CMD +static ssize_t samsung_rda_cmd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = snprintf(buf, PAGE_SIZE, "n/a\n"); + pr_info("%s: 'n/a'\n", __func__); + return ret; +} + +static ssize_t samsung_wta_cmd(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + uint32 cmd; + + sscanf(buf, "%x", &cmd); + samsung_write_cmd((u8)cmd); + + return ret; +} + +static DEVICE_ATTR(cmd, S_IRUGO | S_IWUGO, samsung_rda_cmd, samsung_wta_cmd); +static struct attribute *fs_attrs[] = { + &dev_attr_cmd.attr, + NULL, +}; +static struct attribute_group fs_attr_group = { + .attrs = fs_attrs, +}; +#endif + +static struct msm_fb_panel_data samsung_panel_data = { + .on = lcdc_samsung_panel_on, + .off = lcdc_samsung_panel_off, +}; + +static int samsung_probe(struct platform_device *pdev) +{ + struct msm_panel_info *pinfo; +#ifdef SYSFS_DEBUG_CMD + struct platform_device *fb_dev; + struct msm_fb_data_type *mfd; + int rc; +#endif + + pr_info("%s: id=%d\n", __func__, pdev->id); + lcdc_samsung_pdata = pdev->dev.platform_data; + + pinfo = &samsung_panel_data.panel_info; + pinfo->xres = 480; + pinfo->yres = 800; + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + pinfo->fb_num = 2; + pinfo->clk_rate = 25600000; /* Max 27.77MHz */ + pinfo->bl_max = 15; + pinfo->bl_min = 1; + + /* AMS367PE02 Operation Manual, Page 7 */ + pinfo->lcdc.h_back_porch = 16-2; /* HBP-HLW */ + pinfo->lcdc.h_front_porch = 16; + pinfo->lcdc.h_pulse_width = 2; + /* AMS367PE02 Operation Manual, Page 6 */ + pinfo->lcdc.v_back_porch = 3-2; /* VBP-VLW */ + pinfo->lcdc.v_front_porch = 28; + pinfo->lcdc.v_pulse_width = 2; + + pinfo->lcdc.border_clr = 0; + pinfo->lcdc.underflow_clr = 0xff; + pinfo->lcdc.hsync_skew = 0; + pdev->dev.platform_data = &samsung_panel_data; + +#ifndef SYSFS_DEBUG_CMD + msm_fb_add_device(pdev); +#else + fb_dev = msm_fb_add_device(pdev); + mfd = platform_get_drvdata(fb_dev); + rc = sysfs_create_group(&mfd->fbi->dev->kobj, &fs_attr_group); + if (rc) { + pr_err("%s: sysfs group creation failed, rc=%d\n", __func__, + rc); + return rc; + } +#endif + return 0; +} + +#ifdef CONFIG_SPI_QUP +static int lcdc_samsung_spi_probe(struct spi_device *spi) +{ + pr_info("%s\n", __func__); + lcdc_spi_client = spi; + lcdc_spi_client->bits_per_word = 32; + return 0; +} +static int lcdc_samsung_spi_remove(struct spi_device *spi) +{ + lcdc_spi_client = NULL; + return 0; +} +static struct spi_driver lcdc_samsung_spi_driver = { + .driver.name = LCDC_SAMSUNG_SPI_DEVICE_NAME, + .driver.owner = THIS_MODULE, + .probe = lcdc_samsung_spi_probe, + .remove = lcdc_samsung_spi_remove, +}; +#endif + +static struct platform_driver this_driver = { + .probe = samsung_probe, + .driver.name = "lcdc_samsung_oled", +}; + +static int __init lcdc_samsung_panel_init(void) +{ + int ret; + + if (msm_fb_detect_client("lcdc_samsung_oled")) { + pr_err("%s: detect failed\n", __func__); + return 0; + } + + ret = platform_driver_register(&this_driver); + if (ret) { + pr_err("%s: driver register failed, rc=%d\n", __func__, ret); + return ret; + } + +#ifdef CONFIG_SPI_QUP + ret = spi_register_driver(&lcdc_samsung_spi_driver); + + if (ret) { + pr_err("%s: spi register failed: rc=%d\n", __func__, ret); + platform_driver_unregister(&this_driver); + } else + pr_info("%s: SUCCESS (SPI)\n", __func__); +#else + pr_info("%s: SUCCESS (BitBang)\n", __func__); +#endif + return ret; +} + +module_init(lcdc_samsung_panel_init); +static void __exit lcdc_samsung_panel_exit(void) +{ + pr_info("%s\n", __func__); +#ifdef CONFIG_SPI_QUP + spi_unregister_driver(&lcdc_samsung_spi_driver); +#endif + platform_driver_unregister(&this_driver); +} +module_exit(lcdc_samsung_panel_exit); diff --git a/drivers/video/msm/lcdc_samsung_wsvga.c b/drivers/video/msm/lcdc_samsung_wsvga.c new file mode 100644 index 0000000000000000000000000000000000000000..e355750848404ef96691289baae68ce0d5b9a64d --- /dev/null +++ b/drivers/video/msm/lcdc_samsung_wsvga.c @@ -0,0 +1,270 @@ +/* Copyright (c) 2009-2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#ifdef CONFIG_PMIC8058_PWM +#include +#include +#endif +#include +#include "msm_fb.h" + + + +#ifdef CONFIG_PMIC8058_PWM +static struct pwm_device *bl_pwm0; +static struct pwm_device *bl_pwm1; + +/* for samsung panel 300hz was the minimum freq where flickering wasnt + * observed as the screen was dimmed + */ + +#define PWM_FREQ_HZ 300 +#define PWM_PERIOD_USEC (USEC_PER_SEC / PWM_FREQ_HZ) +#define PWM_LEVEL 100 +#define PWM_DUTY_LEVEL (PWM_PERIOD_USEC / PWM_LEVEL) +#endif + +struct lcdc_samsung_data { + struct msm_panel_common_pdata *pdata; +#ifdef CONFIG_FB_MSM_LCDC_DSUB + int vga_enabled; +#endif + struct platform_device *fbpdev; +}; + +static struct lcdc_samsung_data *dd; + + +static void lcdc_samsung_panel_set_backlight(struct msm_fb_data_type *mfd) +{ +#ifdef CONFIG_PMIC8058_PWM + int bl_level; + int ret; + + bl_level = mfd->bl_level; + + if (bl_pwm0) { + ret = pwm_config(bl_pwm0, PWM_DUTY_LEVEL * bl_level, + PWM_PERIOD_USEC); + if (ret) + printk(KERN_ERR "pwm_config on pwm 0 failed %d\n", ret); + } + + if (bl_pwm1) { + ret = pwm_config(bl_pwm1, + PWM_PERIOD_USEC - (PWM_DUTY_LEVEL * bl_level), + PWM_PERIOD_USEC); + if (ret) + printk(KERN_ERR "pwm_config on pwm 1 failed %d\n", ret); + } + + if (bl_pwm0) { + ret = pwm_enable(bl_pwm0); + if (ret) + printk(KERN_ERR "pwm_enable on pwm 0 failed %d\n", ret); + } + + if (bl_pwm1) { + ret = pwm_enable(bl_pwm1); + if (ret) + printk(KERN_ERR "pwm_enable on pwm 1 failed %d\n", ret); + } +#endif + +} + +#ifdef CONFIG_FB_MSM_LCDC_DSUB +static ssize_t show_vga_enable(struct device *device, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", dd->vga_enabled); +} + +static ssize_t store_vga_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long enable; + int rc; + + rc = strict_strtoul(buf, 10, &enable); + if (rc) + return -EINVAL; + + if (dd->pdata && dd->pdata->vga_switch) + rc = dd->pdata->vga_switch(enable); + else + rc = -ENODEV; + if (!rc) { + dd->vga_enabled = enable; + rc = count; + } + return rc; +} + +static DEVICE_ATTR(vga_enable, S_IRUGO|S_IWUSR, show_vga_enable, + store_vga_enable); +static struct attribute *attrs[] = { + &dev_attr_vga_enable.attr, + NULL, +}; +static struct attribute_group attr_group = { + .attrs = attrs, +}; +#endif + +static int samsung_probe(struct platform_device *pdev) +{ + int rc = 0; +#ifdef CONFIG_FB_MSM_LCDC_DSUB + struct msm_fb_data_type *mfd; +#endif + + if (pdev->id == 0) { + dd = kzalloc(sizeof *dd, GFP_KERNEL); + if (!dd) + return -ENOMEM; +#ifdef CONFIG_FB_MSM_LCDC_DSUB + dd->vga_enabled = 0; +#endif + dd->pdata = pdev->dev.platform_data; + return 0; + } else if (!dd) + return -ENODEV; + +#ifdef CONFIG_PMIC8058_PWM + bl_pwm0 = pwm_request(dd->pdata->gpio_num[0], "backlight1"); + if (bl_pwm0 == NULL || IS_ERR(bl_pwm0)) { + pr_err("%s pwm_request() failed\n", __func__); + bl_pwm0 = NULL; + } + + bl_pwm1 = pwm_request(dd->pdata->gpio_num[1], "backlight2"); + if (bl_pwm1 == NULL || IS_ERR(bl_pwm1)) { + pr_err("%s pwm_request() failed\n", __func__); + bl_pwm1 = NULL; + } + + pr_debug("samsung_probe: bl_pwm0=%p LPG_chan0=%d " + "bl_pwm1=%p LPG_chan1=%d\n", + bl_pwm0, (int)dd->pdata->gpio_num[0], + bl_pwm1, (int)dd->pdata->gpio_num[1] + ); +#endif + + + dd->fbpdev = msm_fb_add_device(pdev); + if (!dd->fbpdev) { + dev_err(&pdev->dev, "failed to add msm_fb device\n"); + rc = -ENODEV; + goto probe_exit; + } + +#ifdef CONFIG_FB_MSM_LCDC_DSUB + mfd = platform_get_drvdata(dd->fbpdev); + if (mfd && mfd->fbi && mfd->fbi->dev) { + rc = sysfs_create_group(&mfd->fbi->dev->kobj, &attr_group); + if (rc) + dev_err(&pdev->dev, "failed to create sysfs group\n"); + } else { + dev_err(&pdev->dev, "no dev to create sysfs group\n"); + rc = -ENODEV; + } +#endif + +probe_exit: + return rc; +} + +#ifdef CONFIG_FB_MSM_LCDC_DSUB +static int samsung_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&dd->fbpdev->dev.kobj, &attr_group); + return 0; +} +#endif + +static struct platform_driver this_driver = { + .probe = samsung_probe, +#ifdef CONFIG_FB_MSM_LCDC_DSUB + .remove = samsung_remove, +#endif + .driver = { + .name = "lcdc_samsung_wsvga", + }, +}; + +static struct msm_fb_panel_data samsung_panel_data = { + .set_backlight = lcdc_samsung_panel_set_backlight, +}; + +static struct platform_device this_device = { + .name = "lcdc_samsung_wsvga", + .id = 1, + .dev = { + .platform_data = &samsung_panel_data, + } +}; + +static int __init lcdc_samsung_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + if (msm_fb_detect_client("lcdc_samsung_wsvga")) + return 0; + + ret = platform_driver_register(&this_driver); + if (ret) + return ret; + + pinfo = &samsung_panel_data.panel_info; + pinfo->xres = 1024; + pinfo->yres = 600; +#ifdef CONFIG_FB_MSM_LCDC_DSUB + /* DSUB (VGA) is on the same bus, this allows us to allocate for the + * max resolution of the DSUB display */ + pinfo->mode2_xres = 1440; + pinfo->mode2_yres = 900; + pinfo->mode2_bpp = 16; +#else + MSM_FB_SINGLE_MODE_PANEL(pinfo); +#endif + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + pinfo->clk_rate = 43192000; + pinfo->bl_max = PWM_LEVEL; + pinfo->bl_min = 1; + + pinfo->lcdc.h_back_porch = 80; + pinfo->lcdc.h_front_porch = 48; + pinfo->lcdc.h_pulse_width = 32; + pinfo->lcdc.v_back_porch = 4; + pinfo->lcdc.v_front_porch = 3; + pinfo->lcdc.v_pulse_width = 1; + pinfo->lcdc.border_clr = 0; + pinfo->lcdc.underflow_clr = 0xff; + pinfo->lcdc.hsync_skew = 0; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + + return ret; +} + +module_init(lcdc_samsung_panel_init); diff --git a/drivers/video/msm/lcdc_sharp_wvga_pt.c b/drivers/video/msm/lcdc_sharp_wvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..f380bb41cceb904ef40c05c94d38ff9e16570d4e --- /dev/null +++ b/drivers/video/msm/lcdc_sharp_wvga_pt.c @@ -0,0 +1,414 @@ +/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#ifdef CONFIG_PMIC8058_PWM +#include +#include +#endif +#ifdef CONFIG_SPI_QSD +#include +#endif +#include +#include "msm_fb.h" + +#ifdef CONFIG_SPI_QSD +#define LCDC_SHARP_SPI_DEVICE_NAME "lcdc_sharp_ls038y7dx01" +static struct spi_device *lcdc_spi_client; +#endif +static int lcdc_sharp_panel_off(struct platform_device *pdev); + +#define BL_MAX 16 + +#ifdef CONFIG_PMIC8058_PWM +static struct pwm_device *bl_pwm; + +#define PWM_PERIOD 1000 /* us, period of 1Khz */ +#define DUTY_LEVEL (PWM_PERIOD / BL_MAX) +#endif + +#ifndef CONFIG_SPI_QSD +static int spi_cs; +static int spi_sclk; +static int spi_mosi; +static int spi_miso; +static unsigned char bit_shift[8] = { (1 << 7), /* MSB */ + (1 << 6), + (1 << 5), + (1 << 4), + (1 << 3), + (1 << 2), + (1 << 1), + (1 << 0) /* LSB */ +}; +#endif + +struct sharp_state_type { + boolean disp_initialized; + boolean display_on; + boolean disp_powered_up; +}; + +struct sharp_spi_data { + u8 addr; + u8 data; +}; + +static struct sharp_spi_data init_sequence[] = { + { 15, 0x01 }, + { 5, 0x01 }, + { 7, 0x10 }, + { 9, 0x1E }, + { 10, 0x04 }, + { 17, 0xFF }, + { 21, 0x8A }, + { 22, 0x00 }, + { 23, 0x82 }, + { 24, 0x24 }, + { 25, 0x22 }, + { 26, 0x6D }, + { 27, 0xEB }, + { 28, 0xB9 }, + { 29, 0x3A }, + { 49, 0x1A }, + { 50, 0x16 }, + { 51, 0x05 }, + { 55, 0x7F }, + { 56, 0x15 }, + { 57, 0x7B }, + { 60, 0x05 }, + { 61, 0x0C }, + { 62, 0x80 }, + { 63, 0x00 }, + { 92, 0x90 }, + { 97, 0x01 }, + { 98, 0xFF }, + { 113, 0x11 }, + { 114, 0x02 }, + { 115, 0x08 }, + { 123, 0xAB }, + { 124, 0x04 }, + { 6, 0x02 }, + { 133, 0x00 }, + { 134, 0xFE }, + { 135, 0x22 }, + { 136, 0x0B }, + { 137, 0xFF }, + { 138, 0x0F }, + { 139, 0x00 }, + { 140, 0xFE }, + { 141, 0x22 }, + { 142, 0x0B }, + { 143, 0xFF }, + { 144, 0x0F }, + { 145, 0x00 }, + { 146, 0xFE }, + { 147, 0x22 }, + { 148, 0x0B }, + { 149, 0xFF }, + { 150, 0x0F }, + { 202, 0x30 }, + { 30, 0x01 }, + { 4, 0x01 }, + { 31, 0x41 }, +}; + +static struct sharp_state_type sharp_state = { 0 }; +static struct msm_panel_common_pdata *lcdc_sharp_pdata; + +#ifndef CONFIG_SPI_QSD +static void sharp_spi_write_byte(u8 val) +{ + int i; + + /* Clock should be Low before entering */ + for (i = 0; i < 8; i++) { + /* #1: Drive the Data (High or Low) */ + if (val & bit_shift[i]) + gpio_set_value(spi_mosi, 1); + else + gpio_set_value(spi_mosi, 0); + + /* #2: Drive the Clk High and then Low */ + gpio_set_value(spi_sclk, 1); + gpio_set_value(spi_sclk, 0); + } +} +#endif + +static int serigo(u8 reg, u8 data) +{ +#ifdef CONFIG_SPI_QSD + char tx_buf[2]; + int rc; + struct spi_message m; + struct spi_transfer t; + + if (!lcdc_spi_client) { + printk(KERN_ERR "%s lcdc_spi_client is NULL\n", __func__); + return -EINVAL; + } + + memset(&t, 0, sizeof t); + t.tx_buf = tx_buf; + spi_setup(lcdc_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + tx_buf[0] = reg; + tx_buf[1] = data; + t.rx_buf = NULL; + t.len = 2; + rc = spi_sync(lcdc_spi_client, &m); + return rc; +#else + /* Enable the Chip Select - low */ + gpio_set_value(spi_cs, 0); + udelay(1); + + /* Transmit register address first, then data */ + sharp_spi_write_byte(reg); + + /* Idle state of MOSI is Low */ + gpio_set_value(spi_mosi, 0); + udelay(1); + sharp_spi_write_byte(data); + + gpio_set_value(spi_mosi, 0); + gpio_set_value(spi_cs, 1); + return 0; +#endif +} + +#ifndef CONFIG_SPI_QSD +static void sharp_spi_init(void) +{ + spi_sclk = *(lcdc_sharp_pdata->gpio_num); + spi_cs = *(lcdc_sharp_pdata->gpio_num + 1); + spi_mosi = *(lcdc_sharp_pdata->gpio_num + 2); + spi_miso = *(lcdc_sharp_pdata->gpio_num + 3); + + /* Set the output so that we don't disturb the slave device */ + gpio_set_value(spi_sclk, 0); + gpio_set_value(spi_mosi, 0); + + /* Set the Chip Select deasserted (active low) */ + gpio_set_value(spi_cs, 1); +} +#endif + +static void sharp_disp_powerup(void) +{ + if (!sharp_state.disp_powered_up && !sharp_state.display_on) + sharp_state.disp_powered_up = TRUE; +} + +static void sharp_disp_on(void) +{ + int i; + + if (sharp_state.disp_powered_up && !sharp_state.display_on) { + for (i = 0; i < ARRAY_SIZE(init_sequence); i++) { + serigo(init_sequence[i].addr, + init_sequence[i].data); + } + mdelay(10); + serigo(31, 0xC1); + mdelay(10); + serigo(31, 0xD9); + serigo(31, 0xDF); + + sharp_state.display_on = TRUE; + } +} + +static int lcdc_sharp_panel_on(struct platform_device *pdev) +{ + if (!sharp_state.disp_initialized) { +#ifndef CONFIG_SPI_QSD + lcdc_sharp_pdata->panel_config_gpio(1); + sharp_spi_init(); +#endif + sharp_disp_powerup(); + sharp_disp_on(); + sharp_state.disp_initialized = TRUE; + } + return 0; +} + +static int lcdc_sharp_panel_off(struct platform_device *pdev) +{ + if (sharp_state.disp_powered_up && sharp_state.display_on) { + serigo(4, 0x00); + mdelay(40); + serigo(31, 0xC1); + mdelay(40); + serigo(31, 0x00); + msleep(16); + sharp_state.display_on = FALSE; + sharp_state.disp_initialized = FALSE; + } + return 0; +} + +static void lcdc_sharp_panel_set_backlight(struct msm_fb_data_type *mfd) +{ + int bl_level; + + bl_level = mfd->bl_level; + +#ifdef CONFIG_PMIC8058_PWM + if (bl_pwm) { + pwm_config(bl_pwm, DUTY_LEVEL * bl_level, PWM_PERIOD); + pwm_enable(bl_pwm); + } +#endif +} + +static int sharp_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + lcdc_sharp_pdata = pdev->dev.platform_data; + return 0; + } + +#ifdef CONFIG_PMIC8058_PWM + bl_pwm = pwm_request(lcdc_sharp_pdata->gpio, "backlight"); + if (bl_pwm == NULL || IS_ERR(bl_pwm)) { + pr_err("%s pwm_request() failed\n", __func__); + bl_pwm = NULL; + } + + printk(KERN_INFO "sharp_probe: bl_pwm=%x LPG_chan=%d\n", + (int) bl_pwm, (int)lcdc_sharp_pdata->gpio); +#endif + + msm_fb_add_device(pdev); + + return 0; +} + +#ifdef CONFIG_SPI_QSD +static int lcdc_sharp_spi_probe(struct spi_device *spi) +{ + lcdc_spi_client = spi; + lcdc_spi_client->bits_per_word = 32; + return 0; +} +static int lcdc_sharp_spi_remove(struct spi_device *spi) +{ + lcdc_spi_client = NULL; + return 0; +} +static struct spi_driver lcdc_sharp_spi_driver = { + .driver = { + .name = LCDC_SHARP_SPI_DEVICE_NAME, + .owner = THIS_MODULE, + }, + .probe = lcdc_sharp_spi_probe, + .remove = lcdc_sharp_spi_remove, +}; +#endif +static struct platform_driver this_driver = { + .probe = sharp_probe, + .driver = { + .name = "lcdc_sharp_wvga", + }, +}; + +static struct msm_fb_panel_data sharp_panel_data = { + .on = lcdc_sharp_panel_on, + .off = lcdc_sharp_panel_off, + .set_backlight = lcdc_sharp_panel_set_backlight, +}; + +static struct platform_device this_device = { + .name = "lcdc_sharp_wvga", + .id = 1, + .dev = { + .platform_data = &sharp_panel_data, + } +}; + +static int __init lcdc_sharp_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + if (msm_fb_detect_client("lcdc_sharp_wvga_pt")) + return 0; +#endif + + ret = platform_driver_register(&this_driver); + if (ret) + return ret; + + pinfo = &sharp_panel_data.panel_info; + pinfo->xres = 480; + pinfo->yres = 800; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + pinfo->clk_rate = 24500000; + pinfo->bl_max = BL_MAX; + pinfo->bl_min = 1; + + pinfo->lcdc.h_back_porch = 20; + pinfo->lcdc.h_front_porch = 10; + pinfo->lcdc.h_pulse_width = 10; + pinfo->lcdc.v_back_porch = 2; + pinfo->lcdc.v_front_porch = 2; + pinfo->lcdc.v_pulse_width = 2; + pinfo->lcdc.border_clr = 0; + pinfo->lcdc.underflow_clr = 0xff; + pinfo->lcdc.hsync_skew = 0; + + ret = platform_device_register(&this_device); + if (ret) { + printk(KERN_ERR "%s not able to register the device\n", + __func__); + goto fail_driver; + } +#ifdef CONFIG_SPI_QSD + ret = spi_register_driver(&lcdc_sharp_spi_driver); + + if (ret) { + printk(KERN_ERR "%s not able to register spi\n", __func__); + goto fail_device; + } +#endif + return ret; +#ifdef CONFIG_SPI_QSD +fail_device: + platform_device_unregister(&this_device); +#endif +fail_driver: + platform_driver_unregister(&this_driver); + + return ret; +} + +module_init(lcdc_sharp_panel_init); +#ifdef CONFIG_SPI_QSD +static void __exit lcdc_sharp_panel_exit(void) +{ + spi_unregister_driver(&lcdc_sharp_spi_driver); +} +module_exit(lcdc_sharp_panel_exit); +#endif + diff --git a/drivers/video/msm/lcdc_st15.c b/drivers/video/msm/lcdc_st15.c new file mode 100644 index 0000000000000000000000000000000000000000..597a08f8cf2d1a79dd1c7aa648b4814a33972c3a --- /dev/null +++ b/drivers/video/msm/lcdc_st15.c @@ -0,0 +1,413 @@ +/* Copyright (c) 2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "msm_fb.h" + +#define DEVICE_NAME "sii9022" +#define SII9022_DEVICE_ID 0xB0 +#define SII9022_ISR 0x3D +#define SII9022_ISR_RXS_STATUS 0x08 + +static int lcdc_sii9022_panel_on(struct platform_device *pdev); +static int lcdc_sii9022_panel_off(struct platform_device *pdev); + +static struct i2c_client *sii9022_i2c_client; + +struct sii9022_data { + struct msm_hdmi_platform_data *pd; + struct platform_device *pdev; + struct work_struct work; + int x_res; + int y_res; + int sysfs_entry_created; + int hdmi_attached; +}; +static struct sii9022_data *dd; + +struct sii9022_i2c_addr_data{ + u8 addr; + u8 data; +}; + +/* video mode data */ +static u8 video_mode_data[] = { + 0x00, + 0xF9, 0x1C, 0x70, 0x17, 0x72, 0x06, 0xEE, 0x02, +}; + +static u8 avi_io_format[] = { + 0x09, + 0x00, 0x00, +}; + +/* power state */ +static struct sii9022_i2c_addr_data regset0[] = { + { 0x60, 0x04 }, + { 0x63, 0x00 }, + { 0x1E, 0x00 }, +}; + +static u8 video_infoframe[] = { + 0x0C, + 0xF0, 0x00, 0x68, 0x00, 0x04, 0x00, 0x19, 0x00, + 0xE9, 0x02, 0x04, 0x01, 0x04, 0x06, +}; + +/* configure audio */ +static struct sii9022_i2c_addr_data regset1[] = { + { 0x26, 0x90 }, + { 0x20, 0x90 }, + { 0x1F, 0x80 }, + { 0x26, 0x80 }, + { 0x24, 0x02 }, + { 0x25, 0x0B }, + { 0xBC, 0x02 }, + { 0xBD, 0x24 }, + { 0xBE, 0x02 }, +}; + +/* enable audio */ +static u8 misc_infoframe[] = { + 0xBF, + 0xC2, 0x84, 0x01, 0x0A, 0x6F, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +/* set HDMI, active */ +static struct sii9022_i2c_addr_data regset2[] = { + { 0x1A, 0x01 }, + { 0x3D, 0x00 }, + { 0x3C, 0x02 }, +}; + +static struct msm_fb_panel_data sii9022_panel_data = { + .on = lcdc_sii9022_panel_on, + .off = lcdc_sii9022_panel_off, +}; + +static struct platform_device sii9022_device = { + .name = DEVICE_NAME, + .id = 1, + .dev = { + .platform_data = &sii9022_panel_data, + } +}; + +static int send_i2c_data(struct i2c_client *client, + struct sii9022_i2c_addr_data *regset, + int size) +{ + int i; + int rc = 0; + + for (i = 0; i < size; i++) { + rc = i2c_smbus_write_byte_data( + client, + regset[i].addr, regset[i].data); + if (rc) + break; + } + return rc; +} + +static void sii9022_work_f(struct work_struct *work) +{ + int isr; + + isr = i2c_smbus_read_byte_data(sii9022_i2c_client, SII9022_ISR); + if (isr < 0) { + dev_err(&sii9022_i2c_client->dev, + "i2c read of isr failed rc = 0x%x\n", isr); + return; + } + if (isr == 0) + return; + + /* reset any set bits */ + i2c_smbus_write_byte_data(sii9022_i2c_client, SII9022_ISR, isr); + dd->hdmi_attached = isr & SII9022_ISR_RXS_STATUS; + if (dd->pd->cable_detect) + dd->pd->cable_detect(dd->hdmi_attached); + if (dd->hdmi_attached) { + dd->x_res = 1280; + dd->y_res = 720; + } else { + dd->x_res = sii9022_panel_data.panel_info.xres; + dd->y_res = sii9022_panel_data.panel_info.yres; + } +} + +static irqreturn_t sii9022_interrupt(int irq, void *dev_id) +{ + struct sii9022_data *dd = dev_id; + + schedule_work(&dd->work); + return IRQ_HANDLED; +} + +static int hdmi_sii_enable(struct i2c_client *client) +{ + int rc; + int retries = 10; + int count; + + rc = i2c_smbus_write_byte_data(client, 0xC7, 0x00); + if (rc) + goto enable_exit; + + do { + msleep(1); + rc = i2c_smbus_read_byte_data(client, 0x1B); + } while ((rc != SII9022_DEVICE_ID) && retries--); + + if (rc != SII9022_DEVICE_ID) + return -ENODEV; + + rc = i2c_smbus_write_byte_data(client, 0x1A, 0x11); + if (rc) + goto enable_exit; + + count = ARRAY_SIZE(video_mode_data); + rc = i2c_master_send(client, video_mode_data, count); + if (rc != count) { + rc = -EIO; + goto enable_exit; + } + + rc = i2c_smbus_write_byte_data(client, 0x08, 0x20); + if (rc) + goto enable_exit; + count = ARRAY_SIZE(avi_io_format); + rc = i2c_master_send(client, avi_io_format, count); + if (rc != count) { + rc = -EIO; + goto enable_exit; + } + + rc = send_i2c_data(client, regset0, ARRAY_SIZE(regset0)); + if (rc) + goto enable_exit; + + count = ARRAY_SIZE(video_infoframe); + rc = i2c_master_send(client, video_infoframe, count); + if (rc != count) { + rc = -EIO; + goto enable_exit; + } + + rc = send_i2c_data(client, regset1, ARRAY_SIZE(regset1)); + if (rc) + goto enable_exit; + + count = ARRAY_SIZE(misc_infoframe); + rc = i2c_master_send(client, misc_infoframe, count); + if (rc != count) { + rc = -EIO; + goto enable_exit; + } + + rc = send_i2c_data(client, regset2, ARRAY_SIZE(regset2)); + if (rc) + goto enable_exit; + + return 0; +enable_exit: + printk(KERN_ERR "%s: exited rc=%d\n", __func__, rc); + return rc; +} + +static ssize_t show_res(struct device *device, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%dx%d\n", dd->x_res, dd->y_res); +} + +static struct device_attribute device_attrs[] = { + __ATTR(screen_resolution, S_IRUGO|S_IWUSR, show_res, NULL), +}; + +static int lcdc_sii9022_panel_on(struct platform_device *pdev) +{ + int rc; + if (!dd->sysfs_entry_created) { + dd->pdev = pdev; + rc = device_create_file(&pdev->dev, &device_attrs[0]); + if (!rc) + dd->sysfs_entry_created = 1; + } + + rc = hdmi_sii_enable(sii9022_i2c_client); + if (rc) { + dd->hdmi_attached = 0; + dd->x_res = sii9022_panel_data.panel_info.xres; + dd->y_res = sii9022_panel_data.panel_info.yres; + } + if (dd->pd->irq) + enable_irq(dd->pd->irq); + /* Don't return the value from hdmi_sii_enable(). + * It may fail on some ST1.5s, but we must return 0 from this + * function in order for the on-board display to turn on. + */ + return 0; +} + +static int lcdc_sii9022_panel_off(struct platform_device *pdev) +{ + if (dd->pd->irq) + disable_irq(dd->pd->irq); + return 0; +} + +static const struct i2c_device_id hmdi_sii_id[] = { + { DEVICE_NAME, 0 }, + { } +}; + +static int hdmi_sii_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) + return -ENODEV; + + dd = kzalloc(sizeof *dd, GFP_KERNEL); + if (!dd) { + rc = -ENOMEM; + goto probe_exit; + } + sii9022_i2c_client = client; + i2c_set_clientdata(client, dd); + dd->pd = client->dev.platform_data; + if (!dd->pd) { + rc = -ENODEV; + goto probe_free; + } + if (dd->pd->irq) { + INIT_WORK(&dd->work, sii9022_work_f); + rc = request_irq(dd->pd->irq, + &sii9022_interrupt, + IRQF_TRIGGER_FALLING, + "sii9022_cable", dd); + if (rc) + goto probe_free; + disable_irq(dd->pd->irq); + } + msm_fb_add_device(&sii9022_device); + dd->x_res = sii9022_panel_data.panel_info.xres; + dd->y_res = sii9022_panel_data.panel_info.yres; + + return 0; + +probe_free: + i2c_set_clientdata(client, NULL); + kfree(dd); +probe_exit: + return rc; +} + +static int hdmi_sii_remove(struct i2c_client *client) +{ + int err = 0 ; + struct msm_hdmi_platform_data *pd; + + if (dd->sysfs_entry_created) + device_remove_file(&dd->pdev->dev, &device_attrs[0]); + pd = client->dev.platform_data; + if (pd && pd->irq) + free_irq(pd->irq, dd); + i2c_set_clientdata(client, NULL); + kfree(dd); + + return err ; +} + +#ifdef CONFIG_PM +static int sii9022_suspend(struct device *dev) +{ + if (dd && dd->pd && dd->pd->irq) + disable_irq(dd->pd->irq); + return 0; +} + +static int sii9022_resume(struct device *dev) +{ + if (dd && dd->pd && dd->pd->irq) + enable_irq(dd->pd->irq); + return 0; +} + +static struct dev_pm_ops sii9022_pm_ops = { + .suspend = sii9022_suspend, + .resume = sii9022_resume, +}; +#endif + +static struct i2c_driver hdmi_sii_i2c_driver = { + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &sii9022_pm_ops, +#endif + }, + .probe = hdmi_sii_probe, + .remove = __exit_p(hdmi_sii_remove), + .id_table = hmdi_sii_id, +}; + +static int __init lcdc_st15_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + if (msm_fb_detect_client("lcdc_st15")) + return 0; + + pinfo = &sii9022_panel_data.panel_info; + pinfo->xres = 1366; + pinfo->yres = 768; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + pinfo->fb_num = 2; + pinfo->clk_rate = 74250000; + + pinfo->lcdc.h_back_porch = 120; + pinfo->lcdc.h_front_porch = 20; + pinfo->lcdc.h_pulse_width = 40; + pinfo->lcdc.v_back_porch = 25; + pinfo->lcdc.v_front_porch = 1; + pinfo->lcdc.v_pulse_width = 7; + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + + ret = i2c_add_driver(&hdmi_sii_i2c_driver); + if (ret) + printk(KERN_ERR "%s: failed to add i2c driver\n", __func__); + + return ret; +} + +static void __exit hdmi_sii_exit(void) +{ + i2c_del_driver(&hdmi_sii_i2c_driver); +} + +module_init(lcdc_st15_init); +module_exit(hdmi_sii_exit); diff --git a/drivers/video/msm/lcdc_toshiba_fwvga_pt.c b/drivers/video/msm/lcdc_toshiba_fwvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..db13c73d0410203a24ab9ef8773d1f802d29de33 --- /dev/null +++ b/drivers/video/msm/lcdc_toshiba_fwvga_pt.c @@ -0,0 +1,471 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include "msm_fb.h" + +static int spi_cs0_N; +static int spi_sclk; +static int spi_mosi; +static int spi_miso; + +struct toshiba_state_type { + boolean disp_initialized; + boolean display_on; + boolean disp_powered_up; +}; + +static struct toshiba_state_type toshiba_state = { 0 }; +static struct msm_panel_common_pdata *lcdc_toshiba_pdata; + +static int toshiba_spi_write(char data1, char data2, int rs) +{ + uint32 bitdata = 0, bnum = 24, bmask = 0x800000; + + gpio_set_value_cansleep(spi_cs0_N, 0); /* cs* low */ + udelay(1); + + if (rs) + bitdata = (0x72 << 16); + else + bitdata = (0x70 << 16); + + bitdata |= ((data1 << 8) | data2); + + while (bnum) { + gpio_set_value_cansleep(spi_sclk, 0); /* clk low */ + udelay(1); + + if (bitdata & bmask) + gpio_set_value_cansleep(spi_mosi, 1); + else + gpio_set_value_cansleep(spi_mosi, 0); + + udelay(1); + gpio_set_value_cansleep(spi_sclk, 1); /* clk high */ + udelay(1); + bmask >>= 1; + bnum--; + } + + gpio_set_value_cansleep(spi_cs0_N, 1); /* cs* high */ + udelay(1); + return 0; +} + +static void spi_pin_assign(void) +{ + /* Setting the Default GPIO's */ + spi_mosi = *(lcdc_toshiba_pdata->gpio_num); + spi_miso = *(lcdc_toshiba_pdata->gpio_num + 1); + spi_sclk = *(lcdc_toshiba_pdata->gpio_num + 2); + spi_cs0_N = *(lcdc_toshiba_pdata->gpio_num + 3); +} + +static void toshiba_disp_powerup(void) +{ + if (!toshiba_state.disp_powered_up && !toshiba_state.display_on) { + /* Reset the hardware first */ + /* Include DAC power up implementation here */ + toshiba_state.disp_powered_up = TRUE; + } +} + +static void toshiba_disp_on(void) +{ + if (toshiba_state.disp_powered_up && !toshiba_state.display_on) { + toshiba_spi_write(0x01, 0x00, 0); + toshiba_spi_write(0x30, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x01, 0x01, 0); + toshiba_spi_write(0x40, 0x10, 1); + +#ifdef TOSHIBA_FWVGA_FULL_INIT + udelay(500); + toshiba_spi_write(0x01, 0x06, 0); + toshiba_spi_write(0x00, 0x00, 1); + msleep(20); + + toshiba_spi_write(0x00, 0x01, 0); + toshiba_spi_write(0x03, 0x10, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x02, 0); + toshiba_spi_write(0x01, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x03, 0); + toshiba_spi_write(0x00, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x07, 0); + toshiba_spi_write(0x00, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x08, 0); + toshiba_spi_write(0x00, 0x04, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x09, 0); + toshiba_spi_write(0x00, 0x0c, 1); +#endif + udelay(500); + toshiba_spi_write(0x00, 0x0c, 0); + toshiba_spi_write(0x40, 0x10, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x0e, 0); + toshiba_spi_write(0x00, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x20, 0); + toshiba_spi_write(0x01, 0x3f, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x22, 0); + toshiba_spi_write(0x76, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x23, 0); + toshiba_spi_write(0x1c, 0x0a, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x24, 0); + toshiba_spi_write(0x1c, 0x2c, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x25, 0); + toshiba_spi_write(0x1c, 0x4e, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x27, 0); + toshiba_spi_write(0x00, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x28, 0); + toshiba_spi_write(0x76, 0x0c, 1); + +#ifdef TOSHIBA_FWVGA_FULL_INIT + udelay(500); + toshiba_spi_write(0x03, 0x00, 0); + toshiba_spi_write(0x00, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x01, 0); + toshiba_spi_write(0x05, 0x02, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x02, 0); + toshiba_spi_write(0x07, 0x05, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x03, 0); + toshiba_spi_write(0x00, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x04, 0); + toshiba_spi_write(0x02, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x05, 0); + toshiba_spi_write(0x07, 0x07, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x06, 0); + toshiba_spi_write(0x10, 0x10, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x07, 0); + toshiba_spi_write(0x02, 0x02, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x08, 0); + toshiba_spi_write(0x07, 0x04, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x09, 0); + toshiba_spi_write(0x07, 0x07, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x0a, 0); + toshiba_spi_write(0x00, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x0b, 0); + toshiba_spi_write(0x00, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x0c, 0); + toshiba_spi_write(0x07, 0x07, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x0d, 0); + toshiba_spi_write(0x10, 0x10, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x10, 0); + toshiba_spi_write(0x01, 0x04, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x11, 0); + toshiba_spi_write(0x05, 0x03, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x12, 0); + toshiba_spi_write(0x03, 0x04, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x15, 0); + toshiba_spi_write(0x03, 0x04, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x16, 0); + toshiba_spi_write(0x03, 0x1c, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x17, 0); + toshiba_spi_write(0x02, 0x04, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x18, 0); + toshiba_spi_write(0x04, 0x02, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x19, 0); + toshiba_spi_write(0x03, 0x05, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x1c, 0); + toshiba_spi_write(0x07, 0x07, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x1d, 0); + toshiba_spi_write(0x02, 0x1f, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x20, 0); + toshiba_spi_write(0x05, 0x07, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x21, 0); + toshiba_spi_write(0x06, 0x04, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x22, 0); + toshiba_spi_write(0x04, 0x05, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x27, 0); + toshiba_spi_write(0x02, 0x03, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x28, 0); + toshiba_spi_write(0x03, 0x00, 1); + + udelay(500); + toshiba_spi_write(0x03, 0x29, 0); + toshiba_spi_write(0x00, 0x02, 1); + +#endif + udelay(500); + toshiba_spi_write(0x01, 0x00, 0); + toshiba_spi_write(0x36, 0x3c, 1); + udelay(500); + + toshiba_spi_write(0x01, 0x01, 0); + toshiba_spi_write(0x40, 0x03, 1); + + udelay(500); + toshiba_spi_write(0x01, 0x02, 0); + toshiba_spi_write(0x00, 0x01, 1); + + udelay(500); + toshiba_spi_write(0x01, 0x03, 0); + toshiba_spi_write(0x3c, 0x58, 1); + + udelay(500); + toshiba_spi_write(0x01, 0x0c, 0); + toshiba_spi_write(0x01, 0x35, 1); + + udelay(500); + toshiba_spi_write(0x01, 0x06, 0); + toshiba_spi_write(0x00, 0x02, 1); + + udelay(500); + toshiba_spi_write(0x00, 0x29, 0); + toshiba_spi_write(0x03, 0xbf, 1); + + udelay(500); + toshiba_spi_write(0x01, 0x06, 0); + toshiba_spi_write(0x00, 0x03, 1); + msleep(32); + + toshiba_spi_write(0x01, 0x01, 0); + toshiba_spi_write(0x40, 0x10, 1); + msleep(80); + + toshiba_state.display_on = TRUE; + } +} + +static int lcdc_toshiba_panel_on(struct platform_device *pdev) +{ + if (!toshiba_state.disp_initialized) { + /* Configure reset GPIO that drives DAC */ + if (lcdc_toshiba_pdata->panel_config_gpio) + lcdc_toshiba_pdata->panel_config_gpio(1); + toshiba_disp_powerup(); + toshiba_disp_on(); + toshiba_state.disp_initialized = TRUE; + } + return 0; +} + +static int lcdc_toshiba_panel_off(struct platform_device *pdev) +{ + if (toshiba_state.disp_powered_up && toshiba_state.display_on) { + toshiba_spi_write(0x01, 0x06, 1); + toshiba_spi_write(0x00, 0x02, 1); + msleep(80); + + toshiba_spi_write(0x01, 0x06, 1); + toshiba_spi_write(0x00, 0x00, 1); + + toshiba_spi_write(0x00, 0x29, 1); + toshiba_spi_write(0x00, 0x02, 1); + + toshiba_spi_write(0x01, 0x00, 1); + toshiba_spi_write(0x30, 0x00, 1); + + if (lcdc_toshiba_pdata->panel_config_gpio) + lcdc_toshiba_pdata->panel_config_gpio(0); + toshiba_state.display_on = FALSE; + toshiba_state.disp_initialized = FALSE; + } + + return 0; +} + +static void lcdc_toshiba_set_backlight(struct msm_fb_data_type *mfd) +{ + int ret; + int bl_level; + + bl_level = mfd->bl_level; + + if (lcdc_toshiba_pdata && lcdc_toshiba_pdata->pmic_backlight) + ret = lcdc_toshiba_pdata->pmic_backlight(bl_level); + else + pr_err("%s(): Backlight level set failed", __func__); + + return; +} + +static int toshiba_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + lcdc_toshiba_pdata = pdev->dev.platform_data; + spi_pin_assign(); + return 0; + } + msm_fb_add_device(pdev); + return 0; +} + +static struct platform_driver this_driver = { + .probe = toshiba_probe, + .driver = { + .name = "lcdc_toshiba_fwvga_pt", + }, +}; + +static struct msm_fb_panel_data toshiba_panel_data = { + .on = lcdc_toshiba_panel_on, + .off = lcdc_toshiba_panel_off, + .set_backlight = lcdc_toshiba_set_backlight, +}; + +static struct platform_device this_device = { + .name = "lcdc_toshiba_fwvga_pt", + .id = 1, + .dev = { + .platform_data = &toshiba_panel_data, + } +}; + +static int __init lcdc_toshiba_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + ret = msm_fb_detect_client("lcdc_toshiba_fwvga_pt"); + if (ret) + return 0; + + + ret = platform_driver_register(&this_driver); + if (ret) + return ret; + + pinfo = &toshiba_panel_data.panel_info; + pinfo->xres = 480; + pinfo->yres = 864; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + /* 30Mhz mdp_lcdc_pclk and mdp_lcdc_pad_pcl */ + pinfo->clk_rate = 30720000; + pinfo->bl_max = 100; + pinfo->bl_min = 1; + + if (cpu_is_msm7x25a() || cpu_is_msm7x25aa() || cpu_is_msm7x25ab()) { + pinfo->yres = 320; + pinfo->lcdc.h_back_porch = 10; + pinfo->lcdc.h_front_porch = 21; + pinfo->lcdc.h_pulse_width = 5; + pinfo->lcdc.v_back_porch = 8; + pinfo->lcdc.v_front_porch = 540; + pinfo->lcdc.v_pulse_width = 42; + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + } else { + pinfo->lcdc.h_back_porch = 8; + pinfo->lcdc.h_front_porch = 16; + pinfo->lcdc.h_pulse_width = 8; + pinfo->lcdc.v_back_porch = 2; + pinfo->lcdc.v_front_porch = 2; + pinfo->lcdc.v_pulse_width = 2; + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + } + + ret = platform_device_register(&this_device); + if (ret) { + printk(KERN_ERR "%s not able to register the device\n", + __func__); + platform_driver_unregister(&this_driver); + } + return ret; +} + +device_initcall(lcdc_toshiba_panel_init); diff --git a/drivers/video/msm/lcdc_toshiba_wvga_pt.c b/drivers/video/msm/lcdc_toshiba_wvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..f9f4f5c27bd47d20c6c073d2cafec54b7d589fb5 --- /dev/null +++ b/drivers/video/msm/lcdc_toshiba_wvga_pt.c @@ -0,0 +1,519 @@ +/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#ifdef CONFIG_SPI_QSD +#include +#endif +#include +#include +#include "msm_fb.h" + +#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM +#include "mddihosti.h" +#endif + +#ifdef CONFIG_SPI_QSD +#define LCDC_TOSHIBA_SPI_DEVICE_NAME "lcdc_toshiba_ltm030dd40" +static struct spi_device *lcdc_toshiba_spi_client; +#else +static int spi_cs; +static int spi_sclk; +static int spi_mosi; +static int spi_miso; +#endif +struct toshiba_state_type{ + boolean disp_initialized; + boolean display_on; + boolean disp_powered_up; +}; + +static struct toshiba_state_type toshiba_state = { 0 }; +static struct msm_panel_common_pdata *lcdc_toshiba_pdata; + +#ifndef CONFIG_SPI_QSD +static void toshiba_spi_write_byte(char dc, uint8 data) +{ + uint32 bit; + int bnum; + + gpio_set_value(spi_sclk, 0); /* clk low */ + /* dc: 0 for command, 1 for parameter */ + gpio_set_value(spi_mosi, dc); + udelay(1); /* at least 20 ns */ + gpio_set_value(spi_sclk, 1); /* clk high */ + udelay(1); /* at least 20 ns */ + bnum = 8; /* 8 data bits */ + bit = 0x80; + while (bnum) { + gpio_set_value(spi_sclk, 0); /* clk low */ + if (data & bit) + gpio_set_value(spi_mosi, 1); + else + gpio_set_value(spi_mosi, 0); + udelay(1); + gpio_set_value(spi_sclk, 1); /* clk high */ + udelay(1); + bit >>= 1; + bnum--; + } +} +#endif + +static int toshiba_spi_write(char cmd, uint32 data, int num) +{ + char *bp; +#ifdef CONFIG_SPI_QSD + char tx_buf[4]; + int rc, i; + struct spi_message m; + struct spi_transfer t; + uint32 final_data = 0; + + if (!lcdc_toshiba_spi_client) { + printk(KERN_ERR "%s lcdc_toshiba_spi_client is NULL\n", + __func__); + return -EINVAL; + } + + memset(&t, 0, sizeof t); + t.tx_buf = tx_buf; + spi_setup(lcdc_toshiba_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + /* command byte first */ + final_data |= cmd << 23; + t.len = num + 2; + if (t.len < 4) + t.bits_per_word = 8 * t.len; + /* followed by parameter bytes */ + if (num) { + bp = (char *)&data;; + bp += (num - 1); + i = 1; + while (num) { + final_data |= 1 << (((4 - i) << 3) - i - 1); + final_data |= *bp << (((4 - i - 1) << 3) - i - 1); + num--; + bp--; + i++; + } + } + + bp = (char *)&final_data; + for (i = 0; i < t.len; i++) + tx_buf[i] = bp[3 - i]; + t.rx_buf = NULL; + rc = spi_sync(lcdc_toshiba_spi_client, &m); + if (rc) + printk(KERN_ERR "spi_sync _write failed %d\n", rc); + return rc; +#else + gpio_set_value(spi_cs, 1); /* cs high */ + + /* command byte first */ + toshiba_spi_write_byte(0, cmd); + + /* followed by parameter bytes */ + if (num) { + bp = (char *)&data;; + bp += (num - 1); + while (num) { + toshiba_spi_write_byte(1, *bp); + num--; + bp--; + } + } + + gpio_set_value(spi_cs, 0); /* cs low */ + udelay(1); + return 0; +#endif +} + +static int toshiba_spi_read_bytes(char cmd, uint32 *data, int num) +{ +#ifdef CONFIG_SPI_QSD + char tx_buf[5]; + char rx_buf[5]; + int rc; + struct spi_message m; + struct spi_transfer t; + + if (!lcdc_toshiba_spi_client) { + printk(KERN_ERR "%s lcdc_toshiba_spi_client is NULL\n", + __func__); + return -EINVAL; + } + + memset(&t, 0, sizeof t); + t.tx_buf = tx_buf; + t.rx_buf = rx_buf; + spi_setup(lcdc_toshiba_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + /* command byte first */ + tx_buf[0] = 0 | ((cmd >> 1) & 0x7f); + tx_buf[1] = (cmd & 0x01) << 7; + tx_buf[2] = 0; + tx_buf[3] = 0; + tx_buf[4] = 0; + + t.len = 5; + + rc = spi_sync(lcdc_toshiba_spi_client, &m); + *data = 0; + *data = ((rx_buf[1] & 0x1f) << 19) | (rx_buf[2] << 11) | + (rx_buf[3] << 3) | ((rx_buf[4] & 0xe0) >> 5); + if (rc) + printk(KERN_ERR "spi_sync _read failed %d\n", rc); + return rc; +#else + uint32 dbit, bits; + int bnum; + + gpio_set_value(spi_cs, 1); /* cs high */ + + /* command byte first */ + toshiba_spi_write_byte(0, cmd); + + if (num > 1) { + /* extra dc bit */ + gpio_set_value(spi_sclk, 0); /* clk low */ + udelay(1); + dbit = gpio_get_value(spi_miso);/* dc bit */ + udelay(1); + gpio_set_value(spi_sclk, 1); /* clk high */ + } + + /* followed by data bytes */ + bnum = num * 8; /* number of bits */ + bits = 0; + while (bnum) { + bits <<= 1; + gpio_set_value(spi_sclk, 0); /* clk low */ + udelay(1); + dbit = gpio_get_value(spi_miso); + udelay(1); + gpio_set_value(spi_sclk, 1); /* clk high */ + bits |= dbit; + bnum--; + } + + *data = bits; + + udelay(1); + gpio_set_value(spi_cs, 0); /* cs low */ + udelay(1); + return 0; +#endif +} + +#ifndef CONFIG_SPI_QSD +static void spi_pin_assign(void) +{ + /* Setting the Default GPIO's */ + spi_sclk = *(lcdc_toshiba_pdata->gpio_num); + spi_cs = *(lcdc_toshiba_pdata->gpio_num + 1); + spi_mosi = *(lcdc_toshiba_pdata->gpio_num + 2); + spi_miso = *(lcdc_toshiba_pdata->gpio_num + 3); +} +#endif + +static void toshiba_disp_powerup(void) +{ + if (!toshiba_state.disp_powered_up && !toshiba_state.display_on) { + /* Reset the hardware first */ + /* Include DAC power up implementation here */ + toshiba_state.disp_powered_up = TRUE; + } +} + +static void toshiba_disp_on(void) +{ + uint32 data; + +#ifndef CONFIG_SPI_QSD + gpio_set_value(spi_cs, 0); /* low */ + gpio_set_value(spi_sclk, 1); /* high */ + gpio_set_value(spi_mosi, 0); + gpio_set_value(spi_miso, 0); +#endif + + if (toshiba_state.disp_powered_up && !toshiba_state.display_on) { + toshiba_spi_write(0, 0, 0); + mdelay(7); + toshiba_spi_write(0, 0, 0); + mdelay(7); + toshiba_spi_write(0, 0, 0); + mdelay(7); + toshiba_spi_write(0xba, 0x11, 1); + toshiba_spi_write(0x36, 0x00, 1); + mdelay(1); + toshiba_spi_write(0x3a, 0x60, 1); + toshiba_spi_write(0xb1, 0x5d, 1); + mdelay(1); + toshiba_spi_write(0xb2, 0x33, 1); + toshiba_spi_write(0xb3, 0x22, 1); + mdelay(1); + toshiba_spi_write(0xb4, 0x02, 1); + toshiba_spi_write(0xb5, 0x1e, 1); /* vcs -- adjust brightness */ + mdelay(1); + toshiba_spi_write(0xb6, 0x27, 1); + toshiba_spi_write(0xb7, 0x03, 1); + mdelay(1); + toshiba_spi_write(0xb9, 0x24, 1); + toshiba_spi_write(0xbd, 0xa1, 1); + mdelay(1); + toshiba_spi_write(0xbb, 0x00, 1); + toshiba_spi_write(0xbf, 0x01, 1); + mdelay(1); + toshiba_spi_write(0xbe, 0x00, 1); + toshiba_spi_write(0xc0, 0x11, 1); + mdelay(1); + toshiba_spi_write(0xc1, 0x11, 1); + toshiba_spi_write(0xc2, 0x11, 1); + mdelay(1); + toshiba_spi_write(0xc3, 0x3232, 2); + mdelay(1); + toshiba_spi_write(0xc4, 0x3232, 2); + mdelay(1); + toshiba_spi_write(0xc5, 0x3232, 2); + mdelay(1); + toshiba_spi_write(0xc6, 0x3232, 2); + mdelay(1); + toshiba_spi_write(0xc7, 0x6445, 2); + mdelay(1); + toshiba_spi_write(0xc8, 0x44, 1); + toshiba_spi_write(0xc9, 0x52, 1); + mdelay(1); + toshiba_spi_write(0xca, 0x00, 1); + mdelay(1); + toshiba_spi_write(0xec, 0x02a4, 2); /* 0x02a4 */ + mdelay(1); + toshiba_spi_write(0xcf, 0x01, 1); + mdelay(1); + toshiba_spi_write(0xd0, 0xc003, 2); /* c003 */ + mdelay(1); + toshiba_spi_write(0xd1, 0x01, 1); + mdelay(1); + toshiba_spi_write(0xd2, 0x0028, 2); + mdelay(1); + toshiba_spi_write(0xd3, 0x0028, 2); + mdelay(1); + toshiba_spi_write(0xd4, 0x26a4, 2); + mdelay(1); + toshiba_spi_write(0xd5, 0x20, 1); + mdelay(1); + toshiba_spi_write(0xef, 0x3200, 2); + mdelay(32); + toshiba_spi_write(0xbc, 0x80, 1); /* wvga pass through */ + toshiba_spi_write(0x3b, 0x00, 1); + mdelay(1); + toshiba_spi_write(0xb0, 0x16, 1); + mdelay(1); + toshiba_spi_write(0xb8, 0xfff5, 2); + mdelay(1); + toshiba_spi_write(0x11, 0, 0); + mdelay(5); + toshiba_spi_write(0x29, 0, 0); + mdelay(5); + toshiba_state.display_on = TRUE; + } + + data = 0; + toshiba_spi_read_bytes(0x04, &data, 3); + printk(KERN_INFO "toshiba_disp_on: id=%x\n", data); + +} + +static int lcdc_toshiba_panel_on(struct platform_device *pdev) +{ + if (!toshiba_state.disp_initialized) { + /* Configure reset GPIO that drives DAC */ + if (lcdc_toshiba_pdata->panel_config_gpio) + lcdc_toshiba_pdata->panel_config_gpio(1); + toshiba_disp_powerup(); + toshiba_disp_on(); + toshiba_state.disp_initialized = TRUE; + } + return 0; +} + +static int lcdc_toshiba_panel_off(struct platform_device *pdev) +{ + if (toshiba_state.disp_powered_up && toshiba_state.display_on) { + /* Main panel power off (Deep standby in) */ + + toshiba_spi_write(0x28, 0, 0); /* display off */ + mdelay(1); + toshiba_spi_write(0xb8, 0x8002, 2); /* output control */ + mdelay(1); + toshiba_spi_write(0x10, 0x00, 1); /* sleep mode in */ + mdelay(85); /* wait 85 msec */ + toshiba_spi_write(0xb0, 0x00, 1); /* deep standby in */ + mdelay(1); + if (lcdc_toshiba_pdata->panel_config_gpio) + lcdc_toshiba_pdata->panel_config_gpio(0); + toshiba_state.display_on = FALSE; + toshiba_state.disp_initialized = FALSE; + } + return 0; +} + +static void lcdc_toshiba_set_backlight(struct msm_fb_data_type *mfd) +{ + int bl_level; + int ret = -EPERM; + int i = 0; + + bl_level = mfd->bl_level; + + while (i++ < 3) { + ret = pmic_set_led_intensity(LED_LCD, bl_level); + if (ret == 0) + return; + msleep(10); + } + + printk(KERN_WARNING "%s: can't set lcd backlight!\n", + __func__); +} + +static int toshiba_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + lcdc_toshiba_pdata = pdev->dev.platform_data; +#ifndef CONFIG_SPI_QSD + spi_pin_assign(); +#endif + return 0; + } + msm_fb_add_device(pdev); + return 0; +} + +#ifdef CONFIG_SPI_QSD +static int lcdc_toshiba_spi_probe(struct spi_device *spi) +{ + lcdc_toshiba_spi_client = spi; + lcdc_toshiba_spi_client->bits_per_word = 32; + return 0; +} +static int lcdc_toshiba_spi_remove(struct spi_device *spi) +{ + lcdc_toshiba_spi_client = NULL; + return 0; +} + +static struct spi_driver lcdc_toshiba_spi_driver = { + .driver = { + .name = LCDC_TOSHIBA_SPI_DEVICE_NAME, + .owner = THIS_MODULE, + }, + .probe = lcdc_toshiba_spi_probe, + .remove = lcdc_toshiba_spi_remove, +}; +#endif +static struct platform_driver this_driver = { + .probe = toshiba_probe, + .driver = { + .name = "lcdc_toshiba_wvga", + }, +}; + +static struct msm_fb_panel_data toshiba_panel_data = { + .on = lcdc_toshiba_panel_on, + .off = lcdc_toshiba_panel_off, + .set_backlight = lcdc_toshiba_set_backlight, +}; + +static struct platform_device this_device = { + .name = "lcdc_toshiba_wvga", + .id = 1, + .dev = { + .platform_data = &toshiba_panel_data, + } +}; + +static int __init lcdc_toshiba_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; +#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM + if (mddi_get_client_id() != 0) + return 0; + + ret = msm_fb_detect_client("lcdc_toshiba_wvga_pt"); + if (ret) + return 0; + +#endif + + ret = platform_driver_register(&this_driver); + if (ret) + return ret; + + pinfo = &toshiba_panel_data.panel_info; + pinfo->xres = 480; + pinfo->yres = 800; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + /* 30Mhz mdp_lcdc_pclk and mdp_lcdc_pad_pcl */ + pinfo->clk_rate = 30720000; + pinfo->bl_max = 15; + pinfo->bl_min = 1; + + pinfo->lcdc.h_back_porch = 184; /* hsw = 8 + hbp=184 */ + pinfo->lcdc.h_front_porch = 4; + pinfo->lcdc.h_pulse_width = 8; + pinfo->lcdc.v_back_porch = 2; /* vsw=1 + vbp = 2 */ + pinfo->lcdc.v_front_porch = 3; + pinfo->lcdc.v_pulse_width = 1; + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + + ret = platform_device_register(&this_device); + if (ret) { + printk(KERN_ERR "%s not able to register the device\n", + __func__); + goto fail_driver; + } +#ifdef CONFIG_SPI_QSD + ret = spi_register_driver(&lcdc_toshiba_spi_driver); + + if (ret) { + printk(KERN_ERR "%s not able to register spi\n", __func__); + goto fail_device; + } +#endif + return ret; + +#ifdef CONFIG_SPI_QSD +fail_device: + platform_device_unregister(&this_device); +#endif +fail_driver: + platform_driver_unregister(&this_driver); + return ret; +} + +device_initcall(lcdc_toshiba_panel_init); diff --git a/drivers/video/msm/lcdc_truly_ips3p2335.c b/drivers/video/msm/lcdc_truly_ips3p2335.c new file mode 100644 index 0000000000000000000000000000000000000000..abb4756365feceb33e32623d163b4921240fcb3f --- /dev/null +++ b/drivers/video/msm/lcdc_truly_ips3p2335.c @@ -0,0 +1,305 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include "msm_fb.h" + +static int prev_bl = 17; + +static int spi_cs; +static int spi_sclk; +static int spi_mosi; +static int gpio_backlight_en; +static int gpio_display_reset; + +struct truly_state_type { + boolean disp_initialized; + boolean display_on; + boolean disp_powered_up; +}; + +static struct truly_state_type truly_state = { 0 }; +static struct msm_panel_common_pdata *lcdc_truly_pdata; + +static char init_item_v1[] = { 0xff, 0x83, 0x57, }; +static char init_item_v2[] = { 0x03, }; +static char init_item_v3[] = { 0x00, 0x13, 0x1C, 0x1C, 0x83, 0x48, }; +static char init_item_v4[] = { 0x43, 0x06, 0x06, 0x06, }; +static char init_item_v5[] = { 0x53, }; +static char init_item_v6[] = { 0x02, 0x40, 0x00, 0x2a, 0x2a, 0x0d, 0x3f, }; +static char init_item_v7[] = { 0x70, 0x50, 0x01, 0x3c, 0xe8, 0x08, }; +static char init_item_v8[] = { 0x17, 0x0f, }; +static char init_item_v9[] = { 0x60}; +static char init_item_v10[] = { 0x00, 0x13, 0x1a, 0x29, 0x2d, 0x41, 0x49, + 0x52, 0x48, 0x41, 0x3c, 0x33, 0x30, 0x1c, + 0x19, 0x03, 0x00, 0x13, 0x1a, 0x29, 0x2d, + 0x41, 0x49, 0x52, 0x48, 0x41, 0x3c, 0x33, + 0x31, 0x1c, 0x19, 0x03, 0x00, 0x01, + }; +static char init_item_v11[] = { 0x40, }; + +static inline void truly_spi_write_byte(char dc, uint8 data) +{ + uint32 bit; + int bnum; + + gpio_set_value_cansleep(spi_sclk, 0); /* clk low */ + /* dc: 0 for command, 1 for parameter */ + gpio_set_value_cansleep(spi_mosi, dc); + udelay(1); /* at least 20 ns */ + gpio_set_value_cansleep(spi_sclk, 1); /* clk high */ + udelay(1); /* at least 20 ns */ + bnum = 8; /* 8 data bits */ + bit = 0x80; + while (bnum) { + gpio_set_value_cansleep(spi_sclk, 0); /* clk low */ + if (data & bit) + gpio_set_value_cansleep(spi_mosi, 1); + else + gpio_set_value_cansleep(spi_mosi, 0); + udelay(1); + gpio_set_value_cansleep(spi_sclk, 1); /* clk high */ + udelay(1); + bit >>= 1; + bnum--; + } +} + +static inline int truly_spi_write(char cmd, char *data, int num) +{ + int i; + + gpio_set_value_cansleep(spi_cs, 0); /* cs low */ + /* command byte first */ + truly_spi_write_byte(0, cmd); + /* followed by parameter bytes */ + for (i = 0; i < num; i++) { + if (data) + truly_spi_write_byte(1, data[i]); + } + gpio_set_value_cansleep(spi_mosi, 1); /* mosi high */ + gpio_set_value_cansleep(spi_cs, 1); /* cs high */ + udelay(10); + return 0; +} + +static void spi_pin_assign(void) +{ + /* Setting the Default GPIO's */ + spi_mosi = *(lcdc_truly_pdata->gpio_num); + spi_sclk = *(lcdc_truly_pdata->gpio_num + 1); + spi_cs = *(lcdc_truly_pdata->gpio_num + 2); + gpio_backlight_en = *(lcdc_truly_pdata->gpio_num + 3); + gpio_display_reset = *(lcdc_truly_pdata->gpio_num + 4); + pr_debug("spi_mosi:%d spi_sclk:%d spi_cs:%d backlight:%d reset:%d\n", + spi_mosi, spi_sclk, spi_cs, gpio_backlight_en, + gpio_display_reset); + +} + +static void truly_disp_powerup(void) +{ + /* Reset the hardware first */ + /* Include DAC power up implementation here */ + if (!truly_state.disp_powered_up && !truly_state.display_on) + truly_state.disp_powered_up = TRUE; +} + +static void truly_disp_reginit(void) +{ + pr_debug("%s disp_powered_up:%d display_on:%d\n", __func__, + truly_state.disp_powered_up, truly_state.display_on); + if (truly_state.disp_powered_up && !truly_state.display_on) { + gpio_set_value_cansleep(spi_cs, 1); /* cs high */ + + truly_spi_write(0xb9, init_item_v1, sizeof(init_item_v1)); + msleep(20); + truly_spi_write(0xcc, init_item_v2, sizeof(init_item_v2)); + truly_spi_write(0xb1, init_item_v3, sizeof(init_item_v3)); + truly_spi_write(0xb3, init_item_v4, sizeof(init_item_v4)); + truly_spi_write(0xb6, init_item_v5, sizeof(init_item_v5)); + truly_spi_write(0xb4, init_item_v6, sizeof(init_item_v6)); + truly_spi_write(0xc0, init_item_v7, sizeof(init_item_v7)); + truly_spi_write(0xe3, init_item_v8, sizeof(init_item_v8)); + truly_spi_write(0x3a, init_item_v9, sizeof(init_item_v9)); + truly_spi_write(0xe0, init_item_v10, sizeof(init_item_v10)); + truly_spi_write(0x36, init_item_v11, sizeof(init_item_v11)); + truly_spi_write(0x11, NULL, 0); + msleep(150); + truly_spi_write(0x29, NULL, 0); + msleep(25); + + truly_state.display_on = TRUE; + } +} + +static int lcdc_truly_panel_on(struct platform_device *pdev) +{ + /* Configure reset GPIO that drives DAC */ + if (lcdc_truly_pdata->panel_config_gpio) + lcdc_truly_pdata->panel_config_gpio(1); + gpio_set_value_cansleep(gpio_display_reset, 1); + truly_disp_powerup(); + truly_disp_reginit(); + truly_state.disp_initialized = TRUE; + return 0; +} + +static int lcdc_truly_panel_off(struct platform_device *pdev) +{ + if (truly_state.disp_powered_up && truly_state.display_on) { + /* Main panel power off (Pull down reset) */ + gpio_set_value_cansleep(gpio_display_reset, 0); + truly_state.display_on = FALSE; + truly_state.disp_initialized = FALSE; + } + return 0; +} + +static void lcdc_truly_set_backlight(struct msm_fb_data_type *mfd) +{ + int step = 0, i = 0; + unsigned long flags; + int bl_level = mfd->bl_level; + + /* real backlight level, 1 - max, 16 - min, 17 - off */ + bl_level = 17 - bl_level; + + if (bl_level > prev_bl) { + step = bl_level - prev_bl; + if (bl_level == 17) + step--; + } else if (bl_level < prev_bl) { + step = bl_level + 16 - prev_bl; + } else { + pr_info("%s: no change\n", __func__); + return; + } + + if (bl_level == 17) { + /* turn off backlight */ + gpio_set_value(gpio_backlight_en, 0); + } else { + local_irq_save(flags); + + if (prev_bl == 17) { + /* turn on backlight */ + gpio_set_value(gpio_backlight_en, 1); + udelay(30); + } + + /* adjust backlight level */ + for (i = 0; i < step; i++) { + gpio_set_value(gpio_backlight_en, 0); + udelay(1); + gpio_set_value(gpio_backlight_en, 1); + udelay(1); + } + + local_irq_restore(flags); + } + msleep(20); + prev_bl = bl_level; + + return; +} + +static int truly_probe(struct platform_device *pdev) +{ + + if (pdev->id == 0) { + lcdc_truly_pdata = pdev->dev.platform_data; + + if (!lcdc_truly_pdata) + pr_err("%s pdata is null\n", __func__); + + spi_pin_assign(); + return 0; + } + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = truly_probe, + .driver = { + .name = "lcdc_truly_hvga_ips3p2335_pt", + }, +}; + +static struct msm_fb_panel_data truly_panel_data = { + .on = lcdc_truly_panel_on, + .off = lcdc_truly_panel_off, + .set_backlight = lcdc_truly_set_backlight, +}; + +static struct platform_device this_device = { + .name = "lcdc_truly_hvga_ips3p2335_pt", + .id = 1, + .dev = { + .platform_data = &truly_panel_data, + } +}; + +static int __init lcdc_truly_panel_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + ret = msm_fb_detect_client("lcdc_truly_hvga_ips3p2335_pt"); + if (ret) + return 0; + + ret = platform_driver_register(&this_driver); + if (ret) { + pr_err("%s() driver registration failed", __func__); + return ret; + } + + pinfo = &truly_panel_data.panel_info; + pinfo->xres = 320; + pinfo->yres = 480; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LCDC_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + /* 10Mhz mdp_lcdc_pclk and mdp_lcdc_pad_pcl */ + pinfo->clk_rate = 10240000; + pinfo->bl_max = 16; + pinfo->bl_min = 1; + + pinfo->lcdc.h_back_porch = 16; /* hsw = 8 + hbp=16 */ + pinfo->lcdc.h_front_porch = 4; + pinfo->lcdc.h_pulse_width = 8; + pinfo->lcdc.v_back_porch = 7; /* vsw=1 + vbp = 7 */ + pinfo->lcdc.v_front_porch = 3; + pinfo->lcdc.v_pulse_width = 1; + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + + ret = platform_device_register(&this_device); + if (ret) { + pr_err("%s not able to register the device\n", __func__); + platform_driver_unregister(&this_driver); + } + return ret; +} + +device_initcall(lcdc_truly_panel_init); diff --git a/drivers/video/msm/lcdc_wxga.c b/drivers/video/msm/lcdc_wxga.c new file mode 100644 index 0000000000000000000000000000000000000000..dca57dee34d14b5dfe3ee4fccc875c0c3deeaacd --- /dev/null +++ b/drivers/video/msm/lcdc_wxga.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" + +static int __init lcdc_wxga_init(void) +{ + int ret; + struct msm_panel_info pinfo; + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + if (msm_fb_detect_client("lcdc_wxga")) + return 0; +#endif + + pinfo.xres = 1280; + pinfo.yres = 720; + MSM_FB_SINGLE_MODE_PANEL(&pinfo); + pinfo.type = LCDC_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.fb_num = 2; + pinfo.clk_rate = 74250000; + + pinfo.lcdc.h_back_porch = 124; + pinfo.lcdc.h_front_porch = 110; + pinfo.lcdc.h_pulse_width = 136; + pinfo.lcdc.v_back_porch = 19; + pinfo.lcdc.v_front_porch = 5; + pinfo.lcdc.v_pulse_width = 6; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + + ret = lcdc_device_register(&pinfo); + if (ret) + printk(KERN_ERR "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(lcdc_wxga_init); diff --git a/drivers/video/msm/logo.c b/drivers/video/msm/logo.c new file mode 100644 index 0000000000000000000000000000000000000000..57d754e4d81aa522935b541e1ccb2dd71a37af60 --- /dev/null +++ b/drivers/video/msm/logo.c @@ -0,0 +1,103 @@ +/* drivers/video/msm/logo.c + * + * Show Logo in RLE 565 format + * + * Copyright (C) 2008 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#define fb_width(fb) ((fb)->var.xres) +#define fb_height(fb) ((fb)->var.yres) +#define fb_size(fb) ((fb)->var.xres * (fb)->var.yres * 2) + +static void memset16(void *_ptr, unsigned short val, unsigned count) +{ + unsigned short *ptr = _ptr; + count >>= 1; + while (count--) + *ptr++ = val; +} + +/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ +int load_565rle_image(char *filename, bool bf_supported) +{ + struct fb_info *info; + int fd, count, err = 0; + unsigned max; + unsigned short *data, *bits, *ptr; + + info = registered_fb[0]; + if (!info) { + printk(KERN_WARNING "%s: Can not access framebuffer\n", + __func__); + return -ENODEV; + } + + fd = sys_open(filename, O_RDONLY, 0); + if (fd < 0) { + printk(KERN_WARNING "%s: Can not open %s\n", + __func__, filename); + return -ENOENT; + } + count = sys_lseek(fd, (off_t)0, 2); + if (count <= 0) { + err = -EIO; + goto err_logo_close_file; + } + sys_lseek(fd, (off_t)0, 0); + data = kmalloc(count, GFP_KERNEL); + if (!data) { + printk(KERN_WARNING "%s: Can not alloc data\n", __func__); + err = -ENOMEM; + goto err_logo_close_file; + } + if (sys_read(fd, (char *)data, count) != count) { + err = -EIO; + goto err_logo_free_data; + } + + max = fb_width(info) * fb_height(info); + ptr = data; + if (bf_supported && (info->node == 1 || info->node == 2)) { + err = -EPERM; + pr_err("%s:%d no info->creen_base on fb%d!\n", + __func__, __LINE__, info->node); + goto err_logo_free_data; + } + bits = (unsigned short *)(info->screen_base); + while (count > 3) { + unsigned n = ptr[0]; + if (n > max) + break; + memset16(bits, ptr[1], n << 1); + bits += n; + max -= n; + ptr += 2; + count -= 4; + } + +err_logo_free_data: + kfree(data); +err_logo_close_file: + sys_close(fd); + return err; +} +EXPORT_SYMBOL(load_565rle_image); diff --git a/drivers/video/msm/lvds.c b/drivers/video/msm/lvds.c new file mode 100644 index 0000000000000000000000000000000000000000..18426c55f0225e9ccc98c3b8d60a75e602f1739e --- /dev/null +++ b/drivers/video/msm/lvds.c @@ -0,0 +1,373 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" +#include "mdp4.h" +static int lvds_probe(struct platform_device *pdev); +static int lvds_remove(struct platform_device *pdev); + +static int lvds_off(struct platform_device *pdev); +static int lvds_on(struct platform_device *pdev); + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; + +static struct clk *lvds_clk; + +static struct platform_driver lvds_driver = { + .probe = lvds_probe, + .remove = lvds_remove, + .suspend = NULL, + .resume = NULL, + .shutdown = NULL, + .driver = { + .name = "lvds", + }, +}; + +static struct lcdc_platform_data *lvds_pdata; + +static void lvds_init(struct msm_fb_data_type *mfd) +{ + unsigned int lvds_intf = 0, lvds_phy_cfg0 = 0; + + MDP_OUTP(MDP_BASE + 0xc2034, 0x33); + usleep(1000); + + /* LVDS PHY PLL configuration */ + MDP_OUTP(MDP_BASE + 0xc3004, 0x62); + MDP_OUTP(MDP_BASE + 0xc3008, 0x30); + MDP_OUTP(MDP_BASE + 0xc300c, 0xc4); + MDP_OUTP(MDP_BASE + 0xc3014, 0x10); + MDP_OUTP(MDP_BASE + 0xc3018, 0x05); + MDP_OUTP(MDP_BASE + 0xc301c, 0x62); + MDP_OUTP(MDP_BASE + 0xc3020, 0x41); + MDP_OUTP(MDP_BASE + 0xc3024, 0x0d); + + MDP_OUTP(MDP_BASE + 0xc3000, 0x01); + /* Wait until LVDS PLL is locked and ready */ + while (!readl_relaxed(MDP_BASE + 0xc3080)) + cpu_relax(); + + writel_relaxed(0x00, mmss_cc_base + 0x0264); + writel_relaxed(0x00, mmss_cc_base + 0x0094); + + writel_relaxed(0x02, mmss_cc_base + 0x00E4); + + writel_relaxed((0x80 | readl_relaxed(mmss_cc_base + 0x00E4)), + mmss_cc_base + 0x00E4); + usleep(1000); + writel_relaxed((~0x80 & readl_relaxed(mmss_cc_base + 0x00E4)), + mmss_cc_base + 0x00E4); + + writel_relaxed(0x05, mmss_cc_base + 0x0094); + writel_relaxed(0x02, mmss_cc_base + 0x0264); + /* Wait until LVDS pixel clock output is enabled */ + mb(); + + if (mfd->panel_info.bpp == 24) { + if (lvds_pdata && + lvds_pdata->lvds_pixel_remap && + lvds_pdata->lvds_pixel_remap()) { + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2014, 0x05080001); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2018, 0x00020304); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc201c, 0x1011090a); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2020, 0x000b0c0d); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2024, 0x191a1213); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2028, 0x00141518); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc202c, 0x171b0607); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2030, 0x000e0f16); + } else { + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2014, 0x03040508); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2018, 0x00000102); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc201c, 0x0c0d1011); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2020, 0x00090a0b); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2024, 0x151a191a); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2028, 0x00121314); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc202c, 0x0f16171b); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2030, 0x0006070e); + } + if (mfd->panel_info.lvds.channel_mode == + LVDS_DUAL_CHANNEL_MODE) { + lvds_intf = 0x0001ff80; + lvds_phy_cfg0 = BIT(6) | BIT(7); + if (mfd->panel_info.lvds.channel_swap) + lvds_intf |= BIT(4); + } else { + lvds_intf = 0x00010f84; + lvds_phy_cfg0 = BIT(6); + } + } else if (mfd->panel_info.bpp == 18) { + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2014, 0x03040508); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2018, 0x00000102); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc201c, 0x0c0d1011); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2020, 0x00090a0b); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2024, 0x1518191a); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2028, 0x00121314); + + if (mfd->panel_info.lvds.channel_mode == + LVDS_DUAL_CHANNEL_MODE) { + lvds_intf = 0x00017788; + lvds_phy_cfg0 = BIT(6) | BIT(7); + if (mfd->panel_info.lvds.channel_swap) + lvds_intf |= BIT(4); + } else { + lvds_intf = 0x0001078c; + lvds_phy_cfg0 = BIT(6); + } + } else { + BUG(); + } + + /* MDP_LVDSPHY_CFG0 */ + MDP_OUTP(MDP_BASE + 0xc3100, lvds_phy_cfg0); + /* MDP_LCDC_LVDS_INTF_CTL */ + MDP_OUTP(MDP_BASE + 0xc2000, lvds_intf); + MDP_OUTP(MDP_BASE + 0xc3108, 0x30); + lvds_phy_cfg0 |= BIT(4); + + /* Wait until LVDS PHY registers are configured */ + mb(); + usleep(1); + /* MDP_LVDSPHY_CFG0, enable serialization */ + MDP_OUTP(MDP_BASE + 0xc3100, lvds_phy_cfg0); +} + +static int lvds_off(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + ret = panel_next_off(pdev); + + if (lvds_clk) + clk_disable_unprepare(lvds_clk); + + MDP_OUTP(MDP_BASE + 0xc3100, 0x0); + MDP_OUTP(MDP_BASE + 0xc3000, 0x0); + usleep(10); + + if (lvds_pdata && lvds_pdata->lcdc_power_save) + lvds_pdata->lcdc_power_save(0); + + if (lvds_pdata && lvds_pdata->lcdc_gpio_config) + ret = lvds_pdata->lcdc_gpio_config(0); + +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(0); +#endif + + return ret; +} + +static int lvds_on(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + unsigned long panel_pixclock_freq = 0; + mfd = platform_get_drvdata(pdev); + + if (lvds_pdata && lvds_pdata->lcdc_get_clk) + panel_pixclock_freq = lvds_pdata->lcdc_get_clk(); + + if (!panel_pixclock_freq) + panel_pixclock_freq = mfd->fbi->var.pixclock; +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(2); +#endif + mfd = platform_get_drvdata(pdev); + + if (lvds_clk) { + mfd->fbi->var.pixclock = clk_round_rate(lvds_clk, + mfd->fbi->var.pixclock); + ret = clk_set_rate(lvds_clk, mfd->fbi->var.pixclock); + if (ret) { + pr_err("%s: Can't set lvds clock to rate %u\n", + __func__, mfd->fbi->var.pixclock); + goto out; + } + clk_prepare_enable(lvds_clk); + } + + if (lvds_pdata && lvds_pdata->lcdc_power_save) + lvds_pdata->lcdc_power_save(1); + if (lvds_pdata && lvds_pdata->lcdc_gpio_config) + ret = lvds_pdata->lcdc_gpio_config(1); + + lvds_init(mfd); + ret = panel_next_on(pdev); + +out: + return ret; +} + +static int lvds_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct fb_info *fbi; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc; + + if (pdev->id == 0) { + lvds_pdata = pdev->dev.platform_data; + + lvds_clk = clk_get(&pdev->dev, "lvds_clk"); + if (IS_ERR_OR_NULL(lvds_clk)) { + pr_err("Couldnt find lvds_clk\n"); + lvds_clk = NULL; + } + return 0; + } + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + + /* + * link to the latest pdev + */ + mfd->pdev = mdp_dev; + mfd->dest = DISPLAY_LCDC; + + /* + * alloc panel device data + */ + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + pr_err("lvds_probe: platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + /* + * data chain + */ + pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data; + pdata->on = lvds_on; + pdata->off = lvds_off; + pdata->next = pdev; + + /* + * get/set panel specific fb info + */ + mfd->panel_info = pdata->panel_info; + + if (mfd->index == 0) + mfd->fb_imgType = MSMFB_DEFAULT_TYPE; + else + mfd->fb_imgType = MDP_RGB_565; + + fbi = mfd->fbi; + if (lvds_clk) { + fbi->var.pixclock = clk_round_rate(lvds_clk, + mfd->panel_info.clk_rate); + } + + fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch; + fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch; + fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch; + fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch; + fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width; + fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width; + + /* + * set driver data + */ + platform_set_drvdata(mdp_dev, mfd); + /* + * register in mdp driver + */ + rc = platform_device_add(mdp_dev); + if (rc) + goto lvds_probe_err; + + pdev_list[pdev_list_cnt++] = pdev; + + return 0; + +lvds_probe_err: + platform_device_put(mdp_dev); + return rc; +} + +static int lvds_remove(struct platform_device *pdev) +{ + return 0; +} + +static int lvds_register_driver(void) +{ + return platform_driver_register(&lvds_driver); +} + +static int __init lvds_driver_init(void) +{ + return lvds_register_driver(); +} + +module_init(lvds_driver_init); diff --git a/drivers/video/msm/lvds_chimei_wxga.c b/drivers/video/msm/lvds_chimei_wxga.c new file mode 100644 index 0000000000000000000000000000000000000000..6074cd82958b638c92514580edf9042b2849c959 --- /dev/null +++ b/drivers/video/msm/lvds_chimei_wxga.c @@ -0,0 +1,167 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm_fb.h" +#include +#include + +#define LVDS_CHIMEI_PWM_FREQ_HZ 300 +#define LVDS_CHIMEI_PWM_PERIOD_USEC (USEC_PER_SEC / LVDS_CHIMEI_PWM_FREQ_HZ) +#define LVDS_CHIMEI_PWM_LEVEL 255 +#define LVDS_CHIMEI_PWM_DUTY_LEVEL \ + (LVDS_CHIMEI_PWM_PERIOD_USEC / LVDS_CHIMEI_PWM_LEVEL) + + +static struct lvds_panel_platform_data *cm_pdata; +static struct platform_device *cm_fbpdev; +static struct pwm_device *bl_lpm; + +static int lvds_chimei_panel_on(struct platform_device *pdev) +{ + return 0; +} + +static int lvds_chimei_panel_off(struct platform_device *pdev) +{ + return 0; +} + +static void lvds_chimei_set_backlight(struct msm_fb_data_type *mfd) +{ + int ret; + + pr_debug("%s: back light level %d\n", __func__, mfd->bl_level); + + if (bl_lpm) { + ret = pwm_config(bl_lpm, LVDS_CHIMEI_PWM_DUTY_LEVEL * + mfd->bl_level, LVDS_CHIMEI_PWM_PERIOD_USEC); + if (ret) { + pr_err("pwm_config on lpm failed %d\n", ret); + return; + } + if (mfd->bl_level) { + ret = pwm_enable(bl_lpm); + if (ret) + pr_err("pwm enable/disable on lpm failed" + "for bl %d\n", mfd->bl_level); + } else { + pwm_disable(bl_lpm); + } + } +} + +static int lvds_chimei_probe(struct platform_device *pdev) +{ + int rc = 0; + + if (pdev->id == 0) { + cm_pdata = pdev->dev.platform_data; + if (cm_pdata == NULL) + pr_err("%s: no PWM gpio specified\n", __func__); + return 0; + } + + if (cm_pdata != NULL) + bl_lpm = pwm_request(cm_pdata->gpio[0], + "backlight"); + + if (bl_lpm == NULL || IS_ERR(bl_lpm)) { + pr_err("%s pwm_request() failed\n", __func__); + bl_lpm = NULL; + } + pr_debug("bl_lpm = %p lpm = %d\n", bl_lpm, + cm_pdata->gpio[0]); + + cm_fbpdev = msm_fb_add_device(pdev); + if (!cm_fbpdev) { + dev_err(&pdev->dev, "failed to add msm_fb device\n"); + rc = -ENODEV; + goto probe_exit; + } + +probe_exit: + return rc; +} + +static struct platform_driver this_driver = { + .probe = lvds_chimei_probe, + .driver = { + .name = "lvds_chimei_wxga", + }, +}; + +static struct msm_fb_panel_data lvds_chimei_panel_data = { + .on = lvds_chimei_panel_on, + .off = lvds_chimei_panel_off, + .set_backlight = lvds_chimei_set_backlight, +}; + +static struct platform_device this_device = { + .name = "lvds_chimei_wxga", + .id = 1, + .dev = { + .platform_data = &lvds_chimei_panel_data, + } +}; + +static int __init lvds_chimei_wxga_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + if (msm_fb_detect_client("lvds_chimei_wxga")) + return 0; + + ret = platform_driver_register(&this_driver); + if (ret) + return ret; + + pinfo = &lvds_chimei_panel_data.panel_info; + pinfo->xres = 1366; + pinfo->yres = 768; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = LVDS_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + pinfo->fb_num = 2; + pinfo->clk_rate = 75000000; + pinfo->bl_max = 255; + pinfo->bl_min = 1; + + /* + * this panel is operated by de, + * vsycn and hsync are ignored + */ + pinfo->lcdc.h_back_porch = 0; + pinfo->lcdc.h_front_porch = 194; + pinfo->lcdc.h_pulse_width = 40; + pinfo->lcdc.v_back_porch = 0; + pinfo->lcdc.v_front_porch = 38; + pinfo->lcdc.v_pulse_width = 20; + pinfo->lcdc.underflow_clr = 0xff; + pinfo->lcdc.hsync_skew = 0; + pinfo->lvds.channel_mode = LVDS_SINGLE_CHANNEL_MODE; + + /* Set border color, padding only for reducing active display region */ + pinfo->lcdc.border_clr = 0x0; + pinfo->lcdc.xres_pad = 0; + pinfo->lcdc.yres_pad = 0; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + + return ret; +} + +module_init(lvds_chimei_wxga_init); diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c new file mode 100644 index 0000000000000000000000000000000000000000..6081995dc1a619d8c3f8d9aa886e6a973e4ffbb4 --- /dev/null +++ b/drivers/video/msm/mddi.c @@ -0,0 +1,590 @@ +/* + * MSM MDDI Transport + * + * Copyright (C) 2007 Google Incorporated + * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "msm_fb.h" +#include "mddihosti.h" +#include "mddihost.h" +#include +#include + +static int mddi_probe(struct platform_device *pdev); +static int mddi_remove(struct platform_device *pdev); + +static int mddi_off(struct platform_device *pdev); +static int mddi_on(struct platform_device *pdev); + +#ifdef CONFIG_PM +static int mddi_suspend(struct platform_device *pdev, pm_message_t state); +static int mddi_resume(struct platform_device *pdev); +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mddi_early_suspend(struct early_suspend *h); +static void mddi_early_resume(struct early_suspend *h); +#endif + +static void pmdh_clk_disable(void); +static void pmdh_clk_enable(void); +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; +static struct clk *mddi_clk; +static struct clk *mddi_pclk; +static struct mddi_platform_data *mddi_pdata; + +DEFINE_MUTEX(mddi_timer_lock); + +static int mddi_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int mddi_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static int mddi_runtime_idle(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: idling...\n"); + return 0; +} + +static struct dev_pm_ops mddi_dev_pm_ops = { + .runtime_suspend = mddi_runtime_suspend, + .runtime_resume = mddi_runtime_resume, + .runtime_idle = mddi_runtime_idle, +}; + +static int pmdh_clk_status; +int irq_enabled; +unsigned char mddi_timer_shutdown_flag; + +static struct platform_driver mddi_driver = { + .probe = mddi_probe, + .remove = mddi_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND +#ifdef CONFIG_PM + .suspend = mddi_suspend, + .resume = mddi_resume, +#endif +#endif + .shutdown = NULL, + .driver = { + .name = "mddi", + .pm = &mddi_dev_pm_ops, + }, +}; + +extern int int_mddi_pri_flag; +DEFINE_MUTEX(pmdh_clk_lock); + +int pmdh_clk_func(int value) +{ + int ret = 0; + + switch (value) { + case 0: + pmdh_clk_disable(); + break; + case 1: + pmdh_clk_enable(); + break; + case 2: + default: + mutex_lock(&pmdh_clk_lock); + ret = pmdh_clk_status; + mutex_unlock(&pmdh_clk_lock); + break; + } + return ret; +} + +static void pmdh_clk_disable() +{ + mutex_lock(&pmdh_clk_lock); + if (pmdh_clk_status == 0) { + mutex_unlock(&pmdh_clk_lock); + return; + } + + if (mddi_host_timer.function) { + mutex_lock(&mddi_timer_lock); + mddi_timer_shutdown_flag = 1; + mutex_unlock(&mddi_timer_lock); + del_timer_sync(&mddi_host_timer); + mutex_lock(&mddi_timer_lock); + mddi_timer_shutdown_flag = 0; + mutex_unlock(&mddi_timer_lock); + } + if (int_mddi_pri_flag && irq_enabled) { + disable_irq(INT_MDDI_PRI); + irq_enabled = 0; + } + + if (mddi_clk) { + clk_disable_unprepare(mddi_clk); + pmdh_clk_status = 0; + } + if (mddi_pclk) + clk_disable_unprepare(mddi_pclk); + mutex_unlock(&pmdh_clk_lock); +} + +static void pmdh_clk_enable() +{ + mutex_lock(&pmdh_clk_lock); + if (pmdh_clk_status == 1) { + mutex_unlock(&pmdh_clk_lock); + return; + } + + if (mddi_clk) { + clk_prepare_enable(mddi_clk); + pmdh_clk_status = 1; + } + if (mddi_pclk) + clk_prepare_enable(mddi_pclk); + + if (int_mddi_pri_flag && !irq_enabled) { + enable_irq(INT_MDDI_PRI); + irq_enabled = 1; + } + + if (mddi_host_timer.function) + mddi_host_timer_service(0); + + mutex_unlock(&pmdh_clk_lock); +} + +static int mddi_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + boolean dma_pending, dma_update_flag; + int ret, i; + + mfd = platform_get_drvdata(pdev); + + for (i = 0; i < 6; i++) { + dma_update_flag = mfd->dma_update_flag; + dma_pending = mfd->dma->busy; + if (dma_update_flag && !dma_pending) + break; + msleep(5); + } + + pmdh_clk_enable(); + ret = panel_next_off(pdev); + pmdh_clk_disable(); + + if (mddi_pdata && mddi_pdata->mddi_power_save) + mddi_pdata->mddi_power_save(0); +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(0); +#else + if (mfd->ebi1_clk) + clk_disable_unprepare(mfd->ebi1_clk); +#endif + pm_runtime_put(&pdev->dev); + return ret; +} + +static int mddi_on(struct platform_device *pdev) +{ + int ret = 0; + u32 clk_rate; + struct msm_fb_data_type *mfd; +#ifdef ENABLE_FWD_LINK_SKEW_CALIBRATION + mddi_host_type host_idx = MDDI_HOST_PRIM; + u32 stat_reg; +#endif + + mfd = platform_get_drvdata(pdev); + pm_runtime_get(&pdev->dev); + if (mddi_pdata && mddi_pdata->mddi_power_save) + mddi_pdata->mddi_power_save(1); + + pmdh_clk_enable(); +#ifdef ENABLE_FWD_LINK_SKEW_CALIBRATION + if (mddi_client_type < 2) { + /* For skew calibration, clock should be less than 50MHz */ + clk_rate = clk_round_rate(mddi_clk, 49000000); + if (!clk_set_rate(mddi_clk, clk_rate)) { + stat_reg = mddi_host_reg_in(STAT); + printk(KERN_DEBUG "\n stat_reg = 0x%x", stat_reg); + mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE); + if (stat_reg & (0x1 << 4)) + mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); + + mddi_host_reg_out(CMD, MDDI_CMD_SEND_RTD); + mddi_send_fw_link_skew_cal(host_idx); + mddi_host_reg_out(CMD, MDDI_CMD_SEND_RTD); + mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1); + } else { + printk(KERN_ERR "%s: clk_set_rate failed\n", + __func__); + } + } +#endif + + clk_rate = mfd->fbi->var.pixclock; + clk_rate = min(clk_rate, mfd->panel_info.clk_max); + + if (mddi_pdata && + mddi_pdata->mddi_sel_clk && + mddi_pdata->mddi_sel_clk(&clk_rate)) + printk(KERN_ERR + "%s: can't select mddi io clk targate rate = %d\n", + __func__, clk_rate); + + clk_rate = clk_round_rate(mddi_clk, clk_rate); + if (clk_set_rate(mddi_clk, clk_rate) < 0) + printk(KERN_ERR "%s: clk_set_rate failed\n", + __func__); + +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(2); +#else + if (mfd->ebi1_clk) + clk_prepare_enable(mfd->ebi1_clk); +#endif + ret = panel_next_on(pdev); + + return ret; +} + +static int mddi_resource_initialized; + +static int mddi_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc; + resource_size_t size ; + u32 clk_rate; + unsigned long rate; + int ret; + struct clk *ebi1_clk = NULL; + + if ((pdev->id == 0) && (pdev->num_resources >= 0)) { + mddi_pdata = pdev->dev.platform_data; + pmdh_clk_status = 0; + + mddi_clk = clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(mddi_clk)) { + pr_err("can't find mddi_clk\n"); + return PTR_ERR(mddi_clk); + } + rate = clk_round_rate(mddi_clk, 49000000); + ret = clk_set_rate(mddi_clk, rate); + if (ret) + pr_err("Can't set mddi_clk min rate to %lu\n", + rate); + + pr_info("mddi_clk init rate is %lu\n", + clk_get_rate(mddi_clk)); + mddi_pclk = clk_get(&pdev->dev, "iface_clk"); + if (IS_ERR(mddi_pclk)) + mddi_pclk = NULL; + pmdh_clk_enable(); + +#ifndef CONFIG_MSM_BUS_SCALING + ebi1_clk = clk_get(&pdev->dev, "mem_clk"); + if (IS_ERR(ebi1_clk)) + return PTR_ERR(ebi1_clk); + clk_set_rate(ebi1_clk, 65000000); +#endif + + size = resource_size(&pdev->resource[0]); + msm_pmdh_base = ioremap(pdev->resource[0].start, size); + + MSM_FB_INFO("primary mddi base phy_addr = 0x%x virt = 0x%x\n", + pdev->resource[0].start, (int) msm_pmdh_base); + + if (unlikely(!msm_pmdh_base)) + return -ENOMEM; + + if (mddi_pdata && mddi_pdata->mddi_power_save) + mddi_pdata->mddi_power_save(1); + + mddi_resource_initialized = 1; + return 0; + } + + if (!mddi_resource_initialized) + return -EPERM; + + mfd = platform_get_drvdata(pdev); + mfd->ebi1_clk = ebi1_clk; + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + + /* + * link to the latest pdev + */ + mfd->pdev = mdp_dev; + mfd->dest = DISPLAY_LCD; + + /* + * alloc panel device data + */ + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + printk(KERN_ERR "mddi_probe: platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + /* + * data chain + */ + pdata = mdp_dev->dev.platform_data; + pdata->on = mddi_on; + pdata->off = mddi_off; + pdata->next = pdev; + pdata->clk_func = pmdh_clk_func; + /* + * get/set panel specific fb info + */ + mfd->panel_info = pdata->panel_info; + + if (mfd->index == 0) + mfd->fb_imgType = MSMFB_DEFAULT_TYPE; + else + mfd->fb_imgType = MDP_RGB_565; + + clk_rate = mfd->panel_info.clk_max; + if (mddi_pdata && + mddi_pdata->mddi_sel_clk && + mddi_pdata->mddi_sel_clk(&clk_rate)) + printk(KERN_ERR + "%s: can't select mddi io clk targate rate = %d\n", + __func__, clk_rate); + + if (clk_set_max_rate(mddi_clk, clk_rate) < 0) + printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__); + mfd->panel_info.clk_rate = mfd->panel_info.clk_min; + + if (!mddi_client_type) + mddi_client_type = mfd->panel_info.lcd.rev; + else if (!mfd->panel_info.lcd.rev) + printk(KERN_ERR + "%s: mddi client is trying to revert back to type 1 !!!\n", + __func__); + + /* + * set driver data + */ + platform_set_drvdata(mdp_dev, mfd); + rc = pm_runtime_set_active(&pdev->dev); + if (rc < 0) + printk(KERN_ERR "pm_runtime: fail to set active\n"); + + rc = 0; + pm_runtime_enable(&pdev->dev); + /* + * register in mdp driver + */ + rc = platform_device_add(mdp_dev); + if (rc) + goto mddi_probe_err; + + pdev_list[pdev_list_cnt++] = pdev; + +#ifdef CONFIG_HAS_EARLYSUSPEND + mfd->mddi_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + mfd->mddi_early_suspend.suspend = mddi_early_suspend; + mfd->mddi_early_suspend.resume = mddi_early_resume; + register_early_suspend(&mfd->mddi_early_suspend); +#endif + + return 0; + +mddi_probe_err: + platform_device_put(mdp_dev); + return rc; +} + +static int mddi_pad_ctrl; +static int mddi_power_locked; + +int mddi_client_power(unsigned int client_id) +{ + int ret = 0; + if (mddi_pdata && mddi_pdata->mddi_client_power) + ret = mddi_pdata->mddi_client_power(client_id); + return ret; +} + +void mddi_disable(int lock) +{ + mddi_host_type host_idx = MDDI_HOST_PRIM; + + if (mddi_power_locked) + return; + + if (lock) + mddi_power_locked = 1; + pmdh_clk_enable(); + + mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL); + mddi_host_reg_out(PAD_CTL, 0x0); + + pmdh_clk_disable(); + + if (mddi_pdata && mddi_pdata->mddi_power_save) + mddi_pdata->mddi_power_save(0); +} + +#ifdef CONFIG_PM +static int mddi_is_in_suspend; + +static int mddi_suspend(struct platform_device *pdev, pm_message_t state) +{ + mddi_host_type host_idx = MDDI_HOST_PRIM; + if (mddi_is_in_suspend) + return 0; + + mddi_is_in_suspend = 1; + + if (mddi_power_locked) + return 0; + + pmdh_clk_enable(); + + mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL); + mddi_host_reg_out(PAD_CTL, 0x0); + + pmdh_clk_disable(); + + return 0; +} + +static int mddi_resume(struct platform_device *pdev) +{ + mddi_host_type host_idx = MDDI_HOST_PRIM; + + if (!mddi_is_in_suspend) + return 0; + + mddi_is_in_suspend = 0; + + if (mddi_power_locked) + return 0; + + pmdh_clk_enable(); + + mddi_host_reg_out(PAD_CTL, mddi_pad_ctrl); + + + return 0; +} +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mddi_early_suspend(struct early_suspend *h) +{ + pm_message_t state; + struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, + mddi_early_suspend); + + state.event = PM_EVENT_SUSPEND; + mddi_suspend(mfd->pdev, state); +} + +static void mddi_early_resume(struct early_suspend *h) +{ + struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, + mddi_early_suspend); + mddi_resume(mfd->pdev); +} +#endif + +static int mddi_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + if (mddi_host_timer.function) { + mutex_lock(&mddi_timer_lock); + mddi_timer_shutdown_flag = 1; + mutex_unlock(&mddi_timer_lock); + del_timer_sync(&mddi_host_timer); + mutex_lock(&mddi_timer_lock); + mddi_timer_shutdown_flag = 0; + mutex_unlock(&mddi_timer_lock); + } + + iounmap(msm_pmdh_base); + + return 0; +} + +static int mddi_register_driver(void) +{ + return platform_driver_register(&mddi_driver); +} + +static int __init mddi_driver_init(void) +{ + int ret; + + ret = mddi_register_driver(); + if (ret) { + pmdh_clk_disable(); + clk_put(mddi_clk); + if (mddi_pclk) + clk_put(mddi_pclk); + printk(KERN_ERR "mddi_register_driver() failed!\n"); + return ret; + } + + mddi_init(); + + return ret; +} + +module_init(mddi_driver_init); diff --git a/drivers/video/msm/mddi_client_dummy.c b/drivers/video/msm/mddi_client_dummy.c new file mode 100644 index 0000000000000000000000000000000000000000..ebbae87885b69f1ff5e09ba46363970e82fc25f2 --- /dev/null +++ b/drivers/video/msm/mddi_client_dummy.c @@ -0,0 +1,97 @@ +/* drivers/video/msm_fb/mddi_client_dummy.c + * + * Support for "dummy" mddi client devices which require no + * special initialization code. + * + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include + +struct panel_info { + struct platform_device pdev; + struct msm_panel_data panel_data; +}; + +static int mddi_dummy_suspend(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_resume(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_blank(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_unblank(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_probe(struct platform_device *pdev) +{ + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct panel_info *panel = + kzalloc(sizeof(struct panel_info), GFP_KERNEL); + int ret; + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + panel->panel_data.suspend = mddi_dummy_suspend; + panel->panel_data.resume = mddi_dummy_resume; + panel->panel_data.blank = mddi_dummy_blank; + panel->panel_data.unblank = mddi_dummy_unblank; + panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + platform_device_add_resources(&panel->pdev, + client_data->fb_resource, 1); + panel->panel_data.fb_data = client_data->private_client_data; + panel->pdev.dev.platform_data = &panel->panel_data; + ret = platform_device_register(&panel->pdev); + if (ret) { + kfree(panel); + return ret; + } + return 0; +} + +static int mddi_dummy_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_dummy = { + .probe = mddi_dummy_probe, + .remove = mddi_dummy_remove, + .driver = { .name = "mddi_c_dummy" }, +}; + +static int __init mddi_client_dummy_init(void) +{ + platform_driver_register(&mddi_client_dummy); + return 0; +} + +module_init(mddi_client_dummy_init); + diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c new file mode 100644 index 0000000000000000000000000000000000000000..eb8c701fed68418516c59c079315f61c0bb85969 --- /dev/null +++ b/drivers/video/msm/mddi_client_nt35399.c @@ -0,0 +1,251 @@ +/* drivers/video/msm_fb/mddi_client_nt35399.c + * + * Support for Novatek NT35399 MDDI client of Sapphire + * + * Copyright (C) 2008 HTC Incorporated + * Author: Solomon Chiu (solomon_chiu@htc.com) + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +static DECLARE_WAIT_QUEUE_HEAD(nt35399_vsync_wait); + +struct panel_info { + struct msm_mddi_client_data *client_data; + struct platform_device pdev; + struct msm_panel_data panel_data; + struct msmfb_callback *fb_callback; + struct work_struct panel_work; + struct workqueue_struct *fb_wq; + int nt35399_got_int; +}; + +static void +nt35399_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->fb_callback = callback; + if (panel->nt35399_got_int) { + panel->nt35399_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } +} + +static void nt35399_wait_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + if (panel->nt35399_got_int) { + panel->nt35399_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + + if (wait_event_timeout(nt35399_vsync_wait, panel->nt35399_got_int, + HZ/2) == 0) + printk(KERN_ERR "timeout waiting for VSYNC\n"); + + panel->nt35399_got_int = 0; + /* interrupt clears when screen dma starts */ +} + +static int nt35399_suspend(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + ret = bridge_data->uninit(bridge_data, client_data); + if (ret) { + printk(KERN_INFO "mddi nt35399 client: non zero return from " + "uninit\n"); + return ret; + } + client_data->suspend(client_data); + return 0; +} + +static int nt35399_resume(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + client_data->resume(client_data); + ret = bridge_data->init(bridge_data, client_data); + if (ret) + return ret; + return 0; +} + +static int nt35399_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->blank(bridge_data, client_data); +} + +static int nt35399_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->unblank(bridge_data, client_data); +} + +irqreturn_t nt35399_vsync_interrupt(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->nt35399_got_int = 1; + + if (panel->fb_callback) { + panel->fb_callback->func(panel->fb_callback); + panel->fb_callback = NULL; + } + + wake_up(&nt35399_vsync_wait); + + return IRQ_HANDLED; +} + +static int setup_vsync(struct panel_info *panel, int init) +{ + int ret; + int gpio = 97; + unsigned int irq; + + if (!init) { + ret = 0; + goto uninit; + } + ret = gpio_request_one(gpio, GPIOF_IN, "vsync"); + if (ret) + goto err_request_gpio_failed; + + ret = irq = gpio_to_irq(gpio); + if (ret < 0) + goto err_get_irq_num_failed; + + ret = request_irq(irq, nt35399_vsync_interrupt, IRQF_TRIGGER_RISING, + "vsync", panel); + if (ret) + goto err_request_irq_failed; + + printk(KERN_INFO "vsync on gpio %d now %d\n", + gpio, gpio_get_value(gpio)); + return 0; + +uninit: + free_irq(gpio_to_irq(gpio), panel->client_data); +err_request_irq_failed: +err_get_irq_num_failed: + gpio_free(gpio); +err_request_gpio_failed: + return ret; +} + +static int mddi_nt35399_probe(struct platform_device *pdev) +{ + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + int ret; + + struct panel_info *panel = kzalloc(sizeof(struct panel_info), + GFP_KERNEL); + + printk(KERN_DEBUG "%s: enter.\n", __func__); + + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + + ret = setup_vsync(panel, 1); + if (ret) { + dev_err(&pdev->dev, "mddi_nt35399_setup_vsync failed\n"); + return ret; + } + + panel->client_data = client_data; + panel->panel_data.suspend = nt35399_suspend; + panel->panel_data.resume = nt35399_resume; + panel->panel_data.wait_vsync = nt35399_wait_vsync; + panel->panel_data.request_vsync = nt35399_request_vsync; + panel->panel_data.blank = nt35399_blank; + panel->panel_data.unblank = nt35399_unblank; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->panel_data.caps = 0; + + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + panel->pdev.resource = client_data->fb_resource; + panel->pdev.num_resources = 1; + panel->pdev.dev.platform_data = &panel->panel_data; + + if (bridge_data->init) + bridge_data->init(bridge_data, client_data); + + platform_device_register(&panel->pdev); + + return 0; +} + +static int mddi_nt35399_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + + setup_vsync(panel, 0); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_0bda_8a47 = { + .probe = mddi_nt35399_probe, + .remove = mddi_nt35399_remove, + .driver = { .name = "mddi_c_0bda_8a47" }, +}; + +static int __init mddi_client_nt35399_init(void) +{ + return platform_driver_register(&mddi_client_0bda_8a47); +} + +module_init(mddi_client_nt35399_init); + diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c new file mode 100644 index 0000000000000000000000000000000000000000..8868781090984dc4c67bc310c744234870995a52 --- /dev/null +++ b/drivers/video/msm/mddi_client_toshiba.c @@ -0,0 +1,256 @@ +/* drivers/video/msm_fb/mddi_client_toshiba.c + * + * Support for Toshiba TC358720XBG mddi client devices which require no + * special initialization code. + * + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + + +#define LCD_CONTROL_BLOCK_BASE 0x110000 +#define CMN (LCD_CONTROL_BLOCK_BASE|0x10) +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define VPOS (LCD_CONTROL_BLOCK_BASE|0xC0) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) + +#define BASE5 0x150000 +#define BASE6 0x160000 +#define BASE7 0x170000 + +#define GPIOIEV (BASE5 + 0x10) +#define GPIOIE (BASE5 + 0x14) +#define GPIORIS (BASE5 + 0x18) +#define GPIOMIS (BASE5 + 0x1C) +#define GPIOIC (BASE5 + 0x20) + +#define INTMASK (BASE6 + 0x0C) +#define INTMASK_VWAKEOUT (1U << 0) +#define INTMASK_VWAKEOUT_ACTIVE_LOW (1U << 8) +#define GPIOSEL (BASE7 + 0x00) +#define GPIOSEL_VWAKEINT (1U << 0) + +static DECLARE_WAIT_QUEUE_HEAD(toshiba_vsync_wait); + +struct panel_info { + struct msm_mddi_client_data *client_data; + struct platform_device pdev; + struct msm_panel_data panel_data; + struct msmfb_callback *toshiba_callback; + int toshiba_got_int; + int irq; +}; + + +static void toshiba_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->toshiba_callback = callback; + if (panel->toshiba_got_int) { + panel->toshiba_got_int = 0; + client_data->activate_link(client_data); + } +} + +static void toshiba_clear_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + client_data->activate_link(client_data); +} + +static void toshiba_wait_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + if (panel->toshiba_got_int) { + panel->toshiba_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + if (wait_event_timeout(toshiba_vsync_wait, panel->toshiba_got_int, + HZ/2) == 0) + printk(KERN_ERR "timeout waiting for VSYNC\n"); + panel->toshiba_got_int = 0; + /* interrupt clears when screen dma starts */ +} + +static int toshiba_suspend(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + ret = bridge_data->uninit(bridge_data, client_data); + if (ret) { + printk(KERN_INFO "mddi toshiba client: non zero return from " + "uninit\n"); + return ret; + } + client_data->suspend(client_data); + return 0; +} + +static int toshiba_resume(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + client_data->resume(client_data); + ret = bridge_data->init(bridge_data, client_data); + if (ret) + return ret; + return 0; +} + +static int toshiba_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->blank(bridge_data, client_data); +} + +static int toshiba_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->unblank(bridge_data, client_data); +} + +irqreturn_t toshiba_vsync_interrupt(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->toshiba_got_int = 1; + if (panel->toshiba_callback) { + panel->toshiba_callback->func(panel->toshiba_callback); + panel->toshiba_callback = 0; + } + wake_up(&toshiba_vsync_wait); + return IRQ_HANDLED; +} + +static int mddi_toshiba_probe(struct platform_device *pdev) +{ + int ret; + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + struct panel_info *panel = + kzalloc(sizeof(struct panel_info), GFP_KERNEL); + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + + /* mddi_remote_write(mddi, 0, WAKEUP); */ + client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL); + client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK); + + ret = platform_get_irq_byname(pdev, "vsync"); + if (ret < 0) + goto err_plat_get_irq; + + panel->irq = ret; + ret = request_irq(panel->irq, toshiba_vsync_interrupt, + IRQF_TRIGGER_RISING, "vsync", panel); + if (ret) { + dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n"); + goto err_req_irq; + } + + panel->client_data = client_data; + panel->panel_data.suspend = toshiba_suspend; + panel->panel_data.resume = toshiba_resume; + panel->panel_data.wait_vsync = toshiba_wait_vsync; + panel->panel_data.request_vsync = toshiba_request_vsync; + panel->panel_data.clear_vsync = toshiba_clear_vsync; + panel->panel_data.blank = toshiba_blank; + panel->panel_data.unblank = toshiba_unblank; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; + + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + panel->pdev.resource = client_data->fb_resource; + panel->pdev.num_resources = 1; + panel->pdev.dev.platform_data = &panel->panel_data; + bridge_data->init(bridge_data, client_data); + platform_device_register(&panel->pdev); + + return 0; + +err_req_irq: +err_plat_get_irq: + kfree(panel); + return ret; +} + +static int mddi_toshiba_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + free_irq(panel->irq, panel); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_d263_0000 = { + .probe = mddi_toshiba_probe, + .remove = mddi_toshiba_remove, + .driver = { .name = "mddi_c_d263_0000" }, +}; + +static int __init mddi_client_toshiba_init(void) +{ + platform_driver_register(&mddi_client_d263_0000); + return 0; +} + +module_init(mddi_client_toshiba_init); + diff --git a/drivers/video/msm/mddi_ext.c b/drivers/video/msm/mddi_ext.c new file mode 100644 index 0000000000000000000000000000000000000000..83831b0620eda113908049f04bf28775868ee8de --- /dev/null +++ b/drivers/video/msm/mddi_ext.c @@ -0,0 +1,354 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" +#include "mddihosti.h" + +static int mddi_ext_probe(struct platform_device *pdev); +static int mddi_ext_remove(struct platform_device *pdev); + +static int mddi_ext_off(struct platform_device *pdev); +static int mddi_ext_on(struct platform_device *pdev); + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; + +static int mddi_ext_suspend(struct platform_device *pdev, pm_message_t state); +static int mddi_ext_resume(struct platform_device *pdev); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mddi_ext_early_suspend(struct early_suspend *h); +static void mddi_ext_early_resume(struct early_suspend *h); +#endif + +static int mddi_ext_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int mddi_ext_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static int mddi_ext_runtime_idle(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: idling...\n"); + return 0; +} +static struct dev_pm_ops mddi_ext_dev_pm_ops = { + .runtime_suspend = mddi_ext_runtime_suspend, + .runtime_resume = mddi_ext_runtime_resume, + .runtime_idle = mddi_ext_runtime_idle, +}; + +static struct platform_driver mddi_ext_driver = { + .probe = mddi_ext_probe, + .remove = mddi_ext_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND +#ifdef CONFIG_PM + .suspend = mddi_ext_suspend, + .resume = mddi_ext_resume, +#endif +#endif + .resume_early = NULL, + .resume = NULL, + .shutdown = NULL, + .driver = { + .name = "mddi_ext", + .pm = &mddi_ext_dev_pm_ops, + }, +}; + +static struct clk *mddi_ext_clk; +static struct clk *mddi_ext_pclk; +static struct mddi_platform_data *mddi_ext_pdata; + +extern int int_mddi_ext_flag; + +static int mddi_ext_off(struct platform_device *pdev) +{ + int ret = 0; + + ret = panel_next_off(pdev); + mddi_host_stop_ext_display(); + pm_runtime_put(&pdev->dev); + return ret; +} + +static int mddi_ext_on(struct platform_device *pdev) +{ + int ret = 0; + u32 clk_rate; + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + pm_runtime_get(&pdev->dev); + clk_rate = mfd->fbi->var.pixclock; + clk_rate = min(clk_rate, mfd->panel_info.clk_max); + + if (mddi_ext_pdata && + mddi_ext_pdata->mddi_sel_clk && + mddi_ext_pdata->mddi_sel_clk(&clk_rate)) + printk(KERN_ERR + "%s: can't select mddi io clk targate rate = %d\n", + __func__, clk_rate); + + clk_rate = clk_round_rate(mddi_ext_clk, clk_rate); + if (clk_set_rate(mddi_ext_clk, clk_rate) < 0) + printk(KERN_ERR "%s: clk_set_rate failed\n", + __func__); + + mddi_host_start_ext_display(); + ret = panel_next_on(pdev); + + return ret; +} + +static int mddi_ext_resource_initialized; + +static int mddi_ext_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc; + resource_size_t size ; + u32 clk_rate; + + if ((pdev->id == 0) && (pdev->num_resources >= 0)) { + mddi_ext_pdata = pdev->dev.platform_data; + mddi_ext_clk = clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(mddi_ext_clk)) { + pr_err("can't find emdh_clk\n"); + return PTR_ERR(mddi_ext_clk); + } + clk_prepare_enable(mddi_ext_clk); + + mddi_ext_pclk = clk_get(&pdev->dev, "iface_clk"); + if (IS_ERR(mddi_ext_pclk)) + mddi_ext_pclk = NULL; + else + clk_prepare_enable(mddi_ext_pclk); + + size = resource_size(&pdev->resource[0]); + msm_emdh_base = ioremap(pdev->resource[0].start, size); + + MSM_FB_INFO("external mddi base address = 0x%x\n", + pdev->resource[0].start); + + if (unlikely(!msm_emdh_base)) + return -ENOMEM; + + mddi_ext_resource_initialized = 1; + return 0; + } + + if (!mddi_ext_resource_initialized) + return -EPERM; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + + /* + * link to the latest pdev + */ + mfd->pdev = mdp_dev; + mfd->dest = DISPLAY_EXT_MDDI; + + /* + * alloc panel device data + */ + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + printk(KERN_ERR "mddi_ext_probe: platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + /* + * data chain + */ + pdata = mdp_dev->dev.platform_data; + pdata->on = mddi_ext_on; + pdata->off = mddi_ext_off; + pdata->next = pdev; + + /* + * get/set panel specific fb info + */ + mfd->panel_info = pdata->panel_info; + mfd->fb_imgType = MDP_RGB_565; + + clk_rate = mfd->panel_info.clk_max; + if (mddi_ext_pdata && + mddi_ext_pdata->mddi_sel_clk && + mddi_ext_pdata->mddi_sel_clk(&clk_rate)) + printk(KERN_ERR + "%s: can't select mddi io clk targate rate = %d\n", + __func__, clk_rate); + + if (clk_set_max_rate(mddi_ext_clk, clk_rate) < 0) + printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__); + mfd->panel_info.clk_rate = mfd->panel_info.clk_min; + + /* + * set driver data + */ + platform_set_drvdata(mdp_dev, mfd); + rc = pm_runtime_set_active(&pdev->dev); + if (rc < 0) + printk(KERN_ERR "pm_runtime: fail to set active\n"); + + rc = 0; + pm_runtime_enable(&pdev->dev); + /* + * register in mdp driver + */ + rc = platform_device_add(mdp_dev); + if (rc) + goto mddi_ext_probe_err; + + pdev_list[pdev_list_cnt++] = pdev; + +#ifdef CONFIG_HAS_EARLYSUSPEND + mfd->mddi_ext_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + mfd->mddi_ext_early_suspend.suspend = mddi_ext_early_suspend; + mfd->mddi_ext_early_suspend.resume = mddi_ext_early_resume; + register_early_suspend(&mfd->mddi_ext_early_suspend); +#endif + + return 0; + +mddi_ext_probe_err: + platform_device_put(mdp_dev); + return rc; +} + +static int mddi_ext_is_in_suspend; + +static int mddi_ext_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (mddi_ext_is_in_suspend) + return 0; + + mddi_ext_is_in_suspend = 1; + + clk_disable_unprepare(mddi_ext_clk); + if (mddi_ext_pclk) + clk_disable_unprepare(mddi_ext_pclk); + + disable_irq(INT_MDDI_EXT); + + return 0; +} + +static int mddi_ext_resume(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mddi_ext_is_in_suspend) + return 0; + + mddi_ext_is_in_suspend = 0; + enable_irq(INT_MDDI_EXT); + + clk_prepare_enable(mddi_ext_clk); + if (mddi_ext_pclk) + clk_prepare_enable(mddi_ext_pclk); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mddi_ext_early_suspend(struct early_suspend *h) +{ + pm_message_t state; + struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, + mddi_ext_early_suspend); + + state.event = PM_EVENT_SUSPEND; + mddi_ext_suspend(mfd->pdev, state); +} + +static void mddi_ext_early_resume(struct early_suspend *h) +{ + struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, + mddi_ext_early_suspend); + mddi_ext_resume(mfd->pdev); +} +#endif + +static int mddi_ext_remove(struct platform_device *pdev) +{ + pm_runtim_disable(&pdev->dev); + iounmap(msm_emdh_base); + return 0; +} + +static int mddi_ext_register_driver(void) +{ + return platform_driver_register(&mddi_ext_driver); +} + +static int __init mddi_ext_driver_init(void) +{ + int ret; + + ret = mddi_ext_register_driver(); + if (ret) { + printk(KERN_ERR "mddi_ext_register_driver() failed!\n"); + return ret; + } + mddi_init(); + + return ret; +} + +module_init(mddi_ext_driver_init); diff --git a/drivers/video/msm/mddi_ext_lcd.c b/drivers/video/msm/mddi_ext_lcd.c new file mode 100644 index 0000000000000000000000000000000000000000..aa4d48425995ca4d35494158b67d34743c254b44 --- /dev/null +++ b/drivers/video/msm/mddi_ext_lcd.c @@ -0,0 +1,90 @@ +/* Copyright (c) 2008-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" + +static int mddi_ext_lcd_on(struct platform_device *pdev); +static int mddi_ext_lcd_off(struct platform_device *pdev); + +static int mddi_ext_lcd_on(struct platform_device *pdev) +{ + return 0; +} + +static int mddi_ext_lcd_off(struct platform_device *pdev) +{ + return 0; +} + +static int __init mddi_ext_lcd_probe(struct platform_device *pdev) +{ + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = mddi_ext_lcd_probe, + .driver = { + .name = "extmddi_svga", + }, +}; + +static struct msm_fb_panel_data mddi_ext_lcd_panel_data = { + .panel_info.xres = 800, + .panel_info.yres = 600, + .panel_info.mode2_xres = 0; + .panel_info.mode2_yres = 0; + .panel_info.mode2_bpp = 0; + .panel_info.type = EXT_MDDI_PANEL, + .panel_info.pdest = DISPLAY_1, + .panel_info.wait_cycle = 0, + .panel_info.bpp = 18, + .panel_info.fb_num = 2, + .panel_info.clk_rate = 122880000, + .panel_info.clk_min = 120000000, + .panel_info.clk_max = 125000000, + .on = mddi_ext_lcd_on, + .off = mddi_ext_lcd_off, +}; + +static struct platform_device this_device = { + .name = "extmddi_svga", + .id = 0, + .dev = { + .platform_data = &mddi_ext_lcd_panel_data, + } +}; + +static int __init mddi_ext_lcd_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + + ret = platform_driver_register(&this_driver); + if (!ret) { + pinfo = &mddi_ext_lcd_panel_data.panel_info; + pinfo->lcd.vsync_enable = FALSE; + pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + } + + return ret; +} + +module_init(mddi_ext_lcd_init); diff --git a/drivers/video/msm/mddi_hw.h b/drivers/video/msm/mddi_hw.h new file mode 100644 index 0000000000000000000000000000000000000000..47bb4494c5f71cbd2b0f95e7ee066121ff12ca56 --- /dev/null +++ b/drivers/video/msm/mddi_hw.h @@ -0,0 +1,314 @@ +/* drivers/video/msm_fb/mddi_hw.h + * + * MSM MDDI Hardware Registers and Structures + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MDDI_HW_H_ +#define _MDDI_HW_H_ + +#include + +#define MDDI_CMD 0x0000 +#define MDDI_VERSION 0x0004 +#define MDDI_PRI_PTR 0x0008 +#define MDDI_SEC_PTR 0x000c +#define MDDI_BPS 0x0010 +#define MDDI_SPM 0x0014 +#define MDDI_INT 0x0018 +#define MDDI_INTEN 0x001c +#define MDDI_REV_PTR 0x0020 +#define MDDI_REV_SIZE 0x0024 +#define MDDI_STAT 0x0028 +#define MDDI_REV_RATE_DIV 0x002c +#define MDDI_REV_CRC_ERR 0x0030 +#define MDDI_TA1_LEN 0x0034 +#define MDDI_TA2_LEN 0x0038 +#define MDDI_TEST_BUS 0x003c +#define MDDI_TEST 0x0040 +#define MDDI_REV_PKT_CNT 0x0044 +#define MDDI_DRIVE_HI 0x0048 +#define MDDI_DRIVE_LO 0x004c +#define MDDI_DISP_WAKE 0x0050 +#define MDDI_REV_ENCAP_SZ 0x0054 +#define MDDI_RTD_VAL 0x0058 +#define MDDI_PAD_CTL 0x0068 +#define MDDI_DRIVER_START_CNT 0x006c +#define MDDI_NEXT_PRI_PTR 0x0070 +#define MDDI_NEXT_SEC_PTR 0x0074 +#define MDDI_MISR_CTL 0x0078 +#define MDDI_MISR_DATA 0x007c +#define MDDI_SF_CNT 0x0080 +#define MDDI_MF_CNT 0x0084 +#define MDDI_CURR_REV_PTR 0x0088 +#define MDDI_CORE_VER 0x008c +#define MDDI_FIFO_ALLOC 0x0090 +#define MDDI_PAD_IO_CTL 0x00a0 +#define MDDI_PAD_CAL 0x00a4 + +#define MDDI_INT_PRI_PTR_READ 0x0001 +#define MDDI_INT_SEC_PTR_READ 0x0002 +#define MDDI_INT_REV_DATA_AVAIL 0x0004 +#define MDDI_INT_DISP_REQ 0x0008 +#define MDDI_INT_PRI_UNDERFLOW 0x0010 +#define MDDI_INT_SEC_UNDERFLOW 0x0020 +#define MDDI_INT_REV_OVERFLOW 0x0040 +#define MDDI_INT_CRC_ERROR 0x0080 +#define MDDI_INT_MDDI_IN 0x0100 +#define MDDI_INT_PRI_OVERWRITE 0x0200 +#define MDDI_INT_SEC_OVERWRITE 0x0400 +#define MDDI_INT_REV_OVERWRITE 0x0800 +#define MDDI_INT_DMA_FAILURE 0x1000 +#define MDDI_INT_LINK_ACTIVE 0x2000 +#define MDDI_INT_IN_HIBERNATION 0x4000 +#define MDDI_INT_PRI_LINK_LIST_DONE 0x8000 +#define MDDI_INT_SEC_LINK_LIST_DONE 0x10000 +#define MDDI_INT_NO_CMD_PKTS_PEND 0x20000 +#define MDDI_INT_RTD_FAILURE 0x40000 +#define MDDI_INT_REV_PKT_RECEIVED 0x80000 +#define MDDI_INT_REV_PKTS_AVAIL 0x100000 + +#define MDDI_INT_NEED_CLEAR ( \ + MDDI_INT_REV_DATA_AVAIL | \ + MDDI_INT_PRI_UNDERFLOW | \ + MDDI_INT_SEC_UNDERFLOW | \ + MDDI_INT_REV_OVERFLOW | \ + MDDI_INT_CRC_ERROR | \ + MDDI_INT_REV_PKT_RECEIVED) + + +#define MDDI_STAT_LINK_ACTIVE 0x0001 +#define MDDI_STAT_NEW_REV_PTR 0x0002 +#define MDDI_STAT_NEW_PRI_PTR 0x0004 +#define MDDI_STAT_NEW_SEC_PTR 0x0008 +#define MDDI_STAT_IN_HIBERNATION 0x0010 +#define MDDI_STAT_PRI_LINK_LIST_DONE 0x0020 +#define MDDI_STAT_SEC_LINK_LIST_DONE 0x0040 +#define MDDI_STAT_PENDING_TIMING_PKT 0x0080 +#define MDDI_STAT_PENDING_REV_ENCAP 0x0100 +#define MDDI_STAT_PENDING_POWERDOWN 0x0200 +#define MDDI_STAT_RTD_MEAS_FAIL 0x0800 +#define MDDI_STAT_CLIENT_WAKEUP_REQ 0x1000 + + +#define MDDI_CMD_POWERDOWN 0x0100 +#define MDDI_CMD_POWERUP 0x0200 +#define MDDI_CMD_HIBERNATE 0x0300 +#define MDDI_CMD_RESET 0x0400 +#define MDDI_CMD_DISP_IGNORE 0x0501 +#define MDDI_CMD_DISP_LISTEN 0x0500 +#define MDDI_CMD_SEND_REV_ENCAP 0x0600 +#define MDDI_CMD_GET_CLIENT_CAP 0x0601 +#define MDDI_CMD_GET_CLIENT_STATUS 0x0602 +#define MDDI_CMD_SEND_RTD 0x0700 +#define MDDI_CMD_LINK_ACTIVE 0x0900 +#define MDDI_CMD_PERIODIC_REV_ENCAP 0x0A00 +#define MDDI_CMD_FORCE_NEW_REV_PTR 0x0C00 + + + +#define MDDI_VIDEO_REV_PKT_SIZE 0x40 +#define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE 0x60 +#define MDDI_MAX_REV_PKT_SIZE 0x60 + +/* #define MDDI_REV_BUFFER_SIZE 128 */ +#define MDDI_REV_BUFFER_SIZE (MDDI_MAX_REV_PKT_SIZE * 4) + +/* MDP sends 256 pixel packets, so lower value hibernates more without + * significantly increasing latency of waiting for next subframe */ +#define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00 + +#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40) +#define MDDI_HOST_TA2_LEN 0x001a +#define MDDI_HOST_REV_RATE_DIV 0x0004 +#else +#define MDDI_HOST_TA2_LEN 0x000c +#define MDDI_HOST_REV_RATE_DIV 0x0002 +#endif + + +struct __attribute__((packed)) mddi_rev_packet { + uint16_t length; + uint16_t type; + uint16_t client_id; +}; + +struct __attribute__((packed)) mddi_client_status { + uint16_t length; + uint16_t type; + uint16_t client_id; + uint16_t reverse_link_request; /* bytes needed in rev encap message */ + uint8_t crc_error_count; + uint8_t capability_change; + uint16_t graphics_busy_flags; + uint16_t crc16; +}; + +struct __attribute__((packed)) mddi_client_caps { + uint16_t length; /* length, exclusive of this field */ + uint16_t type; /* 66 */ + uint16_t client_id; + + uint16_t Protocol_Version; + uint16_t Minimum_Protocol_Version; + uint16_t Data_Rate_Capability; + uint8_t Interface_Type_Capability; + uint8_t Number_of_Alt_Displays; + uint16_t PostCal_Data_Rate; + uint16_t Bitmap_Width; + uint16_t Bitmap_Height; + uint16_t Display_Window_Width; + uint16_t Display_Window_Height; + uint32_t Color_Map_Size; + uint16_t Color_Map_RGB_Width; + uint16_t RGB_Capability; + uint8_t Monochrome_Capability; + uint8_t Reserved_1; + uint16_t Y_Cb_Cr_Capability; + uint16_t Bayer_Capability; + uint16_t Alpha_Cursor_Image_Planes; + uint32_t Client_Feature_Capability_Indicators; + uint8_t Maximum_Video_Frame_Rate_Capability; + uint8_t Minimum_Video_Frame_Rate_Capability; + uint16_t Minimum_Sub_frame_Rate; + uint16_t Audio_Buffer_Depth; + uint16_t Audio_Channel_Capability; + uint16_t Audio_Sample_Rate_Capability; + uint8_t Audio_Sample_Resolution; + uint8_t Mic_Audio_Sample_Resolution; + uint16_t Mic_Sample_Rate_Capability; + uint8_t Keyboard_Data_Format; + uint8_t pointing_device_data_format; + uint16_t content_protection_type; + uint16_t Mfr_Name; + uint16_t Product_Code; + uint16_t Reserved_3; + uint32_t Serial_Number; + uint8_t Week_of_Manufacture; + uint8_t Year_of_Manufacture; + + uint16_t crc16; +} mddi_client_capability_type; + + +struct __attribute__((packed)) mddi_video_stream { + uint16_t length; + uint16_t type; /* 16 */ + uint16_t client_id; /* 0 */ + + uint16_t video_data_format_descriptor; +/* format of each pixel in the Pixel Data in the present stream in the + * present packet. + * If bits [15:13] = 000 monochrome + * If bits [15:13] = 001 color pixels (palette). + * If bits [15:13] = 010 color pixels in raw RGB + * If bits [15:13] = 011 data in 4:2:2 Y Cb Cr format + * If bits [15:13] = 100 Bayer pixels + */ + + uint16_t pixel_data_attributes; +/* interpreted as follows: + * Bits [1:0] = 11 pixel data is displayed to both eyes + * Bits [1:0] = 10 pixel data is routed to the left eye only. + * Bits [1:0] = 01 pixel data is routed to the right eye only. + * Bits [1:0] = 00 pixel data is routed to the alternate display. + * Bit 2 is 0 Pixel Data is in the standard progressive format. + * Bit 2 is 1 Pixel Data is in interlace format. + * Bit 3 is 0 Pixel Data is in the standard progressive format. + * Bit 3 is 1 Pixel Data is in alternate pixel format. + * Bit 4 is 0 Pixel Data is to or from the display frame buffer. + * Bit 4 is 1 Pixel Data is to or from the camera. + * Bit 5 is 0 pixel data contains the next consecutive row of pixels. + * Bit 5 is 1 X Left Edge, Y Top Edge, X Right Edge, Y Bottom Edge, + * X Start, and Y Start parameters are not defined and + * shall be ignored by the client. + * Bits [7:6] = 01 Pixel data is written to the offline image buffer. + * Bits [7:6] = 00 Pixel data is written to the buffer to refresh display. + * Bits [7:6] = 11 Pixel data is written to all image buffers. + * Bits [7:6] = 10 Invalid. Reserved for future use. + * Bits 8 through 11 alternate display number. + * Bits 12 through 14 are reserved for future use and shall be set to zero. + * Bit 15 is 1 the row of pixels is the last row of pixels in a frame. + */ + + uint16_t x_left_edge; + uint16_t y_top_edge; + /* X,Y coordinate of the top left edge of the screen window */ + + uint16_t x_right_edge; + uint16_t y_bottom_edge; + /* X,Y coordinate of the bottom right edge of the window being + * updated. */ + + uint16_t x_start; + uint16_t y_start; + /* (X Start, Y Start) is the first pixel in the Pixel Data field + * below. */ + + uint16_t pixel_count; + /* number of pixels in the Pixel Data field below. */ + + uint16_t parameter_CRC; + /* 16-bit CRC of all bytes from the Packet Length to the Pixel Count. */ + + uint16_t reserved; + /* 16-bit variable to make structure align on 4 byte boundary */ +}; + +#define TYPE_VIDEO_STREAM 16 +#define TYPE_CLIENT_CAPS 66 +#define TYPE_REGISTER_ACCESS 146 +#define TYPE_CLIENT_STATUS 70 + +struct __attribute__((packed)) mddi_register_access { + uint16_t length; + uint16_t type; /* 146 */ + uint16_t client_id; + + uint16_t read_write_info; + /* Bits 13:0 a 14-bit unsigned integer that specifies the number of + * 32-bit Register Data List items to be transferred in the + * Register Data List field. + * Bits[15:14] = 00 Write to register(s); + * Bits[15:14] = 10 Read from register(s); + * Bits[15:14] = 11 Response to a Read. + * Bits[15:14] = 01 this value is reserved for future use. */ +#define MDDI_WRITE (0 << 14) +#define MDDI_READ (2 << 14) +#define MDDI_READ_RESP (3 << 14) + + uint32_t register_address; + /* the register address that is to be written to or read from. */ + + uint16_t crc16; + + uint32_t register_data_list; + /* list of 4-byte register data values for/from client registers */ +}; + +struct __attribute__((packed)) mddi_llentry { + uint16_t flags; + uint16_t header_count; + uint16_t data_count; + dma_addr_t data; /* 32 bit */ + struct mddi_llentry *next; + uint16_t reserved; + union { + struct mddi_video_stream v; + struct mddi_register_access r; + uint32_t _[12]; + } u; +}; + +#endif diff --git a/drivers/video/msm/mddi_orise.c b/drivers/video/msm/mddi_orise.c new file mode 100644 index 0000000000000000000000000000000000000000..bf9f1fa10e98c5ac7a66c58fe97336b2d665f685 --- /dev/null +++ b/drivers/video/msm/mddi_orise.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" + +#define MDDI_ORISE_1_2 1 +#define write_client_reg(__X, __Y, __Z) {\ + mddi_queue_register_write(__X, __Y, TRUE, 0);\ +} + +static int mddi_orise_lcd_on(struct platform_device *pdev); +static int mddi_orise_lcd_off(struct platform_device *pdev); +static int __init mddi_orise_probe(struct platform_device *pdev); +static int __init mddi_orise_init(void); + +/* function used to turn on the display */ +static void mddi_orise_prim_lcd_init(void) +{ + write_client_reg(0x00110000, 0, TRUE); + mddi_wait(150); + write_client_reg(0x00290000, 0, TRUE); +} + +static struct platform_driver this_driver = { + .driver = { + .name = "mddi_orise", + }, +}; + +static struct msm_fb_panel_data mddi_orise_panel_data = { + .on = mddi_orise_lcd_on, + .off = mddi_orise_lcd_off, +}; + +static struct platform_device this_device = { + .name = "mddi_orise", + .id = MDDI_ORISE_1_2, + .dev = { + .platform_data = &mddi_orise_panel_data, + } +}; + +static int mddi_orise_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + mfd = platform_get_drvdata(pdev); + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mddi_orise_prim_lcd_init(); + + return 0; +} + +static int mddi_orise_lcd_off(struct platform_device *pdev) +{ + return 0; +} + +static int __init mddi_orise_probe(struct platform_device *pdev) +{ + msm_fb_add_device(pdev); + return 0; +} + +static int __init mddi_orise_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + u32 id; + ret = msm_fb_detect_client("mddi_orise"); + if (ret == -ENODEV) + return 0; + + if (ret) { + id = mddi_get_client_id(); + if (((id >> 16) != 0xbe8d) || ((id & 0xffff) != 0x8031)) + return 0; + } +#endif + ret = platform_driver_probe(&this_driver, mddi_orise_probe); + if (!ret) { + pinfo = &mddi_orise_panel_data.panel_info; + pinfo->xres = 480; + pinfo->yres = 800; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = MDDI_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->mddi.is_type1 = TRUE; + pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + pinfo->clk_rate = 192000000; + pinfo->clk_min = 192000000; + pinfo->clk_max = 192000000; + pinfo->lcd.rev = 2; + pinfo->lcd.vsync_enable = FALSE; + pinfo->lcd.refx100 = 6050; + pinfo->lcd.v_back_porch = 2; + pinfo->lcd.v_front_porch = 2; + pinfo->lcd.v_pulse_width = 105; + pinfo->lcd.hw_vsync_mode = TRUE; + pinfo->lcd.vsync_notifier_period = 0; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + } + return ret; +} +module_init(mddi_orise_init); diff --git a/drivers/video/msm/mddi_prism.c b/drivers/video/msm/mddi_prism.c new file mode 100644 index 0000000000000000000000000000000000000000..c85b800a18460e4d2fc5ed32e06b2b7affd61bfb --- /dev/null +++ b/drivers/video/msm/mddi_prism.c @@ -0,0 +1,112 @@ +/* Copyright (c) 2008-2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" + +static int prism_lcd_on(struct platform_device *pdev); +static int prism_lcd_off(struct platform_device *pdev); + +static int prism_lcd_on(struct platform_device *pdev) +{ + /* Set the MDP pixel data attributes for Primary Display */ + mddi_host_write_pix_attr_reg(0x00C3); + + return 0; +} + +static int prism_lcd_off(struct platform_device *pdev) +{ + return 0; +} + +static int prism_probe(struct platform_device *pdev) +{ + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = prism_probe, + .driver = { + .name = "mddi_prism_wvga", + }, +}; + +static struct msm_fb_panel_data prism_panel_data = { + .on = prism_lcd_on, + .off = prism_lcd_off, +}; + +static struct platform_device this_device = { + .name = "mddi_prism_wvga", + .id = 0, + .dev = { + .platform_data = &prism_panel_data, + } +}; + +static int __init prism_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + u32 id; + + ret = msm_fb_detect_client("mddi_prism_wvga"); + if (ret == -ENODEV) + return 0; + + if (ret) { + id = mddi_get_client_id(); + + if (((id >> 16) != 0x4474) || ((id & 0xffff) == 0x8960)) + return 0; + } +#endif + ret = platform_driver_register(&this_driver); + if (!ret) { + pinfo = &prism_panel_data.panel_info; + pinfo->xres = 800; + pinfo->yres = 480; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = MDDI_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR; + pinfo->wait_cycle = 0; + pinfo->mddi.is_type1 = TRUE; + pinfo->bpp = 18; + pinfo->fb_num = 2; + pinfo->clk_rate = 153600000; + pinfo->clk_min = 140000000; + pinfo->clk_max = 160000000; + pinfo->lcd.vsync_enable = TRUE; + pinfo->lcd.refx100 = 6050; + pinfo->lcd.v_back_porch = 23; + pinfo->lcd.v_front_porch = 20; + pinfo->lcd.v_pulse_width = 105; + pinfo->lcd.hw_vsync_mode = TRUE; + pinfo->lcd.vsync_notifier_period = 0; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + } + + return ret; +} + +module_init(prism_init); diff --git a/drivers/video/msm/mddi_quickvx.c b/drivers/video/msm/mddi_quickvx.c new file mode 100644 index 0000000000000000000000000000000000000000..f89f0baf895bf86e9b57a4d8f92c004298b06232 --- /dev/null +++ b/drivers/video/msm/mddi_quickvx.c @@ -0,0 +1,719 @@ +/* Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" + +/* WVGA Primary Display */ +#define MDDI_QUICKVX_1_2 1 +/* MDDI Manufacturer Code */ +#define QUICKVX_MDDI_MFR_CODE 0xc583 +/* MDDI Product Code */ +#define QUICKVX_MDDI_PRD_CODE 0x5800 + +/* Register Address Maps */ +/* MDDI Address Anti-fuse values for bits [31:22] */ +#define QUICKVX_ADDR_31_22_AF (0X000 << 22) + +/* MDDI Address Maps */ +/* VEE Block Address Base */ +#define QUICKVX_VEE_BASE (QUICKVX_ADDR_31_22_AF | 0x00000000) +/* SPI Block Address Base */ +#define QUICKVX_SPI_BASE (QUICKVX_ADDR_31_22_AF | 0x00010000) +/* Clock and Reset (CAR) Address Base */ +#define QUICKVX_CAR_BASE (QUICKVX_ADDR_31_22_AF | 0x00020000) +/* Register Control Block (RCB) Address Base */ +#define QUICKVX_RCB_BASE (QUICKVX_ADDR_31_22_AF | 0x00030000) +/* Cellular RAM Address Base */ +#define QUICKVX_CELLRAM_BASE (QUICKVX_ADDR_31_22_AF | 0x00100000) +/* FB through A2F Address Base */ +#define QUICKVX_FB_A2F_BASE (QUICKVX_ADDR_31_22_AF | 0x00200000) + + +/*************************************************** + * Common Registers in Register Control Block (RCB) Registers + ***************************************************/ + /* CellRAM Configuration RCR Register */ +#define QUICKVX_RCB_RCR_REG (QUICKVX_RCB_BASE | 0x00000000) +/* Image Effect Register */ +#define QUICKVX_RCB_IER_REG (QUICKVX_RCB_BASE | 0x00000004) +/* Row Number Register */ +#define QUICKVX_RCB_ROWNUM_REG (QUICKVX_RCB_BASE | 0x00000008) +/* TCON Timing0 Register */ +#define QUICKVX_RCB_TCON0_REG (QUICKVX_RCB_BASE | 0x0000000C) +/* TCON Timing1 Register */ +#define QUICKVX_RCB_TCON1_REG (QUICKVX_RCB_BASE | 0x00000010) +/* TCON Timing2 Register */ +#define QUICKVX_RCB_TCON2_REG (QUICKVX_RCB_BASE | 0x00000014) +/* PWM Control Register */ +#define QUICKVX_RCB_PWMC_REG (QUICKVX_RCB_BASE | 0x00000018) +/* PWM Width Register */ +#define QUICKVX_RCB_PWMW_REG (QUICKVX_RCB_BASE | 0x0000001C) +/* VEE Configuration Register */ +#define QUICKVX_RCB_VEECONF_REG (QUICKVX_RCB_BASE | 0x00000020) +/* CellRAM Configuration BCR Register */ +#define QUICKVX_RCB_CELLBCR_REG (QUICKVX_RCB_BASE | 0x00000024) +/* CellRAM Configuration Control Register */ +#define QUICKVX_RCB_CELLCC_REG (QUICKVX_RCB_BASE | 0x00000028) +/* Use Case Register */ +#define QUICKVX_RCB_USECASE_REG (QUICKVX_RCB_BASE | 0x00000100) +/* Video Parameter Register */ +#define QUICKVX_RCB_VPARM_REG (QUICKVX_RCB_BASE | 0x00000104) +/* MDDI Client Wake-up Register */ +#define QUICKVX_RCB_MCW_REG (QUICKVX_RCB_BASE | 0x00000108) +/* Burst Length Register */ +#define QUICKVX_RCB_BURSTLN_REG (QUICKVX_RCB_BASE | 0x0000010C) +/* Display Attributes Register */ +#define QUICKVX_RCB_DISPATTR_REG (QUICKVX_RCB_BASE | 0x00000110) +/* Error Status Register */ +#define QUICKVX_RCB_ERRSTAT_REG (QUICKVX_RCB_BASE | 0x00000114) +/* Error Mask Register */ +#define QUICKVX_RCB_ERRMSK_REG (QUICKVX_RCB_BASE | 0x00000118) +/* MDDI ASSP FIFO Overflow Address Register */ +#define QUICKVX_RCB_ASSPFOA_REG (QUICKVX_RCB_BASE | 0x0000011C) +/* MDDI Fabric FIFO Overflow Address Register */ +#define QUICKVX_RCB_FABFOA_REG (QUICKVX_RCB_BASE | 0x00000120) +/* Incoming RGB FIFO Overflow Address Register */ +#define QUICKVX_RCB_IRFOA_REG (QUICKVX_RCB_BASE | 0x00000124) +/* SPI Overflow Address Register */ +#define QUICKVX_RCB_SPIOA_REG (QUICKVX_RCB_BASE | 0x00000128) +/* Ping Buffer Address Register */ +#define QUICKVX_RCB_PINGBA_REG (QUICKVX_RCB_BASE | 0x0000012C) +/* Pong Buffer Address Register */ +#define QUICKVX_RCB_PONGBA_REG (QUICKVX_RCB_BASE | 0x00000130) +/* Configuration Done Register */ +#define QUICKVX_RCB_CONFDONE_REG (QUICKVX_RCB_BASE | 0x00000134) +/* FIFO Flush Register */ +#define QUICKVX_RCB_FFLUSH_REG (QUICKVX_RCB_BASE | 0x00000138) + + +/*************************************************** + * SPI Block Registers + ***************************************************/ +/* SPI Rx0 Register */ +#define QUICKVX_SPI_RX0_REG (QUICKVX_SPI_BASE | 0x00000000) +/* SPI Rx1 Register */ +#define QUICKVX_SPI_RX1_REG (QUICKVX_SPI_BASE | 0x00000004) +/* SPI Rx2 Register */ +#define QUICKVX_SPI_RX2_REG (QUICKVX_SPI_BASE | 0x00000008) +/* SPI Rx3 Register */ +#define QUICKVX_SPI_RX3_REG (QUICKVX_SPI_BASE | 0x0000000C) +/* SPI Rx4 Register */ +#define QUICKVX_SPI_RX4_REG (QUICKVX_SPI_BASE | 0x00000010) +/* SPI Rx5 Register */ +#define QUICKVX_SPI_RX5_REG (QUICKVX_SPI_BASE | 0x00000014) +/* SPI Rx6 Register */ +#define QUICKVX_SPI_RX6_REG (QUICKVX_SPI_BASE | 0x00000018) +/* SPI Rx7 Register */ +#define QUICKVX_SPI_RX7_REG (QUICKVX_SPI_BASE | 0x0000001C) +/* SPI Tx0 Register */ +#define QUICKVX_SPI_TX0_REG (QUICKVX_SPI_BASE | 0x00000020) +/* SPI Tx1 Register */ +#define QUICKVX_SPI_TX1_REG (QUICKVX_SPI_BASE | 0x00000024) +/* SPI Tx2 Register */ +#define QUICKVX_SPI_TX2_REG (QUICKVX_SPI_BASE | 0x00000028) +/* SPI Tx3 Register */ +#define QUICKVX_SPI_TX3_REG (QUICKVX_SPI_BASE | 0x0000002C) +/* SPI Tx4 Register */ +#define QUICKVX_SPI_TX4_REG (QUICKVX_SPI_BASE | 0x00000030) +/* SPI Tx5 Register */ +#define QUICKVX_SPI_TX5_REG (QUICKVX_SPI_BASE | 0x00000034) +/* SPI Tx6 Register */ +#define QUICKVX_SPI_TX6_REG (QUICKVX_SPI_BASE | 0x00000038) +/* SPI Tx7 Register */ +#define QUICKVX_SPI_TX7_REG (QUICKVX_SPI_BASE | 0x0000003C) +/* SPI Control Register */ +#define QUICKVX_SPI_CTRL_REG (QUICKVX_SPI_BASE | 0x00000040) +/* SPI Transfer Length Register */ +#define QUICKVX_SPI_TLEN_REG (QUICKVX_SPI_BASE | 0x00000044) + + +/*************************************************** + * Clock and Reset (CAR) Block Registers + ***************************************************/ +/* ASSP Global Clock Enable Register */ +#define QUICKVX_CAR_ASSP_GCE_REG (QUICKVX_CAR_BASE | 0x00000000) +/* VLP Control1 Register */ +#define QUICKVX_CAR_VLPCTRL1_REG (QUICKVX_CAR_BASE | 0x00000004) +/* VLP Control2 Register */ +#define QUICKVX_CAR_VLPCTRL2_REG (QUICKVX_CAR_BASE | 0x00000008) +/* Clock Selection Register */ +#define QUICKVX_CAR_CLKSEL_REG (QUICKVX_CAR_BASE | 0x0000000C) +/* PLL Control Register */ +#define QUICKVX_CAR_PLLCTRL_REG (QUICKVX_CAR_BASE | 0x00000010) +/* PLL Clock Ratio Register */ +#define QUICKVX_CAR_PLLCLKRATIO_REG (QUICKVX_CAR_BASE | 0x00000014) + + +/*************************************************** + * VEE Block Registers + ***************************************************/ +/* VEE Control Register */ +#define QUICKVX_VEE_VEECTRL_REG (QUICKVX_VEE_BASE | 0x00000000) +/* Strength Register */ +#define QUICKVX_VEE_STRENGTH_REG (QUICKVX_VEE_BASE | 0x0000000C) +/* Variance Register */ +#define QUICKVX_VEE_VARIANCE_REG (QUICKVX_VEE_BASE | 0x00000010) +/* Slope Register */ +#define QUICKVX_VEE_SLOPE_REG (QUICKVX_VEE_BASE | 0x00000014) +/* Sharpen Control0 Register */ +#define QUICKVX_VEE_SHRPCTRL0_REG (QUICKVX_VEE_BASE | 0x0000001C) +/* Sharpen Control1 Register */ +#define QUICKVX_VEE_SHRPCTRL1_REG (QUICKVX_VEE_BASE | 0x00000020) +/* Upper Horizontal Positon Register */ +#define QUICKVX_VEE_UHPOS_REG (QUICKVX_VEE_BASE | 0x00000024) +/* Lower Horizontal Positon Register */ +#define QUICKVX_VEE_LHPOS_REG (QUICKVX_VEE_BASE | 0x00000028) +/* Upper Vertical Positon Register */ +#define QUICKVX_VEE_UVPOS_REG (QUICKVX_VEE_BASE | 0x0000002C) +/* Lower Vertical Positon Register */ +#define QUICKVX_VEE_LVPOS_REG (QUICKVX_VEE_BASE | 0x00000030) +/* Upper Frame Width Register */ +#define QUICKVX_VEE_UFWDTH_REG (QUICKVX_VEE_BASE | 0x00000034) +/* Lower Frame Width Register */ +#define QUICKVX_VEE_LFWDTH_REG (QUICKVX_VEE_BASE | 0x00000038) +/* Upper Frame Height Register */ +#define QUICKVX_VEE_UFHGHT_REG (QUICKVX_VEE_BASE | 0x0000003C) +/* Lower Frame Height Register */ +#define QUICKVX_VEE_LFHGHT_REG (QUICKVX_VEE_BASE | 0x00000040) +/* Control0 Register */ +#define QUICKVX_VEE_CTRL0_REG (QUICKVX_VEE_BASE | 0x00000044) +/* Control1 Register */ +#define QUICKVX_VEE_CTRL1_REG (QUICKVX_VEE_BASE | 0x00000048) +/* Video Enhancement Enable Register */ +#define QUICKVX_VEE_VDOEEN_REG (QUICKVX_VEE_BASE | 0x0000004C) +/* Black Level Register */ +#define QUICKVX_VEE_BLCKLEV_REG (QUICKVX_VEE_BASE | 0x00000050) +/* White Level Register */ +#define QUICKVX_VEE_WHTLEV_REG (QUICKVX_VEE_BASE | 0x00000054) +/* Amplification Limits Register */ +#define QUICKVX_VEE_AMPLMTS_REG (QUICKVX_VEE_BASE | 0x00000060) +/* Dithering Mode Register */ +#define QUICKVX_VEE_DITHMOD_REG (QUICKVX_VEE_BASE | 0x00000064) +/* Upper Look-up Data Register */ +#define QUICKVX_VEE_ULUD_REG (QUICKVX_VEE_BASE | 0x00000080) +/* Lower Look-up Data Register */ +#define QUICKVX_VEE_LLUD_REG (QUICKVX_VEE_BASE | 0x00000084) +/* Look-up Address Register */ +#define QUICKVX_VEE_LUADDR_REG (QUICKVX_VEE_BASE | 0x00000088) +/* Look-up Write Enable Register */ +#define QUICKVX_VEE_LUWREN_REG (QUICKVX_VEE_BASE | 0x0000008C) +/* VEE ID Register */ +#define QUICKVX_VEE_VEEID_REG (QUICKVX_VEE_BASE | 0x000003FC) +/* M_11 Register */ +#define QUICKVX_VEE_M_11_REG (QUICKVX_VEE_BASE | 0x000000C0) +/* M_12 Register */ +#define QUICKVX_VEE_M_12_REG (QUICKVX_VEE_BASE | 0x000000C4) +/* M_13 Register */ +#define QUICKVX_VEE_M_13_REG (QUICKVX_VEE_BASE | 0x000000C8) +/* M_21 Register */ +#define QUICKVX_VEE_M_21_REG (QUICKVX_VEE_BASE | 0x000000CC) +/* M_22 Register */ +#define QUICKVX_VEE_M_22_REG (QUICKVX_VEE_BASE | 0x000000D0) +/* M_23 Register */ +#define QUICKVX_VEE_M_23_REG (QUICKVX_VEE_BASE | 0x000000D4) +/* M_31 Register */ +#define QUICKVX_VEE_M_31_REG (QUICKVX_VEE_BASE | 0x000000D8) +/* M_32 Register */ +#define QUICKVX_VEE_M_32_REG (QUICKVX_VEE_BASE | 0x000000DC) +/* M_33 Register */ +#define QUICKVX_VEE_M_33_REG (QUICKVX_VEE_BASE | 0x000000E0) +/* R Offset Register */ +#define QUICKVX_VEE_OFFSET_R_REG (QUICKVX_VEE_BASE | 0x000000E8) +/* G Offset Register */ +#define QUICKVX_VEE_OFFSET_G_REG (QUICKVX_VEE_BASE | 0x000000EC) +/* B Offset Register */ +#define QUICKVX_VEE_OFFSET_B_REG (QUICKVX_VEE_BASE | 0x000000F0) + +/* LCD Reset Register */ +#define QUICKVX_FB_A2F_LCD_RESET_REG (QUICKVX_FB_A2F_BASE | 0x00000000) + +/* Register bit defines */ +/* PLL Lock bit in the PLL Control Register */ +#define QUICKVX_PLL_LOCK_BIT (1 << 7) + +#define QL_SPI_CTRL_rSPISTart(x) (x) +#define QL_SPI_CTRL_rCPHA(x) (x << 1) +#define QL_SPI_CTRL_rCPOL(x) (x << 2) +#define QL_SPI_CTRL_rLSB(x) (x << 3) +#define QL_SPI_CTRL_rSLVSEL(x) (x << 4) +#define QL_SPI_CTRL_MASK_rTxDone (1 << 9) + +#define QL_SPI_LCD_DEV_ID 0x1c +#define QL_SPI_LCD_RS(x) (x << 1) +#define QL_SPI_LCD_RW(x) (x) +#define QL_SPI_LCD_INDEX_START_BYTE ((QL_SPI_LCD_DEV_ID << 2) | \ + QL_SPI_LCD_RS(0) | QL_SPI_LCD_RW(0)) +#define QL_SPI_LCD_CMD_START_BYTE ((QL_SPI_LCD_DEV_ID << 2) | \ + QL_SPI_LCD_RS(1) | QL_SPI_LCD_RW(0)) +#define QL_SPI_CTRL_LCD_START (QL_SPI_CTRL_rSPISTart(1) | \ + QL_SPI_CTRL_rCPHA(1) | QL_SPI_CTRL_rCPOL(1) | \ + QL_SPI_CTRL_rLSB(0) | QL_SPI_CTRL_rSLVSEL(0)) + +int ql_mddi_write(uint32 address, uint32 value) +{ + uint32 regval = 0; + int ret = 0; + + ret = mddi_queue_register_write(address, value, TRUE, 0); + + if (!ret) { + ret = mddi_queue_register_read(address, ®val, TRUE, 0); + if (regval != value) { + MDDI_MSG_DEBUG("\nMismatch: ql_mddi_write[0x%x]->0x%x " + "r0x%x\n", address, value, regval); + } else { + MDDI_MSG_DEBUG("\nMatch: ql_mddi_write[0x%x]->0x%x " + "r0x%x\n", address, value, regval); + } + } + + return ret; +} + +int ql_mddi_read(uint32 address, uint32 *regval) +{ + int ret = 0; + + ret = mddi_queue_register_read(address, regval, TRUE, 0); + MDDI_MSG_DEBUG("\nql_mddi_read[0x%x]=0x%x", address, *regval); + + return ret; +} + +int ql_send_spi_cmd_to_lcd(uint32 index, uint32 cmd) +{ + int retry, ret; + uint32 readval; + + MDDI_MSG_DEBUG("\n %s(): index 0x%x, cmd 0x%x", __func__, index, cmd); + /* do the index phase */ + /* send 24 bits in the index phase */ + ql_mddi_write(QUICKVX_SPI_TLEN_REG, 23); + + /* send 24 bits in the index phase, starting at bit 23 of TX0 reg */ + ql_mddi_write(QUICKVX_SPI_TX0_REG, + (QL_SPI_LCD_INDEX_START_BYTE << 16) | index); + + /* set start */ + ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START); + retry = 0; + + do { + ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval); + + if (ret || ++retry > 5) { + MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry " + "timeout at index phase, ret = %d", ret); + return -EIO; + } + mddi_wait(1); + } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0); + + /* do the command phase */ + /* send 24 bits in the cmd phase */ + ql_mddi_write(QUICKVX_SPI_TLEN_REG, 23); + + /* send 24 bits in the cmd phase, starting at bit 23 of TX0 reg. */ + ql_mddi_write(QUICKVX_SPI_TX0_REG, + (QL_SPI_LCD_CMD_START_BYTE << 16) | cmd); + + /* set start */ + ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START); + retry = 0; + + do { + ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval); + + if (ret || ++retry > 5) { + MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry " + "timeout at cmd phase, ret = %d", ret); + return -EIO; + } + mddi_wait(1); + } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0); + + return 0; +} + + +int ql_send_spi_data_from_lcd(uint32 index, uint32 *value) +{ + int retry, ret; + uint32 readval; + + MDDI_MSG_DEBUG("\n %s(): index 0x%x", __func__, index); + /* do the index phase */ + /* send 24 bits in the index phase */ + ql_mddi_write(QUICKVX_SPI_TLEN_REG, 23); + + /* send 24 bits in the index phase, starting at bit 23 of TX0 reg */ + ql_mddi_write(QUICKVX_SPI_TX0_REG, + (QL_SPI_LCD_INDEX_START_BYTE << 16) | index); + + /* set start */ + ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START); + retry = 0; + + do { + ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval); + + if (ret || ++retry > 5) { + MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry " + "timeout at index phase, ret = %d", ret); + return -EIO; + } + mddi_wait(1); + } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0); + + /* do the command phase */ + /* send 8 bits and read 24 bits in the cmd phase, so total 32 bits */ + ql_mddi_write(QUICKVX_SPI_TLEN_REG, 31); + + /* send 24 bits in the cmd phase, starting at bit 31 of TX0 reg */ + ql_mddi_write(QUICKVX_SPI_TX0_REG, + ((QL_SPI_LCD_CMD_START_BYTE << 16)) << 8); + + /* set start */ + ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START); + retry = 0; + + do { + ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval); + + if (ret || ++retry > 5) { + MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry " + "timeout at cmd phase, ret = %d", ret); + return -EIO; + } + mddi_wait(1); + } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0); + + /* value will appear at lower 16 bits */ + ret = ql_mddi_read(QUICKVX_SPI_RX0_REG, value); + + if (!ret) { + *value = *value & 0xffff; + MDDI_MSG_DEBUG("\n QUICKVX_SPI_RX0_REG value = 0x%x", *value); + } else + MDDI_MSG_DEBUG("\n Read QUICKVX_SPI_RX0_REG Failed"); + + return ret; +} + +/* Global Variables */ +static uint32 mddi_quickvx_rows_per_second; +static uint32 mddi_quickvx_usecs_per_refresh; +static uint32 mddi_quickvx_rows_per_refresh; + +void mddi_quickvx_configure_registers(void) +{ + MDDI_MSG_DEBUG("\n%s(): ", __func__); + ql_mddi_write(QUICKVX_CAR_CLKSEL_REG, 0x00007000); + + ql_mddi_write(QUICKVX_RCB_PWMW_REG, 0x0000FFFF); + + ql_mddi_write(QUICKVX_RCB_PWMC_REG, 0x00000001); + + ql_mddi_write(QUICKVX_RCB_CONFDONE_REG, 0x00000000); + + /* display is x width = 480, y width = 864 */ + ql_mddi_write(QUICKVX_RCB_TCON0_REG, 0x035f01df); + + /* VFP=2, VBP=4, HFP=16, HBP=16 */ + ql_mddi_write(QUICKVX_RCB_TCON1_REG, 0x01e301e1); + + /* VSW =2, HSW=8 */ + ql_mddi_write(QUICKVX_RCB_TCON2_REG, 0x000000e1); + + ql_mddi_write(QUICKVX_RCB_DISPATTR_REG, 0x00000000); + + ql_mddi_write(QUICKVX_RCB_USECASE_REG, 0x00000025); + + ql_mddi_write(QUICKVX_RCB_VPARM_REG, 0x00000888); + + ql_mddi_write(QUICKVX_RCB_VEECONF_REG, 0x00000001); + + ql_mddi_write(QUICKVX_RCB_IER_REG, 0x00000000); + + ql_mddi_write(QUICKVX_RCB_RCR_REG, 0x80000010); + + ql_mddi_write(QUICKVX_RCB_CELLBCR_REG, 0x8008746F); + + ql_mddi_write(QUICKVX_RCB_CELLCC_REG, 0x800000A3); + + ql_mddi_write(QUICKVX_RCB_CONFDONE_REG, 0x00000001); +} + +void mddi_quickvx_prim_lcd_init(void) +{ + uint32 value; + + MDDI_MSG_DEBUG("\n%s(): ", __func__); + ql_send_spi_data_from_lcd(0, &value); + + ql_send_spi_cmd_to_lcd(0x0100, 0x3000); /* power control1 */ + ql_send_spi_cmd_to_lcd(0x0101, 0x4010); /* power control2 */ + ql_send_spi_cmd_to_lcd(0x0106, 0x0000); /* auto seq setting */ + mddi_wait(3); + + ql_mddi_write(QUICKVX_FB_A2F_LCD_RESET_REG, 0x00000001); + mddi_wait(1); + ql_mddi_write(QUICKVX_FB_A2F_LCD_RESET_REG, 0x00000000); + mddi_wait(1); + ql_mddi_write(QUICKVX_FB_A2F_LCD_RESET_REG, 0x00000001); + mddi_wait(10); + + ql_send_spi_cmd_to_lcd(0x0001, 0x0310); /* driver out control */ + ql_send_spi_cmd_to_lcd(0x0002, 0x0100); /* lcd ac control */ + ql_send_spi_cmd_to_lcd(0x0003, 0x0000); /* entry mode */ + ql_send_spi_cmd_to_lcd(0x0007, 0x0000); /* disp cont1 */ + ql_send_spi_cmd_to_lcd(0x0008, 0x0004); /* disp cont2 */ + ql_send_spi_cmd_to_lcd(0x0009, 0x000C); /* disp cont3 */ + ql_send_spi_cmd_to_lcd(0x000C, 0x4010); /* disp if cont1 */ + ql_send_spi_cmd_to_lcd(0x000E, 0x0000); /* disp if cont2 */ + ql_send_spi_cmd_to_lcd(0x0020, 0x013F); /* panel if cont1 */ + ql_send_spi_cmd_to_lcd(0x0022, 0x7600); /* panel if cont3 */ + ql_send_spi_cmd_to_lcd(0x0023, 0x1C0A); /* panel if cont4 */ + ql_send_spi_cmd_to_lcd(0x0024, 0x1C2C); /* panel if cont5 */ + ql_send_spi_cmd_to_lcd(0x0025, 0x1C4E); /* panel if cont6 */ + ql_send_spi_cmd_to_lcd(0x0027, 0x0000); /* panel if cont8 */ + ql_send_spi_cmd_to_lcd(0x0028, 0x760C); /* panel if cont9 */ + ql_send_spi_cmd_to_lcd(0x0300, 0x0000); /* gamma adj0 */ + ql_send_spi_cmd_to_lcd(0x0301, 0x0502); /* gamma adj1 */ + ql_send_spi_cmd_to_lcd(0x0302, 0x0705); /* gamma adj2 */ + ql_send_spi_cmd_to_lcd(0x0303, 0x0000); /* gamma adj3 */ + ql_send_spi_cmd_to_lcd(0x0304, 0x0200); /* gamma adj4 */ + ql_send_spi_cmd_to_lcd(0x0305, 0x0707); /* gamma adj5 */ + ql_send_spi_cmd_to_lcd(0x0306, 0x1010); /* gamma adj6 */ + ql_send_spi_cmd_to_lcd(0x0307, 0x0202); /* gamma adj7 */ + ql_send_spi_cmd_to_lcd(0x0308, 0x0704); /* gamma adj8 */ + ql_send_spi_cmd_to_lcd(0x0309, 0x0707); /* gamma adj9 */ + ql_send_spi_cmd_to_lcd(0x030A, 0x0000); /* gamma adja */ + ql_send_spi_cmd_to_lcd(0x030B, 0x0000); /* gamma adjb */ + ql_send_spi_cmd_to_lcd(0x030C, 0x0707); /* gamma adjc */ + ql_send_spi_cmd_to_lcd(0x030D, 0x1010); /* gamma adjd */ + ql_send_spi_cmd_to_lcd(0x0310, 0x0104); /* gamma adj10 */ + ql_send_spi_cmd_to_lcd(0x0311, 0x0503); /* gamma adj11 */ + ql_send_spi_cmd_to_lcd(0x0312, 0x0304); /* gamma adj12 */ + ql_send_spi_cmd_to_lcd(0x0315, 0x0304); /* gamma adj15 */ + ql_send_spi_cmd_to_lcd(0x0316, 0x031C); /* gamma adj16 */ + ql_send_spi_cmd_to_lcd(0x0317, 0x0204); /* gamma adj17 */ + ql_send_spi_cmd_to_lcd(0x0318, 0x0402); /* gamma adj18 */ + ql_send_spi_cmd_to_lcd(0x0319, 0x0305); /* gamma adj19 */ + ql_send_spi_cmd_to_lcd(0x031C, 0x0707); /* gamma adj1c */ + ql_send_spi_cmd_to_lcd(0x031D, 0x021F); /* gamma adj1d */ + ql_send_spi_cmd_to_lcd(0x0320, 0x0507); /* gamma adj20 */ + ql_send_spi_cmd_to_lcd(0x0321, 0x0604); /* gamma adj21 */ + ql_send_spi_cmd_to_lcd(0x0322, 0x0405); /* gamma adj22 */ + ql_send_spi_cmd_to_lcd(0x0327, 0x0203); /* gamma adj27 */ + ql_send_spi_cmd_to_lcd(0x0328, 0x0300); /* gamma adj28 */ + ql_send_spi_cmd_to_lcd(0x0329, 0x0002); /* gamma adj29 */ + ql_send_spi_cmd_to_lcd(0x0100, 0x363C); /* power cont1 */ + mddi_wait(1); + ql_send_spi_cmd_to_lcd(0x0101, 0x4003); /* power cont2 */ + ql_send_spi_cmd_to_lcd(0x0102, 0x0001); /* power cont3 */ + ql_send_spi_cmd_to_lcd(0x0103, 0x3C58); /* power cont4 */ + ql_send_spi_cmd_to_lcd(0x010C, 0x0135); /* power cont6 */ + ql_send_spi_cmd_to_lcd(0x0106, 0x0002); /* auto seq */ + ql_send_spi_cmd_to_lcd(0x0029, 0x03BF); /* panel if cont10 */ + ql_send_spi_cmd_to_lcd(0x0106, 0x0003); /* auto seq */ + mddi_wait(5); + ql_send_spi_cmd_to_lcd(0x0101, 0x4010); /* power cont2 */ + mddi_wait(10); +} + +/* Function to Power On the Primary and Secondary LCD panels */ +static int mddi_quickvx_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + MDDI_MSG_DEBUG("\n%s(): ", __func__); + mfd = platform_get_drvdata(pdev); + + if (!mfd) { + MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_on: Device not found!"); + return -ENODEV; + } + + if (mfd->key != MFD_KEY) { + MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_on: Invalid MFD key!"); + return -EINVAL; + } + + mddi_host_client_cnt_reset(); + mddi_quickvx_configure_registers(); + mddi_quickvx_prim_lcd_init(); + + return 0; +} + + +/* Function to Power Off the Primary and Secondary LCD panels */ +static int mddi_quickvx_lcd_off(struct platform_device *pdev) +{ + MDDI_MSG_DEBUG("\n%s(): ", __func__); + mddi_wait(1); + ql_send_spi_cmd_to_lcd(0x0106, 0x0002); /* Auto Sequencer setting */ + mddi_wait(10); + ql_send_spi_cmd_to_lcd(0x0106, 0x0000); /* Auto Sequencer setting */ + ql_send_spi_cmd_to_lcd(0x0029, 0x0002); /* Panel IF control 10 */ + ql_send_spi_cmd_to_lcd(0x0100, 0x300D); /* Power Control 1 */ + mddi_wait(1); + + return 0; +} + +/* Function to set the Backlight brightness level */ +static void mddi_quickvx_lcd_set_backlight(struct msm_fb_data_type *mfd) +{ + int32 level, i = 0, ret; + + MDDI_MSG_DEBUG("%s(): ", __func__); + + level = mfd->bl_level; + MDDI_MSG_DEBUG("\n level = %d", level); + if (level < 0) { + MDDI_MSG_DEBUG("mddi_quickvx_lcd_set_backlight: " + "Invalid backlight level (%d)!\n", level); + return; + } + while (i++ < 3) { + ret = pmic_set_led_intensity(LED_LCD, level); + if (ret == 0) + return; + msleep(10); + } + + MDDI_MSG_DEBUG("%s: can't set lcd backlight!\n", + __func__); +} + +/* Driver Probe function */ +static int mddi_quickvx_lcd_probe(struct platform_device *pdev) +{ + MDDI_MSG_DEBUG("\n%s(): id is %d", __func__, pdev->id); + msm_fb_add_device(pdev); + return 0; +} + +/* Driver data structure */ +static struct platform_driver this_driver = { + .probe = mddi_quickvx_lcd_probe, + .driver = { + .name = "mddi_quickvx", + }, +}; + + +/* Primary LCD panel data structure */ +static struct msm_fb_panel_data mddi_quickvx_panel_data0 = { + .on = mddi_quickvx_lcd_on, + .off = mddi_quickvx_lcd_off, + .set_backlight = mddi_quickvx_lcd_set_backlight, +}; + + +/* Primary LCD panel device structure */ +static struct platform_device this_device0 = { + .name = "mddi_quickvx", + .id = MDDI_QUICKVX_1_2, + .dev = { + .platform_data = &mddi_quickvx_panel_data0, + } +}; + +/* Module init - driver main entry point */ +static int __init mddi_quickvx_lcd_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + u32 cid; + MDDI_MSG_DEBUG("\n%s(): ", __func__); + + ret = msm_fb_detect_client("mddi_quickvx"); + + if (ret == -ENODEV) { + /* Device not found */ + MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_init: No device found!"); + return 0; + } + + if (ret) { + cid = mddi_get_client_id(); + + MDDI_MSG_DEBUG("\n cid = 0x%x", cid); + if (((cid >> 16) != QUICKVX_MDDI_MFR_CODE) || + ((cid & 0xFFFF) != QUICKVX_MDDI_PRD_CODE)) { + /* MDDI Client ID not matching */ + MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_init: " + "Client ID missmatch!"); + + return 0; + } + MDDI_MSG_DEBUG("\n mddi_quickvx_lcd_init: " + "QuickVX LCD panel detected!"); + } + +#endif /* CONFIG_FB_MSM_MDDI_AUTO_DETECT */ + + mddi_quickvx_rows_per_refresh = 872; + mddi_quickvx_rows_per_second = 52364; + mddi_quickvx_usecs_per_refresh = 16574; + + ret = platform_driver_register(&this_driver); + + if (!ret) { + pinfo = &mddi_quickvx_panel_data0.panel_info; + pinfo->xres = 480; + pinfo->yres = 864; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = MDDI_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + pinfo->fb_num = 2; + + pinfo->clk_rate = 192000000; + pinfo->clk_min = 192000000; + pinfo->clk_max = 200000000; + pinfo->lcd.rev = 1; + pinfo->lcd.vsync_enable = TRUE; + pinfo->lcd.refx100 = (mddi_quickvx_rows_per_second \ + * 100)/mddi_quickvx_rows_per_refresh; + pinfo->mddi.is_type1 = TRUE; + pinfo->lcd.v_back_porch = 4; + pinfo->lcd.v_front_porch = 2; + pinfo->lcd.v_pulse_width = 2; + pinfo->lcd.hw_vsync_mode = TRUE; + pinfo->lcd.vsync_notifier_period = (1 * HZ); + pinfo->bl_max = 10; + pinfo->bl_min = 0; + + ret = platform_device_register(&this_device0); + if (ret) { + platform_driver_unregister(&this_driver); + MDDI_MSG_DEBUG("mddi_quickvx_lcd_init: " + "Primary device registration failed!\n"); + } + } + + return ret; +} + +module_init(mddi_quickvx_lcd_init); + diff --git a/drivers/video/msm/mddi_sharp.c b/drivers/video/msm/mddi_sharp.c new file mode 100644 index 0000000000000000000000000000000000000000..7efd638e3c2453381d2c854f6f1acf7dc9a919a9 --- /dev/null +++ b/drivers/video/msm/mddi_sharp.c @@ -0,0 +1,901 @@ +/* Copyright (c) 2008-2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" + +#define SHARP_QVGA_PRIM 1 +#define SHARP_128X128_SECD 2 + +extern uint32 mddi_host_core_version; +static boolean mddi_debug_prim_wait = FALSE; +static boolean mddi_sharp_vsync_wake = TRUE; +static boolean mddi_sharp_monitor_refresh_value = TRUE; +static boolean mddi_sharp_report_refresh_measurements = FALSE; +static uint32 mddi_sharp_rows_per_second = 13830; /* 5200000/376 */ +static uint32 mddi_sharp_rows_per_refresh = 338; +static uint32 mddi_sharp_usecs_per_refresh = 24440; /* (376+338)/5200000 */ +static boolean mddi_sharp_debug_60hz_refresh = FALSE; + +extern mddi_gpio_info_type mddi_gpio; +extern boolean mddi_vsync_detect_enabled; +static msm_fb_vsync_handler_type mddi_sharp_vsync_handler; +static void *mddi_sharp_vsync_handler_arg; +static uint16 mddi_sharp_vsync_attempts; + +static void mddi_sharp_prim_lcd_init(void); +static void mddi_sharp_sub_lcd_init(void); +static void mddi_sharp_lcd_set_backlight(struct msm_fb_data_type *mfd); +static void mddi_sharp_vsync_set_handler(msm_fb_vsync_handler_type handler, + void *); +static void mddi_sharp_lcd_vsync_detected(boolean detected); +static struct msm_panel_common_pdata *mddi_sharp_pdata; + +#define REG_SYSCTL 0x0000 +#define REG_INTR 0x0006 +#define REG_CLKCNF 0x000C +#define REG_CLKDIV1 0x000E +#define REG_CLKDIV2 0x0010 + +#define REG_GIOD 0x0040 +#define REG_GIOA 0x0042 + +#define REG_AGM 0x010A +#define REG_FLFT 0x0110 +#define REG_FRGT 0x0112 +#define REG_FTOP 0x0114 +#define REG_FBTM 0x0116 +#define REG_FSTRX 0x0118 +#define REG_FSTRY 0x011A +#define REG_VRAM 0x0202 +#define REG_SSDCTL 0x0330 +#define REG_SSD0 0x0332 +#define REG_PSTCTL1 0x0400 +#define REG_PSTCTL2 0x0402 +#define REG_PTGCTL 0x042A +#define REG_PTHP 0x042C +#define REG_PTHB 0x042E +#define REG_PTHW 0x0430 +#define REG_PTHF 0x0432 +#define REG_PTVP 0x0434 +#define REG_PTVB 0x0436 +#define REG_PTVW 0x0438 +#define REG_PTVF 0x043A +#define REG_VBLKS 0x0458 +#define REG_VBLKE 0x045A +#define REG_SUBCTL 0x0700 +#define REG_SUBTCMD 0x0702 +#define REG_SUBTCMDD 0x0704 +#define REG_REVBYTE 0x0A02 +#define REG_REVCNT 0x0A04 +#define REG_REVATTR 0x0A06 +#define REG_REVFMT 0x0A08 + +#define SHARP_SUB_UNKNOWN 0xffffffff +#define SHARP_SUB_HYNIX 1 +#define SHARP_SUB_ROHM 2 + +static uint32 sharp_subpanel_type = SHARP_SUB_UNKNOWN; + +static void sub_through_write(int sub_rs, uint32 sub_data) +{ + mddi_queue_register_write(REG_SUBTCMDD, sub_data, FALSE, 0); + + /* CS=1,RD=1,WE=1,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x000e | sub_rs, FALSE, 0); + + /* CS=0,RD=1,WE=1,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x0006 | sub_rs, FALSE, 0); + + /* CS=0,RD=1,WE=0,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x0004 | sub_rs, FALSE, 0); + + /* CS=0,RD=1,WE=1,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x0006 | sub_rs, FALSE, 0); + + /* CS=1,RD=1,WE=1,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x000e | sub_rs, TRUE, 0); +} + +static uint32 sub_through_read(int sub_rs) +{ + uint32 sub_data; + + /* CS=1,RD=1,WE=1,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x000e | sub_rs, FALSE, 0); + + /* CS=0,RD=1,WE=1,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x0006 | sub_rs, FALSE, 0); + + /* CS=0,RD=1,WE=0,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x0002 | sub_rs, TRUE, 0); + + mddi_queue_register_read(REG_SUBTCMDD, &sub_data, TRUE, 0); + + /* CS=0,RD=1,WE=1,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x0006 | sub_rs, FALSE, 0); + + /* CS=1,RD=1,WE=1,RS=sub_rs */ + mddi_queue_register_write(REG_SUBTCMD, 0x000e | sub_rs, TRUE, 0); + + return sub_data; +} + +static void serigo(uint32 ssd) +{ + uint32 ssdctl; + + mddi_queue_register_read(REG_SSDCTL, &ssdctl, TRUE, 0); + ssdctl = ((ssdctl & 0xE7) | 0x02); + + mddi_queue_register_write(REG_SSD0, ssd, FALSE, 0); + mddi_queue_register_write(REG_SSDCTL, ssdctl, TRUE, 0); + + do { + mddi_queue_register_read(REG_SSDCTL, &ssdctl, TRUE, 0); + } while ((ssdctl & 0x0002) != 0); + + if (mddi_debug_prim_wait) + mddi_wait(2); +} + +static void mddi_sharp_lcd_powerdown(void) +{ + serigo(0x0131); + serigo(0x0300); + mddi_wait(40); + serigo(0x0135); + mddi_wait(20); + serigo(0x2122); + mddi_wait(20); + serigo(0x0201); + mddi_wait(20); + serigo(0x2100); + mddi_wait(20); + serigo(0x2000); + mddi_wait(20); + + mddi_queue_register_write(REG_PSTCTL1, 0x1, TRUE, 0); + mddi_wait(100); + mddi_queue_register_write(REG_PSTCTL1, 0x0, TRUE, 0); + mddi_wait(2); + mddi_queue_register_write(REG_SYSCTL, 0x1, TRUE, 0); + mddi_wait(2); + mddi_queue_register_write(REG_CLKDIV1, 0x3, TRUE, 0); + mddi_wait(2); + mddi_queue_register_write(REG_SSDCTL, 0x0000, TRUE, 0); /* SSDRESET */ + mddi_queue_register_write(REG_SYSCTL, 0x0, TRUE, 0); + mddi_wait(2); +} + +static void mddi_sharp_lcd_set_backlight(struct msm_fb_data_type *mfd) +{ + uint32 regdata; + int32 level; + int max = mfd->panel_info.bl_max; + int min = mfd->panel_info.bl_min; + + if (mddi_sharp_pdata && mddi_sharp_pdata->backlight_level) { + level = mddi_sharp_pdata->backlight_level(mfd->bl_level, + max, + min); + + if (level < 0) + return; + + /* use Rodem GPIO(2:0) to give 8 levels of backlight (7-0) */ + /* Set lower 3 GPIOs as Outputs (set to 0) */ + mddi_queue_register_read(REG_GIOA, ®data, TRUE, 0); + mddi_queue_register_write(REG_GIOA, regdata & 0xfff8, TRUE, 0); + + /* Set lower 3 GPIOs as level */ + mddi_queue_register_read(REG_GIOD, ®data, TRUE, 0); + mddi_queue_register_write(REG_GIOD, + (regdata & 0xfff8) | (0x07 & level), TRUE, 0); + } +} + +static void mddi_sharp_prim_lcd_init(void) +{ + mddi_queue_register_write(REG_SYSCTL, 0x4000, TRUE, 0); + mddi_wait(1); + mddi_queue_register_write(REG_SYSCTL, 0x0000, TRUE, 0); + mddi_wait(5); + mddi_queue_register_write(REG_SYSCTL, 0x0001, FALSE, 0); + mddi_queue_register_write(REG_CLKDIV1, 0x000b, FALSE, 0); + + /* new reg write below */ + if (mddi_sharp_debug_60hz_refresh) + mddi_queue_register_write(REG_CLKCNF, 0x070d, FALSE, 0); + else + mddi_queue_register_write(REG_CLKCNF, 0x0708, FALSE, 0); + + mddi_queue_register_write(REG_SYSCTL, 0x0201, FALSE, 0); + mddi_queue_register_write(REG_PTGCTL, 0x0010, FALSE, 0); + mddi_queue_register_write(REG_PTHP, 4, FALSE, 0); + mddi_queue_register_write(REG_PTHB, 40, FALSE, 0); + mddi_queue_register_write(REG_PTHW, 240, FALSE, 0); + if (mddi_sharp_debug_60hz_refresh) + mddi_queue_register_write(REG_PTHF, 12, FALSE, 0); + else + mddi_queue_register_write(REG_PTHF, 92, FALSE, 0); + + mddi_wait(1); + + mddi_queue_register_write(REG_PTVP, 1, FALSE, 0); + mddi_queue_register_write(REG_PTVB, 2, FALSE, 0); + mddi_queue_register_write(REG_PTVW, 320, FALSE, 0); + mddi_queue_register_write(REG_PTVF, 15, FALSE, 0); + + mddi_wait(1); + + /* vram_color set REG_AGM???? */ + mddi_queue_register_write(REG_AGM, 0x0000, TRUE, 0); + + mddi_queue_register_write(REG_SSDCTL, 0x0000, FALSE, 0); + mddi_queue_register_write(REG_SSDCTL, 0x0001, TRUE, 0); + mddi_wait(1); + mddi_queue_register_write(REG_PSTCTL1, 0x0001, TRUE, 0); + mddi_wait(10); + + serigo(0x0701); + /* software reset */ + mddi_wait(1); + /* Wait over 50us */ + + serigo(0x0400); + /* DCLK~ACHSYNC~ACVSYNC polarity setting */ + serigo(0x2900); + /* EEPROM start read address setting */ + serigo(0x2606); + /* EEPROM start read register setting */ + mddi_wait(20); + /* Wait over 20ms */ + + serigo(0x0503); + /* Horizontal timing setting */ + serigo(0x062C); + /* Veritical timing setting */ + serigo(0x2001); + /* power initialize setting(VDC2) */ + mddi_wait(20); + /* Wait over 20ms */ + + serigo(0x2120); + /* Initialize power setting(CPS) */ + mddi_wait(20); + /* Wait over 20ms */ + + serigo(0x2130); + /* Initialize power setting(CPS) */ + mddi_wait(20); + /* Wait over 20ms */ + + serigo(0x2132); + /* Initialize power setting(CPS) */ + mddi_wait(10); + /* Wait over 10ms */ + + serigo(0x2133); + /* Initialize power setting(CPS) */ + mddi_wait(20); + /* Wait over 20ms */ + + serigo(0x0200); + /* Panel initialize release(INIT) */ + mddi_wait(1); + /* Wait over 1ms */ + + serigo(0x0131); + /* Panel setting(CPS) */ + mddi_wait(1); + /* Wait over 1ms */ + + mddi_queue_register_write(REG_PSTCTL1, 0x0003, TRUE, 0); + + /* if (FFA LCD is upside down) -> serigo(0x0100); */ + serigo(0x0130); + + /* Black mask release(display ON) */ + mddi_wait(1); + /* Wait over 1ms */ + + if (mddi_sharp_vsync_wake) { + mddi_queue_register_write(REG_VBLKS, 0x1001, TRUE, 0); + mddi_queue_register_write(REG_VBLKE, 0x1002, TRUE, 0); + } + + /* Set the MDP pixel data attributes for Primary Display */ + mddi_host_write_pix_attr_reg(0x00C3); + return; + +} + +void mddi_sharp_sub_lcd_init(void) +{ + + mddi_queue_register_write(REG_SYSCTL, 0x4000, FALSE, 0); + mddi_queue_register_write(REG_SYSCTL, 0x0000, TRUE, 0); + mddi_wait(100); + + mddi_queue_register_write(REG_SYSCTL, 0x0001, FALSE, 0); + mddi_queue_register_write(REG_CLKDIV1, 0x000b, FALSE, 0); + mddi_queue_register_write(REG_CLKCNF, 0x0708, FALSE, 0); + mddi_queue_register_write(REG_SYSCTL, 0x0201, FALSE, 0); + mddi_queue_register_write(REG_PTGCTL, 0x0010, FALSE, 0); + mddi_queue_register_write(REG_PTHP, 4, FALSE, 0); + mddi_queue_register_write(REG_PTHB, 40, FALSE, 0); + mddi_queue_register_write(REG_PTHW, 128, FALSE, 0); + mddi_queue_register_write(REG_PTHF, 92, FALSE, 0); + mddi_queue_register_write(REG_PTVP, 1, FALSE, 0); + mddi_queue_register_write(REG_PTVB, 2, FALSE, 0); + mddi_queue_register_write(REG_PTVW, 128, FALSE, 0); + mddi_queue_register_write(REG_PTVF, 15, FALSE, 0); + + /* Now the sub display..... */ + /* Reset High */ + mddi_queue_register_write(REG_SUBCTL, 0x0200, FALSE, 0); + /* CS=1,RD=1,WE=1,RS=1 */ + mddi_queue_register_write(REG_SUBTCMD, 0x000f, TRUE, 0); + mddi_wait(1); + /* Wait 5us */ + + if (sharp_subpanel_type == SHARP_SUB_UNKNOWN) { + uint32 data; + + sub_through_write(1, 0x05); + sub_through_write(1, 0x6A); + sub_through_write(1, 0x1D); + sub_through_write(1, 0x05); + data = sub_through_read(1); + if (data == 0x6A) { + sharp_subpanel_type = SHARP_SUB_HYNIX; + } else { + sub_through_write(0, 0x36); + sub_through_write(1, 0xA8); + sub_through_write(0, 0x09); + data = sub_through_read(1); + data = sub_through_read(1); + if (data == 0x54) { + sub_through_write(0, 0x36); + sub_through_write(1, 0x00); + sharp_subpanel_type = SHARP_SUB_ROHM; + } + } + } + + if (sharp_subpanel_type == SHARP_SUB_HYNIX) { + sub_through_write(1, 0x00); /* Display setting 1 */ + sub_through_write(1, 0x04); + sub_through_write(1, 0x01); + sub_through_write(1, 0x05); + sub_through_write(1, 0x0280); + sub_through_write(1, 0x0301); + sub_through_write(1, 0x0402); + sub_through_write(1, 0x0500); + sub_through_write(1, 0x0681); + sub_through_write(1, 0x077F); + sub_through_write(1, 0x08C0); + sub_through_write(1, 0x0905); + sub_through_write(1, 0x0A02); + sub_through_write(1, 0x0B00); + sub_through_write(1, 0x0C00); + sub_through_write(1, 0x0D00); + sub_through_write(1, 0x0E00); + sub_through_write(1, 0x0F00); + + sub_through_write(1, 0x100B); /* Display setting 2 */ + sub_through_write(1, 0x1103); + sub_through_write(1, 0x1237); + sub_through_write(1, 0x1300); + sub_through_write(1, 0x1400); + sub_through_write(1, 0x1500); + sub_through_write(1, 0x1605); + sub_through_write(1, 0x1700); + sub_through_write(1, 0x1800); + sub_through_write(1, 0x192E); + sub_through_write(1, 0x1A00); + sub_through_write(1, 0x1B00); + sub_through_write(1, 0x1C00); + + sub_through_write(1, 0x151A); /* Power setting */ + + sub_through_write(1, 0x2002); /* Gradation Palette setting */ + sub_through_write(1, 0x2107); + sub_through_write(1, 0x220C); + sub_through_write(1, 0x2310); + sub_through_write(1, 0x2414); + sub_through_write(1, 0x2518); + sub_through_write(1, 0x261C); + sub_through_write(1, 0x2720); + sub_through_write(1, 0x2824); + sub_through_write(1, 0x2928); + sub_through_write(1, 0x2A2B); + sub_through_write(1, 0x2B2E); + sub_through_write(1, 0x2C31); + sub_through_write(1, 0x2D34); + sub_through_write(1, 0x2E37); + sub_through_write(1, 0x2F3A); + sub_through_write(1, 0x303C); + sub_through_write(1, 0x313E); + sub_through_write(1, 0x323F); + sub_through_write(1, 0x3340); + sub_through_write(1, 0x3441); + sub_through_write(1, 0x3543); + sub_through_write(1, 0x3646); + sub_through_write(1, 0x3749); + sub_through_write(1, 0x384C); + sub_through_write(1, 0x394F); + sub_through_write(1, 0x3A52); + sub_through_write(1, 0x3B59); + sub_through_write(1, 0x3C60); + sub_through_write(1, 0x3D67); + sub_through_write(1, 0x3E6E); + sub_through_write(1, 0x3F7F); + sub_through_write(1, 0x4001); + sub_through_write(1, 0x4107); + sub_through_write(1, 0x420C); + sub_through_write(1, 0x4310); + sub_through_write(1, 0x4414); + sub_through_write(1, 0x4518); + sub_through_write(1, 0x461C); + sub_through_write(1, 0x4720); + sub_through_write(1, 0x4824); + sub_through_write(1, 0x4928); + sub_through_write(1, 0x4A2B); + sub_through_write(1, 0x4B2E); + sub_through_write(1, 0x4C31); + sub_through_write(1, 0x4D34); + sub_through_write(1, 0x4E37); + sub_through_write(1, 0x4F3A); + sub_through_write(1, 0x503C); + sub_through_write(1, 0x513E); + sub_through_write(1, 0x523F); + sub_through_write(1, 0x5340); + sub_through_write(1, 0x5441); + sub_through_write(1, 0x5543); + sub_through_write(1, 0x5646); + sub_through_write(1, 0x5749); + sub_through_write(1, 0x584C); + sub_through_write(1, 0x594F); + sub_through_write(1, 0x5A52); + sub_through_write(1, 0x5B59); + sub_through_write(1, 0x5C60); + sub_through_write(1, 0x5D67); + sub_through_write(1, 0x5E6E); + sub_through_write(1, 0x5F7E); + sub_through_write(1, 0x6000); + sub_through_write(1, 0x6107); + sub_through_write(1, 0x620C); + sub_through_write(1, 0x6310); + sub_through_write(1, 0x6414); + sub_through_write(1, 0x6518); + sub_through_write(1, 0x661C); + sub_through_write(1, 0x6720); + sub_through_write(1, 0x6824); + sub_through_write(1, 0x6928); + sub_through_write(1, 0x6A2B); + sub_through_write(1, 0x6B2E); + sub_through_write(1, 0x6C31); + sub_through_write(1, 0x6D34); + sub_through_write(1, 0x6E37); + sub_through_write(1, 0x6F3A); + sub_through_write(1, 0x703C); + sub_through_write(1, 0x713E); + sub_through_write(1, 0x723F); + sub_through_write(1, 0x7340); + sub_through_write(1, 0x7441); + sub_through_write(1, 0x7543); + sub_through_write(1, 0x7646); + sub_through_write(1, 0x7749); + sub_through_write(1, 0x784C); + sub_through_write(1, 0x794F); + sub_through_write(1, 0x7A52); + sub_through_write(1, 0x7B59); + sub_through_write(1, 0x7C60); + sub_through_write(1, 0x7D67); + sub_through_write(1, 0x7E6E); + sub_through_write(1, 0x7F7D); + + sub_through_write(1, 0x1851); /* Display on */ + + mddi_queue_register_write(REG_AGM, 0x0000, TRUE, 0); + + /* 1 pixel / 1 post clock */ + mddi_queue_register_write(REG_CLKDIV2, 0x3b00, FALSE, 0); + + /* SUB LCD select */ + mddi_queue_register_write(REG_PSTCTL2, 0x0080, FALSE, 0); + + /* RS=0,command initiate number=0,select master mode */ + mddi_queue_register_write(REG_SUBCTL, 0x0202, FALSE, 0); + + /* Sub LCD Data transform start */ + mddi_queue_register_write(REG_PSTCTL1, 0x0003, FALSE, 0); + + } else if (sharp_subpanel_type == SHARP_SUB_ROHM) { + + sub_through_write(0, 0x01); /* Display setting */ + sub_through_write(1, 0x00); + + mddi_wait(1); + /* Wait 100us <----- ******* Update 2005/01/24 */ + + sub_through_write(0, 0xB6); + sub_through_write(1, 0x0C); + sub_through_write(1, 0x4A); + sub_through_write(1, 0x20); + sub_through_write(0, 0x3A); + sub_through_write(1, 0x05); + sub_through_write(0, 0xB7); + sub_through_write(1, 0x01); + sub_through_write(0, 0xBA); + sub_through_write(1, 0x20); + sub_through_write(1, 0x02); + sub_through_write(0, 0x25); + sub_through_write(1, 0x4F); + sub_through_write(0, 0xBB); + sub_through_write(1, 0x00); + sub_through_write(0, 0x36); + sub_through_write(1, 0x00); + sub_through_write(0, 0xB1); + sub_through_write(1, 0x05); + sub_through_write(0, 0xBE); + sub_through_write(1, 0x80); + sub_through_write(0, 0x26); + sub_through_write(1, 0x01); + sub_through_write(0, 0x2A); + sub_through_write(1, 0x02); + sub_through_write(1, 0x81); + sub_through_write(0, 0x2B); + sub_through_write(1, 0x00); + sub_through_write(1, 0x7F); + + sub_through_write(0, 0x2C); + sub_through_write(0, 0x11); /* Sleep mode off */ + + mddi_wait(1); + /* Wait 100 ms <----- ******* Update 2005/01/24 */ + + sub_through_write(0, 0x29); /* Display on */ + sub_through_write(0, 0xB3); + sub_through_write(1, 0x20); + sub_through_write(1, 0xAA); + sub_through_write(1, 0xA0); + sub_through_write(1, 0x20); + sub_through_write(1, 0x30); + sub_through_write(1, 0xA6); + sub_through_write(1, 0xFF); + sub_through_write(1, 0x9A); + sub_through_write(1, 0x9F); + sub_through_write(1, 0xAF); + sub_through_write(1, 0xBC); + sub_through_write(1, 0xCF); + sub_through_write(1, 0xDF); + sub_through_write(1, 0x20); + sub_through_write(1, 0x9C); + sub_through_write(1, 0x8A); + + sub_through_write(0, 0x002C); /* Display on */ + + /* 1 pixel / 2 post clock */ + mddi_queue_register_write(REG_CLKDIV2, 0x7b00, FALSE, 0); + + /* SUB LCD select */ + mddi_queue_register_write(REG_PSTCTL2, 0x0080, FALSE, 0); + + /* RS=1,command initiate number=0,select master mode */ + mddi_queue_register_write(REG_SUBCTL, 0x0242, FALSE, 0); + + /* Sub LCD Data transform start */ + mddi_queue_register_write(REG_PSTCTL1, 0x0003, FALSE, 0); + + } + + /* Set the MDP pixel data attributes for Sub Display */ + mddi_host_write_pix_attr_reg(0x00C0); +} + +void mddi_sharp_lcd_vsync_detected(boolean detected) +{ + /* static timetick_type start_time = 0; */ + static struct timeval start_time; + static boolean first_time = TRUE; + /* uint32 mdp_cnt_val = 0; */ + /* timetick_type elapsed_us; */ + struct timeval now; + uint32 elapsed_us; + uint32 num_vsyncs; + + if ((detected) || (mddi_sharp_vsync_attempts > 5)) { + if ((detected) && (mddi_sharp_monitor_refresh_value)) { + /* if (start_time != 0) */ + if (!first_time) { + jiffies_to_timeval(jiffies, &now); + elapsed_us = + (now.tv_sec - start_time.tv_sec) * 1000000 + + now.tv_usec - start_time.tv_usec; + /* + * LCD is configured for a refresh every usecs, + * so to determine the number of vsyncs that + * have occurred since the last measurement add + * half that to the time difference and divide + * by the refresh rate. + */ + num_vsyncs = (elapsed_us + + (mddi_sharp_usecs_per_refresh >> + 1)) / + mddi_sharp_usecs_per_refresh; + /* + * LCD is configured for * hsyncs (rows) per + * refresh cycle. Calculate new rows_per_second + * value based upon these new measurements. + * MDP can update with this new value. + */ + mddi_sharp_rows_per_second = + (mddi_sharp_rows_per_refresh * 1000 * + num_vsyncs) / (elapsed_us / 1000); + } + /* start_time = timetick_get(); */ + first_time = FALSE; + jiffies_to_timeval(jiffies, &start_time); + if (mddi_sharp_report_refresh_measurements) { + /* mdp_cnt_val = MDP_LINE_COUNT; */ + } + } + /* if detected = TRUE, client initiated wakeup was detected */ + if (mddi_sharp_vsync_handler != NULL) { + (*mddi_sharp_vsync_handler) + (mddi_sharp_vsync_handler_arg); + mddi_sharp_vsync_handler = NULL; + } + mddi_vsync_detect_enabled = FALSE; + mddi_sharp_vsync_attempts = 0; + /* need to clear this vsync wakeup */ + if (!mddi_queue_register_write_int(REG_INTR, 0x0000)) { + MDDI_MSG_ERR("Vsync interrupt clear failed!\n"); + } + if (!detected) { + /* give up after 5 failed attempts but show error */ + MDDI_MSG_NOTICE("Vsync detection failed!\n"); + } else if ((mddi_sharp_monitor_refresh_value) && + (mddi_sharp_report_refresh_measurements)) { + MDDI_MSG_NOTICE(" Lines Per Second=%d!\n", + mddi_sharp_rows_per_second); + } + } else + /* if detected = FALSE, we woke up from hibernation, but did not + * detect client initiated wakeup. + */ + mddi_sharp_vsync_attempts++; +} + +/* ISR to be executed */ +void mddi_sharp_vsync_set_handler(msm_fb_vsync_handler_type handler, void *arg) +{ + boolean error = FALSE; + unsigned long flags; + + /* Disable interrupts */ + spin_lock_irqsave(&mddi_host_spin_lock, flags); + /* INTLOCK(); */ + + if (mddi_sharp_vsync_handler != NULL) + error = TRUE; + + /* Register the handler for this particular GROUP interrupt source */ + mddi_sharp_vsync_handler = handler; + mddi_sharp_vsync_handler_arg = arg; + + /* Restore interrupts */ + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + /* INTFREE(); */ + + if (error) + MDDI_MSG_ERR("MDDI: Previous Vsync handler never called\n"); + + /* Enable the vsync wakeup */ + mddi_queue_register_write(REG_INTR, 0x8100, FALSE, 0); + + mddi_sharp_vsync_attempts = 1; + mddi_vsync_detect_enabled = TRUE; +} /* mddi_sharp_vsync_set_handler */ + +static int mddi_sharp_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + mddi_host_client_cnt_reset(); + + if (mfd->panel.id == SHARP_QVGA_PRIM) + mddi_sharp_prim_lcd_init(); + else + mddi_sharp_sub_lcd_init(); + + return 0; +} + +static int mddi_sharp_lcd_off(struct platform_device *pdev) +{ + if (mddi_sharp_vsync_handler != NULL) { + (*mddi_sharp_vsync_handler) + (mddi_sharp_vsync_handler_arg); + mddi_sharp_vsync_handler = NULL; + printk(KERN_INFO "%s: clean up vsyn_handler=%x\n", __func__, + (int)mddi_sharp_vsync_handler); + } + + mddi_sharp_lcd_powerdown(); + return 0; +} + +static int mddi_sharp_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + mddi_sharp_pdata = pdev->dev.platform_data; + return 0; + } + + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = mddi_sharp_probe, + .driver = { + .name = "mddi_sharp_qvga", + }, +}; + +static struct msm_fb_panel_data mddi_sharp_panel_data0 = { + .on = mddi_sharp_lcd_on, + .off = mddi_sharp_lcd_off, + .set_backlight = mddi_sharp_lcd_set_backlight, + .set_vsync_notifier = mddi_sharp_vsync_set_handler, +}; + +static struct platform_device this_device_0 = { + .name = "mddi_sharp_qvga", + .id = SHARP_QVGA_PRIM, + .dev = { + .platform_data = &mddi_sharp_panel_data0, + } +}; + +static struct msm_fb_panel_data mddi_sharp_panel_data1 = { + .on = mddi_sharp_lcd_on, + .off = mddi_sharp_lcd_off, +}; + +static struct platform_device this_device_1 = { + .name = "mddi_sharp_qvga", + .id = SHARP_128X128_SECD, + .dev = { + .platform_data = &mddi_sharp_panel_data1, + } +}; + +static int __init mddi_sharp_init(void) +{ + int ret; + struct msm_panel_info *pinfo; + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + u32 id; + + ret = msm_fb_detect_client("mddi_sharp_qvga"); + if (ret == -ENODEV) + return 0; + + if (ret) { + id = mddi_get_client_id(); + + if (((id >> 16) != 0x0) || ((id & 0xffff) != 0x8835)) + return 0; + } +#endif + if (mddi_host_core_version > 8) { + /* can use faster refresh with newer hw revisions */ + mddi_sharp_debug_60hz_refresh = TRUE; + + /* Timing variables for tracking vsync */ + /* dot_clock = 6.00MHz + * horizontal count = 296 + * vertical count = 338 + * refresh rate = 6000000/(296+338) = 60Hz + */ + mddi_sharp_rows_per_second = 20270; /* 6000000/296 */ + mddi_sharp_rows_per_refresh = 338; + mddi_sharp_usecs_per_refresh = 16674; /* (296+338)/6000000 */ + } else { + /* Timing variables for tracking vsync */ + /* dot_clock = 5.20MHz + * horizontal count = 376 + * vertical count = 338 + * refresh rate = 5200000/(376+338) = 41Hz + */ + mddi_sharp_rows_per_second = 13830; /* 5200000/376 */ + mddi_sharp_rows_per_refresh = 338; + mddi_sharp_usecs_per_refresh = 24440; /* (376+338)/5200000 */ + } + + ret = platform_driver_register(&this_driver); + if (!ret) { + pinfo = &mddi_sharp_panel_data0.panel_info; + pinfo->xres = 240; + pinfo->yres = 320; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = MDDI_PANEL; + pinfo->pdest = DISPLAY_1; + pinfo->mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->fb_num = 2; + pinfo->clk_rate = 122880000; + pinfo->clk_min = 120000000; + pinfo->clk_max = 125000000; + pinfo->lcd.vsync_enable = TRUE; + pinfo->mddi.is_type1 = TRUE; + pinfo->lcd.refx100 = + (mddi_sharp_rows_per_second * 100) / + mddi_sharp_rows_per_refresh; + pinfo->lcd.v_back_porch = 12; + pinfo->lcd.v_front_porch = 6; + pinfo->lcd.v_pulse_width = 0; + pinfo->lcd.hw_vsync_mode = FALSE; + pinfo->lcd.vsync_notifier_period = (1 * HZ); + pinfo->bl_max = 7; + pinfo->bl_min = 1; + + ret = platform_device_register(&this_device_0); + if (ret) + platform_driver_unregister(&this_driver); + + pinfo = &mddi_sharp_panel_data1.panel_info; + pinfo->xres = 128; + pinfo->yres = 128; + MSM_FB_SINGLE_MODE_PANEL(pinfo); + pinfo->type = MDDI_PANEL; + pinfo->pdest = DISPLAY_2; + pinfo->mddi.vdopkt = 0x400; + pinfo->wait_cycle = 0; + pinfo->bpp = 18; + pinfo->clk_rate = 122880000; + pinfo->clk_min = 120000000; + pinfo->clk_max = 125000000; + pinfo->fb_num = 2; + + ret = platform_device_register(&this_device_1); + if (ret) { + platform_device_unregister(&this_device_0); + platform_driver_unregister(&this_driver); + } + } + + if (!ret) + mddi_lcd.vsync_detected = mddi_sharp_lcd_vsync_detected; + + return ret; +} + +module_init(mddi_sharp_init); diff --git a/drivers/video/msm/mddi_toshiba.c b/drivers/video/msm/mddi_toshiba.c new file mode 100644 index 0000000000000000000000000000000000000000..bba83866a7560f971f08d3d89579d609543add2d --- /dev/null +++ b/drivers/video/msm/mddi_toshiba.c @@ -0,0 +1,1753 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" +#include "mddi_toshiba.h" + +#define TM_GET_DID(id) ((id) & 0xff) +#define TM_GET_PID(id) (((id) & 0xff00)>>8) + +#define MDDI_CLIENT_CORE_BASE 0x108000 +#define LCD_CONTROL_BLOCK_BASE 0x110000 +#define SPI_BLOCK_BASE 0x120000 +#define PWM_BLOCK_BASE 0x140000 +#define SYSTEM_BLOCK1_BASE 0x160000 + +#define TTBUSSEL (MDDI_CLIENT_CORE_BASE|0x18) +#define DPSET0 (MDDI_CLIENT_CORE_BASE|0x1C) +#define DPSET1 (MDDI_CLIENT_CORE_BASE|0x20) +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define DPRUN (MDDI_CLIENT_CORE_BASE|0x28) +#define SYSCKENA (MDDI_CLIENT_CORE_BASE|0x2C) + +#define BITMAP0 (MDDI_CLIENT_CORE_BASE|0x44) +#define BITMAP1 (MDDI_CLIENT_CORE_BASE|0x48) +#define BITMAP2 (MDDI_CLIENT_CORE_BASE|0x4C) +#define BITMAP3 (MDDI_CLIENT_CORE_BASE|0x50) +#define BITMAP4 (MDDI_CLIENT_CORE_BASE|0x54) + +#define SRST (LCD_CONTROL_BLOCK_BASE|0x00) +#define PORT_ENB (LCD_CONTROL_BLOCK_BASE|0x04) +#define START (LCD_CONTROL_BLOCK_BASE|0x08) +#define PORT (LCD_CONTROL_BLOCK_BASE|0x0C) + +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define INTMSK (LCD_CONTROL_BLOCK_BASE|0x1C) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) + +#define PXL (LCD_CONTROL_BLOCK_BASE|0x30) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HSW (LCD_CONTROL_BLOCK_BASE|0x38) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define HDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x40) +#define VCYCLE (LCD_CONTROL_BLOCK_BASE|0x44) +#define VSW (LCD_CONTROL_BLOCK_BASE|0x48) +#define VDE_START (LCD_CONTROL_BLOCK_BASE|0x4C) +#define VDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x50) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) +#define VSYNIF (LCD_CONTROL_BLOCK_BASE|0x60) +#define WRSTB (LCD_CONTROL_BLOCK_BASE|0x64) +#define RDSTB (LCD_CONTROL_BLOCK_BASE|0x68) +#define ASY_DATA (LCD_CONTROL_BLOCK_BASE|0x6C) +#define ASY_DATB (LCD_CONTROL_BLOCK_BASE|0x70) +#define ASY_DATC (LCD_CONTROL_BLOCK_BASE|0x74) +#define ASY_DATD (LCD_CONTROL_BLOCK_BASE|0x78) +#define ASY_DATE (LCD_CONTROL_BLOCK_BASE|0x7C) +#define ASY_DATF (LCD_CONTROL_BLOCK_BASE|0x80) +#define ASY_DATG (LCD_CONTROL_BLOCK_BASE|0x84) +#define ASY_DATH (LCD_CONTROL_BLOCK_BASE|0x88) +#define ASY_CMDSET (LCD_CONTROL_BLOCK_BASE|0x8C) +#define MONI (LCD_CONTROL_BLOCK_BASE|0xB0) +#define VPOS (LCD_CONTROL_BLOCK_BASE|0xC0) + +#define SSICTL (SPI_BLOCK_BASE|0x00) +#define SSITIME (SPI_BLOCK_BASE|0x04) +#define SSITX (SPI_BLOCK_BASE|0x08) +#define SSIINTS (SPI_BLOCK_BASE|0x14) + +#define TIMER0LOAD (PWM_BLOCK_BASE|0x00) +#define TIMER0CTRL (PWM_BLOCK_BASE|0x08) +#define PWM0OFF (PWM_BLOCK_BASE|0x1C) +#define TIMER1LOAD (PWM_BLOCK_BASE|0x20) +#define TIMER1CTRL (PWM_BLOCK_BASE|0x28) +#define PWM1OFF (PWM_BLOCK_BASE|0x3C) +#define TIMER2LOAD (PWM_BLOCK_BASE|0x40) +#define TIMER2CTRL (PWM_BLOCK_BASE|0x48) +#define PWM2OFF (PWM_BLOCK_BASE|0x5C) +#define PWMCR (PWM_BLOCK_BASE|0x68) + +#define GPIOIS (GPIO_BLOCK_BASE|0x08) +#define GPIOIEV (GPIO_BLOCK_BASE|0x10) +#define GPIOIC (GPIO_BLOCK_BASE|0x20) + +#define WKREQ (SYSTEM_BLOCK1_BASE|0x00) +#define CLKENB (SYSTEM_BLOCK1_BASE|0x04) +#define DRAMPWR (SYSTEM_BLOCK1_BASE|0x08) +#define INTMASK (SYSTEM_BLOCK1_BASE|0x0C) +#define CNT_DIS (SYSTEM_BLOCK1_BASE|0x10) + +typedef enum { + TOSHIBA_STATE_OFF, + TOSHIBA_STATE_PRIM_SEC_STANDBY, + TOSHIBA_STATE_PRIM_SEC_READY, + TOSHIBA_STATE_PRIM_NORMAL_MODE, + TOSHIBA_STATE_SEC_NORMAL_MODE +} mddi_toshiba_state_t; + +static uint32 mddi_toshiba_curr_vpos; +static boolean mddi_toshiba_monitor_refresh_value = FALSE; +static boolean mddi_toshiba_report_refresh_measurements = FALSE; + +boolean mddi_toshiba_61Hz_refresh = TRUE; + +/* Modifications to timing to increase refresh rate to > 60Hz. + * 20MHz dot clock. + * 646 total rows. + * 506 total columns. + * refresh rate = 61.19Hz + */ +static uint32 mddi_toshiba_rows_per_second = 39526; +static uint32 mddi_toshiba_usecs_per_refresh = 16344; +static uint32 mddi_toshiba_rows_per_refresh = 646; +extern boolean mddi_vsync_detect_enabled; + +static msm_fb_vsync_handler_type mddi_toshiba_vsync_handler; +static void *mddi_toshiba_vsync_handler_arg; +static uint16 mddi_toshiba_vsync_attempts; + +static mddi_toshiba_state_t toshiba_state = TOSHIBA_STATE_OFF; + +static struct msm_panel_common_pdata *mddi_toshiba_pdata; + +static int mddi_toshiba_lcd_on(struct platform_device *pdev); +static int mddi_toshiba_lcd_off(struct platform_device *pdev); + +static void mddi_toshiba_state_transition(mddi_toshiba_state_t a, + mddi_toshiba_state_t b) +{ + if (toshiba_state != a) { + MDDI_MSG_ERR("toshiba state trans. (%d->%d) found %d\n", a, b, + toshiba_state); + } + toshiba_state = b; +} + +#define GORDON_REG_IMGCTL1 0x10 /* Image interface control 1 */ +#define GORDON_REG_IMGCTL2 0x11 /* Image interface control 2 */ +#define GORDON_REG_IMGSET1 0x12 /* Image interface settings 1 */ +#define GORDON_REG_IMGSET2 0x13 /* Image interface settings 2 */ +#define GORDON_REG_IVBP1 0x14 /* DM0: Vert back porch */ +#define GORDON_REG_IHBP1 0x15 /* DM0: Horiz back porch */ +#define GORDON_REG_IVNUM1 0x16 /* DM0: Num of vert lines */ +#define GORDON_REG_IHNUM1 0x17 /* DM0: Num of pixels per line */ +#define GORDON_REG_IVBP2 0x18 /* DM1: Vert back porch */ +#define GORDON_REG_IHBP2 0x19 /* DM1: Horiz back porch */ +#define GORDON_REG_IVNUM2 0x1A /* DM1: Num of vert lines */ +#define GORDON_REG_IHNUM2 0x1B /* DM1: Num of pixels per line */ +#define GORDON_REG_LCDIFCTL1 0x30 /* LCD interface control 1 */ +#define GORDON_REG_VALTRAN 0x31 /* LCD IF ctl: VALTRAN sync flag */ +#define GORDON_REG_AVCTL 0x33 +#define GORDON_REG_LCDIFCTL2 0x34 /* LCD interface control 2 */ +#define GORDON_REG_LCDIFCTL3 0x35 /* LCD interface control 3 */ +#define GORDON_REG_LCDIFSET1 0x36 /* LCD interface settings 1 */ +#define GORDON_REG_PCCTL 0x3C +#define GORDON_REG_TPARAM1 0x40 +#define GORDON_REG_TLCDIF1 0x41 +#define GORDON_REG_TSSPB_ST1 0x42 +#define GORDON_REG_TSSPB_ED1 0x43 +#define GORDON_REG_TSCK_ST1 0x44 +#define GORDON_REG_TSCK_WD1 0x45 +#define GORDON_REG_TGSPB_VST1 0x46 +#define GORDON_REG_TGSPB_VED1 0x47 +#define GORDON_REG_TGSPB_CH1 0x48 +#define GORDON_REG_TGCK_ST1 0x49 +#define GORDON_REG_TGCK_ED1 0x4A +#define GORDON_REG_TPCTL_ST1 0x4B +#define GORDON_REG_TPCTL_ED1 0x4C +#define GORDON_REG_TPCHG_ED1 0x4D +#define GORDON_REG_TCOM_CH1 0x4E +#define GORDON_REG_THBP1 0x4F +#define GORDON_REG_TPHCTL1 0x50 +#define GORDON_REG_EVPH1 0x51 +#define GORDON_REG_EVPL1 0x52 +#define GORDON_REG_EVNH1 0x53 +#define GORDON_REG_EVNL1 0x54 +#define GORDON_REG_TBIAS1 0x55 +#define GORDON_REG_TPARAM2 0x56 +#define GORDON_REG_TLCDIF2 0x57 +#define GORDON_REG_TSSPB_ST2 0x58 +#define GORDON_REG_TSSPB_ED2 0x59 +#define GORDON_REG_TSCK_ST2 0x5A +#define GORDON_REG_TSCK_WD2 0x5B +#define GORDON_REG_TGSPB_VST2 0x5C +#define GORDON_REG_TGSPB_VED2 0x5D +#define GORDON_REG_TGSPB_CH2 0x5E +#define GORDON_REG_TGCK_ST2 0x5F +#define GORDON_REG_TGCK_ED2 0x60 +#define GORDON_REG_TPCTL_ST2 0x61 +#define GORDON_REG_TPCTL_ED2 0x62 +#define GORDON_REG_TPCHG_ED2 0x63 +#define GORDON_REG_TCOM_CH2 0x64 +#define GORDON_REG_THBP2 0x65 +#define GORDON_REG_TPHCTL2 0x66 +#define GORDON_REG_EVPH2 0x67 +#define GORDON_REG_EVPL2 0x68 +#define GORDON_REG_EVNH2 0x69 +#define GORDON_REG_EVNL2 0x6A +#define GORDON_REG_TBIAS2 0x6B +#define GORDON_REG_POWCTL 0x80 +#define GORDON_REG_POWOSC1 0x81 +#define GORDON_REG_POWOSC2 0x82 +#define GORDON_REG_POWSET 0x83 +#define GORDON_REG_POWTRM1 0x85 +#define GORDON_REG_POWTRM2 0x86 +#define GORDON_REG_POWTRM3 0x87 +#define GORDON_REG_POWTRMSEL 0x88 +#define GORDON_REG_POWHIZ 0x89 + +void serigo(uint16 reg, uint8 data) +{ + uint32 mddi_val = 0; + mddi_queue_register_read(SSIINTS, &mddi_val, TRUE, 0); + if (mddi_val & (1 << 8)) + mddi_wait(1); + /* No De-assert of CS and send 2 bytes */ + mddi_val = 0x90000 | ((0x00FF & reg) << 8) | data; + mddi_queue_register_write(SSITX, mddi_val, TRUE, 0); +} + +void gordon_init(void) +{ + /* Image interface settings ***/ + serigo(GORDON_REG_IMGCTL2, 0x00); + serigo(GORDON_REG_IMGSET1, 0x01); + + /* Exchange the RGB signal for J510(Softbank mobile) */ + serigo(GORDON_REG_IMGSET2, 0x12); + serigo(GORDON_REG_LCDIFSET1, 0x00); + mddi_wait(2); + + /* Pre-charge settings */ + serigo(GORDON_REG_PCCTL, 0x09); + serigo(GORDON_REG_LCDIFCTL2, 0x1B); + mddi_wait(1); +} + +void gordon_disp_on(void) +{ + /*gordon_dispmode setting */ + /*VGA settings */ + serigo(GORDON_REG_TPARAM1, 0x30); + serigo(GORDON_REG_TLCDIF1, 0x00); + serigo(GORDON_REG_TSSPB_ST1, 0x8B); + serigo(GORDON_REG_TSSPB_ED1, 0x93); + mddi_wait(2); + serigo(GORDON_REG_TSCK_ST1, 0x88); + serigo(GORDON_REG_TSCK_WD1, 0x00); + serigo(GORDON_REG_TGSPB_VST1, 0x01); + serigo(GORDON_REG_TGSPB_VED1, 0x02); + mddi_wait(2); + serigo(GORDON_REG_TGSPB_CH1, 0x5E); + serigo(GORDON_REG_TGCK_ST1, 0x80); + serigo(GORDON_REG_TGCK_ED1, 0x3C); + serigo(GORDON_REG_TPCTL_ST1, 0x50); + mddi_wait(2); + serigo(GORDON_REG_TPCTL_ED1, 0x74); + serigo(GORDON_REG_TPCHG_ED1, 0x78); + serigo(GORDON_REG_TCOM_CH1, 0x50); + serigo(GORDON_REG_THBP1, 0x84); + mddi_wait(2); + serigo(GORDON_REG_TPHCTL1, 0x00); + serigo(GORDON_REG_EVPH1, 0x70); + serigo(GORDON_REG_EVPL1, 0x64); + serigo(GORDON_REG_EVNH1, 0x56); + mddi_wait(2); + serigo(GORDON_REG_EVNL1, 0x48); + serigo(GORDON_REG_TBIAS1, 0x88); + mddi_wait(2); + serigo(GORDON_REG_TPARAM2, 0x28); + serigo(GORDON_REG_TLCDIF2, 0x14); + serigo(GORDON_REG_TSSPB_ST2, 0x49); + serigo(GORDON_REG_TSSPB_ED2, 0x4B); + mddi_wait(2); + serigo(GORDON_REG_TSCK_ST2, 0x4A); + serigo(GORDON_REG_TSCK_WD2, 0x02); + serigo(GORDON_REG_TGSPB_VST2, 0x02); + serigo(GORDON_REG_TGSPB_VED2, 0x03); + mddi_wait(2); + serigo(GORDON_REG_TGSPB_CH2, 0x2F); + serigo(GORDON_REG_TGCK_ST2, 0x40); + serigo(GORDON_REG_TGCK_ED2, 0x1E); + serigo(GORDON_REG_TPCTL_ST2, 0x2C); + mddi_wait(2); + serigo(GORDON_REG_TPCTL_ED2, 0x3A); + serigo(GORDON_REG_TPCHG_ED2, 0x3C); + serigo(GORDON_REG_TCOM_CH2, 0x28); + serigo(GORDON_REG_THBP2, 0x4D); + mddi_wait(2); + serigo(GORDON_REG_TPHCTL2, 0x1A); + mddi_wait(2); + serigo(GORDON_REG_IVBP1, 0x02); + serigo(GORDON_REG_IHBP1, 0x90); + serigo(GORDON_REG_IVNUM1, 0xA0); + serigo(GORDON_REG_IHNUM1, 0x78); + mddi_wait(2); + serigo(GORDON_REG_IVBP2, 0x02); + serigo(GORDON_REG_IHBP2, 0x48); + serigo(GORDON_REG_IVNUM2, 0x50); + serigo(GORDON_REG_IHNUM2, 0x3C); + mddi_wait(2); + serigo(GORDON_REG_POWCTL, 0x03); + mddi_wait(15); + serigo(GORDON_REG_POWCTL, 0x07); + mddi_wait(15); + serigo(GORDON_REG_POWCTL, 0x0F); + mddi_wait(15); + serigo(GORDON_REG_AVCTL, 0x03); + mddi_wait(15); + serigo(GORDON_REG_POWCTL, 0x1F); + mddi_wait(15); + serigo(GORDON_REG_POWCTL, 0x5F); + mddi_wait(15); + serigo(GORDON_REG_POWCTL, 0x7F); + mddi_wait(15); + serigo(GORDON_REG_LCDIFCTL1, 0x02); + mddi_wait(15); + serigo(GORDON_REG_IMGCTL1, 0x00); + mddi_wait(15); + serigo(GORDON_REG_LCDIFCTL3, 0x00); + mddi_wait(15); + serigo(GORDON_REG_VALTRAN, 0x01); + mddi_wait(15); + serigo(GORDON_REG_LCDIFCTL1, 0x03); + serigo(GORDON_REG_LCDIFCTL1, 0x03); + mddi_wait(1); +} + +void gordon_disp_off(void) +{ + serigo(GORDON_REG_LCDIFCTL2, 0x7B); + serigo(GORDON_REG_VALTRAN, 0x01); + serigo(GORDON_REG_LCDIFCTL1, 0x02); + serigo(GORDON_REG_LCDIFCTL3, 0x01); + mddi_wait(20); + serigo(GORDON_REG_VALTRAN, 0x01); + serigo(GORDON_REG_IMGCTL1, 0x01); + serigo(GORDON_REG_LCDIFCTL1, 0x00); + mddi_wait(20); + serigo(GORDON_REG_POWCTL, 0x1F); + mddi_wait(40); + serigo(GORDON_REG_POWCTL, 0x07); + mddi_wait(40); + serigo(GORDON_REG_POWCTL, 0x03); + mddi_wait(40); + serigo(GORDON_REG_POWCTL, 0x00); + mddi_wait(40); +} + +void gordon_disp_init(void) +{ + gordon_init(); + mddi_wait(20); + gordon_disp_on(); +} + +static void toshiba_common_initial_setup(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) { + write_client_reg(DPSET0 , 0x4bec0066, TRUE); + write_client_reg(DPSET1 , 0x00000113, TRUE); + write_client_reg(DPSUS , 0x00000000, TRUE); + write_client_reg(DPRUN , 0x00000001, TRUE); + mddi_wait(5); + write_client_reg(SYSCKENA , 0x00000001, TRUE); + write_client_reg(CLKENB , 0x0000a0e9, TRUE); + + write_client_reg(GPIODATA , 0x03FF0000, TRUE); + write_client_reg(GPIODIR , 0x0000024D, TRUE); + write_client_reg(GPIOSEL , 0x00000173, TRUE); + write_client_reg(GPIOPC , 0x03C300C0, TRUE); + write_client_reg(WKREQ , 0x00000000, TRUE); + write_client_reg(GPIOIS , 0x00000000, TRUE); + write_client_reg(GPIOIEV , 0x00000001, TRUE); + write_client_reg(GPIOIC , 0x000003FF, TRUE); + write_client_reg(GPIODATA , 0x00040004, TRUE); + + write_client_reg(GPIODATA , 0x00080008, TRUE); + write_client_reg(DRAMPWR , 0x00000001, TRUE); + write_client_reg(CLKENB , 0x0000a0eb, TRUE); + write_client_reg(PWMCR , 0x00000000, TRUE); + mddi_wait(1); + + write_client_reg(SSICTL , 0x00060399, TRUE); + write_client_reg(SSITIME , 0x00000100, TRUE); + write_client_reg(CNT_DIS , 0x00000002, TRUE); + write_client_reg(SSICTL , 0x0006039b, TRUE); + + write_client_reg(SSITX , 0x00000000, TRUE); + mddi_wait(7); + write_client_reg(SSITX , 0x00000000, TRUE); + mddi_wait(7); + write_client_reg(SSITX , 0x00000000, TRUE); + mddi_wait(7); + + write_client_reg(SSITX , 0x000800BA, TRUE); + write_client_reg(SSITX , 0x00000111, TRUE); + write_client_reg(SSITX , 0x00080036, TRUE); + write_client_reg(SSITX , 0x00000100, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x0008003A, TRUE); + write_client_reg(SSITX , 0x00000160, TRUE); + write_client_reg(SSITX , 0x000800B1, TRUE); + write_client_reg(SSITX , 0x0000015D, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800B2, TRUE); + write_client_reg(SSITX , 0x00000133, TRUE); + write_client_reg(SSITX , 0x000800B3, TRUE); + write_client_reg(SSITX , 0x00000122, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800B4, TRUE); + write_client_reg(SSITX , 0x00000102, TRUE); + write_client_reg(SSITX , 0x000800B5, TRUE); + write_client_reg(SSITX , 0x0000011E, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800B6, TRUE); + write_client_reg(SSITX , 0x00000127, TRUE); + write_client_reg(SSITX , 0x000800B7, TRUE); + write_client_reg(SSITX , 0x00000103, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800B9, TRUE); + write_client_reg(SSITX , 0x00000124, TRUE); + write_client_reg(SSITX , 0x000800BD, TRUE); + write_client_reg(SSITX , 0x000001A1, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800BB, TRUE); + write_client_reg(SSITX , 0x00000100, TRUE); + write_client_reg(SSITX , 0x000800BF, TRUE); + write_client_reg(SSITX , 0x00000101, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800BE, TRUE); + write_client_reg(SSITX , 0x00000100, TRUE); + write_client_reg(SSITX , 0x000800C0, TRUE); + write_client_reg(SSITX , 0x00000111, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800C1, TRUE); + write_client_reg(SSITX , 0x00000111, TRUE); + write_client_reg(SSITX , 0x000800C2, TRUE); + write_client_reg(SSITX , 0x00000111, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800C3, TRUE); + write_client_reg(SSITX , 0x00080132, TRUE); + write_client_reg(SSITX , 0x00000132, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800C4, TRUE); + write_client_reg(SSITX , 0x00080132, TRUE); + write_client_reg(SSITX , 0x00000132, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800C5, TRUE); + write_client_reg(SSITX , 0x00080132, TRUE); + write_client_reg(SSITX , 0x00000132, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800C6, TRUE); + write_client_reg(SSITX , 0x00080132, TRUE); + write_client_reg(SSITX , 0x00000132, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800C7, TRUE); + write_client_reg(SSITX , 0x00080164, TRUE); + write_client_reg(SSITX , 0x00000145, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800C8, TRUE); + write_client_reg(SSITX , 0x00000144, TRUE); + write_client_reg(SSITX , 0x000800C9, TRUE); + write_client_reg(SSITX , 0x00000152, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800CA, TRUE); + write_client_reg(SSITX , 0x00000100, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800EC, TRUE); + write_client_reg(SSITX , 0x00080101, TRUE); + write_client_reg(SSITX , 0x000001FC, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800CF, TRUE); + write_client_reg(SSITX , 0x00000101, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800D0, TRUE); + write_client_reg(SSITX , 0x00080110, TRUE); + write_client_reg(SSITX , 0x00000104, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800D1, TRUE); + write_client_reg(SSITX , 0x00000101, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800D2, TRUE); + write_client_reg(SSITX , 0x00080100, TRUE); + write_client_reg(SSITX , 0x00000128, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800D3, TRUE); + write_client_reg(SSITX , 0x00080100, TRUE); + write_client_reg(SSITX , 0x00000128, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800D4, TRUE); + write_client_reg(SSITX , 0x00080126, TRUE); + write_client_reg(SSITX , 0x000001A4, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800D5, TRUE); + write_client_reg(SSITX , 0x00000120, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800EF, TRUE); + write_client_reg(SSITX , 0x00080132, TRUE); + write_client_reg(SSITX , 0x00000100, TRUE); + mddi_wait(1); + + write_client_reg(BITMAP0 , 0x032001E0, TRUE); + write_client_reg(BITMAP1 , 0x032001E0, TRUE); + write_client_reg(BITMAP2 , 0x014000F0, TRUE); + write_client_reg(BITMAP3 , 0x014000F0, TRUE); + write_client_reg(BITMAP4 , 0x014000F0, TRUE); + write_client_reg(CLKENB , 0x0000A1EB, TRUE); + write_client_reg(PORT_ENB , 0x00000001, TRUE); + write_client_reg(PORT , 0x00000004, TRUE); + write_client_reg(PXL , 0x00000002, TRUE); + write_client_reg(MPLFBUF , 0x00000000, TRUE); + write_client_reg(HCYCLE , 0x000000FD, TRUE); + write_client_reg(HSW , 0x00000003, TRUE); + write_client_reg(HDE_START , 0x00000007, TRUE); + write_client_reg(HDE_SIZE , 0x000000EF, TRUE); + write_client_reg(VCYCLE , 0x00000325, TRUE); + write_client_reg(VSW , 0x00000001, TRUE); + write_client_reg(VDE_START , 0x00000003, TRUE); + write_client_reg(VDE_SIZE , 0x0000031F, TRUE); + write_client_reg(START , 0x00000001, TRUE); + mddi_wait(32); + write_client_reg(SSITX , 0x000800BC, TRUE); + write_client_reg(SSITX , 0x00000180, TRUE); + write_client_reg(SSITX , 0x0008003B, TRUE); + write_client_reg(SSITX , 0x00000100, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800B0, TRUE); + write_client_reg(SSITX , 0x00000116, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x000800B8, TRUE); + write_client_reg(SSITX , 0x000801FF, TRUE); + write_client_reg(SSITX , 0x000001F5, TRUE); + mddi_wait(1); + write_client_reg(SSITX , 0x00000011, TRUE); + mddi_wait(5); + write_client_reg(SSITX , 0x00000029, TRUE); + return; + } + + if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) { + write_client_reg(DPSET0, 0x4BEC0066, TRUE); + write_client_reg(DPSET1, 0x00000113, TRUE); + write_client_reg(DPSUS, 0x00000000, TRUE); + write_client_reg(DPRUN, 0x00000001, TRUE); + mddi_wait(14); + write_client_reg(SYSCKENA, 0x00000001, TRUE); + write_client_reg(CLKENB, 0x000000EF, TRUE); + write_client_reg(GPIO_BLOCK_BASE, 0x03FF0000, TRUE); + write_client_reg(GPIODIR, 0x0000024D, TRUE); + write_client_reg(SYSTEM_BLOCK2_BASE, 0x00000173, TRUE); + write_client_reg(GPIOPC, 0x03C300C0, TRUE); + write_client_reg(SYSTEM_BLOCK1_BASE, 0x00000000, TRUE); + write_client_reg(GPIOIS, 0x00000000, TRUE); + write_client_reg(GPIOIEV, 0x00000001, TRUE); + write_client_reg(GPIOIC, 0x000003FF, TRUE); + write_client_reg(GPIO_BLOCK_BASE, 0x00060006, TRUE); + write_client_reg(GPIO_BLOCK_BASE, 0x00080008, TRUE); + write_client_reg(GPIO_BLOCK_BASE, 0x02000200, TRUE); + write_client_reg(DRAMPWR, 0x00000001, TRUE); + write_client_reg(TIMER0CTRL, 0x00000060, TRUE); + write_client_reg(PWM_BLOCK_BASE, 0x00001388, TRUE); + write_client_reg(PWM0OFF, 0x00001387, TRUE); + write_client_reg(TIMER1CTRL, 0x00000060, TRUE); + write_client_reg(TIMER1LOAD, 0x00001388, TRUE); + write_client_reg(PWM1OFF, 0x00001387, TRUE); + write_client_reg(TIMER0CTRL, 0x000000E0, TRUE); + write_client_reg(TIMER1CTRL, 0x000000E0, TRUE); + write_client_reg(PWMCR, 0x00000003, TRUE); + mddi_wait(1); + write_client_reg(SPI_BLOCK_BASE, 0x00063111, TRUE); + write_client_reg(SSITIME, 0x00000100, TRUE); + write_client_reg(SPI_BLOCK_BASE, 0x00063113, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x00000000, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x00000000, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x00000000, TRUE); + mddi_wait(1); + write_client_reg(CLKENB, 0x0000A1EF, TRUE); + write_client_reg(START, 0x00000000, TRUE); + write_client_reg(WRSTB, 0x0000003F, TRUE); + write_client_reg(RDSTB, 0x00000432, TRUE); + write_client_reg(PORT_ENB, 0x00000002, TRUE); + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x80000000, TRUE); + write_client_reg(ASY_DATB, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(10); + write_client_reg(ASY_DATA, 0x80000000, TRUE); + write_client_reg(ASY_DATB, 0x80000000, TRUE); + write_client_reg(ASY_DATC, 0x80000000, TRUE); + write_client_reg(ASY_DATD, 0x80000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000009, TRUE); + write_client_reg(ASY_CMDSET, 0x00000008, TRUE); + write_client_reg(ASY_DATA, 0x80000007, TRUE); + write_client_reg(ASY_DATB, 0x00004005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(20); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + + write_client_reg(VSYNIF, 0x00000001, TRUE); + write_client_reg(PORT_ENB, 0x00000001, TRUE); + } else { + write_client_reg(DPSET0, 0x4BEC0066, TRUE); + write_client_reg(DPSET1, 0x00000113, TRUE); + write_client_reg(DPSUS, 0x00000000, TRUE); + write_client_reg(DPRUN, 0x00000001, TRUE); + mddi_wait(14); + write_client_reg(SYSCKENA, 0x00000001, TRUE); + write_client_reg(CLKENB, 0x000000EF, TRUE); + write_client_reg(GPIODATA, 0x03FF0000, TRUE); + write_client_reg(GPIODIR, 0x0000024D, TRUE); + write_client_reg(GPIOSEL, 0x00000173, TRUE); + write_client_reg(GPIOPC, 0x03C300C0, TRUE); + write_client_reg(WKREQ, 0x00000000, TRUE); + write_client_reg(GPIOIS, 0x00000000, TRUE); + write_client_reg(GPIOIEV, 0x00000001, TRUE); + write_client_reg(GPIOIC, 0x000003FF, TRUE); + write_client_reg(GPIODATA, 0x00060006, TRUE); + write_client_reg(GPIODATA, 0x00080008, TRUE); + write_client_reg(GPIODATA, 0x02000200, TRUE); + + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA) { + mddi_wait(400); + write_client_reg(DRAMPWR, 0x00000001, TRUE); + + write_client_reg(CNT_DIS, 0x00000002, TRUE); + write_client_reg(BITMAP0, 0x01E00320, TRUE); + write_client_reg(PORT_ENB, 0x00000001, TRUE); + write_client_reg(PORT, 0x00000004, TRUE); + write_client_reg(PXL, 0x0000003A, TRUE); + write_client_reg(MPLFBUF, 0x00000000, TRUE); + write_client_reg(HCYCLE, 0x00000253, TRUE); + write_client_reg(HSW, 0x00000003, TRUE); + write_client_reg(HDE_START, 0x00000017, TRUE); + write_client_reg(HDE_SIZE, 0x0000018F, TRUE); + write_client_reg(VCYCLE, 0x000001FF, TRUE); + write_client_reg(VSW, 0x00000001, TRUE); + write_client_reg(VDE_START, 0x00000003, TRUE); + write_client_reg(VDE_SIZE, 0x000001DF, TRUE); + write_client_reg(START, 0x00000001, TRUE); + mddi_wait(1); + write_client_reg(TIMER0CTRL, 0x00000060, TRUE); + write_client_reg(TIMER0LOAD, 0x00001388, TRUE); + write_client_reg(TIMER1CTRL, 0x00000060, TRUE); + write_client_reg(TIMER1LOAD, 0x00001388, TRUE); + write_client_reg(PWM1OFF, 0x00000087, TRUE); + } else { + write_client_reg(DRAMPWR, 0x00000001, TRUE); + write_client_reg(TIMER0CTRL, 0x00000060, TRUE); + write_client_reg(TIMER0LOAD, 0x00001388, TRUE); + write_client_reg(TIMER1CTRL, 0x00000060, TRUE); + write_client_reg(TIMER1LOAD, 0x00001388, TRUE); + write_client_reg(PWM1OFF, 0x00001387, TRUE); + } + + write_client_reg(TIMER0CTRL, 0x000000E0, TRUE); + write_client_reg(TIMER1CTRL, 0x000000E0, TRUE); + write_client_reg(PWMCR, 0x00000003, TRUE); + mddi_wait(1); + write_client_reg(SSICTL, 0x00000799, TRUE); + write_client_reg(SSITIME, 0x00000100, TRUE); + write_client_reg(SSICTL, 0x0000079b, TRUE); + write_client_reg(SSITX, 0x00000000, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x00000000, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x00000000, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x000800BA, TRUE); + write_client_reg(SSITX, 0x00000111, TRUE); + write_client_reg(SSITX, 0x00080036, TRUE); + write_client_reg(SSITX, 0x00000100, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800BB, TRUE); + write_client_reg(SSITX, 0x00000100, TRUE); + write_client_reg(SSITX, 0x0008003A, TRUE); + write_client_reg(SSITX, 0x00000160, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800BF, TRUE); + write_client_reg(SSITX, 0x00000100, TRUE); + write_client_reg(SSITX, 0x000800B1, TRUE); + write_client_reg(SSITX, 0x0000015D, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800B2, TRUE); + write_client_reg(SSITX, 0x00000133, TRUE); + write_client_reg(SSITX, 0x000800B3, TRUE); + write_client_reg(SSITX, 0x00000122, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800B4, TRUE); + write_client_reg(SSITX, 0x00000102, TRUE); + write_client_reg(SSITX, 0x000800B5, TRUE); + write_client_reg(SSITX, 0x0000011F, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800B6, TRUE); + write_client_reg(SSITX, 0x00000128, TRUE); + write_client_reg(SSITX, 0x000800B7, TRUE); + write_client_reg(SSITX, 0x00000103, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800B9, TRUE); + write_client_reg(SSITX, 0x00000120, TRUE); + write_client_reg(SSITX, 0x000800BD, TRUE); + write_client_reg(SSITX, 0x00000102, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800BE, TRUE); + write_client_reg(SSITX, 0x00000100, TRUE); + write_client_reg(SSITX, 0x000800C0, TRUE); + write_client_reg(SSITX, 0x00000111, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800C1, TRUE); + write_client_reg(SSITX, 0x00000111, TRUE); + write_client_reg(SSITX, 0x000800C2, TRUE); + write_client_reg(SSITX, 0x00000111, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800C3, TRUE); + write_client_reg(SSITX, 0x0008010A, TRUE); + write_client_reg(SSITX, 0x0000010A, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800C4, TRUE); + write_client_reg(SSITX, 0x00080160, TRUE); + write_client_reg(SSITX, 0x00000160, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800C5, TRUE); + write_client_reg(SSITX, 0x00080160, TRUE); + write_client_reg(SSITX, 0x00000160, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800C6, TRUE); + write_client_reg(SSITX, 0x00080160, TRUE); + write_client_reg(SSITX, 0x00000160, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800C7, TRUE); + write_client_reg(SSITX, 0x00080133, TRUE); + write_client_reg(SSITX, 0x00000143, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800C8, TRUE); + write_client_reg(SSITX, 0x00000144, TRUE); + write_client_reg(SSITX, 0x000800C9, TRUE); + write_client_reg(SSITX, 0x00000133, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800CA, TRUE); + write_client_reg(SSITX, 0x00000100, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800EC, TRUE); + write_client_reg(SSITX, 0x00080102, TRUE); + write_client_reg(SSITX, 0x00000118, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800CF, TRUE); + write_client_reg(SSITX, 0x00000101, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D0, TRUE); + write_client_reg(SSITX, 0x00080110, TRUE); + write_client_reg(SSITX, 0x00000104, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D1, TRUE); + write_client_reg(SSITX, 0x00000101, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D2, TRUE); + write_client_reg(SSITX, 0x00080100, TRUE); + write_client_reg(SSITX, 0x0000013A, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D3, TRUE); + write_client_reg(SSITX, 0x00080100, TRUE); + write_client_reg(SSITX, 0x0000013A, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D4, TRUE); + write_client_reg(SSITX, 0x00080124, TRUE); + write_client_reg(SSITX, 0x0000016E, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x000800D5, TRUE); + write_client_reg(SSITX, 0x00000124, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800ED, TRUE); + write_client_reg(SSITX, 0x00080101, TRUE); + write_client_reg(SSITX, 0x0000010A, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D6, TRUE); + write_client_reg(SSITX, 0x00000101, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D7, TRUE); + write_client_reg(SSITX, 0x00080110, TRUE); + write_client_reg(SSITX, 0x0000010A, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D8, TRUE); + write_client_reg(SSITX, 0x00000101, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800D9, TRUE); + write_client_reg(SSITX, 0x00080100, TRUE); + write_client_reg(SSITX, 0x00000114, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800DE, TRUE); + write_client_reg(SSITX, 0x00080100, TRUE); + write_client_reg(SSITX, 0x00000114, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800DF, TRUE); + write_client_reg(SSITX, 0x00080112, TRUE); + write_client_reg(SSITX, 0x0000013F, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800E0, TRUE); + write_client_reg(SSITX, 0x0000010B, TRUE); + write_client_reg(SSITX, 0x000800E2, TRUE); + write_client_reg(SSITX, 0x00000101, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800E3, TRUE); + write_client_reg(SSITX, 0x00000136, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800E4, TRUE); + write_client_reg(SSITX, 0x00080100, TRUE); + write_client_reg(SSITX, 0x00000103, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800E5, TRUE); + write_client_reg(SSITX, 0x00080102, TRUE); + write_client_reg(SSITX, 0x00000104, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800E6, TRUE); + write_client_reg(SSITX, 0x00000103, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800E7, TRUE); + write_client_reg(SSITX, 0x00080104, TRUE); + write_client_reg(SSITX, 0x0000010A, TRUE); + mddi_wait(2); + write_client_reg(SSITX, 0x000800E8, TRUE); + write_client_reg(SSITX, 0x00000104, TRUE); + write_client_reg(CLKENB, 0x000001EF, TRUE); + write_client_reg(START, 0x00000000, TRUE); + write_client_reg(WRSTB, 0x0000003F, TRUE); + write_client_reg(RDSTB, 0x00000432, TRUE); + write_client_reg(PORT_ENB, 0x00000002, TRUE); + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x80000000, TRUE); + write_client_reg(ASY_DATB, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(10); + write_client_reg(ASY_DATA, 0x80000000, TRUE); + write_client_reg(ASY_DATB, 0x80000000, TRUE); + write_client_reg(ASY_DATC, 0x80000000, TRUE); + write_client_reg(ASY_DATD, 0x80000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000009, TRUE); + write_client_reg(ASY_CMDSET, 0x00000008, TRUE); + write_client_reg(ASY_DATA, 0x80000007, TRUE); + write_client_reg(ASY_DATB, 0x00004005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(20); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + write_client_reg(VSYNIF, 0x00000001, TRUE); + write_client_reg(PORT_ENB, 0x00000001, TRUE); + } + + mddi_toshiba_state_transition(TOSHIBA_STATE_PRIM_SEC_STANDBY, + TOSHIBA_STATE_PRIM_SEC_READY); +} + +static void toshiba_prim_start(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) + return; + + if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) { + write_client_reg(BITMAP1, 0x01E000F0, TRUE); + write_client_reg(BITMAP2, 0x01E000F0, TRUE); + write_client_reg(BITMAP3, 0x01E000F0, TRUE); + write_client_reg(BITMAP4, 0x00DC00B0, TRUE); + write_client_reg(CLKENB, 0x000001EF, TRUE); + write_client_reg(PORT_ENB, 0x00000001, TRUE); + write_client_reg(PORT, 0x00000016, TRUE); + write_client_reg(PXL, 0x00000002, TRUE); + write_client_reg(MPLFBUF, 0x00000000, TRUE); + write_client_reg(HCYCLE, 0x00000185, TRUE); + write_client_reg(HSW, 0x00000018, TRUE); + write_client_reg(HDE_START, 0x0000004A, TRUE); + write_client_reg(HDE_SIZE, 0x000000EF, TRUE); + write_client_reg(VCYCLE, 0x0000028E, TRUE); + write_client_reg(VSW, 0x00000004, TRUE); + write_client_reg(VDE_START, 0x00000009, TRUE); + write_client_reg(VDE_SIZE, 0x0000027F, TRUE); + write_client_reg(START, 0x00000001, TRUE); + write_client_reg(SYSTEM_BLOCK1_BASE, 0x00000002, TRUE); + } else{ + + write_client_reg(VSYNIF, 0x00000001, TRUE); + write_client_reg(PORT_ENB, 0x00000001, TRUE); + write_client_reg(BITMAP1, 0x01E000F0, TRUE); + write_client_reg(BITMAP2, 0x01E000F0, TRUE); + write_client_reg(BITMAP3, 0x01E000F0, TRUE); + write_client_reg(BITMAP4, 0x00DC00B0, TRUE); + write_client_reg(CLKENB, 0x000001EF, TRUE); + write_client_reg(PORT_ENB, 0x00000001, TRUE); + write_client_reg(PORT, 0x00000004, TRUE); + write_client_reg(PXL, 0x00000002, TRUE); + write_client_reg(MPLFBUF, 0x00000000, TRUE); + + if (mddi_toshiba_61Hz_refresh) { + write_client_reg(HCYCLE, 0x000000FC, TRUE); + mddi_toshiba_rows_per_second = 39526; + mddi_toshiba_rows_per_refresh = 646; + mddi_toshiba_usecs_per_refresh = 16344; + } else { + write_client_reg(HCYCLE, 0x0000010b, TRUE); + mddi_toshiba_rows_per_second = 37313; + mddi_toshiba_rows_per_refresh = 646; + mddi_toshiba_usecs_per_refresh = 17313; + } + + write_client_reg(HSW, 0x00000003, TRUE); + write_client_reg(HDE_START, 0x00000007, TRUE); + write_client_reg(HDE_SIZE, 0x000000EF, TRUE); + write_client_reg(VCYCLE, 0x00000285, TRUE); + write_client_reg(VSW, 0x00000001, TRUE); + write_client_reg(VDE_START, 0x00000003, TRUE); + write_client_reg(VDE_SIZE, 0x0000027F, TRUE); + write_client_reg(START, 0x00000001, TRUE); + mddi_wait(10); + write_client_reg(SSITX, 0x000800BC, TRUE); + write_client_reg(SSITX, 0x00000180, TRUE); + write_client_reg(SSITX, 0x0008003B, TRUE); + write_client_reg(SSITX, 0x00000100, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x000800B0, TRUE); + write_client_reg(SSITX, 0x00000116, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x000800B8, TRUE); + write_client_reg(SSITX, 0x000801FF, TRUE); + write_client_reg(SSITX, 0x000001F5, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x00000011, TRUE); + write_client_reg(SSITX, 0x00000029, TRUE); + write_client_reg(WKREQ, 0x00000000, TRUE); + write_client_reg(WAKEUP, 0x00000000, TRUE); + write_client_reg(INTMSK, 0x00000001, TRUE); + } + + mddi_toshiba_state_transition(TOSHIBA_STATE_PRIM_SEC_READY, + TOSHIBA_STATE_PRIM_NORMAL_MODE); +} + +static void toshiba_sec_start(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) + return; + + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(PORT_ENB, 0x00000002, TRUE); + write_client_reg(CLKENB, 0x000011EF, TRUE); + write_client_reg(BITMAP0, 0x028001E0, TRUE); + write_client_reg(BITMAP1, 0x00000000, TRUE); + write_client_reg(BITMAP2, 0x00000000, TRUE); + write_client_reg(BITMAP3, 0x00000000, TRUE); + write_client_reg(BITMAP4, 0x00DC00B0, TRUE); + write_client_reg(PORT, 0x00000000, TRUE); + write_client_reg(PXL, 0x00000000, TRUE); + write_client_reg(MPLFBUF, 0x00000004, TRUE); + write_client_reg(HCYCLE, 0x0000006B, TRUE); + write_client_reg(HSW, 0x00000003, TRUE); + write_client_reg(HDE_START, 0x00000007, TRUE); + write_client_reg(HDE_SIZE, 0x00000057, TRUE); + write_client_reg(VCYCLE, 0x000000E6, TRUE); + write_client_reg(VSW, 0x00000001, TRUE); + write_client_reg(VDE_START, 0x00000003, TRUE); + write_client_reg(VDE_SIZE, 0x000000DB, TRUE); + write_client_reg(ASY_DATA, 0x80000001, TRUE); + write_client_reg(ASY_DATB, 0x0000011B, TRUE); + write_client_reg(ASY_DATC, 0x80000002, TRUE); + write_client_reg(ASY_DATD, 0x00000700, TRUE); + write_client_reg(ASY_DATE, 0x80000003, TRUE); + write_client_reg(ASY_DATF, 0x00000230, TRUE); + write_client_reg(ASY_DATG, 0x80000008, TRUE); + write_client_reg(ASY_DATH, 0x00000402, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x80000009, TRUE); + write_client_reg(ASY_DATB, 0x00000000, TRUE); + write_client_reg(ASY_DATC, 0x8000000B, TRUE); + write_client_reg(ASY_DATD, 0x00000000, TRUE); + write_client_reg(ASY_DATE, 0x8000000C, TRUE); + write_client_reg(ASY_DATF, 0x00000000, TRUE); + write_client_reg(ASY_DATG, 0x8000000D, TRUE); + write_client_reg(ASY_DATH, 0x00000409, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x8000000E, TRUE); + write_client_reg(ASY_DATB, 0x00000409, TRUE); + write_client_reg(ASY_DATC, 0x80000030, TRUE); + write_client_reg(ASY_DATD, 0x00000000, TRUE); + write_client_reg(ASY_DATE, 0x80000031, TRUE); + write_client_reg(ASY_DATF, 0x00000100, TRUE); + write_client_reg(ASY_DATG, 0x80000032, TRUE); + write_client_reg(ASY_DATH, 0x00000104, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x80000033, TRUE); + write_client_reg(ASY_DATB, 0x00000400, TRUE); + write_client_reg(ASY_DATC, 0x80000034, TRUE); + write_client_reg(ASY_DATD, 0x00000306, TRUE); + write_client_reg(ASY_DATE, 0x80000035, TRUE); + write_client_reg(ASY_DATF, 0x00000706, TRUE); + write_client_reg(ASY_DATG, 0x80000036, TRUE); + write_client_reg(ASY_DATH, 0x00000707, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x80000037, TRUE); + write_client_reg(ASY_DATB, 0x00000004, TRUE); + write_client_reg(ASY_DATC, 0x80000038, TRUE); + write_client_reg(ASY_DATD, 0x00000000, TRUE); + write_client_reg(ASY_DATE, 0x80000039, TRUE); + write_client_reg(ASY_DATF, 0x00000000, TRUE); + write_client_reg(ASY_DATG, 0x8000003A, TRUE); + write_client_reg(ASY_DATH, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x80000044, TRUE); + write_client_reg(ASY_DATB, 0x0000AF00, TRUE); + write_client_reg(ASY_DATC, 0x80000045, TRUE); + write_client_reg(ASY_DATD, 0x0000DB00, TRUE); + write_client_reg(ASY_DATE, 0x08000042, TRUE); + write_client_reg(ASY_DATF, 0x0000DB00, TRUE); + write_client_reg(ASY_DATG, 0x80000021, TRUE); + write_client_reg(ASY_DATH, 0x00000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + write_client_reg(PXL, 0x0000000C, TRUE); + write_client_reg(VSYNIF, 0x00000001, TRUE); + write_client_reg(ASY_DATA, 0x80000022, TRUE); + write_client_reg(ASY_CMDSET, 0x00000003, TRUE); + write_client_reg(START, 0x00000001, TRUE); + mddi_wait(60); + write_client_reg(PXL, 0x00000000, TRUE); + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(START, 0x00000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x80000050, TRUE); + write_client_reg(ASY_DATB, 0x00000000, TRUE); + write_client_reg(ASY_DATC, 0x80000051, TRUE); + write_client_reg(ASY_DATD, 0x00000E00, TRUE); + write_client_reg(ASY_DATE, 0x80000052, TRUE); + write_client_reg(ASY_DATF, 0x00000D01, TRUE); + write_client_reg(ASY_DATG, 0x80000053, TRUE); + write_client_reg(ASY_DATH, 0x00000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + write_client_reg(ASY_DATA, 0x80000058, TRUE); + write_client_reg(ASY_DATB, 0x00000000, TRUE); + write_client_reg(ASY_DATC, 0x8000005A, TRUE); + write_client_reg(ASY_DATD, 0x00000E01, TRUE); + write_client_reg(ASY_CMDSET, 0x00000009, TRUE); + write_client_reg(ASY_CMDSET, 0x00000008, TRUE); + write_client_reg(ASY_DATA, 0x80000011, TRUE); + write_client_reg(ASY_DATB, 0x00000812, TRUE); + write_client_reg(ASY_DATC, 0x80000012, TRUE); + write_client_reg(ASY_DATD, 0x00000003, TRUE); + write_client_reg(ASY_DATE, 0x80000013, TRUE); + write_client_reg(ASY_DATF, 0x00000909, TRUE); + write_client_reg(ASY_DATG, 0x80000010, TRUE); + write_client_reg(ASY_DATH, 0x00000040, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + mddi_wait(40); + write_client_reg(ASY_DATA, 0x80000010, TRUE); + write_client_reg(ASY_DATB, 0x00000340, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(60); + write_client_reg(ASY_DATA, 0x80000010, TRUE); + write_client_reg(ASY_DATB, 0x00003340, TRUE); + write_client_reg(ASY_DATC, 0x80000007, TRUE); + write_client_reg(ASY_DATD, 0x00004007, TRUE); + write_client_reg(ASY_CMDSET, 0x00000009, TRUE); + write_client_reg(ASY_CMDSET, 0x00000008, TRUE); + mddi_wait(1); + write_client_reg(ASY_DATA, 0x80000007, TRUE); + write_client_reg(ASY_DATB, 0x00004017, TRUE); + write_client_reg(ASY_DATC, 0x8000005B, TRUE); + write_client_reg(ASY_DATD, 0x00000000, TRUE); + write_client_reg(ASY_DATE, 0x80000059, TRUE); + write_client_reg(ASY_DATF, 0x00000011, TRUE); + write_client_reg(ASY_CMDSET, 0x0000000D, TRUE); + write_client_reg(ASY_CMDSET, 0x0000000C, TRUE); + mddi_wait(20); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + /* LTPS I/F control */ + write_client_reg(ASY_DATB, 0x00000019, TRUE); + /* Direct cmd transfer enable */ + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + /* Direct cmd transfer disable */ + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(20); + /* Index setting of SUB LCDD */ + write_client_reg(ASY_DATA, 0x80000059, TRUE); + /* LTPS I/F control */ + write_client_reg(ASY_DATB, 0x00000079, TRUE); + /* Direct cmd transfer enable */ + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + /* Direct cmd transfer disable */ + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(20); + /* Index setting of SUB LCDD */ + write_client_reg(ASY_DATA, 0x80000059, TRUE); + /* LTPS I/F control */ + write_client_reg(ASY_DATB, 0x000003FD, TRUE); + /* Direct cmd transfer enable */ + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + /* Direct cmd transfer disable */ + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(20); + mddi_toshiba_state_transition(TOSHIBA_STATE_PRIM_SEC_READY, + TOSHIBA_STATE_SEC_NORMAL_MODE); +} + +static void toshiba_prim_lcd_off(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) { + gordon_disp_off(); + } else{ + + /* Main panel power off (Deep standby in) */ + write_client_reg(SSITX, 0x000800BC, TRUE); + write_client_reg(SSITX, 0x00000100, TRUE); + write_client_reg(SSITX, 0x00000028, TRUE); + mddi_wait(1); + write_client_reg(SSITX, 0x000800B8, TRUE); + write_client_reg(SSITX, 0x00000180, TRUE); + write_client_reg(SSITX, 0x00000102, TRUE); + write_client_reg(SSITX, 0x00000010, TRUE); + } + write_client_reg(PORT, 0x00000003, TRUE); + write_client_reg(REGENB, 0x00000001, TRUE); + mddi_wait(1); + write_client_reg(PXL, 0x00000000, TRUE); + write_client_reg(START, 0x00000000, TRUE); + write_client_reg(REGENB, 0x00000001, TRUE); + mddi_wait(3); + if (TM_GET_PID(mfd->panel.id) != LCD_SHARP_2P4_VGA) { + write_client_reg(SSITX, 0x000800B0, TRUE); + write_client_reg(SSITX, 0x00000100, TRUE); + } + mddi_toshiba_state_transition(TOSHIBA_STATE_PRIM_NORMAL_MODE, + TOSHIBA_STATE_PRIM_SEC_STANDBY); +} + +static void toshiba_sec_lcd_off(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) + return; + + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(PORT_ENB, 0x00000002, TRUE); + write_client_reg(ASY_DATA, 0x80000007, TRUE); + write_client_reg(ASY_DATB, 0x00004016, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000019, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x0000000B, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000002, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(4); + write_client_reg(ASY_DATA, 0x80000010, TRUE); + write_client_reg(ASY_DATB, 0x00000300, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(4); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000007, TRUE); + write_client_reg(ASY_DATB, 0x00004004, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(PORT, 0x00000000, TRUE); + write_client_reg(PXL, 0x00000000, TRUE); + write_client_reg(START, 0x00000000, TRUE); + write_client_reg(VSYNIF, 0x00000001, TRUE); + write_client_reg(PORT_ENB, 0x00000001, TRUE); + write_client_reg(REGENB, 0x00000001, TRUE); + mddi_toshiba_state_transition(TOSHIBA_STATE_SEC_NORMAL_MODE, + TOSHIBA_STATE_PRIM_SEC_STANDBY); +} + +static void toshiba_sec_cont_update_start(struct msm_fb_data_type *mfd) +{ + + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) + return; + + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(PORT_ENB, 0x00000002, TRUE); + write_client_reg(INTMASK, 0x00000001, TRUE); + write_client_reg(TTBUSSEL, 0x0000000B, TRUE); + write_client_reg(MONI, 0x00000008, TRUE); + write_client_reg(CLKENB, 0x000000EF, TRUE); + write_client_reg(CLKENB, 0x000010EF, TRUE); + write_client_reg(CLKENB, 0x000011EF, TRUE); + write_client_reg(BITMAP4, 0x00DC00B0, TRUE); + write_client_reg(HCYCLE, 0x0000006B, TRUE); + write_client_reg(HSW, 0x00000003, TRUE); + write_client_reg(HDE_START, 0x00000002, TRUE); + write_client_reg(HDE_SIZE, 0x00000057, TRUE); + write_client_reg(VCYCLE, 0x000000E6, TRUE); + write_client_reg(VSW, 0x00000001, TRUE); + write_client_reg(VDE_START, 0x00000003, TRUE); + write_client_reg(VDE_SIZE, 0x000000DB, TRUE); + write_client_reg(WRSTB, 0x00000015, TRUE); + write_client_reg(MPLFBUF, 0x00000004, TRUE); + write_client_reg(ASY_DATA, 0x80000021, TRUE); + write_client_reg(ASY_DATB, 0x00000000, TRUE); + write_client_reg(ASY_DATC, 0x80000022, TRUE); + write_client_reg(ASY_CMDSET, 0x00000007, TRUE); + write_client_reg(PXL, 0x00000089, TRUE); + write_client_reg(VSYNIF, 0x00000001, TRUE); + mddi_wait(2); +} + +static void toshiba_sec_cont_update_stop(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) + return; + + write_client_reg(PXL, 0x00000000, TRUE); + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(START, 0x00000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + mddi_wait(3); + write_client_reg(SRST, 0x00000002, TRUE); + mddi_wait(3); + write_client_reg(SRST, 0x00000003, TRUE); +} + +static void toshiba_sec_backlight_on(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) + return; + + write_client_reg(TIMER0CTRL, 0x00000060, TRUE); + write_client_reg(TIMER0LOAD, 0x00001388, TRUE); + write_client_reg(PWM0OFF, 0x00000001, TRUE); + write_client_reg(TIMER1CTRL, 0x00000060, TRUE); + write_client_reg(TIMER1LOAD, 0x00001388, TRUE); + write_client_reg(PWM1OFF, 0x00001387, TRUE); + write_client_reg(TIMER0CTRL, 0x000000E0, TRUE); + write_client_reg(TIMER1CTRL, 0x000000E0, TRUE); + write_client_reg(PWMCR, 0x00000003, TRUE); +} + +static void toshiba_sec_sleep_in(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) + return; + + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(PORT_ENB, 0x00000002, TRUE); + write_client_reg(ASY_DATA, 0x80000007, TRUE); + write_client_reg(ASY_DATB, 0x00004016, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000019, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x0000000B, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000002, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(4); + write_client_reg(ASY_DATA, 0x80000010, TRUE); + write_client_reg(ASY_DATB, 0x00000300, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(4); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000000, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000007, TRUE); + write_client_reg(ASY_DATB, 0x00004004, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(PORT, 0x00000000, TRUE); + write_client_reg(PXL, 0x00000000, TRUE); + write_client_reg(START, 0x00000000, TRUE); + write_client_reg(REGENB, 0x00000001, TRUE); + /* Sleep in sequence */ + write_client_reg(ASY_DATA, 0x80000010, TRUE); + write_client_reg(ASY_DATB, 0x00000302, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); +} + +static void toshiba_sec_sleep_out(struct msm_fb_data_type *mfd) +{ + if (TM_GET_PID(mfd->panel.id) == LCD_TOSHIBA_2P4_WVGA_PT) + return; + + write_client_reg(VSYNIF, 0x00000000, TRUE); + write_client_reg(PORT_ENB, 0x00000002, TRUE); + write_client_reg(ASY_DATA, 0x80000010, TRUE); + write_client_reg(ASY_DATB, 0x00000300, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + /* Display ON sequence */ + write_client_reg(ASY_DATA, 0x80000011, TRUE); + write_client_reg(ASY_DATB, 0x00000812, TRUE); + write_client_reg(ASY_DATC, 0x80000012, TRUE); + write_client_reg(ASY_DATD, 0x00000003, TRUE); + write_client_reg(ASY_DATE, 0x80000013, TRUE); + write_client_reg(ASY_DATF, 0x00000909, TRUE); + write_client_reg(ASY_DATG, 0x80000010, TRUE); + write_client_reg(ASY_DATH, 0x00000040, TRUE); + write_client_reg(ASY_CMDSET, 0x00000001, TRUE); + write_client_reg(ASY_CMDSET, 0x00000000, TRUE); + mddi_wait(4); + write_client_reg(ASY_DATA, 0x80000010, TRUE); + write_client_reg(ASY_DATB, 0x00000340, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(6); + write_client_reg(ASY_DATA, 0x80000010, TRUE); + write_client_reg(ASY_DATB, 0x00003340, TRUE); + write_client_reg(ASY_DATC, 0x80000007, TRUE); + write_client_reg(ASY_DATD, 0x00004007, TRUE); + write_client_reg(ASY_CMDSET, 0x00000009, TRUE); + write_client_reg(ASY_CMDSET, 0x00000008, TRUE); + mddi_wait(1); + write_client_reg(ASY_DATA, 0x80000007, TRUE); + write_client_reg(ASY_DATB, 0x00004017, TRUE); + write_client_reg(ASY_DATC, 0x8000005B, TRUE); + write_client_reg(ASY_DATD, 0x00000000, TRUE); + write_client_reg(ASY_DATE, 0x80000059, TRUE); + write_client_reg(ASY_DATF, 0x00000011, TRUE); + write_client_reg(ASY_CMDSET, 0x0000000D, TRUE); + write_client_reg(ASY_CMDSET, 0x0000000C, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000019, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x00000079, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); + write_client_reg(ASY_DATA, 0x80000059, TRUE); + write_client_reg(ASY_DATB, 0x000003FD, TRUE); + write_client_reg(ASY_CMDSET, 0x00000005, TRUE); + write_client_reg(ASY_CMDSET, 0x00000004, TRUE); + mddi_wait(2); +} + +static void mddi_toshiba_lcd_set_backlight(struct msm_fb_data_type *mfd) +{ + int32 level; + int ret = -EPERM; + int max = mfd->panel_info.bl_max; + int min = mfd->panel_info.bl_min; + int i = 0; + + if (mddi_toshiba_pdata && mddi_toshiba_pdata->pmic_backlight) { + while (i++ < 3) { + ret = mddi_toshiba_pdata->pmic_backlight(mfd->bl_level); + if (!ret) + return; + msleep(10); + } + printk(KERN_WARNING "%s: pmic_backlight Failed\n", __func__); + } + + + if (ret && mddi_toshiba_pdata && mddi_toshiba_pdata->backlight_level) { + level = mddi_toshiba_pdata->backlight_level(mfd->bl_level, + max, min); + + if (level < 0) + return; + + if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) + write_client_reg(TIMER0LOAD, 0x00001388, TRUE); + } else { + if (!max) + level = 0; + else + level = (mfd->bl_level * 4999) / max; + } + + write_client_reg(PWM0OFF, level, TRUE); +} + +static void mddi_toshiba_vsync_set_handler(msm_fb_vsync_handler_type handler, /* ISR to be executed */ + void *arg) +{ + boolean error = FALSE; + unsigned long flags; + + /* Disable interrupts */ + spin_lock_irqsave(&mddi_host_spin_lock, flags); + /* INTLOCK(); */ + + if (mddi_toshiba_vsync_handler != NULL) { + error = TRUE; + } else { + /* Register the handler for this particular GROUP interrupt source */ + mddi_toshiba_vsync_handler = handler; + mddi_toshiba_vsync_handler_arg = arg; + } + + /* Restore interrupts */ + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + /* MDDI_INTFREE(); */ + if (error) { + MDDI_MSG_ERR("MDDI: Previous Vsync handler never called\n"); + } else { + /* Enable the vsync wakeup */ + mddi_queue_register_write(INTMSK, 0x0000, FALSE, 0); + + mddi_toshiba_vsync_attempts = 1; + mddi_vsync_detect_enabled = TRUE; + } +} /* mddi_toshiba_vsync_set_handler */ + +static void mddi_toshiba_lcd_vsync_detected(boolean detected) +{ + /* static timetick_type start_time = 0; */ + static struct timeval start_time; + static boolean first_time = TRUE; + /* uint32 mdp_cnt_val = 0; */ + /* timetick_type elapsed_us; */ + struct timeval now; + uint32 elapsed_us; + uint32 num_vsyncs; + + if ((detected) || (mddi_toshiba_vsync_attempts > 5)) { + if ((detected) && (mddi_toshiba_monitor_refresh_value)) { + /* if (start_time != 0) */ + if (!first_time) { + jiffies_to_timeval(jiffies, &now); + elapsed_us = + (now.tv_sec - start_time.tv_sec) * 1000000 + + now.tv_usec - start_time.tv_usec; + /* + * LCD is configured for a refresh every usecs, + * so to determine the number of vsyncs that + * have occurred since the last measurement + * add half that to the time difference and + * divide by the refresh rate. + */ + num_vsyncs = (elapsed_us + + (mddi_toshiba_usecs_per_refresh >> + 1)) / + mddi_toshiba_usecs_per_refresh; + /* + * LCD is configured for * hsyncs (rows) per + * refresh cycle. Calculate new rows_per_second + * value based upon these new measurements. + * MDP can update with this new value. + */ + mddi_toshiba_rows_per_second = + (mddi_toshiba_rows_per_refresh * 1000 * + num_vsyncs) / (elapsed_us / 1000); + } + /* start_time = timetick_get(); */ + first_time = FALSE; + jiffies_to_timeval(jiffies, &start_time); + if (mddi_toshiba_report_refresh_measurements) { + (void)mddi_queue_register_read_int(VPOS, + &mddi_toshiba_curr_vpos); + /* mdp_cnt_val = MDP_LINE_COUNT; */ + } + } + /* if detected = TRUE, client initiated wakeup was detected */ + if (mddi_toshiba_vsync_handler != NULL) { + (*mddi_toshiba_vsync_handler) + (mddi_toshiba_vsync_handler_arg); + mddi_toshiba_vsync_handler = NULL; + } + mddi_vsync_detect_enabled = FALSE; + mddi_toshiba_vsync_attempts = 0; + /* need to disable the interrupt wakeup */ + if (!mddi_queue_register_write_int(INTMSK, 0x0001)) + MDDI_MSG_ERR("Vsync interrupt disable failed!\n"); + if (!detected) { + /* give up after 5 failed attempts but show error */ + MDDI_MSG_NOTICE("Vsync detection failed!\n"); + } else if ((mddi_toshiba_monitor_refresh_value) && + (mddi_toshiba_report_refresh_measurements)) { + MDDI_MSG_NOTICE(" Last Line Counter=%d!\n", + mddi_toshiba_curr_vpos); + /* MDDI_MSG_NOTICE(" MDP Line Counter=%d!\n",mdp_cnt_val); */ + MDDI_MSG_NOTICE(" Lines Per Second=%d!\n", + mddi_toshiba_rows_per_second); + } + /* clear the interrupt */ + if (!mddi_queue_register_write_int(INTFLG, 0x0001)) + MDDI_MSG_ERR("Vsync interrupt clear failed!\n"); + } else { + /* if detected = FALSE, we woke up from hibernation, but did not + * detect client initiated wakeup. + */ + mddi_toshiba_vsync_attempts++; + } +} + +static void mddi_toshiba_prim_init(struct msm_fb_data_type *mfd) +{ + + switch (toshiba_state) { + case TOSHIBA_STATE_PRIM_SEC_READY: + break; + case TOSHIBA_STATE_OFF: + toshiba_state = TOSHIBA_STATE_PRIM_SEC_STANDBY; + toshiba_common_initial_setup(mfd); + break; + case TOSHIBA_STATE_PRIM_SEC_STANDBY: + toshiba_common_initial_setup(mfd); + break; + case TOSHIBA_STATE_SEC_NORMAL_MODE: + toshiba_sec_cont_update_stop(mfd); + toshiba_sec_sleep_in(mfd); + toshiba_sec_sleep_out(mfd); + toshiba_sec_lcd_off(mfd); + toshiba_common_initial_setup(mfd); + break; + default: + MDDI_MSG_ERR("mddi_toshiba_prim_init from state %d\n", + toshiba_state); + } + + toshiba_prim_start(mfd); + if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) + gordon_disp_init(); + mddi_host_write_pix_attr_reg(0x00C3); +} + +static void mddi_toshiba_sec_init(struct msm_fb_data_type *mfd) +{ + + switch (toshiba_state) { + case TOSHIBA_STATE_PRIM_SEC_READY: + break; + case TOSHIBA_STATE_PRIM_SEC_STANDBY: + toshiba_common_initial_setup(mfd); + break; + case TOSHIBA_STATE_PRIM_NORMAL_MODE: + toshiba_prim_lcd_off(mfd); + toshiba_common_initial_setup(mfd); + break; + default: + MDDI_MSG_ERR("mddi_toshiba_sec_init from state %d\n", + toshiba_state); + } + + toshiba_sec_start(mfd); + toshiba_sec_backlight_on(mfd); + toshiba_sec_cont_update_start(mfd); + mddi_host_write_pix_attr_reg(0x0400); +} + +static void mddi_toshiba_lcd_powerdown(struct msm_fb_data_type *mfd) +{ + switch (toshiba_state) { + case TOSHIBA_STATE_PRIM_SEC_READY: + mddi_toshiba_prim_init(mfd); + mddi_toshiba_lcd_powerdown(mfd); + return; + case TOSHIBA_STATE_PRIM_SEC_STANDBY: + break; + case TOSHIBA_STATE_PRIM_NORMAL_MODE: + toshiba_prim_lcd_off(mfd); + break; + case TOSHIBA_STATE_SEC_NORMAL_MODE: + toshiba_sec_cont_update_stop(mfd); + toshiba_sec_sleep_in(mfd); + toshiba_sec_sleep_out(mfd); + toshiba_sec_lcd_off(mfd); + break; + default: + MDDI_MSG_ERR("mddi_toshiba_lcd_powerdown from state %d\n", + toshiba_state); + } +} + +static int mddi_sharpgordon_firsttime = 1; + +static int mddi_toshiba_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + mfd = platform_get_drvdata(pdev); + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mddi_host_client_cnt_reset(); + + if (TM_GET_DID(mfd->panel.id) == TOSHIBA_VGA_PRIM) + mddi_toshiba_prim_init(mfd); + else + mddi_toshiba_sec_init(mfd); + if (TM_GET_PID(mfd->panel.id) == LCD_SHARP_2P4_VGA) { + if (mddi_sharpgordon_firsttime) { + mddi_sharpgordon_firsttime = 0; + write_client_reg(REGENB, 0x00000001, TRUE); + } + } + return 0; +} + +static int mddi_toshiba_lcd_off(struct platform_device *pdev) +{ + if (mddi_toshiba_vsync_handler != NULL) { + (*mddi_toshiba_vsync_handler) + (mddi_toshiba_vsync_handler_arg); + mddi_toshiba_vsync_handler = NULL; + printk(KERN_INFO "%s: clean up vsyn_handler=%x\n", __func__, + (int)mddi_toshiba_vsync_handler); + } + + mddi_toshiba_lcd_powerdown(platform_get_drvdata(pdev)); + return 0; +} + +static int mddi_toshiba_lcd_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + mddi_toshiba_pdata = pdev->dev.platform_data; + return 0; + } + + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = mddi_toshiba_lcd_probe, + .driver = { + .name = "mddi_toshiba", + }, +}; + +static struct msm_fb_panel_data toshiba_panel_data = { + .on = mddi_toshiba_lcd_on, + .off = mddi_toshiba_lcd_off, +}; + +static int ch_used[3]; + +int mddi_toshiba_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + if ((channel != TOSHIBA_VGA_PRIM) && + mddi_toshiba_pdata && mddi_toshiba_pdata->panel_num) + if (mddi_toshiba_pdata->panel_num() < 2) + return -ENODEV; + + ch_used[channel] = TRUE; + + pdev = platform_device_alloc("mddi_toshiba", (panel << 8)|channel); + if (!pdev) + return -ENOMEM; + + if (channel == TOSHIBA_VGA_PRIM) { + toshiba_panel_data.set_backlight = + mddi_toshiba_lcd_set_backlight; + + if (pinfo->lcd.vsync_enable) { + toshiba_panel_data.set_vsync_notifier = + mddi_toshiba_vsync_set_handler; + mddi_lcd.vsync_detected = + mddi_toshiba_lcd_vsync_detected; + } + } else { + toshiba_panel_data.set_backlight = NULL; + toshiba_panel_data.set_vsync_notifier = NULL; + } + + toshiba_panel_data.panel_info = *pinfo; + + ret = platform_device_add_data(pdev, &toshiba_panel_data, + sizeof(toshiba_panel_data)); + if (ret) { + printk(KERN_ERR + "%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + printk(KERN_ERR + "%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static int __init mddi_toshiba_lcd_init(void) +{ + return platform_driver_register(&this_driver); +} + +module_init(mddi_toshiba_lcd_init); diff --git a/drivers/video/msm/mddi_toshiba.h b/drivers/video/msm/mddi_toshiba.h new file mode 100644 index 0000000000000000000000000000000000000000..854817fb6b5b108d0213f3bbb06cc3a536a206ab --- /dev/null +++ b/drivers/video/msm/mddi_toshiba.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MDDI_TOSHIBA_H +#define MDDI_TOSHIBA_H + +#define TOSHIBA_VGA_PRIM 1 +#define TOSHIBA_VGA_SECD 2 + +#define LCD_TOSHIBA_2P4_VGA 0 +#define LCD_TOSHIBA_2P4_WVGA 1 +#define LCD_TOSHIBA_2P4_WVGA_PT 2 +#define LCD_SHARP_2P4_VGA 3 + +#define GPIO_BLOCK_BASE 0x150000 +#define SYSTEM_BLOCK2_BASE 0x170000 + +#define GPIODIR (GPIO_BLOCK_BASE|0x04) +#define GPIOSEL (SYSTEM_BLOCK2_BASE|0x00) +#define GPIOPC (GPIO_BLOCK_BASE|0x28) +#define GPIODATA (GPIO_BLOCK_BASE|0x00) + +#define write_client_reg(__X, __Y, __Z) {\ + mddi_queue_register_write(__X, __Y, TRUE, 0);\ +} + +#endif /* MDDI_TOSHIBA_H */ diff --git a/drivers/video/msm/mddi_toshiba_vga.c b/drivers/video/msm/mddi_toshiba_vga.c new file mode 100644 index 0000000000000000000000000000000000000000..3b4a85d8bb96e682722dc15f408ac0929e2d7601 --- /dev/null +++ b/drivers/video/msm/mddi_toshiba_vga.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2009-2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" +#include "mddi_toshiba.h" + +static uint32 read_client_reg(uint32 addr) +{ + uint32 val; + mddi_queue_register_read(addr, &val, TRUE, 0); + return val; +} + +static uint32 toshiba_lcd_gpio_read(void) +{ + uint32 val; + + write_client_reg(GPIODIR, 0x0000000C, TRUE); + write_client_reg(GPIOSEL, 0x00000000, TRUE); + write_client_reg(GPIOSEL, 0x00000000, TRUE); + write_client_reg(GPIOPC, 0x03CF00C0, TRUE); + val = read_client_reg(GPIODATA) & 0x2C0; + + return val; +} + +static u32 mddi_toshiba_panel_detect(void) +{ + mddi_host_type host_idx = MDDI_HOST_PRIM; + uint32 lcd_gpio; + u32 mddi_toshiba_lcd = LCD_TOSHIBA_2P4_VGA; + + /* Toshiba display requires larger drive_lo value */ + mddi_host_reg_out(DRIVE_LO, 0x0050); + + lcd_gpio = toshiba_lcd_gpio_read(); + switch (lcd_gpio) { + case 0x0080: + mddi_toshiba_lcd = LCD_SHARP_2P4_VGA; + break; + + case 0x00C0: + default: + mddi_toshiba_lcd = LCD_TOSHIBA_2P4_VGA; + break; + } + + return mddi_toshiba_lcd; +} + +static int __init mddi_toshiba_vga_init(void) +{ + int ret; + struct msm_panel_info pinfo; + u32 panel; + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + u32 id; + + ret = msm_fb_detect_client("mddi_toshiba_vga"); + if (ret == -ENODEV) + return 0; + + if (ret) { + id = mddi_get_client_id(); + if ((id >> 16) != 0xD263) + return 0; + } +#endif + + panel = mddi_toshiba_panel_detect(); + + pinfo.xres = 480; + pinfo.yres = 640; + pinfo.type = MDDI_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR; + pinfo.wait_cycle = 0; + pinfo.bpp = 18; + pinfo.lcd.vsync_enable = TRUE; + pinfo.mddi.is_type1 = TRUE; + pinfo.lcd.refx100 = 6118; + pinfo.lcd.v_back_porch = 6; + pinfo.lcd.v_front_porch = 0; + pinfo.lcd.v_pulse_width = 0; + pinfo.lcd.hw_vsync_mode = FALSE; + pinfo.lcd.vsync_notifier_period = (1 * HZ); + pinfo.bl_max = 99; + pinfo.bl_min = 1; + pinfo.clk_rate = 122880000; + pinfo.clk_min = 120000000; + pinfo.clk_max = 200000000; + pinfo.fb_num = 2; + + ret = mddi_toshiba_device_register(&pinfo, TOSHIBA_VGA_PRIM, panel); + if (ret) { + printk(KERN_ERR "%s: failed to register device!\n", __func__); + return ret; + } + + pinfo.xres = 176; + pinfo.yres = 220; + MSM_FB_SINGLE_MODE_PANEL(&pinfo); + pinfo.type = MDDI_PANEL; + pinfo.pdest = DISPLAY_2; + pinfo.mddi.vdopkt = 0x400; + pinfo.wait_cycle = 0; + pinfo.bpp = 18; + pinfo.clk_rate = 122880000; + pinfo.clk_min = 120000000; + pinfo.clk_max = 200000000; + pinfo.fb_num = 2; + + ret = mddi_toshiba_device_register(&pinfo, TOSHIBA_VGA_SECD, panel); + if (ret) + printk(KERN_WARNING + "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mddi_toshiba_vga_init); diff --git a/drivers/video/msm/mddi_toshiba_wvga.c b/drivers/video/msm/mddi_toshiba_wvga.c new file mode 100644 index 0000000000000000000000000000000000000000..7e7b0363cada93e39aa78f164bd7878d3a1752b6 --- /dev/null +++ b/drivers/video/msm/mddi_toshiba_wvga.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2009-2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddi_toshiba.h" + +static int __init mddi_toshiba_wvga_init(void) +{ + int ret; + struct msm_panel_info pinfo; + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + if (msm_fb_detect_client("mddi_toshiba_wvga")) + return 0; +#endif + + pinfo.xres = 800; + pinfo.yres = 480; + MSM_FB_SINGLE_MODE_PANEL(&pinfo); + pinfo.pdest = DISPLAY_2; + pinfo.type = MDDI_PANEL; + pinfo.mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR; + pinfo.wait_cycle = 0; + pinfo.bpp = 18; + pinfo.lcd.vsync_enable = TRUE; + pinfo.mddi.is_type1 = TRUE; + pinfo.lcd.refx100 = 6118; + pinfo.lcd.v_back_porch = 6; + pinfo.lcd.v_front_porch = 0; + pinfo.lcd.v_pulse_width = 0; + pinfo.lcd.hw_vsync_mode = FALSE; + pinfo.lcd.vsync_notifier_period = (1 * HZ); + pinfo.bl_max = 4; + pinfo.bl_min = 1; + pinfo.clk_rate = 192000000; + pinfo.clk_min = 190000000; + pinfo.clk_max = 200000000; + pinfo.fb_num = 2; + + ret = mddi_toshiba_device_register(&pinfo, TOSHIBA_VGA_PRIM, + LCD_TOSHIBA_2P4_WVGA); + if (ret) { + printk(KERN_ERR "%s: failed to register device!\n", __func__); + return ret; + } + + return ret; +} + +module_init(mddi_toshiba_wvga_init); diff --git a/drivers/video/msm/mddi_toshiba_wvga_pt.c b/drivers/video/msm/mddi_toshiba_wvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..bdb04ff062d825fa6eb3abf939814f255375be24 --- /dev/null +++ b/drivers/video/msm/mddi_toshiba_wvga_pt.c @@ -0,0 +1,69 @@ +/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" +#include "mddi_toshiba.h" + +static struct msm_panel_info pinfo; + +static int __init mddi_toshiba_wvga_pt_init(void) +{ + int ret; +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + uint id; + + ret = msm_fb_detect_client("mddi_toshiba_wvga_pt"); + if (ret == -ENODEV) + return 0; + + if (ret) { + id = mddi_get_client_id(); + if (id != 0xd2638722) + return 0; + } +#endif + + pinfo.xres = 480; + pinfo.yres = 800; + MSM_FB_SINGLE_MODE_PANEL(&pinfo); + pinfo.type = MDDI_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.mddi.vdopkt = MDDI_DEFAULT_PRIM_PIX_ATTR; + pinfo.wait_cycle = 0; + pinfo.bpp = 18; + pinfo.lcd.vsync_enable = TRUE; + pinfo.lcd.refx100 = 6102; /* adjust refx100 to prevent tearing */ + pinfo.mddi.is_type1 = TRUE; + pinfo.lcd.v_back_porch = 8; /* vsw=10 + vbp = 8 */ + pinfo.lcd.v_front_porch = 2; + pinfo.lcd.v_pulse_width = 10; + pinfo.lcd.hw_vsync_mode = FALSE; + pinfo.lcd.vsync_notifier_period = (1 * HZ); + pinfo.bl_max = 15; + pinfo.bl_min = 1; + pinfo.clk_rate = 222750000; + pinfo.clk_min = 200000000; + pinfo.clk_max = 240000000; + pinfo.fb_num = 2; + + ret = mddi_toshiba_device_register(&pinfo, TOSHIBA_VGA_PRIM, + LCD_TOSHIBA_2P4_WVGA_PT); + if (ret) + printk(KERN_ERR "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mddi_toshiba_wvga_pt_init); diff --git a/drivers/video/msm/mddihost.c b/drivers/video/msm/mddihost.c new file mode 100644 index 0000000000000000000000000000000000000000..c2a7af3d70cd0a19e0bad4653590b8d1371053bd --- /dev/null +++ b/drivers/video/msm/mddihost.c @@ -0,0 +1,626 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" + +#include +#include + +struct semaphore mddi_host_mutex; + +struct clk *mddi_io_clk; +static boolean mddi_host_powered = FALSE; +static boolean mddi_host_initialized = FALSE; +extern uint32 *mddi_reg_read_value_ptr; + +mddi_lcd_func_type mddi_lcd; + +extern mddi_client_capability_type mddi_client_capability_pkt; + +#ifdef MDDI_HOST_WINDOW_WORKAROUND +/* Tables showing number of rows that would cause a packet length + * ending in 0x02, for each number of columns. These tables have + * been generated for MDDI packets that have 16 and 16 bits-per-pixel. + * This is a work-around for MDDI clients that declare a CRC error + * on MDDI packets where ((length & 0x00ff) == 0x02). + */ +static uint16 error_vals_16bpp[] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 10, 0, 0, 0, 14, 0, 0, 0, 2, 0, 0, 4, 6, 12, 0, +0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, +0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 11, 4, 0, 12, 0, +0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, +0, 10, 0, 1, 0, 14, 0, 0, 0, 2, 0, 3, 4, 6, 12, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 10, 0, 0, 0, 14, 0, 0, 0, 2, 0, 0, 4, 6, 12, 0, +0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, +0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 11, 4, 0, 12, 0, +0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, +}; + +static uint16 error_vals_18bpp[] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 14, +0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 9, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 7, +0, 0, 0, 0, 0, 0, 1, 0, 0, 16, 0, 0, 0, 0, 0, 6, +14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +7, 0, 0, 0, 0, 0, 0, 4, 0, 16, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, +0, 7, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 9, 0 +}; +#endif + +#ifdef FEATURE_MDDI_HITACHI +extern void mddi_hitachi_window_adjust(uint16 x1, + uint16 x2, uint16 y1, uint16 y2); +#endif + +extern void mddi_toshiba_lcd_init(void); + +#ifdef FEATURE_MDDI_S6D0142 +extern void mddi_s6d0142_lcd_init(void); +extern void mddi_s6d0142_window_adjust(uint16 x1, + uint16 x2, + uint16 y1, + uint16 y2, + mddi_llist_done_cb_type done_cb); +#endif + +void mddi_init(void) +{ + if (mddi_host_initialized) + return; + + mddi_host_initialized = TRUE; + + sema_init(&mddi_host_mutex, 1); + + if (!mddi_host_powered) { + down(&mddi_host_mutex); + mddi_host_init(MDDI_HOST_PRIM); + mddi_host_powered = TRUE; + up(&mddi_host_mutex); + mdelay(10); + } +} + +int mddi_host_register_read(uint32 reg_addr, + uint32 *reg_value_ptr, boolean wait, mddi_host_type host) { + mddi_linked_list_type *curr_llist_ptr; + mddi_register_access_packet_type *regacc_pkt_ptr; + uint16 curr_llist_idx; + int ret = 0; + + if (in_interrupt()) + MDDI_MSG_CRIT("Called from ISR context\n"); + + if (!mddi_host_powered) { + MDDI_MSG_ERR("MDDI powered down!\n"); + mddi_init(); + } + + down(&mddi_host_mutex); + + mddi_reg_read_value_ptr = reg_value_ptr; + curr_llist_idx = mddi_get_reg_read_llist_item(host, TRUE); + if (curr_llist_idx == UNASSIGNED_INDEX) { + up(&mddi_host_mutex); + + /* need to change this to some sort of wait */ + MDDI_MSG_ERR("Attempting to queue up more than 1 reg read\n"); + return -EINVAL; + } + + curr_llist_ptr = &llist_extern[host][curr_llist_idx]; + curr_llist_ptr->link_controller_flags = 0x11; + curr_llist_ptr->packet_header_count = 14; + curr_llist_ptr->packet_data_count = 0; + + curr_llist_ptr->next_packet_pointer = NULL; + curr_llist_ptr->packet_data_pointer = NULL; + curr_llist_ptr->reserved = 0; + + regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt; + + regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count; + regacc_pkt_ptr->packet_type = 146; /* register access packet */ + regacc_pkt_ptr->bClient_ID = 0; + regacc_pkt_ptr->read_write_info = 0x8001; + regacc_pkt_ptr->register_address = reg_addr; + + /* now adjust pointers */ + mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, wait, + NULL, host); + /* need to check if we can write the pointer or not */ + + up(&mddi_host_mutex); + + if (wait) { + int wait_ret; + + mddi_linked_list_notify_type *llist_notify_ptr; + llist_notify_ptr = &llist_extern_notify[host][curr_llist_idx]; + wait_ret = wait_for_completion_timeout( + &(llist_notify_ptr->done_comp), 5 * HZ); + + if (wait_ret <= 0) + ret = -EBUSY; + + if (wait_ret < 0) + printk(KERN_ERR "%s: failed to wait for completion!\n", + __func__); + else if (!wait_ret) + printk(KERN_ERR "%s: Timed out waiting!\n", __func__); + + if (!ret && (mddi_reg_read_value_ptr == reg_value_ptr) && + (*reg_value_ptr == -EBUSY)) { + printk(KERN_ERR "%s - failed to get data from client", + __func__); + mddi_reg_read_value_ptr = NULL; + ret = -EBUSY; + } + } + + MDDI_MSG_DEBUG("Reg Read value=0x%x\n", *reg_value_ptr); + + return ret; +} /* mddi_host_register_read */ + +int mddi_host_register_write(uint32 reg_addr, + uint32 reg_val, enum mddi_data_packet_size_type packet_size, + boolean wait, mddi_llist_done_cb_type done_cb, mddi_host_type host) { + mddi_linked_list_type *curr_llist_ptr; + mddi_linked_list_type *curr_llist_dma_ptr; + mddi_register_access_packet_type *regacc_pkt_ptr; + uint16 curr_llist_idx; + int ret = 0; + + if (in_interrupt()) + MDDI_MSG_CRIT("Called from ISR context\n"); + + if (!mddi_host_powered) { + MDDI_MSG_ERR("MDDI powered down!\n"); + mddi_init(); + } + + down(&mddi_host_mutex); + + curr_llist_idx = mddi_get_next_free_llist_item(host, TRUE); + curr_llist_ptr = &llist_extern[host][curr_llist_idx]; + curr_llist_dma_ptr = &llist_dma_extern[host][curr_llist_idx]; + + curr_llist_ptr->link_controller_flags = 1; + curr_llist_ptr->packet_header_count = 14; + curr_llist_ptr->packet_data_count = 4; + + curr_llist_ptr->next_packet_pointer = NULL; + curr_llist_ptr->reserved = 0; + + regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt; + + regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count + + (uint16)packet_size; + regacc_pkt_ptr->packet_type = 146; /* register access packet */ + regacc_pkt_ptr->bClient_ID = 0; + regacc_pkt_ptr->read_write_info = 0x0001; + regacc_pkt_ptr->register_address = reg_addr; + regacc_pkt_ptr->register_data_list[0] = reg_val; + + MDDI_MSG_DEBUG("Reg Access write reg=0x%x, value=0x%x\n", + regacc_pkt_ptr->register_address, + regacc_pkt_ptr->register_data_list[0]); + + regacc_pkt_ptr = &curr_llist_dma_ptr->packet_header.register_pkt; + curr_llist_ptr->packet_data_pointer = + (void *)(®acc_pkt_ptr->register_data_list[0]); + + /* now adjust pointers */ + mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, wait, + done_cb, host); + + up(&mddi_host_mutex); + + if (wait) { + int wait_ret; + + mddi_linked_list_notify_type *llist_notify_ptr; + llist_notify_ptr = &llist_extern_notify[host][curr_llist_idx]; + wait_ret = wait_for_completion_timeout( + &(llist_notify_ptr->done_comp), 5 * HZ); + + if (wait_ret <= 0) + ret = -EBUSY; + + if (wait_ret < 0) + printk(KERN_ERR "%s: failed to wait for completion!\n", + __func__); + else if (!wait_ret) + printk(KERN_ERR "%s: Timed out waiting!\n", __func__); + } + + return ret; +} /* mddi_host_register_write */ + +boolean mddi_host_register_read_int + (uint32 reg_addr, uint32 *reg_value_ptr, mddi_host_type host) { + mddi_linked_list_type *curr_llist_ptr; + mddi_register_access_packet_type *regacc_pkt_ptr; + uint16 curr_llist_idx; + + if (!in_interrupt()) + MDDI_MSG_CRIT("Called from TASK context\n"); + + if (!mddi_host_powered) { + MDDI_MSG_ERR("MDDI powered down!\n"); + return FALSE; + } + + if (down_trylock(&mddi_host_mutex) != 0) + return FALSE; + + mddi_reg_read_value_ptr = reg_value_ptr; + curr_llist_idx = mddi_get_reg_read_llist_item(host, FALSE); + if (curr_llist_idx == UNASSIGNED_INDEX) { + up(&mddi_host_mutex); + return FALSE; + } + + curr_llist_ptr = &llist_extern[host][curr_llist_idx]; + curr_llist_ptr->link_controller_flags = 0x11; + curr_llist_ptr->packet_header_count = 14; + curr_llist_ptr->packet_data_count = 0; + + curr_llist_ptr->next_packet_pointer = NULL; + curr_llist_ptr->packet_data_pointer = NULL; + curr_llist_ptr->reserved = 0; + + regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt; + + regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count; + regacc_pkt_ptr->packet_type = 146; /* register access packet */ + regacc_pkt_ptr->bClient_ID = 0; + regacc_pkt_ptr->read_write_info = 0x8001; + regacc_pkt_ptr->register_address = reg_addr; + + /* now adjust pointers */ + mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, FALSE, + NULL, host); + /* need to check if we can write the pointer or not */ + + up(&mddi_host_mutex); + + return TRUE; + +} /* mddi_host_register_read */ + +boolean mddi_host_register_write_int + (uint32 reg_addr, + uint32 reg_val, mddi_llist_done_cb_type done_cb, mddi_host_type host) { + mddi_linked_list_type *curr_llist_ptr; + mddi_linked_list_type *curr_llist_dma_ptr; + mddi_register_access_packet_type *regacc_pkt_ptr; + uint16 curr_llist_idx; + + if (!in_interrupt()) + MDDI_MSG_CRIT("Called from TASK context\n"); + + if (!mddi_host_powered) { + MDDI_MSG_ERR("MDDI powered down!\n"); + return FALSE; + } + + if (down_trylock(&mddi_host_mutex) != 0) + return FALSE; + + curr_llist_idx = mddi_get_next_free_llist_item(host, FALSE); + if (curr_llist_idx == UNASSIGNED_INDEX) { + up(&mddi_host_mutex); + return FALSE; + } + + curr_llist_ptr = &llist_extern[host][curr_llist_idx]; + curr_llist_dma_ptr = &llist_dma_extern[host][curr_llist_idx]; + + curr_llist_ptr->link_controller_flags = 1; + curr_llist_ptr->packet_header_count = 14; + curr_llist_ptr->packet_data_count = 4; + + curr_llist_ptr->next_packet_pointer = NULL; + curr_llist_ptr->reserved = 0; + + regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt; + + regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count + 4; + regacc_pkt_ptr->packet_type = 146; /* register access packet */ + regacc_pkt_ptr->bClient_ID = 0; + regacc_pkt_ptr->read_write_info = 0x0001; + regacc_pkt_ptr->register_address = reg_addr; + regacc_pkt_ptr->register_data_list[0] = reg_val; + + regacc_pkt_ptr = &curr_llist_dma_ptr->packet_header.register_pkt; + curr_llist_ptr->packet_data_pointer = + (void *)(&(regacc_pkt_ptr->register_data_list[0])); + + /* now adjust pointers */ + mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, FALSE, + done_cb, host); + up(&mddi_host_mutex); + + return TRUE; + +} /* mddi_host_register_write */ + +void mddi_wait(uint16 time_ms) +{ + mdelay(time_ms); +} + +void mddi_client_lcd_vsync_detected(boolean detected) +{ + if (mddi_lcd.vsync_detected) + (*mddi_lcd.vsync_detected) (detected); +} + +/* extended version of function includes done callback */ +void mddi_window_adjust_ext(struct msm_fb_data_type *mfd, + uint16 x1, + uint16 x2, + uint16 y1, + uint16 y2, mddi_llist_done_cb_type done_cb) +{ +#ifdef FEATURE_MDDI_HITACHI + if (mfd->panel.id == HITACHI) + mddi_hitachi_window_adjust(x1, x2, y1, y2); +#elif defined(FEATURE_MDDI_S6D0142) + if (mfd->panel.id == MDDI_LCD_S6D0142) + mddi_s6d0142_window_adjust(x1, x2, y1, y2, done_cb); +#else + /* Do nothing then... except avoid lint/compiler warnings */ + (void)x1; + (void)x2; + (void)y1; + (void)y2; + (void)done_cb; +#endif +} + +void mddi_window_adjust(struct msm_fb_data_type *mfd, + uint16 x1, uint16 x2, uint16 y1, uint16 y2) +{ + mddi_window_adjust_ext(mfd, x1, x2, y1, y2, NULL); +} + +#ifdef MDDI_HOST_WINDOW_WORKAROUND +uint16 mddi_assign_pkt_height(uint16 pkt_width, + uint16 pkt_height, uint16 bpp) +{ + uint16 new_pkt_height; + uint16 problem_height = 0; + + if (pkt_width <= 240) { + if (bpp == 16) + problem_height = error_vals_16bpp[pkt_width-1]; + else if (bpp == 18) + problem_height = error_vals_18bpp[pkt_width-1]; + else { + printk(KERN_ERR"Invalid bpp value"); + return -EINVAL; + } + } + if (problem_height == pkt_height) + new_pkt_height = problem_height - 1; + else + new_pkt_height = pkt_height; + + return new_pkt_height; +} +#endif + +#ifdef ENABLE_MDDI_MULTI_READ_WRITE +int mddi_host_register_multiwrite(uint32 reg_addr, + uint32 *value_list_ptr, + uint32 value_count, boolean wait, mddi_llist_done_cb_type done_cb, + mddi_host_type host) +{ + mddi_linked_list_type *curr_llist_ptr; + mddi_linked_list_type *curr_llist_dma_ptr; + mddi_register_access_packet_type *regacc_pkt_ptr; + uint16 curr_llist_idx; + int ret = 0; + + if (!value_list_ptr || !value_count || + value_count > MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR) { + MDDI_MSG_ERR("\n Invalid value_list or value_count"); + return -EINVAL; + } + + if (in_interrupt()) + MDDI_MSG_CRIT("Called from ISR context\n"); + + if (!mddi_host_powered) { + MDDI_MSG_ERR("MDDI powered down!\n"); + mddi_init(); + } + + down(&mddi_host_mutex); + + curr_llist_idx = mddi_get_next_free_llist_item(host, TRUE); + curr_llist_ptr = &llist_extern[host][curr_llist_idx]; + curr_llist_dma_ptr = &llist_dma_extern[host][curr_llist_idx]; + + curr_llist_ptr->link_controller_flags = 1; + curr_llist_ptr->packet_header_count = 14; + curr_llist_ptr->packet_data_count = + (uint16)(value_count * 4); + + curr_llist_ptr->next_packet_pointer = NULL; + curr_llist_ptr->reserved = 0; + + regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt; + + regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count + + curr_llist_ptr->packet_data_count; + regacc_pkt_ptr->packet_type = 146; /* register access packet */ + regacc_pkt_ptr->bClient_ID = 0; + regacc_pkt_ptr->read_write_info = value_count; + regacc_pkt_ptr->register_address = reg_addr; + memcpy((void *)®acc_pkt_ptr->register_data_list[0], value_list_ptr, + curr_llist_ptr->packet_data_count); + + regacc_pkt_ptr = &curr_llist_dma_ptr->packet_header.register_pkt; + curr_llist_ptr->packet_data_pointer = + (void *)(®acc_pkt_ptr->register_data_list[0]); + MDDI_MSG_DEBUG("MultiReg Access write reg=0x%x, value[0]=0x%x\n", + regacc_pkt_ptr->register_address, + regacc_pkt_ptr->register_data_list[0]); + + /* now adjust pointers */ + mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, wait, + done_cb, host); + + up(&mddi_host_mutex); + + if (wait) { + int wait_ret; + + mddi_linked_list_notify_type *llist_notify_ptr; + llist_notify_ptr = &llist_extern_notify[host][curr_llist_idx]; + wait_ret = wait_for_completion_timeout( + &(llist_notify_ptr->done_comp), 5 * HZ); + + if (wait_ret <= 0) + ret = -EBUSY; + + if (wait_ret < 0) + printk(KERN_ERR "%s: failed to wait for completion!\n", + __func__); + else if (!wait_ret) + printk(KERN_ERR "%s: Timed out waiting!\n", __func__); + } + + return ret; +} + +int mddi_host_register_multiread(uint32 reg_addr, + uint32 *value_list_ptr, uint32 value_count, + boolean wait, mddi_host_type host) { + mddi_linked_list_type *curr_llist_ptr; + mddi_register_access_packet_type *regacc_pkt_ptr; + uint16 curr_llist_idx; + int ret = 0; + + if (!value_list_ptr || !value_count || + value_count >= MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR) { + MDDI_MSG_ERR("\n Invalid value_list or value_count"); + return -EINVAL; + } + + if (in_interrupt()) + MDDI_MSG_CRIT("Called from ISR context\n"); + + if (!mddi_host_powered) { + MDDI_MSG_ERR("MDDI powered down!\n"); + mddi_init(); + } + + down(&mddi_host_mutex); + + mddi_reg_read_value_ptr = value_list_ptr; + curr_llist_idx = mddi_get_reg_read_llist_item(host, TRUE); + if (curr_llist_idx == UNASSIGNED_INDEX) { + up(&mddi_host_mutex); + + /* need to change this to some sort of wait */ + MDDI_MSG_ERR("Attempting to queue up more than 1 reg read\n"); + return -EINVAL; + } + + curr_llist_ptr = &llist_extern[host][curr_llist_idx]; + curr_llist_ptr->link_controller_flags = 0x11; + curr_llist_ptr->packet_header_count = 14; + curr_llist_ptr->packet_data_count = 0; + + curr_llist_ptr->next_packet_pointer = NULL; + curr_llist_ptr->packet_data_pointer = NULL; + curr_llist_ptr->reserved = 0; + + regacc_pkt_ptr = &curr_llist_ptr->packet_header.register_pkt; + + regacc_pkt_ptr->packet_length = curr_llist_ptr->packet_header_count; + regacc_pkt_ptr->packet_type = 146; /* register access packet */ + regacc_pkt_ptr->bClient_ID = 0; + regacc_pkt_ptr->read_write_info = 0x8000 | value_count; + regacc_pkt_ptr->register_address = reg_addr; + + /* now adjust pointers */ + mddi_queue_forward_packets(curr_llist_idx, curr_llist_idx, wait, + NULL, host); + /* need to check if we can write the pointer or not */ + + up(&mddi_host_mutex); + + if (wait) { + int wait_ret; + + mddi_linked_list_notify_type *llist_notify_ptr; + llist_notify_ptr = &llist_extern_notify[host][curr_llist_idx]; + wait_ret = wait_for_completion_timeout( + &(llist_notify_ptr->done_comp), 5 * HZ); + + if (wait_ret <= 0) + ret = -EBUSY; + + if (wait_ret < 0) + printk(KERN_ERR "%s: failed to wait for completion!\n", + __func__); + else if (!wait_ret) + printk(KERN_ERR "%s: Timed out waiting!\n", __func__); + + if (!ret && (mddi_reg_read_value_ptr == value_list_ptr) && + (*value_list_ptr == -EBUSY)) { + printk(KERN_ERR "%s - failed to get data from client", + __func__); + mddi_reg_read_value_ptr = NULL; + ret = -EBUSY; + } + } + + MDDI_MSG_DEBUG("MultiReg Read value[0]=0x%x\n", *value_list_ptr); + + return ret; +} +#endif diff --git a/drivers/video/msm/mddihost.h b/drivers/video/msm/mddihost.h new file mode 100644 index 0000000000000000000000000000000000000000..db2df38aebe4ae841d800fa9fa64391fec25dba4 --- /dev/null +++ b/drivers/video/msm/mddihost.h @@ -0,0 +1,231 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MDDIHOST_H +#define MDDIHOST_H + +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "msm_fb_panel.h" + +#undef FEATURE_MDDI_MC4 +#undef FEATURE_MDDI_S6D0142 +#undef FEATURE_MDDI_HITACHI +#define FEATURE_MDDI_SHARP +#define FEATURE_MDDI_TOSHIBA +#undef FEATURE_MDDI_E751 +#define FEATURE_MDDI_CORONA +#define FEATURE_MDDI_PRISM + +#define T_MSM7500 + +typedef enum { + format_16bpp, + format_18bpp, + format_24bpp +} mddi_video_format; + +typedef enum { + MDDI_LCD_NONE = 0, + MDDI_LCD_MC4, + MDDI_LCD_S6D0142, + MDDI_LCD_SHARP, + MDDI_LCD_E751, + MDDI_LCD_CORONA, + MDDI_LCD_HITACHI, + MDDI_LCD_TOSHIBA, + MDDI_LCD_PRISM, + MDDI_LCD_TP2, + MDDI_NUM_LCD_TYPES, + MDDI_LCD_DEFAULT = MDDI_LCD_TOSHIBA +} mddi_lcd_type; + +typedef enum { + MDDI_HOST_PRIM = 0, + MDDI_HOST_EXT, + MDDI_NUM_HOST_CORES +} mddi_host_type; + +typedef enum { + MDDI_DRIVER_RESET, /* host core registers have not been written. */ + MDDI_DRIVER_DISABLED, /* registers written, interrupts disabled. */ + MDDI_DRIVER_ENABLED /* registers written, interrupts enabled. */ +} mddi_host_driver_state_type; + +typedef enum { + MDDI_GPIO_INT_0 = 0, + MDDI_GPIO_INT_1, + MDDI_GPIO_INT_2, + MDDI_GPIO_INT_3, + MDDI_GPIO_INT_4, + MDDI_GPIO_INT_5, + MDDI_GPIO_INT_6, + MDDI_GPIO_INT_7, + MDDI_GPIO_INT_8, + MDDI_GPIO_INT_9, + MDDI_GPIO_INT_10, + MDDI_GPIO_INT_11, + MDDI_GPIO_INT_12, + MDDI_GPIO_INT_13, + MDDI_GPIO_INT_14, + MDDI_GPIO_INT_15, + MDDI_GPIO_NUM_INTS +} mddi_gpio_int_type; + +enum mddi_data_packet_size_type { + MDDI_DATA_PACKET_4_BYTES = 4, + MDDI_DATA_PACKET_8_BYTES = 8, + MDDI_DATA_PACKET_12_BYTES = 12, + MDDI_DATA_PACKET_16_BYTES = 16, + MDDI_DATA_PACKET_24_BYTES = 24 +}; + +typedef struct { + uint32 addr; + uint32 value; +} mddi_reg_write_type; + +boolean mddi_vsync_set_handler(msm_fb_vsync_handler_type handler, void *arg); + +typedef void (*mddi_llist_done_cb_type) (void); + +typedef void (*mddi_rev_handler_type) (void *); + +boolean mddi_set_rev_handler(mddi_rev_handler_type handler, uint16 pkt_type); + +#define MDDI_DEFAULT_PRIM_PIX_ATTR 0xC3 +#define MDDI_DEFAULT_SECD_PIX_ATTR 0xC0 + +typedef int gpio_int_polarity_type; +typedef int gpio_int_handler_type; + +typedef struct { + void (*vsync_detected) (boolean); +} mddi_lcd_func_type; + +extern mddi_lcd_func_type mddi_lcd; +extern int irq_enabled; +extern unsigned char mddi_timer_shutdown_flag; +extern struct mutex mddi_timer_lock; + +void mddi_init(void); +void mddi_powerdown(void); + +void mddi_host_start_ext_display(void); +void mddi_host_stop_ext_display(void); + +extern spinlock_t mddi_host_spin_lock; +#ifdef T_MSM7500 +void mddi_reset(void); +#ifdef FEATURE_DUAL_PROC_MODEM_DISPLAY +void mddi_host_switch_proc_control(boolean on); +#endif +#endif +void mddi_host_exit_power_collapse(void); + +void mddi_queue_splash_screen + (void *buf_ptr, + boolean clear_area, + int16 src_width, + int16 src_starting_row, + int16 src_starting_column, + int16 num_of_rows, + int16 num_of_columns, int16 dst_starting_row, int16 dst_starting_column); + +void mddi_queue_image + (void *buf_ptr, + uint8 stereo_video, + boolean clear_area, + int16 src_width, + int16 src_starting_row, + int16 src_starting_column, + int16 num_of_rows, + int16 num_of_columns, int16 dst_starting_row, int16 dst_starting_column); + +int mddi_host_register_read + (uint32 reg_addr, + uint32 *reg_value_ptr, boolean wait, mddi_host_type host_idx); +int mddi_host_register_write + (uint32 reg_addr, uint32 reg_val, + enum mddi_data_packet_size_type packet_size, + boolean wait, mddi_llist_done_cb_type done_cb, mddi_host_type host); +boolean mddi_host_register_write_int + (uint32 reg_addr, + uint32 reg_val, mddi_llist_done_cb_type done_cb, mddi_host_type host); +boolean mddi_host_register_read_int + (uint32 reg_addr, uint32 *reg_value_ptr, mddi_host_type host_idx); +void mddi_queue_register_write_static + (uint32 reg_addr, + uint32 reg_val, boolean wait, mddi_llist_done_cb_type done_cb); +void mddi_queue_static_window_adjust + (const mddi_reg_write_type *reg_write, + uint16 num_writes, mddi_llist_done_cb_type done_cb); + +#ifdef ENABLE_MDDI_MULTI_READ_WRITE +int mddi_host_register_multiwrite(uint32 reg_addr, + uint32 *value_list_ptr, uint32 value_count, + boolean wait, mddi_llist_done_cb_type done_cb, + mddi_host_type host); +int mddi_host_register_multiread(uint32 reg_addr, + uint32 *value_list_ptr, uint32 value_count, + boolean wait, mddi_host_type host); +#endif + +#define mddi_queue_register_read(reg, val_ptr, wait, sig) \ + mddi_host_register_read(reg, val_ptr, wait, MDDI_HOST_PRIM) +#define mddi_queue_register_write(reg, val, wait, sig) \ + mddi_host_register_write(reg, val, MDDI_DATA_PACKET_4_BYTES,\ + wait, NULL, MDDI_HOST_PRIM) +#define mddi_queue_register_write_extn(reg, val, pkt_size, wait, sig) \ + mddi_host_register_write(reg, val, pkt_size, \ + wait, NULL, MDDI_HOST_PRIM) +#define mddi_queue_register_write_int(reg, val) \ + mddi_host_register_write_int(reg, val, NULL, MDDI_HOST_PRIM) +#define mddi_queue_register_read_int(reg, val_ptr) \ + mddi_host_register_read_int(reg, val_ptr, MDDI_HOST_PRIM) +#define mddi_queue_register_writes(reg_ptr, val, wait, sig) \ + mddi_host_register_writes(reg_ptr, val, wait, sig, MDDI_HOST_PRIM) + +void mddi_wait(uint16 time_ms); +void mddi_assign_max_pkt_dimensions(uint16 image_cols, + uint16 image_rows, + uint16 bpp, + uint16 *max_cols, uint16 * max_rows); +#ifdef MDDI_HOST_WINDOW_WORKAROUND +uint16 mddi_assign_pkt_height(uint16 pkt_width, uint16 pkt_height, uint16 bpp); +#endif +void mddi_queue_reverse_encapsulation(boolean wait); +int mddi_client_power(unsigned int client_id); +void mddi_disable(int lock); +void mddi_window_adjust(struct msm_fb_data_type *mfd, + uint16 x1, uint16 x2, uint16 y1, uint16 y2); +void mddi_send_fw_link_skew_cal(mddi_host_type host_idx); +int pmdh_clk_func(int enable); + +#endif /* MDDIHOST_H */ diff --git a/drivers/video/msm/mddihost_e.c b/drivers/video/msm/mddihost_e.c new file mode 100644 index 0000000000000000000000000000000000000000..275e4ee1c5d8715fa7ecb670f2f44c7dd51be2fb --- /dev/null +++ b/drivers/video/msm/mddihost_e.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" +#include "mddihost.h" +#include "mddihosti.h" + +#include +#include + +extern struct semaphore mddi_host_mutex; +static boolean mddi_host_ext_powered = FALSE; + +void mddi_host_start_ext_display(void) +{ + down(&mddi_host_mutex); + + if (!mddi_host_ext_powered) { + mddi_host_init(MDDI_HOST_EXT); + + mddi_host_ext_powered = TRUE; + } + + up(&mddi_host_mutex); +} + +void mddi_host_stop_ext_display(void) +{ + down(&mddi_host_mutex); + + if (mddi_host_ext_powered) { + mddi_host_powerdown(MDDI_HOST_EXT); + + mddi_host_ext_powered = FALSE; + } + + up(&mddi_host_mutex); +} diff --git a/drivers/video/msm/mddihosti.c b/drivers/video/msm/mddihosti.c new file mode 100644 index 0000000000000000000000000000000000000000..b4429f6bcf87a00f8c26541fa1fa314cdbd26f85 --- /dev/null +++ b/drivers/video/msm/mddihosti.c @@ -0,0 +1,2268 @@ +/* Copyright (c) 2008-2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb_panel.h" +#include "mddihost.h" +#include "mddihosti.h" + +#define FEATURE_MDDI_UNDERRUN_RECOVERY +#ifndef FEATURE_MDDI_DISABLE_REVERSE +static void mddi_read_rev_packet(byte *data_ptr); +#endif + +struct timer_list mddi_host_timer; + +#define MDDI_DEFAULT_TIMER_LENGTH 5000 /* 5 seconds */ +uint32 mddi_rtd_frequency = 60000; /* send RTD every 60 seconds */ +uint32 mddi_client_status_frequency = 60000; /* get status pkt every 60 secs */ + +boolean mddi_vsync_detect_enabled = FALSE; +mddi_gpio_info_type mddi_gpio; + +uint32 mddi_host_core_version; +boolean mddi_debug_log_statistics = FALSE; +/* #define FEATURE_MDDI_HOST_ENABLE_EARLY_HIBERNATION */ +/* default to TRUE in case MDP does not vote */ +static boolean mddi_host_mdp_active_flag = TRUE; +static uint32 mddi_log_stats_counter; +uint32 mddi_log_stats_frequency = 4000; +int32 mddi_client_type; + +#define MDDI_DEFAULT_REV_PKT_SIZE 0x20 + +#ifndef FEATURE_MDDI_DISABLE_REVERSE +static boolean mddi_rev_ptr_workaround = TRUE; +static uint32 mddi_reg_read_retry; +static uint32 mddi_reg_read_retry_max = 20; +static boolean mddi_enable_reg_read_retry = TRUE; +static boolean mddi_enable_reg_read_retry_once = FALSE; + +#define MDDI_MAX_REV_PKT_SIZE 0x60 + +#define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE 0x60 + +#define MDDI_VIDEO_REV_PKT_SIZE 0x40 +#define MDDI_REV_BUFFER_SIZE MDDI_MAX_REV_PKT_SIZE +static byte rev_packet_data[MDDI_MAX_REV_PKT_SIZE]; +#endif /* FEATURE_MDDI_DISABLE_REVERSE */ +/* leave these variables so graphics will compile */ + +#define MDDI_MAX_REV_DATA_SIZE 128 +/*lint -d__align(x) */ +boolean mddi_debug_clear_rev_data = TRUE; + +uint32 *mddi_reg_read_value_ptr; + +mddi_client_capability_type mddi_client_capability_pkt; +static boolean mddi_client_capability_request = FALSE; + +#ifndef FEATURE_MDDI_DISABLE_REVERSE + +#define MAX_MDDI_REV_HANDLERS 2 +#define INVALID_PKT_TYPE 0xFFFF + +typedef struct { + mddi_rev_handler_type handler; /* ISR to be executed */ + uint16 pkt_type; +} mddi_rev_pkt_handler_type; +static mddi_rev_pkt_handler_type mddi_rev_pkt_handler[MAX_MDDI_REV_HANDLERS] = + { {NULL, INVALID_PKT_TYPE}, {NULL, INVALID_PKT_TYPE} }; + +static boolean mddi_rev_encap_user_request = FALSE; +static mddi_linked_list_notify_type mddi_rev_user; + +spinlock_t mddi_host_spin_lock; +extern uint32 mdp_in_processing; +#endif + +typedef enum { + MDDI_REV_IDLE +#ifndef FEATURE_MDDI_DISABLE_REVERSE + , MDDI_REV_REG_READ_ISSUED, + MDDI_REV_REG_READ_SENT, + MDDI_REV_ENCAP_ISSUED, + MDDI_REV_STATUS_REQ_ISSUED, + MDDI_REV_CLIENT_CAP_ISSUED +#endif +} mddi_rev_link_state_type; + +typedef enum { + MDDI_LINK_DISABLED, + MDDI_LINK_HIBERNATING, + MDDI_LINK_ACTIVATING, + MDDI_LINK_ACTIVE +} mddi_host_link_state_type; + +typedef struct { + uint32 count; + uint32 in_count; + uint32 disp_req_count; + uint32 state_change_count; + uint32 ll_done_count; + uint32 rev_avail_count; + uint32 error_count; + uint32 rev_encap_count; + uint32 llist_ptr_write_1; + uint32 llist_ptr_write_2; +} mddi_host_int_type; + +typedef struct { + uint32 fwd_crc_count; + uint32 rev_crc_count; + uint32 pri_underflow; + uint32 sec_underflow; + uint32 rev_overflow; + uint32 pri_overwrite; + uint32 sec_overwrite; + uint32 rev_overwrite; + uint32 dma_failure; + uint32 rtd_failure; + uint32 reg_read_failure; +#ifdef FEATURE_MDDI_UNDERRUN_RECOVERY + uint32 pri_underrun_detected; +#endif +} mddi_host_stat_type; + +typedef struct { + uint32 rtd_cnt; + uint32 rev_enc_cnt; + uint32 vid_cnt; + uint32 reg_acc_cnt; + uint32 cli_stat_cnt; + uint32 cli_cap_cnt; + uint32 reg_read_cnt; + uint32 link_active_cnt; + uint32 link_hibernate_cnt; + uint32 vsync_response_cnt; + uint32 fwd_crc_cnt; + uint32 rev_crc_cnt; +} mddi_log_params_struct_type; + +typedef struct { + uint32 rtd_value; + uint32 rtd_counter; + uint32 client_status_cnt; + boolean rev_ptr_written; + uint8 *rev_ptr_start; + uint8 *rev_ptr_curr; + uint32 mddi_rev_ptr_write_val; + dma_addr_t rev_data_dma_addr; + uint16 rev_pkt_size; + mddi_rev_link_state_type rev_state; + mddi_host_link_state_type link_state; + mddi_host_driver_state_type driver_state; + boolean disable_hibernation; + uint32 saved_int_reg; + uint32 saved_int_en; + mddi_linked_list_type *llist_ptr; + dma_addr_t llist_dma_addr; + mddi_linked_list_type *llist_dma_ptr; + uint32 *rev_data_buf; + struct completion mddi_llist_avail_comp; + boolean mddi_waiting_for_llist_avail; + mddi_host_int_type int_type; + mddi_host_stat_type stats; + mddi_log_params_struct_type log_parms; + mddi_llist_info_type llist_info; + mddi_linked_list_notify_type llist_notify[MDDI_MAX_NUM_LLIST_ITEMS]; +} mddi_host_cntl_type; + +static mddi_host_type mddi_curr_host = MDDI_HOST_PRIM; +static mddi_host_cntl_type mhctl[MDDI_NUM_HOST_CORES]; +mddi_linked_list_type *llist_extern[MDDI_NUM_HOST_CORES]; +mddi_linked_list_type *llist_dma_extern[MDDI_NUM_HOST_CORES]; +mddi_linked_list_notify_type *llist_extern_notify[MDDI_NUM_HOST_CORES]; +static mddi_log_params_struct_type prev_parms[MDDI_NUM_HOST_CORES]; + +extern uint32 mdp_total_vdopkts; + +static boolean mddi_host_io_clock_on = FALSE; +static boolean mddi_host_hclk_on = FALSE; + +int int_mddi_pri_flag = FALSE; +int int_mddi_ext_flag = FALSE; + +static void mddi_report_errors(uint32 int_reg) +{ + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + if (int_reg & MDDI_INT_PRI_UNDERFLOW) { + pmhctl->stats.pri_underflow++; + MDDI_MSG_ERR("!!! MDDI Primary Underflow !!!\n"); + } + if (int_reg & MDDI_INT_SEC_UNDERFLOW) { + pmhctl->stats.sec_underflow++; + MDDI_MSG_ERR("!!! MDDI Secondary Underflow !!!\n"); + } +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (int_reg & MDDI_INT_REV_OVERFLOW) { + pmhctl->stats.rev_overflow++; + MDDI_MSG_ERR("!!! MDDI Reverse Overflow !!!\n"); + pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; + mddi_host_reg_out(REV_PTR, pmhctl->mddi_rev_ptr_write_val); + + } + if (int_reg & MDDI_INT_CRC_ERROR) + MDDI_MSG_ERR("!!! MDDI Reverse CRC Error !!!\n"); +#endif + if (int_reg & MDDI_INT_PRI_OVERWRITE) { + pmhctl->stats.pri_overwrite++; + MDDI_MSG_ERR("!!! MDDI Primary Overwrite !!!\n"); + } + if (int_reg & MDDI_INT_SEC_OVERWRITE) { + pmhctl->stats.sec_overwrite++; + MDDI_MSG_ERR("!!! MDDI Secondary Overwrite !!!\n"); + } +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (int_reg & MDDI_INT_REV_OVERWRITE) { + pmhctl->stats.rev_overwrite++; + /* This will show up normally and is not a problem */ + MDDI_MSG_DEBUG("MDDI Reverse Overwrite!\n"); + } + if (int_reg & MDDI_INT_RTD_FAILURE) { + mddi_host_reg_outm(INTEN, MDDI_INT_RTD_FAILURE, 0); + pmhctl->stats.rtd_failure++; + MDDI_MSG_ERR("!!! MDDI RTD Failure !!!\n"); + } +#endif + if (int_reg & MDDI_INT_DMA_FAILURE) { + pmhctl->stats.dma_failure++; + MDDI_MSG_ERR("!!! MDDI DMA Abort !!!\n"); + } +} + +static void mddi_host_enable_io_clock(void) +{ + if (!MDDI_HOST_IS_IO_CLOCK_ON) + MDDI_HOST_ENABLE_IO_CLOCK; +} + +static void mddi_host_enable_hclk(void) +{ + + if (!MDDI_HOST_IS_HCLK_ON) + MDDI_HOST_ENABLE_HCLK; +} + +static void mddi_host_disable_io_clock(void) +{ +#ifndef FEATURE_MDDI_HOST_IO_CLOCK_CONTROL_DISABLE + if (MDDI_HOST_IS_IO_CLOCK_ON) + MDDI_HOST_DISABLE_IO_CLOCK; +#endif +} + +static void mddi_host_disable_hclk(void) +{ +#ifndef FEATURE_MDDI_HOST_HCLK_CONTROL_DISABLE + if (MDDI_HOST_IS_HCLK_ON) + MDDI_HOST_DISABLE_HCLK; +#endif +} + +static void mddi_vote_to_sleep(mddi_host_type host_idx, boolean sleep) +{ + uint16 vote_mask; + + if (host_idx == MDDI_HOST_PRIM) + vote_mask = 0x01; + else + vote_mask = 0x02; +} + +static void mddi_report_state_change(uint32 int_reg) +{ + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + if ((pmhctl->saved_int_reg & MDDI_INT_IN_HIBERNATION) && + (pmhctl->saved_int_reg & MDDI_INT_LINK_ACTIVE)) { + /* recover from condition where the io_clock was turned off by the + clock driver during a transition to hibernation. The io_clock + disable is to prevent MDP/MDDI underruns when changing ARM + clock speeds. In the process of halting the ARM, the hclk + divider needs to be set to 1. When it is set to 1, there is + a small time (usecs) when hclk is off or slow, and this can + cause an underrun. To prevent the underrun, clock driver turns + off the MDDI io_clock before making the change. */ + mddi_host_reg_out(CMD, MDDI_CMD_POWERUP); + } + + if (int_reg & MDDI_INT_LINK_ACTIVE) { + pmhctl->link_state = MDDI_LINK_ACTIVE; + pmhctl->log_parms.link_active_cnt++; + pmhctl->rtd_value = mddi_host_reg_in(RTD_VAL); + MDDI_MSG_DEBUG("!!! MDDI Active RTD:0x%x!!!\n", + pmhctl->rtd_value); + /* now interrupt on hibernation */ + mddi_host_reg_outm(INTEN, + (MDDI_INT_IN_HIBERNATION | + MDDI_INT_LINK_ACTIVE), + MDDI_INT_IN_HIBERNATION); + +#ifdef DEBUG_MDDIHOSTI + /* if gpio interrupt is enabled, start polling at fastest + * registered rate + */ + if (mddi_gpio.polling_enabled) { + timer_reg(&mddi_gpio_poll_timer, + mddi_gpio_poll_timer_cb, 0, mddi_gpio.polling_interval, 0); + } +#endif +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (mddi_rev_ptr_workaround) { + /* HW CR: need to reset reverse register stuff */ + pmhctl->rev_ptr_written = FALSE; + pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; + } +#endif + /* vote on sleep */ + mddi_vote_to_sleep(host_idx, FALSE); + + if (host_idx == MDDI_HOST_PRIM) { + if (mddi_vsync_detect_enabled) { + /* + * Indicate to client specific code that vsync + * was enabled, but we did not detect a client + * intiated wakeup. The client specific + * handler can either reassert vsync detection, + * or treat this as a valid vsync. + */ + mddi_client_lcd_vsync_detected(FALSE); + pmhctl->log_parms.vsync_response_cnt++; + } + } + } + if (int_reg & MDDI_INT_IN_HIBERNATION) { + pmhctl->link_state = MDDI_LINK_HIBERNATING; + pmhctl->log_parms.link_hibernate_cnt++; + MDDI_MSG_DEBUG("!!! MDDI Hibernating !!!\n"); + + if (mddi_client_type == 2) { + mddi_host_reg_out(PAD_CTL, 0x402a850f); + mddi_host_reg_out(PAD_CAL, 0x10220020); + mddi_host_reg_out(TA1_LEN, 0x0010); + mddi_host_reg_out(TA2_LEN, 0x0040); + } + /* now interrupt on link_active */ +#ifdef FEATURE_MDDI_DISABLE_REVERSE + mddi_host_reg_outm(INTEN, + (MDDI_INT_MDDI_IN | + MDDI_INT_IN_HIBERNATION | + MDDI_INT_LINK_ACTIVE), + MDDI_INT_LINK_ACTIVE); +#else + mddi_host_reg_outm(INTEN, + (MDDI_INT_MDDI_IN | + MDDI_INT_IN_HIBERNATION | + MDDI_INT_LINK_ACTIVE), + (MDDI_INT_MDDI_IN | MDDI_INT_LINK_ACTIVE)); + + pmhctl->rtd_counter = mddi_rtd_frequency; + + if (pmhctl->rev_state != MDDI_REV_IDLE) { + /* a rev_encap will not wake up the link, so we do that here */ + pmhctl->link_state = MDDI_LINK_ACTIVATING; + mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); + } +#endif + + if (pmhctl->disable_hibernation) { + mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE); + mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); + pmhctl->link_state = MDDI_LINK_ACTIVATING; + } +#ifdef FEATURE_MDDI_UNDERRUN_RECOVERY + if ((pmhctl->llist_info.transmitting_start_idx != + UNASSIGNED_INDEX) + && + ((pmhctl-> + saved_int_reg & (MDDI_INT_PRI_LINK_LIST_DONE | + MDDI_INT_PRI_PTR_READ)) == + MDDI_INT_PRI_PTR_READ)) { + mddi_linked_list_type *llist_dma; + llist_dma = pmhctl->llist_dma_ptr; + /* + * All indications are that we have not received a + * linked list done interrupt, due to an underrun + * condition. Recovery attempt is to send again. + */ + dma_coherent_pre_ops(); + /* Write to primary pointer register again */ + mddi_host_reg_out(PRI_PTR, + &llist_dma[pmhctl->llist_info. + transmitting_start_idx]); + pmhctl->stats.pri_underrun_detected++; + } +#endif + + /* vote on sleep */ + if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { + mddi_vote_to_sleep(host_idx, TRUE); + } + +#ifdef DEBUG_MDDIHOSTI + /* need to stop polling timer */ + if (mddi_gpio.polling_enabled) { + (void) timer_clr(&mddi_gpio_poll_timer, T_NONE); + } +#endif + } +} + +void mddi_host_timer_service(unsigned long data) +{ +#ifndef FEATURE_MDDI_DISABLE_REVERSE + unsigned long flags; +#endif + mddi_host_type host_idx; + mddi_host_cntl_type *pmhctl; + + unsigned long time_ms = MDDI_DEFAULT_TIMER_LENGTH; + init_timer(&mddi_host_timer); + for (host_idx = MDDI_HOST_PRIM; host_idx < MDDI_NUM_HOST_CORES; + host_idx++) { + pmhctl = &(mhctl[host_idx]); + mddi_log_stats_counter += (uint32) time_ms; +#ifndef FEATURE_MDDI_DISABLE_REVERSE + pmhctl->rtd_counter += (uint32) time_ms; + pmhctl->client_status_cnt += (uint32) time_ms; + + if (host_idx == MDDI_HOST_PRIM) { + if (pmhctl->client_status_cnt >= + mddi_client_status_frequency) { + if ((pmhctl->link_state == + MDDI_LINK_HIBERNATING) + && (pmhctl->client_status_cnt > + mddi_client_status_frequency)) { + /* + * special case where we are hibernating + * and mddi_host_isr is not firing, so + * kick the link so that the status can + * be retrieved + */ + + /* need to wake up link before issuing + * rev encap command + */ + MDDI_MSG_INFO("wake up link!\n"); + spin_lock_irqsave(&mddi_host_spin_lock, + flags); + mddi_host_enable_hclk(); + mddi_host_enable_io_clock(); + pmhctl->link_state = + MDDI_LINK_ACTIVATING; + mddi_host_reg_out(CMD, + MDDI_CMD_LINK_ACTIVE); + spin_unlock_irqrestore + (&mddi_host_spin_lock, flags); + } else + if ((pmhctl->link_state == MDDI_LINK_ACTIVE) + && pmhctl->disable_hibernation) { + /* + * special case where we have disabled + * hibernation and mddi_host_isr + * is not firing, so enable interrupt + * for no pkts pending, which will + * generate an interrupt + */ + MDDI_MSG_INFO("kick isr!\n"); + spin_lock_irqsave(&mddi_host_spin_lock, + flags); + mddi_host_enable_hclk(); + mddi_host_reg_outm(INTEN, + MDDI_INT_NO_CMD_PKTS_PEND, + MDDI_INT_NO_CMD_PKTS_PEND); + spin_unlock_irqrestore + (&mddi_host_spin_lock, flags); + } + } + } +#endif /* #ifndef FEATURE_MDDI_DISABLE_REVERSE */ + } + + /* Check if logging is turned on */ + for (host_idx = MDDI_HOST_PRIM; host_idx < MDDI_NUM_HOST_CORES; + host_idx++) { + mddi_log_params_struct_type *prev_ptr = &(prev_parms[host_idx]); + pmhctl = &(mhctl[host_idx]); + + if (mddi_debug_log_statistics) { + + /* get video pkt count from MDP, since MDDI sw cannot know this */ + pmhctl->log_parms.vid_cnt = mdp_total_vdopkts; + + if (mddi_log_stats_counter >= mddi_log_stats_frequency) { + /* mddi_log_stats_counter = 0; */ + if (mddi_debug_log_statistics) { + MDDI_MSG_NOTICE + ("MDDI Statistics since last report:\n"); + MDDI_MSG_NOTICE(" Packets sent:\n"); + MDDI_MSG_NOTICE + (" %d RTD packet(s)\n", + pmhctl->log_parms.rtd_cnt - + prev_ptr->rtd_cnt); + if (prev_ptr->rtd_cnt != + pmhctl->log_parms.rtd_cnt) { + unsigned long flags; + spin_lock_irqsave + (&mddi_host_spin_lock, + flags); + mddi_host_enable_hclk(); + pmhctl->rtd_value = + mddi_host_reg_in(RTD_VAL); + spin_unlock_irqrestore + (&mddi_host_spin_lock, + flags); + MDDI_MSG_NOTICE + (" RTD value=%d\n", + pmhctl->rtd_value); + } + MDDI_MSG_NOTICE + (" %d VIDEO packets\n", + pmhctl->log_parms.vid_cnt - + prev_ptr->vid_cnt); + MDDI_MSG_NOTICE + (" %d Register Access packets\n", + pmhctl->log_parms.reg_acc_cnt - + prev_ptr->reg_acc_cnt); + MDDI_MSG_NOTICE + (" %d Reverse Encapsulation packet(s)\n", + pmhctl->log_parms.rev_enc_cnt - + prev_ptr->rev_enc_cnt); + if (prev_ptr->rev_enc_cnt != + pmhctl->log_parms.rev_enc_cnt) { + /* report # of reverse CRC errors */ + MDDI_MSG_NOTICE + (" %d reverse CRC errors detected\n", + pmhctl->log_parms. + rev_crc_cnt - + prev_ptr->rev_crc_cnt); + } + MDDI_MSG_NOTICE + (" Packets received:\n"); + MDDI_MSG_NOTICE + (" %d Client Status packets", + pmhctl->log_parms.cli_stat_cnt - + prev_ptr->cli_stat_cnt); + if (prev_ptr->cli_stat_cnt != + pmhctl->log_parms.cli_stat_cnt) { + MDDI_MSG_NOTICE + (" %d forward CRC errors reported\n", + pmhctl->log_parms. + fwd_crc_cnt - + prev_ptr->fwd_crc_cnt); + } + MDDI_MSG_NOTICE + (" %d Register Access Read packets\n", + pmhctl->log_parms.reg_read_cnt - + prev_ptr->reg_read_cnt); + + if (pmhctl->link_state == + MDDI_LINK_ACTIVE) { + MDDI_MSG_NOTICE + (" Current Link Status: Active\n"); + } else + if ((pmhctl->link_state == + MDDI_LINK_HIBERNATING) + || (pmhctl->link_state == + MDDI_LINK_ACTIVATING)) { + MDDI_MSG_NOTICE + (" Current Link Status: Hibernation\n"); + } else { + MDDI_MSG_NOTICE + (" Current Link Status: Inactive\n"); + } + MDDI_MSG_NOTICE + (" Active state entered %d times\n", + pmhctl->log_parms.link_active_cnt - + prev_ptr->link_active_cnt); + MDDI_MSG_NOTICE + (" Hibernation state entered %d times\n", + pmhctl->log_parms. + link_hibernate_cnt - + prev_ptr->link_hibernate_cnt); + } + } + prev_parms[host_idx] = pmhctl->log_parms; + } + } + if (mddi_log_stats_counter >= mddi_log_stats_frequency) + mddi_log_stats_counter = 0; + + mutex_lock(&mddi_timer_lock); + if (!mddi_timer_shutdown_flag) { + mddi_host_timer.function = mddi_host_timer_service; + mddi_host_timer.data = 0; + mddi_host_timer.expires = jiffies + ((time_ms * HZ) / 1000); + add_timer(&mddi_host_timer); + } + mutex_unlock(&mddi_timer_lock); + + return; +} /* mddi_host_timer_cb */ + +static void mddi_process_link_list_done(void) +{ + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + /* normal forward linked list packet(s) were sent */ + if (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) { + MDDI_MSG_ERR("**** getting LL done, but no list ****\n"); + } else { + uint16 idx; + +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (pmhctl->rev_state == MDDI_REV_REG_READ_ISSUED) { + /* special case where a register read packet was sent */ + pmhctl->rev_state = MDDI_REV_REG_READ_SENT; + if (pmhctl->llist_info.reg_read_idx == UNASSIGNED_INDEX) { + MDDI_MSG_ERR + ("**** getting LL done, but no list ****\n"); + } + } +#endif + for (idx = pmhctl->llist_info.transmitting_start_idx;;) { + uint16 next_idx = pmhctl->llist_notify[idx].next_idx; + /* with reg read we don't release the waiting tcb until after + * the reverse encapsulation has completed. + */ + if (idx != pmhctl->llist_info.reg_read_idx) { + /* notify task that may be waiting on this completion */ + if (pmhctl->llist_notify[idx].waiting) { + complete(& + (pmhctl->llist_notify[idx]. + done_comp)); + } + if (pmhctl->llist_notify[idx].done_cb != NULL) { + (*(pmhctl->llist_notify[idx].done_cb)) + (); + } + + pmhctl->llist_notify[idx].in_use = FALSE; + pmhctl->llist_notify[idx].waiting = FALSE; + pmhctl->llist_notify[idx].done_cb = NULL; + if (idx < MDDI_NUM_DYNAMIC_LLIST_ITEMS) { + /* static LLIST items are configured only once */ + pmhctl->llist_notify[idx].next_idx = + UNASSIGNED_INDEX; + } + /* + * currently, all linked list packets are + * register access, so we can increment the + * counter for that packet type here. + */ + pmhctl->log_parms.reg_acc_cnt++; + } + if (idx == pmhctl->llist_info.transmitting_end_idx) + break; + idx = next_idx; + if (idx == UNASSIGNED_INDEX) + MDDI_MSG_CRIT("MDDI linked list corruption!\n"); + } + + pmhctl->llist_info.transmitting_start_idx = UNASSIGNED_INDEX; + pmhctl->llist_info.transmitting_end_idx = UNASSIGNED_INDEX; + + if (pmhctl->mddi_waiting_for_llist_avail) { + if (! + (pmhctl-> + llist_notify[pmhctl->llist_info.next_free_idx]. + in_use)) { + pmhctl->mddi_waiting_for_llist_avail = FALSE; + complete(&(pmhctl->mddi_llist_avail_comp)); + } + } + } + + /* Turn off MDDI_INT_PRI_LINK_LIST_DONE interrupt */ + mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE, 0); + +} + +static void mddi_queue_forward_linked_list(void) +{ + uint16 first_pkt_index; + mddi_linked_list_type *llist_dma; + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + llist_dma = pmhctl->llist_dma_ptr; + + first_pkt_index = UNASSIGNED_INDEX; + + if (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) { +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (pmhctl->llist_info.reg_read_waiting) { + if (pmhctl->rev_state == MDDI_REV_IDLE) { + /* + * we have a register read to send and + * can send it now + */ + pmhctl->rev_state = MDDI_REV_REG_READ_ISSUED; + mddi_reg_read_retry = 0; + first_pkt_index = + pmhctl->llist_info.waiting_start_idx; + pmhctl->llist_info.reg_read_waiting = FALSE; + } + } else +#endif + { + /* + * not register read to worry about, go ahead and write + * anything that may be on the waiting list. + */ + first_pkt_index = pmhctl->llist_info.waiting_start_idx; + } + } + + if (first_pkt_index != UNASSIGNED_INDEX) { + pmhctl->llist_info.transmitting_start_idx = + pmhctl->llist_info.waiting_start_idx; + pmhctl->llist_info.transmitting_end_idx = + pmhctl->llist_info.waiting_end_idx; + pmhctl->llist_info.waiting_start_idx = UNASSIGNED_INDEX; + pmhctl->llist_info.waiting_end_idx = UNASSIGNED_INDEX; + + /* write to the primary pointer register */ + MDDI_MSG_DEBUG("MDDI writing primary ptr with idx=%d\n", + first_pkt_index); + + pmhctl->int_type.llist_ptr_write_2++; + + dma_coherent_pre_ops(); + mddi_host_reg_out(PRI_PTR, &llist_dma[first_pkt_index]); + + /* enable interrupt when complete */ + mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE, + MDDI_INT_PRI_LINK_LIST_DONE); + + } + +} + +#ifndef FEATURE_MDDI_DISABLE_REVERSE +static void mddi_read_rev_packet(byte *data_ptr) +{ + uint16 i, length; + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + uint8 *rev_ptr_overflow = + (pmhctl->rev_ptr_start + MDDI_REV_BUFFER_SIZE); + + /* first determine the length and handle invalid lengths */ + length = *pmhctl->rev_ptr_curr++; + if (pmhctl->rev_ptr_curr >= rev_ptr_overflow) + pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; + length |= ((*pmhctl->rev_ptr_curr++) << 8); + if (pmhctl->rev_ptr_curr >= rev_ptr_overflow) + pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; + if (length > (pmhctl->rev_pkt_size - 2)) { + MDDI_MSG_ERR("Invalid rev pkt length %d\n", length); + /* rev_pkt_size should always be <= rev_ptr_size so limit to packet size */ + length = pmhctl->rev_pkt_size - 2; + } + + /* If the data pointer is NULL, just increment the pmhctl->rev_ptr_curr. + * Loop around if necessary. Don't bother reading the data. + */ + if (data_ptr == NULL) { + pmhctl->rev_ptr_curr += length; + if (pmhctl->rev_ptr_curr >= rev_ptr_overflow) + pmhctl->rev_ptr_curr -= MDDI_REV_BUFFER_SIZE; + return; + } + + data_ptr[0] = length & 0x0ff; + data_ptr[1] = length >> 8; + data_ptr += 2; + /* copy the data to data_ptr byte-at-a-time */ + for (i = 0; (i < length) && (pmhctl->rev_ptr_curr < rev_ptr_overflow); + i++) + *data_ptr++ = *pmhctl->rev_ptr_curr++; + if (pmhctl->rev_ptr_curr >= rev_ptr_overflow) + pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; + for (; (i < length) && (pmhctl->rev_ptr_curr < rev_ptr_overflow); i++) + *data_ptr++ = *pmhctl->rev_ptr_curr++; +} + +static void mddi_process_rev_packets(void) +{ + uint32 rev_packet_count; + word i; + uint32 crc_errors; + boolean mddi_reg_read_successful = FALSE; + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + pmhctl->log_parms.rev_enc_cnt++; + if ((pmhctl->rev_state != MDDI_REV_ENCAP_ISSUED) && + (pmhctl->rev_state != MDDI_REV_STATUS_REQ_ISSUED) && + (pmhctl->rev_state != MDDI_REV_CLIENT_CAP_ISSUED)) { + MDDI_MSG_ERR("Wrong state %d for reverse int\n", + pmhctl->rev_state); + } + /* Turn off MDDI_INT_REV_AVAIL interrupt */ + mddi_host_reg_outm(INTEN, MDDI_INT_REV_DATA_AVAIL, 0); + + /* Clear rev data avail int */ + mddi_host_reg_out(INT, MDDI_INT_REV_DATA_AVAIL); + + /* Get Number of packets */ + rev_packet_count = mddi_host_reg_in(REV_PKT_CNT); + +#ifndef T_MSM7500 + /* Clear out rev packet counter */ + mddi_host_reg_out(REV_PKT_CNT, 0x0000); +#endif + +#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) + if ((pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED) && + (rev_packet_count > 0) && + (mddi_host_core_version == 0x28 || + mddi_host_core_version == 0x30)) { + + uint32 int_reg; + uint32 max_count = 0; + + mddi_host_reg_out(REV_PTR, pmhctl->mddi_rev_ptr_write_val); + int_reg = mddi_host_reg_in(INT); + while ((int_reg & 0x100000) == 0) { + udelay(3); + int_reg = mddi_host_reg_in(INT); + if (++max_count > 100) + break; + } + } +#endif + + /* Get CRC error count */ + crc_errors = mddi_host_reg_in(REV_CRC_ERR); + if (crc_errors != 0) { + pmhctl->log_parms.rev_crc_cnt += crc_errors; + pmhctl->stats.rev_crc_count += crc_errors; + MDDI_MSG_ERR("!!! MDDI %d Reverse CRC Error(s) !!!\n", + crc_errors); +#ifndef T_MSM7500 + /* Clear CRC error count */ + mddi_host_reg_out(REV_CRC_ERR, 0x0000); +#endif + /* also issue an RTD to attempt recovery */ + pmhctl->rtd_counter = mddi_rtd_frequency; + } + + pmhctl->rtd_value = mddi_host_reg_in(RTD_VAL); + + MDDI_MSG_DEBUG("MDDI rev pkt cnt=%d, ptr=0x%x, RTD:0x%x\n", + rev_packet_count, + pmhctl->rev_ptr_curr - pmhctl->rev_ptr_start, + pmhctl->rtd_value); + + if (rev_packet_count >= 1) { + mddi_invalidate_cache_lines((uint32 *) pmhctl->rev_ptr_start, + MDDI_REV_BUFFER_SIZE); + } else { + MDDI_MSG_ERR("Reverse pkt sent, no data rxd\n"); + if (mddi_reg_read_value_ptr) + *mddi_reg_read_value_ptr = -EBUSY; + } + /* order the reads */ + dma_coherent_post_ops(); + for (i = 0; i < rev_packet_count; i++) { + mddi_rev_packet_type *rev_pkt_ptr; + + mddi_read_rev_packet(rev_packet_data); + + rev_pkt_ptr = (mddi_rev_packet_type *) rev_packet_data; + + if (rev_pkt_ptr->packet_length > pmhctl->rev_pkt_size) { + MDDI_MSG_ERR("!!!invalid packet size: %d\n", + rev_pkt_ptr->packet_length); + } + + MDDI_MSG_DEBUG("MDDI rev pkt 0x%x size 0x%x\n", + rev_pkt_ptr->packet_type, + rev_pkt_ptr->packet_length); + + /* Do whatever you want to do with the data based on the packet type */ + switch (rev_pkt_ptr->packet_type) { + case 66: /* Client Capability */ + { + mddi_client_capability_type + *client_capability_pkt_ptr; + + client_capability_pkt_ptr = + (mddi_client_capability_type *) + rev_packet_data; + MDDI_MSG_NOTICE + ("Client Capability: Week=%d, Year=%d\n", + client_capability_pkt_ptr-> + Week_of_Manufacture, + client_capability_pkt_ptr-> + Year_of_Manufacture); + memcpy((void *)&mddi_client_capability_pkt, + (void *)rev_packet_data, + sizeof(mddi_client_capability_type)); + pmhctl->log_parms.cli_cap_cnt++; + } + break; + + case 70: /* Display Status */ + { + mddi_client_status_type *client_status_pkt_ptr; + + client_status_pkt_ptr = + (mddi_client_status_type *) rev_packet_data; + if ((client_status_pkt_ptr->crc_error_count != + 0) + || (client_status_pkt_ptr-> + reverse_link_request != 0)) { + MDDI_MSG_ERR + ("Client Status: RevReq=%d, CrcErr=%d\n", + client_status_pkt_ptr-> + reverse_link_request, + client_status_pkt_ptr-> + crc_error_count); + } else { + MDDI_MSG_DEBUG + ("Client Status: RevReq=%d, CrcErr=%d\n", + client_status_pkt_ptr-> + reverse_link_request, + client_status_pkt_ptr-> + crc_error_count); + } + pmhctl->log_parms.fwd_crc_cnt += + client_status_pkt_ptr->crc_error_count; + pmhctl->stats.fwd_crc_count += + client_status_pkt_ptr->crc_error_count; + pmhctl->log_parms.cli_stat_cnt++; + } + break; + + case 146: /* register access packet */ + { + mddi_register_access_packet_type + * regacc_pkt_ptr; + uint32 data_count; + + regacc_pkt_ptr = + (mddi_register_access_packet_type *) + rev_packet_data; + + /* Bits[0:13] - read data count */ + data_count = regacc_pkt_ptr->read_write_info + & 0x3FFF; + MDDI_MSG_DEBUG("\n MDDI rev read: 0x%x", + regacc_pkt_ptr->read_write_info); + MDDI_MSG_DEBUG("Reg Acc parse reg=0x%x," + "value=0x%x\n", regacc_pkt_ptr-> + register_address, regacc_pkt_ptr-> + register_data_list[0]); + + /* Copy register value to location passed in */ + if (mddi_reg_read_value_ptr) { +#if defined(T_MSM6280) && !defined(T_MSM7200) + /* only least significant 16 bits are valid with 6280 */ + *mddi_reg_read_value_ptr = + regacc_pkt_ptr-> + register_data_list[0] & 0x0000ffff; + mddi_reg_read_successful = TRUE; + mddi_reg_read_value_ptr = NULL; +#else + if (data_count && data_count <= + MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR) { + memcpy(mddi_reg_read_value_ptr, + (void *)®acc_pkt_ptr-> + register_data_list[0], + data_count * 4); + mddi_reg_read_successful = TRUE; + mddi_reg_read_value_ptr = NULL; + } +#endif + } + +#ifdef DEBUG_MDDIHOSTI + if ((mddi_gpio.polling_enabled) && + (regacc_pkt_ptr->register_address == + mddi_gpio.polling_reg)) { + /* + * ToDo: need to call Linux GPIO call + * here... + */ + mddi_client_lcd_gpio_poll( + regacc_pkt_ptr->register_data_list[0]); + } +#endif + pmhctl->log_parms.reg_read_cnt++; + } + break; + + case INVALID_PKT_TYPE: /* 0xFFFF */ + MDDI_MSG_ERR("!!!INVALID_PKT_TYPE rcvd\n"); + break; + + default: /* any other packet */ + { + uint16 hdlr; + + for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS; + hdlr++) { + if (mddi_rev_pkt_handler[hdlr]. + handler == NULL) + continue; + if (mddi_rev_pkt_handler[hdlr]. + pkt_type == + rev_pkt_ptr->packet_type) { + (*(mddi_rev_pkt_handler[hdlr]. + handler)) (rev_pkt_ptr); + /* pmhctl->rev_state = MDDI_REV_IDLE; */ + break; + } + } + if (hdlr >= MAX_MDDI_REV_HANDLERS) + MDDI_MSG_ERR("MDDI unknown rev pkt\n"); + } + break; + } + } + if ((pmhctl->rev_ptr_curr + pmhctl->rev_pkt_size) >= + (pmhctl->rev_ptr_start + MDDI_REV_BUFFER_SIZE)) { + pmhctl->rev_ptr_written = FALSE; + } + + if (pmhctl->rev_state == MDDI_REV_ENCAP_ISSUED) { + pmhctl->rev_state = MDDI_REV_IDLE; + if (mddi_rev_user.waiting) { + mddi_rev_user.waiting = FALSE; + complete(&(mddi_rev_user.done_comp)); + } else if (pmhctl->llist_info.reg_read_idx == UNASSIGNED_INDEX) { + MDDI_MSG_ERR + ("Reverse Encap state, but no reg read in progress\n"); + } else { + if ((!mddi_reg_read_successful) && + (mddi_reg_read_retry < mddi_reg_read_retry_max) && + (mddi_enable_reg_read_retry)) { + /* + * There is a race condition that can happen + * where the reverse encapsulation message is + * sent out by the MDDI host before the register + * read packet is sent. As a work-around for + * that problem we issue the reverse + * encapsulation one more time before giving up. + */ + if (mddi_enable_reg_read_retry_once) + mddi_reg_read_retry = + mddi_reg_read_retry_max; + else + mddi_reg_read_retry++; + pmhctl->rev_state = MDDI_REV_REG_READ_SENT; + pmhctl->stats.reg_read_failure++; + } else { + uint16 reg_read_idx = + pmhctl->llist_info.reg_read_idx; + + mddi_reg_read_retry = 0; + if (pmhctl->llist_notify[reg_read_idx].waiting) { + complete(& + (pmhctl-> + llist_notify[reg_read_idx]. + done_comp)); + } + pmhctl->llist_info.reg_read_idx = + UNASSIGNED_INDEX; + if (pmhctl->llist_notify[reg_read_idx]. + done_cb != NULL) { + (* + (pmhctl->llist_notify[reg_read_idx]. + done_cb)) (); + } + pmhctl->llist_notify[reg_read_idx].next_idx = + UNASSIGNED_INDEX; + pmhctl->llist_notify[reg_read_idx].in_use = + FALSE; + pmhctl->llist_notify[reg_read_idx].waiting = + FALSE; + pmhctl->llist_notify[reg_read_idx].done_cb = + NULL; + if (!mddi_reg_read_successful) + pmhctl->stats.reg_read_failure++; + } + } + } else if (pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED) { +#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) + if (mddi_host_core_version == 0x28 || + mddi_host_core_version == 0x30) { + mddi_host_reg_out(FIFO_ALLOC, 0x00); + pmhctl->rev_ptr_written = TRUE; + mddi_host_reg_out(REV_PTR, + pmhctl->mddi_rev_ptr_write_val); + pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; + mddi_host_reg_out(CMD, 0xC00); + } +#endif + + if (mddi_rev_user.waiting) { + mddi_rev_user.waiting = FALSE; + complete(&(mddi_rev_user.done_comp)); + } + pmhctl->rev_state = MDDI_REV_IDLE; + } else { + pmhctl->rev_state = MDDI_REV_IDLE; + } + + /* pmhctl->rev_state = MDDI_REV_IDLE; */ + + /* Re-enable interrupt */ + mddi_host_reg_outm(INTEN, MDDI_INT_REV_DATA_AVAIL, + MDDI_INT_REV_DATA_AVAIL); + +} + +static void mddi_issue_reverse_encapsulation(void) +{ + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + /* Only issue a reverse encapsulation packet if: + * 1) another reverse is not in progress (MDDI_REV_IDLE). + * 2) a register read has been sent (MDDI_REV_REG_READ_SENT). + * 3) forward is not in progress, because of a hw bug in client that + * causes forward crc errors on packet immediately after rev encap. + */ + if (((pmhctl->rev_state == MDDI_REV_IDLE) || + (pmhctl->rev_state == MDDI_REV_REG_READ_SENT)) && + (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) && + (!mdp_in_processing)) { + uint32 mddi_command = MDDI_CMD_SEND_REV_ENCAP; + + if ((pmhctl->rev_state == MDDI_REV_REG_READ_SENT) || + (mddi_rev_encap_user_request == TRUE)) { + mddi_host_enable_io_clock(); + if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { + /* need to wake up link before issuing rev encap command */ + MDDI_MSG_DEBUG("wake up link!\n"); + pmhctl->link_state = MDDI_LINK_ACTIVATING; + mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); + } else { + if (pmhctl->rtd_counter >= mddi_rtd_frequency) { + MDDI_MSG_DEBUG + ("mddi sending RTD command!\n"); + mddi_host_reg_out(CMD, + MDDI_CMD_SEND_RTD); + pmhctl->rtd_counter = 0; + pmhctl->log_parms.rtd_cnt++; + } + if (pmhctl->rev_state != MDDI_REV_REG_READ_SENT) { + /* this is generic reverse request by user, so + * reset the waiting flag. */ + mddi_rev_encap_user_request = FALSE; + } + /* link is active so send reverse encap to get register read results */ + pmhctl->rev_state = MDDI_REV_ENCAP_ISSUED; + mddi_command = MDDI_CMD_SEND_REV_ENCAP; + MDDI_MSG_DEBUG("sending rev encap!\n"); + } + } else + if ((pmhctl->client_status_cnt >= + mddi_client_status_frequency) + || mddi_client_capability_request) { + mddi_host_enable_io_clock(); + if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { + /* only wake up the link if it client status is overdue */ + if ((pmhctl->client_status_cnt >= + (mddi_client_status_frequency * 2)) + || mddi_client_capability_request) { + /* need to wake up link before issuing rev encap command */ + MDDI_MSG_DEBUG("wake up link!\n"); + pmhctl->link_state = + MDDI_LINK_ACTIVATING; + mddi_host_reg_out(CMD, + MDDI_CMD_LINK_ACTIVE); + } + } else { + if (pmhctl->rtd_counter >= mddi_rtd_frequency) { + MDDI_MSG_DEBUG + ("mddi sending RTD command!\n"); + mddi_host_reg_out(CMD, + MDDI_CMD_SEND_RTD); + pmhctl->rtd_counter = 0; + pmhctl->log_parms.rtd_cnt++; + } + /* periodically get client status */ + MDDI_MSG_DEBUG + ("mddi sending rev enc! (get status)\n"); + if (mddi_client_capability_request) { + pmhctl->rev_state = + MDDI_REV_CLIENT_CAP_ISSUED; + mddi_command = MDDI_CMD_GET_CLIENT_CAP; + mddi_client_capability_request = FALSE; + } else { + pmhctl->rev_state = + MDDI_REV_STATUS_REQ_ISSUED; + pmhctl->client_status_cnt = 0; + mddi_command = + MDDI_CMD_GET_CLIENT_STATUS; + } + } + } + if ((pmhctl->rev_state == MDDI_REV_ENCAP_ISSUED) || + (pmhctl->rev_state == MDDI_REV_STATUS_REQ_ISSUED) || + (pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED)) { + pmhctl->int_type.rev_encap_count++; +#if defined(T_MSM6280) && !defined(T_MSM7200) + mddi_rev_pointer_written = TRUE; + mddi_host_reg_out(REV_PTR, mddi_rev_ptr_write_val); + mddi_rev_ptr_curr = mddi_rev_ptr_start; + /* force new rev ptr command */ + mddi_host_reg_out(CMD, 0xC00); +#else + if (!pmhctl->rev_ptr_written) { + MDDI_MSG_DEBUG("writing reverse pointer!\n"); + pmhctl->rev_ptr_written = TRUE; +#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) + if ((pmhctl->rev_state == + MDDI_REV_CLIENT_CAP_ISSUED) && + (mddi_host_core_version == 0x28 || + mddi_host_core_version == 0x30)) { + pmhctl->rev_ptr_written = FALSE; + mddi_host_reg_out(FIFO_ALLOC, 0x02); + } else + mddi_host_reg_out(REV_PTR, + pmhctl-> + mddi_rev_ptr_write_val); +#else + mddi_host_reg_out(REV_PTR, + pmhctl-> + mddi_rev_ptr_write_val); +#endif + } +#endif + if (mddi_debug_clear_rev_data) { + uint16 i; + for (i = 0; i < MDDI_MAX_REV_DATA_SIZE / 4; i++) + pmhctl->rev_data_buf[i] = 0xdddddddd; + /* clean cache */ + mddi_flush_cache_lines(pmhctl->rev_data_buf, + MDDI_MAX_REV_DATA_SIZE); + } + + /* send reverse encapsulation to get needed data */ + mddi_host_reg_out(CMD, mddi_command); + } + } + +} + +static void mddi_process_client_initiated_wakeup(void) +{ + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + /* Disable MDDI_INT Interrupt, we detect client initiated wakeup one + * time for each entry into hibernation */ + mddi_host_reg_outm(INTEN, MDDI_INT_MDDI_IN, 0); + + if (host_idx == MDDI_HOST_PRIM) { + if (mddi_vsync_detect_enabled) { + mddi_host_enable_io_clock(); +#ifndef MDDI_HOST_DISP_LISTEN + /* issue command to bring up link */ + /* need to do this to clear the vsync condition */ + if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { + pmhctl->link_state = MDDI_LINK_ACTIVATING; + mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); + } +#endif + /* + * Indicate to client specific code that vsync was + * enabled, and we did not detect a client initiated + * wakeup. The client specific handler can clear the + * condition if necessary to prevent subsequent + * client initiated wakeups. + */ + mddi_client_lcd_vsync_detected(TRUE); + pmhctl->log_parms.vsync_response_cnt++; + MDDI_MSG_NOTICE("MDDI_INT_IN condition\n"); + + } + } + + if (mddi_gpio.polling_enabled) { + mddi_host_enable_io_clock(); + /* check interrupt status now */ + (void)mddi_queue_register_read_int(mddi_gpio.polling_reg, + &mddi_gpio.polling_val); + } +} +#endif /* FEATURE_MDDI_DISABLE_REVERSE */ + +static void mddi_host_isr(void) +{ + uint32 int_reg, int_en; +#ifndef FEATURE_MDDI_DISABLE_REVERSE + uint32 status_reg; +#endif + mddi_host_type host_idx = mddi_curr_host; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + if (!MDDI_HOST_IS_HCLK_ON) { + MDDI_HOST_ENABLE_HCLK; + } + int_reg = mddi_host_reg_in(INT); + int_en = mddi_host_reg_in(INTEN); + pmhctl->saved_int_reg = int_reg; + pmhctl->saved_int_en = int_en; + int_reg = int_reg & int_en; + pmhctl->int_type.count++; + + +#ifndef FEATURE_MDDI_DISABLE_REVERSE + status_reg = mddi_host_reg_in(STAT); + + if ((int_reg & MDDI_INT_MDDI_IN) || + ((int_en & MDDI_INT_MDDI_IN) && + ((int_reg == 0) || (status_reg & MDDI_STAT_CLIENT_WAKEUP_REQ)))) { + /* + * The MDDI_IN condition will clear itself, and so it is + * possible that MDDI_IN was the reason for the isr firing, + * even though the interrupt register does not have the + * MDDI_IN bit set. To check if this was the case we need to + * look at the status register bit that signifies a client + * initiated wakeup. If the status register bit is set, as well + * as the MDDI_IN interrupt enabled, then we treat this as a + * client initiated wakeup. + */ + if (int_reg & MDDI_INT_MDDI_IN) + pmhctl->int_type.in_count++; + mddi_process_client_initiated_wakeup(); + } +#endif + + if (int_reg & MDDI_INT_LINK_STATE_CHANGES) { + pmhctl->int_type.state_change_count++; + mddi_report_state_change(int_reg); + } + + if (int_reg & MDDI_INT_PRI_LINK_LIST_DONE) { + pmhctl->int_type.ll_done_count++; + mddi_process_link_list_done(); + } +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (int_reg & MDDI_INT_REV_DATA_AVAIL) { + pmhctl->int_type.rev_avail_count++; + mddi_process_rev_packets(); + } +#endif + + if (int_reg & MDDI_INT_ERROR_CONDITIONS) { + pmhctl->int_type.error_count++; + mddi_report_errors(int_reg); + + mddi_host_reg_out(INT, int_reg & MDDI_INT_ERROR_CONDITIONS); + } +#ifndef FEATURE_MDDI_DISABLE_REVERSE + mddi_issue_reverse_encapsulation(); + + if ((pmhctl->rev_state != MDDI_REV_ENCAP_ISSUED) && + (pmhctl->rev_state != MDDI_REV_STATUS_REQ_ISSUED)) +#endif + /* don't want simultaneous reverse and forward with Eagle */ + mddi_queue_forward_linked_list(); + + if (int_reg & MDDI_INT_NO_CMD_PKTS_PEND) { + /* this interrupt is used to kick the isr when hibernation is disabled */ + mddi_host_reg_outm(INTEN, MDDI_INT_NO_CMD_PKTS_PEND, 0); + } + + if ((!mddi_host_mdp_active_flag) && + (!mddi_vsync_detect_enabled) && + (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) && + (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) && + (pmhctl->rev_state == MDDI_REV_IDLE)) { + if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { + mddi_host_disable_io_clock(); + mddi_host_disable_hclk(); + } +#ifdef FEATURE_MDDI_HOST_ENABLE_EARLY_HIBERNATION + else if ((pmhctl->link_state == MDDI_LINK_ACTIVE) && + (!pmhctl->disable_hibernation)) { + mddi_host_reg_out(CMD, MDDI_CMD_POWERDOWN); + } +#endif + } +} + +static void mddi_host_isr_primary(void) +{ + mddi_curr_host = MDDI_HOST_PRIM; + mddi_host_isr(); +} + +irqreturn_t mddi_pmdh_isr_proxy(int irq, void *ptr) +{ + mddi_host_isr_primary(); + return IRQ_HANDLED; +} + +static void mddi_host_isr_external(void) +{ + mddi_curr_host = MDDI_HOST_EXT; + mddi_host_isr(); + mddi_curr_host = MDDI_HOST_PRIM; +} + +irqreturn_t mddi_emdh_isr_proxy(int irq, void *ptr) +{ + mddi_host_isr_external(); + return IRQ_HANDLED; +} + +static void mddi_host_initialize_registers(mddi_host_type host_idx) +{ + uint32 pad_reg_val; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + if (pmhctl->driver_state == MDDI_DRIVER_ENABLED) + return; + + /* turn on HCLK to MDDI host core */ + mddi_host_enable_hclk(); + + /* MDDI Reset command */ + mddi_host_reg_out(CMD, MDDI_CMD_RESET); + + /* Version register (= 0x01) */ + mddi_host_reg_out(VERSION, 0x0001); + + /* Bytes per subframe register */ + mddi_host_reg_out(BPS, MDDI_HOST_BYTES_PER_SUBFRAME); + + /* Subframes per media frames register (= 0x03) */ + mddi_host_reg_out(SPM, 0x0003); + + /* Turn Around 1 register (= 0x05) */ + mddi_host_reg_out(TA1_LEN, 0x0005); + + /* Turn Around 2 register (= 0x0C) */ + mddi_host_reg_out(TA2_LEN, MDDI_HOST_TA2_LEN); + + /* Drive hi register (= 0x96) */ + mddi_host_reg_out(DRIVE_HI, 0x0096); + + /* Drive lo register (= 0x32) */ + mddi_host_reg_out(DRIVE_LO, 0x0032); + + /* Display wakeup count register (= 0x3c) */ + mddi_host_reg_out(DISP_WAKE, 0x003c); + + /* Reverse Rate Divisor register (= 0x2) */ + mddi_host_reg_out(REV_RATE_DIV, MDDI_HOST_REV_RATE_DIV); + +#ifndef FEATURE_MDDI_DISABLE_REVERSE + /* Reverse Pointer Size */ + mddi_host_reg_out(REV_SIZE, MDDI_REV_BUFFER_SIZE); + + /* Rev Encap Size */ + mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size); +#endif + + /* Periodic Rev Encap */ + /* don't send periodically */ + mddi_host_reg_out(CMD, MDDI_CMD_PERIODIC_REV_ENCAP); + + pad_reg_val = mddi_host_reg_in(PAD_CTL); + if (pad_reg_val == 0) { + /* If we are turning on band gap, need to wait 5us before turning + * on the rest of the PAD */ + mddi_host_reg_out(PAD_CTL, 0x08000); + udelay(5); + } +#ifdef T_MSM7200 + /* Recommendation from PAD hw team */ + mddi_host_reg_out(PAD_CTL, 0xa850a); +#else + /* Recommendation from PAD hw team */ + mddi_host_reg_out(PAD_CTL, 0xa850f); +#endif + + pad_reg_val = 0x00220020; + +#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) + mddi_host_reg_out(PAD_IO_CTL, 0x00320000); + mddi_host_reg_out(PAD_CAL, pad_reg_val); +#endif + + mddi_host_core_version = mddi_host_reg_inm(CORE_VER, 0xffff); + +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (mddi_host_core_version >= 8) + mddi_rev_ptr_workaround = FALSE; + pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; +#endif + + if ((mddi_host_core_version > 8) && (mddi_host_core_version < 0x19)) + mddi_host_reg_out(TEST, 0x2); + + /* Need an even number for counts */ + mddi_host_reg_out(DRIVER_START_CNT, 0x60006); + +#ifndef T_MSM7500 + /* Setup defaults for MDP related register */ + mddi_host_reg_out(MDP_VID_FMT_DES, 0x5666); + mddi_host_reg_out(MDP_VID_PIX_ATTR, 0x00C3); + mddi_host_reg_out(MDP_VID_CLIENTID, 0); +#endif + + /* automatically hibernate after 1 empty subframe */ + if (pmhctl->disable_hibernation) + mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE); + else + mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1); + + /* Bring up link if display (client) requests it */ +#ifdef MDDI_HOST_DISP_LISTEN + mddi_host_reg_out(CMD, MDDI_CMD_DISP_LISTEN); +#else + mddi_host_reg_out(CMD, MDDI_CMD_DISP_IGNORE); +#endif + +} + +void mddi_host_configure_interrupts(mddi_host_type host_idx, boolean enable) +{ + unsigned long flags; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + spin_lock_irqsave(&mddi_host_spin_lock, flags); + + /* turn on HCLK to MDDI host core if it has been disabled */ + mddi_host_enable_hclk(); + /* Clear MDDI Interrupt enable reg */ + mddi_host_reg_out(INTEN, 0); + + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + + if (enable) { + pmhctl->driver_state = MDDI_DRIVER_ENABLED; + + if (host_idx == MDDI_HOST_PRIM) { + if (request_irq + (INT_MDDI_PRI, mddi_pmdh_isr_proxy, IRQF_DISABLED, + "PMDH", 0) != 0) + printk(KERN_ERR + "a mddi: unable to request_irq\n"); + else { + int_mddi_pri_flag = TRUE; + irq_enabled = 1; + } + } else { + if (request_irq + (INT_MDDI_EXT, mddi_emdh_isr_proxy, IRQF_DISABLED, + "EMDH", 0) != 0) + printk(KERN_ERR + "b mddi: unable to request_irq\n"); + else + int_mddi_ext_flag = TRUE; + } + + /* Set MDDI Interrupt enable reg -- Enable Reverse data avail */ +#ifdef FEATURE_MDDI_DISABLE_REVERSE + mddi_host_reg_out(INTEN, + MDDI_INT_ERROR_CONDITIONS | + MDDI_INT_LINK_STATE_CHANGES); +#else + /* Reverse Pointer register */ + pmhctl->rev_ptr_written = FALSE; + + mddi_host_reg_out(INTEN, + MDDI_INT_REV_DATA_AVAIL | + MDDI_INT_ERROR_CONDITIONS | + MDDI_INT_LINK_STATE_CHANGES); + pmhctl->rtd_counter = mddi_rtd_frequency; + pmhctl->client_status_cnt = 0; +#endif + } else { + if (pmhctl->driver_state == MDDI_DRIVER_ENABLED) + pmhctl->driver_state = MDDI_DRIVER_DISABLED; + } + +} + +/* + * mddi_host_client_cnt_reset: + * reset client_status_cnt to 0 to make sure host does not + * send RTD cmd to client right after resume before mddi + * client be powered up. this fix "MDDI RTD Failure" problem + */ +void mddi_host_client_cnt_reset(void) +{ + unsigned long flags; + mddi_host_cntl_type *pmhctl; + + pmhctl = &(mhctl[MDDI_HOST_PRIM]); + spin_lock_irqsave(&mddi_host_spin_lock, flags); + pmhctl->client_status_cnt = 0; + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); +} + +static void mddi_host_powerup(mddi_host_type host_idx) +{ + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + if (pmhctl->link_state != MDDI_LINK_DISABLED) + return; + + /* enable IO_CLK and hclk to MDDI host core */ + mddi_host_enable_io_clock(); + + mddi_host_initialize_registers(host_idx); + mddi_host_configure_interrupts(host_idx, TRUE); + + pmhctl->link_state = MDDI_LINK_ACTIVATING; + + /* Link activate command */ + mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); + +#ifdef CLKRGM_MDDI_IO_CLOCK_IN_MHZ + MDDI_MSG_NOTICE("MDDI Host: Activating Link %d Mbps\n", + CLKRGM_MDDI_IO_CLOCK_IN_MHZ * 2); +#else + MDDI_MSG_NOTICE("MDDI Host: Activating Link\n"); +#endif + + /* Initialize the timer */ + if (host_idx == MDDI_HOST_PRIM) + mddi_host_timer_service(0); +} + +void mddi_send_fw_link_skew_cal(mddi_host_type host_idx) +{ + mddi_host_reg_out(CMD, MDDI_CMD_FW_LINK_SKEW_CAL); + MDDI_MSG_DEBUG("%s: Skew Calibration done!!\n", __func__); +} + + +void mddi_host_init(mddi_host_type host_idx) +/* Write out the MDDI configuration registers */ +{ + static boolean initialized = FALSE; + mddi_host_cntl_type *pmhctl; + + if (host_idx >= MDDI_NUM_HOST_CORES) { + MDDI_MSG_ERR("Invalid host core index\n"); + return; + } + + if (!initialized) { + uint16 idx; + mddi_host_type host; + + for (host = MDDI_HOST_PRIM; host < MDDI_NUM_HOST_CORES; host++) { + pmhctl = &(mhctl[host]); + initialized = TRUE; + + pmhctl->llist_ptr = + dma_alloc_coherent(NULL, MDDI_LLIST_POOL_SIZE, + &(pmhctl->llist_dma_addr), + GFP_KERNEL); + pmhctl->llist_dma_ptr = + (mddi_linked_list_type *) (void *)pmhctl-> + llist_dma_addr; +#ifdef FEATURE_MDDI_DISABLE_REVERSE + pmhctl->rev_data_buf = NULL; + if (pmhctl->llist_ptr == NULL) +#else + mddi_rev_user.waiting = FALSE; + init_completion(&(mddi_rev_user.done_comp)); + pmhctl->rev_data_buf = + dma_alloc_coherent(NULL, MDDI_MAX_REV_DATA_SIZE, + &(pmhctl->rev_data_dma_addr), + GFP_KERNEL); + if ((pmhctl->llist_ptr == NULL) + || (pmhctl->rev_data_buf == NULL)) +#endif + { + MDDI_MSG_CRIT + ("unable to alloc non-cached memory\n"); + } + llist_extern[host] = pmhctl->llist_ptr; + llist_dma_extern[host] = pmhctl->llist_dma_ptr; + llist_extern_notify[host] = pmhctl->llist_notify; + + for (idx = 0; idx < UNASSIGNED_INDEX; idx++) { + init_completion(& + (pmhctl->llist_notify[idx]. + done_comp)); + } + init_completion(&(pmhctl->mddi_llist_avail_comp)); + spin_lock_init(&mddi_host_spin_lock); + pmhctl->mddi_waiting_for_llist_avail = FALSE; + pmhctl->mddi_rev_ptr_write_val = + (uint32) (void *)(pmhctl->rev_data_dma_addr); + pmhctl->rev_ptr_start = (void *)pmhctl->rev_data_buf; + + pmhctl->rev_pkt_size = MDDI_DEFAULT_REV_PKT_SIZE; + pmhctl->rev_state = MDDI_REV_IDLE; +#ifdef IMAGE_MODEM_PROC + /* assume hibernation state is last state from APPS proc, so that + * we don't reinitialize the host core */ + pmhctl->link_state = MDDI_LINK_HIBERNATING; +#else + pmhctl->link_state = MDDI_LINK_DISABLED; +#endif + pmhctl->driver_state = MDDI_DRIVER_DISABLED; + pmhctl->disable_hibernation = FALSE; + + /* initialize llist variables */ + pmhctl->llist_info.transmitting_start_idx = + UNASSIGNED_INDEX; + pmhctl->llist_info.transmitting_end_idx = + UNASSIGNED_INDEX; + pmhctl->llist_info.waiting_start_idx = UNASSIGNED_INDEX; + pmhctl->llist_info.waiting_end_idx = UNASSIGNED_INDEX; + pmhctl->llist_info.reg_read_idx = UNASSIGNED_INDEX; + pmhctl->llist_info.next_free_idx = + MDDI_FIRST_DYNAMIC_LLIST_IDX; + pmhctl->llist_info.reg_read_waiting = FALSE; + + mddi_vsync_detect_enabled = FALSE; + mddi_gpio.polling_enabled = FALSE; + + pmhctl->int_type.count = 0; + pmhctl->int_type.in_count = 0; + pmhctl->int_type.disp_req_count = 0; + pmhctl->int_type.state_change_count = 0; + pmhctl->int_type.ll_done_count = 0; + pmhctl->int_type.rev_avail_count = 0; + pmhctl->int_type.error_count = 0; + pmhctl->int_type.rev_encap_count = 0; + pmhctl->int_type.llist_ptr_write_1 = 0; + pmhctl->int_type.llist_ptr_write_2 = 0; + + pmhctl->stats.fwd_crc_count = 0; + pmhctl->stats.rev_crc_count = 0; + pmhctl->stats.pri_underflow = 0; + pmhctl->stats.sec_underflow = 0; + pmhctl->stats.rev_overflow = 0; + pmhctl->stats.pri_overwrite = 0; + pmhctl->stats.sec_overwrite = 0; + pmhctl->stats.rev_overwrite = 0; + pmhctl->stats.dma_failure = 0; + pmhctl->stats.rtd_failure = 0; + pmhctl->stats.reg_read_failure = 0; +#ifdef FEATURE_MDDI_UNDERRUN_RECOVERY + pmhctl->stats.pri_underrun_detected = 0; +#endif + + pmhctl->log_parms.rtd_cnt = 0; + pmhctl->log_parms.rev_enc_cnt = 0; + pmhctl->log_parms.vid_cnt = 0; + pmhctl->log_parms.reg_acc_cnt = 0; + pmhctl->log_parms.cli_stat_cnt = 0; + pmhctl->log_parms.cli_cap_cnt = 0; + pmhctl->log_parms.reg_read_cnt = 0; + pmhctl->log_parms.link_active_cnt = 0; + pmhctl->log_parms.link_hibernate_cnt = 0; + pmhctl->log_parms.fwd_crc_cnt = 0; + pmhctl->log_parms.rev_crc_cnt = 0; + pmhctl->log_parms.vsync_response_cnt = 0; + + prev_parms[host_idx] = pmhctl->log_parms; + mddi_client_capability_pkt.packet_length = 0; + } + +#ifndef T_MSM7500 + /* tell clock driver we are user of this PLL */ + MDDI_HOST_ENABLE_IO_CLOCK; +#endif + } + + mddi_host_powerup(host_idx); + pmhctl = &(mhctl[host_idx]); +} + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT +static uint32 mddi_client_id; + +uint32 mddi_get_client_id(void) +{ + +#ifndef FEATURE_MDDI_DISABLE_REVERSE + mddi_host_type host_idx = MDDI_HOST_PRIM; + static boolean client_detection_try = FALSE; + mddi_host_cntl_type *pmhctl; + unsigned long flags; + uint16 saved_rev_pkt_size; + int ret; + + if (!client_detection_try) { + /* Toshiba display requires larger drive_lo value */ + mddi_host_reg_out(DRIVE_LO, 0x0050); + + pmhctl = &(mhctl[MDDI_HOST_PRIM]); + + saved_rev_pkt_size = pmhctl->rev_pkt_size; + + /* Increase Rev Encap Size */ + pmhctl->rev_pkt_size = MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE; + mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size); + + /* disable hibernation temporarily */ + if (!pmhctl->disable_hibernation) + mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE); + + mddi_rev_user.waiting = TRUE; + INIT_COMPLETION(mddi_rev_user.done_comp); + + spin_lock_irqsave(&mddi_host_spin_lock, flags); + + /* turn on clock(s), if they have been disabled */ + mddi_host_enable_hclk(); + mddi_host_enable_io_clock(); + + mddi_client_capability_request = TRUE; + + if (pmhctl->rev_state == MDDI_REV_IDLE) { + /* attempt to send the reverse encapsulation now */ + mddi_issue_reverse_encapsulation(); + } + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + + wait_for_completion_killable(&(mddi_rev_user.done_comp)); + + /* Set Rev Encap Size back to its original value */ + pmhctl->rev_pkt_size = saved_rev_pkt_size; + mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size); + + /* reenable auto-hibernate */ + if (!pmhctl->disable_hibernation) + mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1); + + mddi_host_reg_out(DRIVE_LO, 0x0032); + client_detection_try = TRUE; + + mddi_client_id = (mddi_client_capability_pkt.Mfr_Name<<16) | + mddi_client_capability_pkt.Product_Code; + + if (!mddi_client_id) + mddi_disable(1); + + ret = mddi_client_power(mddi_client_id); + if (ret < 0) + MDDI_MSG_ERR("mddi_client_power return %d", ret); + } + +#endif + + return mddi_client_id; +} +#endif + +void mddi_host_powerdown(mddi_host_type host_idx) +{ + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + if (host_idx >= MDDI_NUM_HOST_CORES) { + MDDI_MSG_ERR("Invalid host core index\n"); + return; + } + + if (pmhctl->driver_state == MDDI_DRIVER_RESET) { + return; + } + + if (host_idx == MDDI_HOST_PRIM) { + /* disable timer */ + del_timer(&mddi_host_timer); + } + + mddi_host_configure_interrupts(host_idx, FALSE); + + /* turn on HCLK to MDDI host core if it has been disabled */ + mddi_host_enable_hclk(); + + /* MDDI Reset command */ + mddi_host_reg_out(CMD, MDDI_CMD_RESET); + + /* Pad Control Register */ + mddi_host_reg_out(PAD_CTL, 0x0); + + /* disable IO_CLK and hclk to MDDI host core */ + mddi_host_disable_io_clock(); + mddi_host_disable_hclk(); + + pmhctl->link_state = MDDI_LINK_DISABLED; + pmhctl->driver_state = MDDI_DRIVER_RESET; + + MDDI_MSG_NOTICE("MDDI Host: Disabling Link\n"); + +} + +uint16 mddi_get_next_free_llist_item(mddi_host_type host_idx, boolean wait) +{ + unsigned long flags; + uint16 ret_idx; + boolean forced_wait = FALSE; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + ret_idx = pmhctl->llist_info.next_free_idx; + + pmhctl->llist_info.next_free_idx++; + if (pmhctl->llist_info.next_free_idx >= MDDI_NUM_DYNAMIC_LLIST_ITEMS) + pmhctl->llist_info.next_free_idx = MDDI_FIRST_DYNAMIC_LLIST_IDX; + spin_lock_irqsave(&mddi_host_spin_lock, flags); + if (pmhctl->llist_notify[ret_idx].in_use) { + if (!wait) { + pmhctl->llist_info.next_free_idx = ret_idx; + ret_idx = UNASSIGNED_INDEX; + } else { + forced_wait = TRUE; + INIT_COMPLETION(pmhctl->mddi_llist_avail_comp); + } + } + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + + if (forced_wait) { + wait_for_completion_killable(& + (pmhctl-> + mddi_llist_avail_comp)); + MDDI_MSG_ERR("task waiting on mddi llist item\n"); + } + + if (ret_idx != UNASSIGNED_INDEX) { + pmhctl->llist_notify[ret_idx].waiting = FALSE; + pmhctl->llist_notify[ret_idx].done_cb = NULL; + pmhctl->llist_notify[ret_idx].in_use = TRUE; + pmhctl->llist_notify[ret_idx].next_idx = UNASSIGNED_INDEX; + } + + return ret_idx; +} + +uint16 mddi_get_reg_read_llist_item(mddi_host_type host_idx, boolean wait) +{ +#ifdef FEATURE_MDDI_DISABLE_REVERSE + MDDI_MSG_CRIT("No reverse link available\n"); + (void)wait; + return FALSE; +#else + unsigned long flags; + uint16 ret_idx; + boolean error = FALSE; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + spin_lock_irqsave(&mddi_host_spin_lock, flags); + if (pmhctl->llist_info.reg_read_idx != UNASSIGNED_INDEX) { + /* need to block here or is this an error condition? */ + error = TRUE; + ret_idx = UNASSIGNED_INDEX; + } + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + + if (!error) { + ret_idx = pmhctl->llist_info.reg_read_idx = + mddi_get_next_free_llist_item(host_idx, wait); + /* clear the reg_read_waiting flag */ + pmhctl->llist_info.reg_read_waiting = FALSE; + } + + if (error) + MDDI_MSG_ERR("***** Reg read still in progress! ****\n"); + return ret_idx; +#endif + +} + +void mddi_queue_forward_packets(uint16 first_llist_idx, + uint16 last_llist_idx, + boolean wait, + mddi_llist_done_cb_type llist_done_cb, + mddi_host_type host_idx) +{ + unsigned long flags; + mddi_linked_list_type *llist; + mddi_linked_list_type *llist_dma; + mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); + + if ((first_llist_idx >= UNASSIGNED_INDEX) || + (last_llist_idx >= UNASSIGNED_INDEX)) { + MDDI_MSG_ERR("MDDI queueing invalid linked list\n"); + return; + } + + if (pmhctl->link_state == MDDI_LINK_DISABLED) + MDDI_MSG_CRIT("MDDI host powered down!\n"); + + llist = pmhctl->llist_ptr; + llist_dma = pmhctl->llist_dma_ptr; + + /* clean cache so MDDI host can read data */ + memory_barrier(); + + pmhctl->llist_notify[last_llist_idx].waiting = wait; + if (wait) + INIT_COMPLETION(pmhctl->llist_notify[last_llist_idx].done_comp); + pmhctl->llist_notify[last_llist_idx].done_cb = llist_done_cb; + + spin_lock_irqsave(&mddi_host_spin_lock, flags); + + if ((pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) && + (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) && + (pmhctl->rev_state == MDDI_REV_IDLE)) { + /* no packets are currently transmitting */ +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (first_llist_idx == pmhctl->llist_info.reg_read_idx) { + /* This is the special case where the packet is a register read. */ + pmhctl->rev_state = MDDI_REV_REG_READ_ISSUED; + mddi_reg_read_retry = 0; + /* mddi_rev_reg_read_attempt = 1; */ + } +#endif + /* assign transmitting index values */ + pmhctl->llist_info.transmitting_start_idx = first_llist_idx; + pmhctl->llist_info.transmitting_end_idx = last_llist_idx; + + /* turn on clock(s), if they have been disabled */ + mddi_host_enable_hclk(); + mddi_host_enable_io_clock(); + pmhctl->int_type.llist_ptr_write_1++; + /* Write to primary pointer register */ + dma_coherent_pre_ops(); + mddi_host_reg_out(PRI_PTR, &llist_dma[first_llist_idx]); + + /* enable interrupt when complete */ + mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE, + MDDI_INT_PRI_LINK_LIST_DONE); + + } else if (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) { +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (first_llist_idx == pmhctl->llist_info.reg_read_idx) { + /* + * we have a register read to send but need to wait + * for current reverse activity to end or there are + * packets currently transmitting + */ + /* mddi_rev_reg_read_attempt = 0; */ + pmhctl->llist_info.reg_read_waiting = TRUE; + } +#endif + + /* assign waiting index values */ + pmhctl->llist_info.waiting_start_idx = first_llist_idx; + pmhctl->llist_info.waiting_end_idx = last_llist_idx; + } else { + uint16 prev_end_idx = pmhctl->llist_info.waiting_end_idx; +#ifndef FEATURE_MDDI_DISABLE_REVERSE + if (first_llist_idx == pmhctl->llist_info.reg_read_idx) { + /* + * we have a register read to send but need to wait + * for current reverse activity to end or there are + * packets currently transmitting + */ + /* mddi_rev_reg_read_attempt = 0; */ + pmhctl->llist_info.reg_read_waiting = TRUE; + } +#endif + + llist = pmhctl->llist_ptr; + + /* clear end flag in previous last packet */ + llist[prev_end_idx].link_controller_flags = 0; + pmhctl->llist_notify[prev_end_idx].next_idx = first_llist_idx; + + /* set the next_packet_pointer of the previous last packet */ + llist[prev_end_idx].next_packet_pointer = + (void *)(&llist_dma[first_llist_idx]); + + /* clean cache so MDDI host can read data */ + memory_barrier(); + + /* assign new waiting last index value */ + pmhctl->llist_info.waiting_end_idx = last_llist_idx; + } + + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + +} + +void mddi_host_write_pix_attr_reg(uint32 value) +{ + (void)value; +} + +void mddi_queue_reverse_encapsulation(boolean wait) +{ +#ifdef FEATURE_MDDI_DISABLE_REVERSE + MDDI_MSG_CRIT("No reverse link available\n"); + (void)wait; +#else + unsigned long flags; + boolean error = FALSE; + mddi_host_type host_idx = MDDI_HOST_PRIM; + mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]); + + spin_lock_irqsave(&mddi_host_spin_lock, flags); + + /* turn on clock(s), if they have been disabled */ + mddi_host_enable_hclk(); + mddi_host_enable_io_clock(); + + if (wait) { + if (!mddi_rev_user.waiting) { + mddi_rev_user.waiting = TRUE; + INIT_COMPLETION(mddi_rev_user.done_comp); + } else + error = TRUE; + } + mddi_rev_encap_user_request = TRUE; + + if (pmhctl->rev_state == MDDI_REV_IDLE) { + /* attempt to send the reverse encapsulation now */ + mddi_host_type orig_host_idx = mddi_curr_host; + mddi_curr_host = host_idx; + mddi_issue_reverse_encapsulation(); + mddi_curr_host = orig_host_idx; + } + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + + if (error) { + MDDI_MSG_ERR("Reverse Encap request already in progress\n"); + } else if (wait) + wait_for_completion_killable(&(mddi_rev_user.done_comp)); +#endif +} + +/* ISR to be executed */ +boolean mddi_set_rev_handler(mddi_rev_handler_type handler, uint16 pkt_type) +{ +#ifdef FEATURE_MDDI_DISABLE_REVERSE + MDDI_MSG_CRIT("No reverse link available\n"); + (void)handler; + (void)pkt_type; + return (FALSE); +#else + unsigned long flags; + uint16 hdlr; + boolean handler_set = FALSE; + boolean overwrite = FALSE; + mddi_host_type host_idx = MDDI_HOST_PRIM; + mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]); + + /* Disable interrupts */ + spin_lock_irqsave(&mddi_host_spin_lock, flags); + + for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS; hdlr++) { + if (mddi_rev_pkt_handler[hdlr].pkt_type == pkt_type) { + mddi_rev_pkt_handler[hdlr].handler = handler; + if (handler == NULL) { + /* clearing handler from table */ + mddi_rev_pkt_handler[hdlr].pkt_type = + INVALID_PKT_TYPE; + handler_set = TRUE; + if (pkt_type == 0x10) { /* video stream packet */ + /* ensure HCLK on to MDDI host core before register write */ + mddi_host_enable_hclk(); + /* No longer getting video, so reset rev encap size to default */ + pmhctl->rev_pkt_size = + MDDI_DEFAULT_REV_PKT_SIZE; + mddi_host_reg_out(REV_ENCAP_SZ, + pmhctl->rev_pkt_size); + } + } else { + /* already a handler for this packet */ + overwrite = TRUE; + } + break; + } + } + if ((hdlr >= MAX_MDDI_REV_HANDLERS) && (handler != NULL)) { + /* assigning new handler */ + for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS; hdlr++) { + if (mddi_rev_pkt_handler[hdlr].pkt_type == + INVALID_PKT_TYPE) { + if ((pkt_type == 0x10) && /* video stream packet */ + (pmhctl->rev_pkt_size < + MDDI_VIDEO_REV_PKT_SIZE)) { + /* ensure HCLK on to MDDI host core before register write */ + mddi_host_enable_hclk(); + /* Increase Rev Encap Size */ + pmhctl->rev_pkt_size = + MDDI_VIDEO_REV_PKT_SIZE; + mddi_host_reg_out(REV_ENCAP_SZ, + pmhctl->rev_pkt_size); + } + mddi_rev_pkt_handler[hdlr].handler = handler; + mddi_rev_pkt_handler[hdlr].pkt_type = pkt_type; + handler_set = TRUE; + break; + } + } + } + + /* Restore interrupts */ + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + + if (overwrite) + MDDI_MSG_ERR("Overwriting previous rev packet handler\n"); + + return handler_set; + +#endif +} /* mddi_set_rev_handler */ + +void mddi_host_disable_hibernation(boolean disable) +{ + mddi_host_type host_idx = MDDI_HOST_PRIM; + mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]); + + if (disable) { + pmhctl->disable_hibernation = TRUE; + /* hibernation will be turned off by isr next time it is entered */ + } else { + if (pmhctl->disable_hibernation) { + unsigned long flags; + spin_lock_irqsave(&mddi_host_spin_lock, flags); + if (!MDDI_HOST_IS_HCLK_ON) + MDDI_HOST_ENABLE_HCLK; + mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1); + spin_unlock_irqrestore(&mddi_host_spin_lock, flags); + pmhctl->disable_hibernation = FALSE; + } + } +} + +void mddi_mhctl_remove(mddi_host_type host_idx) +{ + mddi_host_cntl_type *pmhctl; + + pmhctl = &(mhctl[host_idx]); + + dma_free_coherent(NULL, MDDI_LLIST_POOL_SIZE, (void *)pmhctl->llist_ptr, + pmhctl->llist_dma_addr); + + dma_free_coherent(NULL, MDDI_MAX_REV_DATA_SIZE, + (void *)pmhctl->rev_data_buf, + pmhctl->rev_data_dma_addr); +} diff --git a/drivers/video/msm/mddihosti.h b/drivers/video/msm/mddihosti.h new file mode 100644 index 0000000000000000000000000000000000000000..96675a0b77c3b537037311206b62ec8ae01769b6 --- /dev/null +++ b/drivers/video/msm/mddihosti.h @@ -0,0 +1,552 @@ +/* Copyright (c) 2008-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MDDIHOSTI_H +#define MDDIHOSTI_H + +#include "msm_fb.h" +#include "mddihost.h" +#include + +/* Register offsets in MDDI, applies to both msm_pmdh_base and + * (u32)msm_emdh_base. */ +#define MDDI_CMD 0x0000 +#define MDDI_VERSION 0x0004 +#define MDDI_PRI_PTR 0x0008 +#define MDDI_BPS 0x0010 +#define MDDI_SPM 0x0014 +#define MDDI_INT 0x0018 +#define MDDI_INTEN 0x001c +#define MDDI_REV_PTR 0x0020 +#define MDDI_REV_SIZE 0x0024 +#define MDDI_STAT 0x0028 +#define MDDI_REV_RATE_DIV 0x002c +#define MDDI_REV_CRC_ERR 0x0030 +#define MDDI_TA1_LEN 0x0034 +#define MDDI_TA2_LEN 0x0038 +#define MDDI_TEST 0x0040 +#define MDDI_REV_PKT_CNT 0x0044 +#define MDDI_DRIVE_HI 0x0048 +#define MDDI_DRIVE_LO 0x004c +#define MDDI_DISP_WAKE 0x0050 +#define MDDI_REV_ENCAP_SZ 0x0054 +#define MDDI_RTD_VAL 0x0058 +#define MDDI_PAD_CTL 0x0068 +#define MDDI_DRIVER_START_CNT 0x006c +#define MDDI_CORE_VER 0x008c +#define MDDI_FIFO_ALLOC 0x0090 +#define MDDI_PAD_IO_CTL 0x00a0 +#define MDDI_PAD_CAL 0x00a4 + +#ifdef ENABLE_MDDI_MULTI_READ_WRITE +#define MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR 128 +#else +#define MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR 1 +#endif + +extern int32 mddi_client_type; +extern u32 mddi_msg_level; + +/* No longer need to write to clear these registers */ +#define xxxx_mddi_host_reg_outm(reg, mask, val) \ +do { \ + if (host_idx == MDDI_HOST_PRIM) \ + mddi_host_reg_outm_pmdh(reg, mask, val); \ + else \ + mddi_host_reg_outm_emdh(reg, mask, val); \ +} while (0) + +#define mddi_host_reg_outm(reg, mask, val) \ +do { \ + unsigned long __addr; \ + if (host_idx == MDDI_HOST_PRIM) \ + __addr = (u32)msm_pmdh_base + MDDI_##reg; \ + else \ + __addr = (u32)msm_emdh_base + MDDI_##reg; \ + writel((readl(__addr) & ~(mask)) | ((val) & (mask)), __addr); \ +} while (0) + +#define xxxx_mddi_host_reg_out(reg, val) \ +do { \ + if (host_idx == MDDI_HOST_PRIM) \ + mddi_host_reg_out_pmdh(reg, val); \ + else \ + mddi_host_reg_out_emdh(reg, val); \ + } while (0) + +#define mddi_host_reg_out(reg, val) \ +do { \ + if (host_idx == MDDI_HOST_PRIM) \ + writel(val, (u32)msm_pmdh_base + MDDI_##reg); \ + else \ + writel(val, (u32)msm_emdh_base + MDDI_##reg); \ +} while (0) + +#define xxxx_mddi_host_reg_in(reg) \ + ((host_idx) ? \ + mddi_host_reg_in_emdh(reg) : mddi_host_reg_in_pmdh(reg)); + +#define mddi_host_reg_in(reg) \ +((host_idx) ? \ + readl((u32)msm_emdh_base + MDDI_##reg) : \ + readl((u32)msm_pmdh_base + MDDI_##reg)) \ + +#define xxxx_mddi_host_reg_inm(reg, mask) \ + ((host_idx) ? \ + mddi_host_reg_inm_emdh(reg, mask) : \ + mddi_host_reg_inm_pmdh(reg, mask);) + +#define mddi_host_reg_inm(reg, mask) \ +((host_idx) ? \ + readl((u32)msm_emdh_base + MDDI_##reg) & (mask) : \ + readl((u32)msm_pmdh_base + MDDI_##reg) & (mask)) \ + +/* Using non-cacheable pmem, so do nothing */ +#define mddi_invalidate_cache_lines(addr_start, num_bytes) +/* + * Using non-cacheable pmem, so do nothing with cache + * but, ensure write goes out to memory + */ +#define mddi_flush_cache_lines(addr_start, num_bytes) \ + (void) addr_start; \ + (void) num_bytes; \ + memory_barrier() + +/* Since this translates to Remote Procedure Calls to check on clock status +* just use a local variable to keep track of io_clock */ +#define MDDI_HOST_IS_IO_CLOCK_ON mddi_host_io_clock_on +#define MDDI_HOST_ENABLE_IO_CLOCK +#define MDDI_HOST_DISABLE_IO_CLOCK +#define MDDI_HOST_IS_HCLK_ON mddi_host_hclk_on +#define MDDI_HOST_ENABLE_HCLK +#define MDDI_HOST_DISABLE_HCLK +#define FEATURE_MDDI_HOST_IO_CLOCK_CONTROL_DISABLE +#define FEATURE_MDDI_HOST_HCLK_CONTROL_DISABLE + +#define TRAMP_MDDI_HOST_ISR TRAMP_MDDI_PRI_ISR +#define TRAMP_MDDI_HOST_EXT_ISR TRAMP_MDDI_EXT_ISR +#define MDP_LINE_COUNT_BMSK 0x3ff +#define MDP_SYNC_STATUS 0x000c +#define MDP_LINE_COUNT \ +(readl(msm_mdp_base + MDP_SYNC_STATUS) & MDP_LINE_COUNT_BMSK) + +/* MDP sends 256 pixel packets, so lower value hibernates more without +* significantly increasing latency of waiting for next subframe */ +#define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00 + +#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) +#define MDDI_HOST_TA2_LEN 0x001a +#define MDDI_HOST_REV_RATE_DIV 0x0004 +#else +#define MDDI_HOST_TA2_LEN 0x000c +#define MDDI_HOST_REV_RATE_DIV 0x0002 +#endif + +#define MDDI_MSG_EMERG(msg, ...) \ + if (mddi_msg_level > 0) \ + printk(KERN_EMERG msg, ## __VA_ARGS__); +#define MDDI_MSG_ALERT(msg, ...) \ + if (mddi_msg_level > 1) \ + printk(KERN_ALERT msg, ## __VA_ARGS__); +#define MDDI_MSG_CRIT(msg, ...) \ + if (mddi_msg_level > 2) \ + printk(KERN_CRIT msg, ## __VA_ARGS__); +#define MDDI_MSG_ERR(msg, ...) \ + if (mddi_msg_level > 3) \ + printk(KERN_ERR msg, ## __VA_ARGS__); +#define MDDI_MSG_WARNING(msg, ...) \ + if (mddi_msg_level > 4) \ + printk(KERN_WARNING msg, ## __VA_ARGS__); +#define MDDI_MSG_NOTICE(msg, ...) \ + if (mddi_msg_level > 5) \ + printk(KERN_NOTICE msg, ## __VA_ARGS__); +#define MDDI_MSG_INFO(msg, ...) \ + if (mddi_msg_level > 6) \ + printk(KERN_INFO msg, ## __VA_ARGS__); +#define MDDI_MSG_DEBUG(msg, ...) \ + if (mddi_msg_level > 7) \ + printk(KERN_DEBUG msg, ## __VA_ARGS__); + +#define GCC_PACKED __attribute__((packed)) +typedef struct GCC_PACKED { + uint16 packet_length; + /* total # of bytes in the packet not including + the packet_length field. */ + + uint16 packet_type; + /* A Packet Type of 70 identifies the packet as + a Client status Packet. */ + + uint16 bClient_ID; + /* This field is reserved for future use and shall + be set to zero. */ + +} mddi_rev_packet_type; + +typedef struct GCC_PACKED { + uint16 packet_length; + /* total # of bytes in the packet not including + the packet_length field. */ + + uint16 packet_type; + /* A Packet Type of 70 identifies the packet as + a Client status Packet. */ + + uint16 bClient_ID; + /* This field is reserved for future use and shall + be set to zero. */ + + uint16 reverse_link_request; + /* 16 bit unsigned integer with number of bytes client + needs in the * reverse encapsulation message + to transmit data. */ + + uint8 crc_error_count; + uint8 capability_change; + uint16 graphics_busy_flags; + + uint16 parameter_CRC; + /* 16-bit CRC of all the bytes in the packet + including Packet Length. */ + +} mddi_client_status_type; + +typedef struct GCC_PACKED { + uint16 packet_length; + /* total # of bytes in the packet not including + the packet_length field. */ + + uint16 packet_type; + /* A Packet Type of 66 identifies the packet as + a Client Capability Packet. */ + + uint16 bClient_ID; + /* This field is reserved for future use and + shall be set to zero. */ + + uint16 Protocol_Version; + uint16 Minimum_Protocol_Version; + uint16 Data_Rate_Capability; + uint8 Interface_Type_Capability; + uint8 Number_of_Alt_Displays; + uint16 PostCal_Data_Rate; + uint16 Bitmap_Width; + uint16 Bitmap_Height; + uint16 Display_Window_Width; + uint16 Display_Window_Height; + uint32 Color_Map_Size; + uint16 Color_Map_RGB_Width; + uint16 RGB_Capability; + uint8 Monochrome_Capability; + uint8 Reserved_1; + uint16 Y_Cb_Cr_Capability; + uint16 Bayer_Capability; + uint16 Alpha_Cursor_Image_Planes; + uint32 Client_Feature_Capability_Indicators; + uint8 Maximum_Video_Frame_Rate_Capability; + uint8 Minimum_Video_Frame_Rate_Capability; + uint16 Minimum_Sub_frame_Rate; + uint16 Audio_Buffer_Depth; + uint16 Audio_Channel_Capability; + uint16 Audio_Sample_Rate_Capability; + uint8 Audio_Sample_Resolution; + uint8 Mic_Audio_Sample_Resolution; + uint16 Mic_Sample_Rate_Capability; + uint8 Keyboard_Data_Format; + uint8 pointing_device_data_format; + uint16 content_protection_type; + uint16 Mfr_Name; + uint16 Product_Code; + uint16 Reserved_3; + uint32 Serial_Number; + uint8 Week_of_Manufacture; + uint8 Year_of_Manufacture; + + uint16 parameter_CRC; + /* 16-bit CRC of all the bytes in the packet including Packet Length. */ + +} mddi_client_capability_type; + +typedef struct GCC_PACKED { + uint16 packet_length; + /* total # of bytes in the packet not including the packet_length field. */ + + uint16 packet_type; + /* A Packet Type of 16 identifies the packet as a Video Stream Packet. */ + + uint16 bClient_ID; + /* This field is reserved for future use and shall be set to zero. */ + + uint16 video_data_format_descriptor; + /* format of each pixel in the Pixel Data in the present stream in the + * present packet. + * If bits [15:13] = 000 monochrome + * If bits [15:13] = 001 color pixels (palette). + * If bits [15:13] = 010 color pixels in raw RGB + * If bits [15:13] = 011 data in 4:2:2 Y Cb Cr format + * If bits [15:13] = 100 Bayer pixels + */ + + uint16 pixel_data_attributes; + /* interpreted as follows: + * Bits [1:0] = 11 pixel data is displayed to both eyes + * Bits [1:0] = 10 pixel data is routed to the left eye only. + * Bits [1:0] = 01 pixel data is routed to the right eye only. + * Bits [1:0] = 00 pixel data is routed to the alternate display. + * Bit 2 is 0 Pixel Data is in the standard progressive format. + * Bit 2 is 1 Pixel Data is in interlace format. + * Bit 3 is 0 Pixel Data is in the standard progressive format. + * Bit 3 is 1 Pixel Data is in alternate pixel format. + * Bit 4 is 0 Pixel Data is to or from the display frame buffer. + * Bit 4 is 1 Pixel Data is to or from the camera. + * Bit 5 is 0 pixel data contains the next consecutive row of pixels. + * Bit 5 is 1 X Left Edge, Y Top Edge, X Right Edge, Y Bottom Edge, + * X Start, and Y Start parameters are not defined and + * shall be ignored by the client. + * Bits [7:6] = 01 Pixel data is written to the offline image buffer. + * Bits [7:6] = 00 Pixel data is written to the buffer to refresh display. + * Bits [7:6] = 11 Pixel data is written to all image buffers. + * Bits [7:6] = 10 Invalid. Reserved for future use. + * Bits 8 through 11 alternate display number. + * Bits 12 through 14 are reserved for future use and shall be set to zero. + * Bit 15 is 1 the row of pixels is the last row of pixels in a frame. + */ + + uint16 x_left_edge; + uint16 y_top_edge; + /* X,Y coordinate of the top left edge of the screen window */ + + uint16 x_right_edge; + uint16 y_bottom_edge; + /* X,Y coordinate of the bottom right edge of the window being updated. */ + + uint16 x_start; + uint16 y_start; + /* (X Start, Y Start) is the first pixel in the Pixel Data field below. */ + + uint16 pixel_count; + /* number of pixels in the Pixel Data field below. */ + + uint16 parameter_CRC; + /* 16-bit CRC of all bytes from the Packet Length to the Pixel Count. */ + + uint16 reserved; + /* 16-bit variable to make structure align on 4 byte boundary */ + +} mddi_video_stream_packet_type; + +typedef struct GCC_PACKED { + uint16 packet_length; + /* total # of bytes in the packet not including the packet_length field. */ + + uint16 packet_type; + /* A Packet Type of 146 identifies the packet as a Register Access Packet. */ + + uint16 bClient_ID; + /* This field is reserved for future use and shall be set to zero. */ + + uint16 read_write_info; + /* Bits 13:0 a 14-bit unsigned integer that specifies the number of + * 32-bit Register Data List items to be transferred in the + * Register Data List field. + * Bits[15:14] = 00 Write to register(s); + * Bits[15:14] = 10 Read from register(s); + * Bits[15:14] = 11 Response to a Read. + * Bits[15:14] = 01 this value is reserved for future use. */ + + uint32 register_address; + /* the register address that is to be written to or read from. */ + + uint16 parameter_CRC; + /* 16-bit CRC of all bytes from the Packet Length to the Register Address. */ + + uint32 register_data_list[MDDI_HOST_MAX_CLIENT_REG_IN_SAME_ADDR]; + /* list of 4-byte register data values for/from client registers */ + /* For multi-read/write, 512(128 * 4) bytes of data available */ + +} mddi_register_access_packet_type; + +typedef union GCC_PACKED { + mddi_video_stream_packet_type video_pkt; + mddi_register_access_packet_type register_pkt; +#ifdef ENABLE_MDDI_MULTI_READ_WRITE + /* add 1008 byte pad to ensure 1024 byte llist struct, that can be + * manipulated easily with cache */ + uint32 alignment_pad[252]; /* 1008 bytes */ +#else + /* add 48 byte pad to ensure 64 byte llist struct, that can be + * manipulated easily with cache */ + uint32 alignment_pad[12]; /* 48 bytes */ +#endif +} mddi_packet_header_type; + +typedef struct GCC_PACKED mddi_host_llist_struct { + uint16 link_controller_flags; + uint16 packet_header_count; + uint16 packet_data_count; + void *packet_data_pointer; + struct mddi_host_llist_struct *next_packet_pointer; + uint16 reserved; + mddi_packet_header_type packet_header; +} mddi_linked_list_type; + +typedef struct { + struct completion done_comp; + mddi_llist_done_cb_type done_cb; + uint16 next_idx; + boolean waiting; + boolean in_use; +} mddi_linked_list_notify_type; + +#ifdef ENABLE_MDDI_MULTI_READ_WRITE +#define MDDI_LLIST_POOL_SIZE 0x10000 +#else +#define MDDI_LLIST_POOL_SIZE 0x1000 +#endif +#define MDDI_MAX_NUM_LLIST_ITEMS (MDDI_LLIST_POOL_SIZE / \ + sizeof(mddi_linked_list_type)) +#define UNASSIGNED_INDEX MDDI_MAX_NUM_LLIST_ITEMS +#define MDDI_FIRST_DYNAMIC_LLIST_IDX 0 + +/* Static llist items can be used for applications that frequently send + * the same set of packets using the linked list interface. */ +/* Here we configure for 6 static linked list items: + * The 1st is used for a the adaptive backlight setting. + * and the remaining 5 are used for sending window adjustments for + * MDDI clients that need windowing info sent separate from video + * packets. */ +#define MDDI_NUM_STATIC_ABL_ITEMS 1 +#define MDDI_NUM_STATIC_WINDOW_ITEMS 5 +#define MDDI_NUM_STATIC_LLIST_ITEMS (MDDI_NUM_STATIC_ABL_ITEMS + \ + MDDI_NUM_STATIC_WINDOW_ITEMS) +#define MDDI_NUM_DYNAMIC_LLIST_ITEMS (MDDI_MAX_NUM_LLIST_ITEMS - \ + MDDI_NUM_STATIC_LLIST_ITEMS) + +#define MDDI_FIRST_STATIC_LLIST_IDX MDDI_NUM_DYNAMIC_LLIST_ITEMS +#define MDDI_FIRST_STATIC_ABL_IDX MDDI_FIRST_STATIC_LLIST_IDX +#define MDDI_FIRST_STATIC_WINDOW_IDX (MDDI_FIRST_STATIC_LLIST_IDX + \ + MDDI_NUM_STATIC_ABL_ITEMS) + +/* GPIO registers */ +#define VSYNC_WAKEUP_REG 0x80 +#define GPIO_REG 0x81 +#define GPIO_OUTPUT_REG 0x82 +#define GPIO_INTERRUPT_REG 0x83 +#define GPIO_INTERRUPT_ENABLE_REG 0x84 +#define GPIO_POLARITY_REG 0x85 + +/* Interrupt Bits */ +#define MDDI_INT_PRI_PTR_READ 0x0001 +#define MDDI_INT_SEC_PTR_READ 0x0002 +#define MDDI_INT_REV_DATA_AVAIL 0x0004 +#define MDDI_INT_DISP_REQ 0x0008 +#define MDDI_INT_PRI_UNDERFLOW 0x0010 +#define MDDI_INT_SEC_UNDERFLOW 0x0020 +#define MDDI_INT_REV_OVERFLOW 0x0040 +#define MDDI_INT_CRC_ERROR 0x0080 +#define MDDI_INT_MDDI_IN 0x0100 +#define MDDI_INT_PRI_OVERWRITE 0x0200 +#define MDDI_INT_SEC_OVERWRITE 0x0400 +#define MDDI_INT_REV_OVERWRITE 0x0800 +#define MDDI_INT_DMA_FAILURE 0x1000 +#define MDDI_INT_LINK_ACTIVE 0x2000 +#define MDDI_INT_IN_HIBERNATION 0x4000 +#define MDDI_INT_PRI_LINK_LIST_DONE 0x8000 +#define MDDI_INT_SEC_LINK_LIST_DONE 0x10000 +#define MDDI_INT_NO_CMD_PKTS_PEND 0x20000 +#define MDDI_INT_RTD_FAILURE 0x40000 + +#define MDDI_INT_ERROR_CONDITIONS ( \ + MDDI_INT_PRI_UNDERFLOW | MDDI_INT_SEC_UNDERFLOW | \ + MDDI_INT_REV_OVERFLOW | MDDI_INT_CRC_ERROR | \ + MDDI_INT_PRI_OVERWRITE | MDDI_INT_SEC_OVERWRITE | \ + MDDI_INT_RTD_FAILURE | \ + MDDI_INT_REV_OVERWRITE | MDDI_INT_DMA_FAILURE) + +#define MDDI_INT_LINK_STATE_CHANGES ( \ + MDDI_INT_LINK_ACTIVE | MDDI_INT_IN_HIBERNATION) + +/* Status Bits */ +#define MDDI_STAT_LINK_ACTIVE 0x0001 +#define MDDI_STAT_NEW_REV_PTR 0x0002 +#define MDDI_STAT_NEW_PRI_PTR 0x0004 +#define MDDI_STAT_NEW_SEC_PTR 0x0008 +#define MDDI_STAT_IN_HIBERNATION 0x0010 +#define MDDI_STAT_PRI_LINK_LIST_DONE 0x0020 +#define MDDI_STAT_SEC_LINK_LIST_DONE 0x0040 +#define MDDI_STAT_PENDING_TIMING_PKT 0x0080 +#define MDDI_STAT_PENDING_REV_ENCAP 0x0100 +#define MDDI_STAT_PENDING_POWERDOWN 0x0200 +#define MDDI_STAT_RTD_MEAS_FAIL 0x0800 +#define MDDI_STAT_CLIENT_WAKEUP_REQ 0x1000 + +/* Command Bits */ +#define MDDI_CMD_POWERDOWN 0x0100 +#define MDDI_CMD_POWERUP 0x0200 +#define MDDI_CMD_HIBERNATE 0x0300 +#define MDDI_CMD_RESET 0x0400 +#define MDDI_CMD_DISP_IGNORE 0x0501 +#define MDDI_CMD_DISP_LISTEN 0x0500 +#define MDDI_CMD_SEND_REV_ENCAP 0x0600 +#define MDDI_CMD_GET_CLIENT_CAP 0x0601 +#define MDDI_CMD_GET_CLIENT_STATUS 0x0602 +#define MDDI_CMD_SEND_RTD 0x0700 +#define MDDI_CMD_LINK_ACTIVE 0x0900 +#define MDDI_CMD_PERIODIC_REV_ENCAP 0x0A00 +#define MDDI_CMD_FW_LINK_SKEW_CAL 0x0D00 + +extern void mddi_host_init(mddi_host_type host); +extern void mddi_host_powerdown(mddi_host_type host); +extern uint16 mddi_get_next_free_llist_item(mddi_host_type host, boolean wait); +extern uint16 mddi_get_reg_read_llist_item(mddi_host_type host, boolean wait); +extern void mddi_queue_forward_packets(uint16 first_llist_idx, + uint16 last_llist_idx, + boolean wait, + mddi_llist_done_cb_type llist_done_cb, + mddi_host_type host); + +extern void mddi_host_write_pix_attr_reg(uint32 value); +extern void mddi_client_lcd_gpio_poll(uint32 poll_reg_val); +extern void mddi_client_lcd_vsync_detected(boolean detected); +extern void mddi_host_disable_hibernation(boolean disable); + +extern mddi_linked_list_type *llist_extern[]; +extern mddi_linked_list_type *llist_dma_extern[]; +extern mddi_linked_list_notify_type *llist_extern_notify[]; +extern struct timer_list mddi_host_timer; + +typedef struct { + uint16 transmitting_start_idx; + uint16 transmitting_end_idx; + uint16 waiting_start_idx; + uint16 waiting_end_idx; + uint16 reg_read_idx; + uint16 next_free_idx; + boolean reg_read_waiting; +} mddi_llist_info_type; + +extern mddi_llist_info_type mddi_llist; + +#define MDDI_GPIO_DEFAULT_POLLING_INTERVAL 200 +typedef struct { + uint32 polling_reg; + uint32 polling_val; + uint32 polling_interval; + boolean polling_enabled; +} mddi_gpio_info_type; + +uint32 mddi_get_client_id(void); +void mddi_mhctl_remove(mddi_host_type host_idx); +void mddi_host_timer_service(unsigned long data); +void mddi_host_client_cnt_reset(void); +#endif /* MDDIHOSTI_H */ diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c new file mode 100644 index 0000000000000000000000000000000000000000..85ba24966f334498203db2bb50483f6596234e5d --- /dev/null +++ b/drivers/video/msm/mdp.c @@ -0,0 +1,2701 @@ +/* drivers/video/msm_fb/mdp.c + * + * MSM MDP Interface (used by framebuffer core) + * + * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved. + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mdp.h" +#include "msm_fb.h" +#ifdef CONFIG_FB_MSM_MDP40 +#include "mdp4.h" +#endif +#include "mipi_dsi.h" + +uint32 mdp4_extn_disp; + +static struct clk *mdp_clk; +static struct clk *mdp_pclk; +static struct clk *mdp_lut_clk; +int mdp_rev; + +static struct platform_device *mdp_init_pdev; +static struct regulator *footswitch; +static unsigned int mdp_footswitch_on; + +struct completion mdp_ppp_comp; +struct semaphore mdp_ppp_mutex; +struct semaphore mdp_pipe_ctrl_mutex; + +unsigned long mdp_timer_duration = (HZ/20); /* 50 msecond */ + +boolean mdp_ppp_waiting = FALSE; +uint32 mdp_tv_underflow_cnt; +uint32 mdp_lcdc_underflow_cnt; + +boolean mdp_current_clk_on = FALSE; +boolean mdp_is_in_isr = FALSE; + +/* + * legacy mdp_in_processing is only for DMA2-MDDI + * this applies to DMA2 block only + */ +uint32 mdp_in_processing = FALSE; + +#ifdef CONFIG_FB_MSM_MDP40 +uint32 mdp_intr_mask = MDP4_ANY_INTR_MASK; +#else +uint32 mdp_intr_mask = MDP_ANY_INTR_MASK; +#endif + +MDP_BLOCK_TYPE mdp_debug[MDP_MAX_BLOCK]; + +atomic_t mdp_block_power_cnt[MDP_MAX_BLOCK]; + +spinlock_t mdp_spin_lock; +struct workqueue_struct *mdp_dma_wq; /*mdp dma wq */ +struct workqueue_struct *mdp_vsync_wq; /*mdp vsync wq */ + +struct workqueue_struct *mdp_hist_wq; /*mdp histogram wq */ + +static struct workqueue_struct *mdp_pipe_ctrl_wq; /* mdp mdp pipe ctrl wq */ +static struct delayed_work mdp_pipe_ctrl_worker; + +static boolean mdp_suspended = FALSE; +DEFINE_MUTEX(mdp_suspend_mutex); + +#ifdef CONFIG_FB_MSM_MDP40 +struct mdp_dma_data dma2_data; +struct mdp_dma_data dma_s_data; +struct mdp_dma_data dma_e_data; +ulong mdp4_display_intf; +#else +static struct mdp_dma_data dma2_data; +static struct mdp_dma_data dma_s_data; +#ifndef CONFIG_FB_MSM_MDP303 +static struct mdp_dma_data dma_e_data; +#endif +#endif + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +struct mdp_dma_data dma_wb_data; +#endif + +static struct mdp_dma_data dma3_data; + +extern ktime_t mdp_dma2_last_update_time; + +extern uint32 mdp_dma2_update_time_in_usec; +extern int mdp_lcd_rd_cnt_offset_slow; +extern int mdp_lcd_rd_cnt_offset_fast; +extern int mdp_usec_diff_threshold; + +extern int first_pixel_start_x; +extern int first_pixel_start_y; + +#ifdef MSM_FB_ENABLE_DBGFS +struct dentry *mdp_dir; +#endif + +#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) +static int mdp_suspend(struct platform_device *pdev, pm_message_t state); +#else +#define mdp_suspend NULL +#endif + +struct timeval mdp_dma2_timeval; +struct timeval mdp_ppp_timeval; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static struct early_suspend early_suspend; +#endif + +static u32 mdp_irq; + +static uint32 mdp_prim_panel_type = NO_PANEL; +#ifndef CONFIG_FB_MSM_MDP22 + +struct list_head mdp_hist_lut_list; +DEFINE_MUTEX(mdp_hist_lut_list_mutex); + +uint32_t mdp_block2base(uint32_t block) +{ + uint32_t base = 0x0; + switch (block) { + case MDP_BLOCK_DMA_P: + base = 0x90000; + break; + case MDP_BLOCK_DMA_S: + base = 0xA0000; + break; + case MDP_BLOCK_VG_1: + base = 0x20000; + break; + case MDP_BLOCK_VG_2: + base = 0x30000; + break; + case MDP_BLOCK_RGB_1: + base = 0x40000; + break; + case MDP_BLOCK_RGB_2: + base = 0x50000; + break; + case MDP_BLOCK_OVERLAY_0: + base = 0x10000; + break; + case MDP_BLOCK_OVERLAY_1: + base = 0x18000; + break; + case MDP_BLOCK_OVERLAY_2: + base = (mdp_rev >= MDP_REV_44) ? 0x88000 : 0; + break; + default: + break; + } + return base; +} + +static uint32_t mdp_pp_block2hist_lut(uint32_t block) +{ + uint32_t valid = 0; + switch (block) { + case MDP_BLOCK_DMA_P: + valid = (mdp_rev >= MDP_REV_40) ? 1 : 0; + break; + case MDP_BLOCK_DMA_S: + valid = (mdp_rev >= MDP_REV_40) ? 1 : 0; + break; + case MDP_BLOCK_VG_1: + valid = (mdp_rev >= MDP_REV_40) ? 1 : 0; + break; + case MDP_BLOCK_VG_2: + valid = (mdp_rev >= MDP_REV_40) ? 1 : 0; + break; + default: + break; + } + return valid; +} + +static void mdp_hist_lut_init_mgmt(struct mdp_hist_lut_mgmt *mgmt, + uint32_t block) +{ + mutex_init(&mgmt->lock); + mgmt->block = block; + + mutex_lock(&mdp_hist_lut_list_mutex); + list_add(&mgmt->list, &mdp_hist_lut_list); + mutex_unlock(&mdp_hist_lut_list_mutex); +} + +static int mdp_hist_lut_init(void) +{ + struct mdp_hist_lut_mgmt *temp; + struct list_head *pos, *q; + INIT_LIST_HEAD(&mdp_hist_lut_list); + + if (mdp_rev >= MDP_REV_30) { + temp = kmalloc(sizeof(struct mdp_hist_lut_mgmt), GFP_KERNEL); + if (!temp) + goto exit; + mdp_hist_lut_init_mgmt(temp, MDP_BLOCK_DMA_P); + } + + if (mdp_rev >= MDP_REV_40) { + temp = kmalloc(sizeof(struct mdp_hist_lut_mgmt), GFP_KERNEL); + if (!temp) + goto exit_list; + mdp_hist_lut_init_mgmt(temp, MDP_BLOCK_VG_1); + + temp = kmalloc(sizeof(struct mdp_hist_lut_mgmt), GFP_KERNEL); + if (!temp) + goto exit_list; + mdp_hist_lut_init_mgmt(temp, MDP_BLOCK_VG_2); + } + + if (mdp_rev > MDP_REV_42) { + temp = kmalloc(sizeof(struct mdp_hist_lut_mgmt), GFP_KERNEL); + if (!temp) + goto exit_list; + mdp_hist_lut_init_mgmt(temp, MDP_BLOCK_DMA_S); + } + return 0; + +exit_list: + mutex_lock(&mdp_hist_lut_list_mutex); + list_for_each_safe(pos, q, &mdp_hist_lut_list) { + temp = list_entry(pos, struct mdp_hist_lut_mgmt, list); + list_del(pos); + kfree(temp); + } + mutex_unlock(&mdp_hist_lut_list_mutex); +exit: + pr_err("Failed initializing histogram LUT memory\n"); + return -ENOMEM; +} + +static int mdp_hist_lut_block2mgmt(uint32_t block, + struct mdp_hist_lut_mgmt **mgmt) +{ + struct mdp_hist_lut_mgmt *temp, *output; + int ret = 0; + + output = NULL; + + mutex_lock(&mdp_hist_lut_list_mutex); + list_for_each_entry(temp, &mdp_hist_lut_list, list) { + if (temp->block == block) + output = temp; + } + mutex_unlock(&mdp_hist_lut_list_mutex); + + if (output == NULL) + ret = -EINVAL; + else + *mgmt = output; + + return ret; +} + +#define MDP_HIST_LUT_SIZE (256) +static int mdp_hist_lut_write_off(struct mdp_hist_lut_data *data, + struct mdp_hist_lut_info *info, uint32_t offset) +{ + int i; + uint32_t element[MDP_HIST_LUT_SIZE]; + uint32_t base = mdp_block2base(info->block); + uint32_t sel = info->bank_sel; + + + if (data->len != MDP_HIST_LUT_SIZE) { + pr_err("%s: data->len != %d", __func__, MDP_HIST_LUT_SIZE); + return -EINVAL; + } + + if (copy_from_user(&element, data->data, + MDP_HIST_LUT_SIZE * sizeof(uint32_t))) { + pr_err("%s: Error copying histogram data", __func__); + return -ENOMEM; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < MDP_HIST_LUT_SIZE; i++) + MDP_OUTP(MDP_BASE + base + offset + (0x400*(sel)) + (4*i), + element[i]); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return 0; +} + +static int mdp_hist_lut_write(struct mdp_hist_lut_data *data, + struct mdp_hist_lut_info *info) +{ + int ret = 0; + + if (data->block != info->block) { + ret = -1; + pr_err("%s, data/info mdp_block mismatch! %d != %d\n", + __func__, data->block, info->block); + goto error; + } + + switch (data->block) { + case MDP_BLOCK_VG_1: + case MDP_BLOCK_VG_2: + ret = mdp_hist_lut_write_off(data, info, 0x3400); + break; + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + ret = mdp_hist_lut_write_off(data, info, 0x4800); + break; + default: + ret = -EINVAL; + goto error; + } + +error: + return ret; +} + +#define MDP_HIST_LUT_VG_EN_MASK (0x20000) +#define MDP_HIST_LUT_VG_EN_SHIFT (17) +#define MDP_HIST_LUT_VG_EN_OFFSET (0x0058) +#define MDP_HIST_LUT_VG_SEL_OFFSET (0x0064) +static void mdp_hist_lut_commit_vg(struct mdp_hist_lut_info *info) +{ + uint32_t out_en, temp_en; + uint32_t base = mdp_block2base(info->block); + temp_en = (info->is_enabled) ? (1 << MDP_HIST_LUT_VG_EN_SHIFT) : 0x0; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + out_en = inpdw(MDP_BASE + base + MDP_HIST_LUT_VG_EN_OFFSET) & + ~MDP_HIST_LUT_VG_EN_MASK; + MDP_OUTP(MDP_BASE + base + MDP_HIST_LUT_VG_EN_OFFSET, out_en | temp_en); + + if (info->has_sel_update) + MDP_OUTP(MDP_BASE + base + MDP_HIST_LUT_VG_SEL_OFFSET, + info->bank_sel); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +#define MDP_HIST_LUT_DMA_EN_MASK (0x7) +#define MDP_HIST_LUT_DMA_SEL_MASK (0x400) +#define MDP_HIST_LUT_DMA_SEL_SHIFT (10) +#define MDP_HIST_LUT_DMA_P_OFFSET (0x0070) +#define MDP_HIST_LUT_DMA_S_OFFSET (0x0028) +static void mdp_hist_lut_commit_dma(struct mdp_hist_lut_info *info) +{ + uint32_t out, temp, mask; + uint32_t base = mdp_block2base(info->block); + uint32_t offset = (info->block == MDP_BLOCK_DMA_P) ? + MDP_HIST_LUT_DMA_P_OFFSET : MDP_HIST_LUT_DMA_S_OFFSET; + + mask = MDP_HIST_LUT_DMA_EN_MASK; + temp = (info->is_enabled) ? 0x7 : 0x0; + + if (info->has_sel_update) { + mask |= MDP_HIST_LUT_DMA_SEL_MASK; + temp |= ((info->bank_sel & 0x1) << MDP_HIST_LUT_DMA_SEL_SHIFT); + } + + out = inpdw(MDP_BASE + base + offset) & ~mask; + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + MDP_OUTP(MDP_BASE + base + offset, out | temp); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +static void mdp_hist_lut_commit_info(struct mdp_hist_lut_info *info) +{ + switch (info->block) { + case MDP_BLOCK_VG_1: + case MDP_BLOCK_VG_2: + mdp_hist_lut_commit_vg(info); + break; + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + mdp_hist_lut_commit_dma(info); + break; + default: + goto error; + } + +error: + return; +} + +static void mdp_hist_lut_update_info(struct mdp_hist_lut_info *info, int ops) +{ + info->bank_sel = (ops & 0x8) >> 3; + info->is_enabled = (ops & 0x1) ? TRUE : FALSE; + info->has_sel_update = (ops & 0x10) ? TRUE : FALSE; +} + +int mdp_hist_lut_config(struct mdp_hist_lut_data *data) +{ + struct mdp_hist_lut_mgmt *mgmt = NULL; + struct mdp_hist_lut_info info; + int ret = 0; + + if (!mdp_pp_block2hist_lut(data->block)) { + ret = -ENOTTY; + goto error; + } + + ret = mdp_hist_lut_block2mgmt(data->block, &mgmt); + if (ret) + goto error; + + mutex_lock(&mgmt->lock); + + info.block = mgmt->block; + + mdp_hist_lut_update_info(&info, data->ops); + + switch ((data->ops & 0x6) >> 1) { + case 0x1: + pr_info("%s: histogram LUT read not supported\n", __func__); + break; + case 0x2: + ret = mdp_hist_lut_write(data, &info); + if (ret) + goto error_lock; + break; + default: + break; + } + + mdp_hist_lut_commit_info(&info); + +error_lock: + mutex_unlock(&mgmt->lock); +error: + return ret; +} + +DEFINE_MUTEX(mdp_lut_push_sem); +static int mdp_lut_i; +static int mdp_lut_hw_update(struct fb_cmap *cmap) +{ + int i; + u16 *c[3]; + u16 r, g, b; + + c[0] = cmap->green; + c[1] = cmap->blue; + c[2] = cmap->red; + + for (i = 0; i < cmap->len; i++) { + if (copy_from_user(&r, cmap->red++, sizeof(r)) || + copy_from_user(&g, cmap->green++, sizeof(g)) || + copy_from_user(&b, cmap->blue++, sizeof(b))) + return -EFAULT; + +#ifdef CONFIG_FB_MSM_MDP40 + MDP_OUTP(MDP_BASE + 0x94800 + +#else + MDP_OUTP(MDP_BASE + 0x93800 + +#endif + (0x400*mdp_lut_i) + cmap->start*4 + i*4, + ((g & 0xff) | + ((b & 0xff) << 8) | + ((r & 0xff) << 16))); + } + + return 0; +} + +static int mdp_lut_push; +static int mdp_lut_push_i; +static int mdp_lut_update_nonlcdc(struct fb_info *info, struct fb_cmap *cmap) +{ + int ret; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + ret = mdp_lut_hw_update(cmap); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + if (ret) + return ret; + + mutex_lock(&mdp_lut_push_sem); + mdp_lut_push = 1; + mdp_lut_push_i = mdp_lut_i; + mutex_unlock(&mdp_lut_push_sem); + + mdp_lut_i = (mdp_lut_i + 1)%2; + + return 0; +} + +static int mdp_lut_update_lcdc(struct fb_info *info, struct fb_cmap *cmap) +{ + int ret; + uint32_t out; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + ret = mdp_lut_hw_update(cmap); + + if (ret) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return ret; + } + + /*mask off non LUT select bits*/ + out = inpdw(MDP_BASE + 0x90070) & ~((0x1 << 10) | 0x7); + MDP_OUTP(MDP_BASE + 0x90070, (mdp_lut_i << 10) | 0x7 | out); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_lut_i = (mdp_lut_i + 1)%2; + + return 0; +} + +static void mdp_lut_enable(void) +{ + uint32_t out; + if (mdp_lut_push) { + mutex_lock(&mdp_lut_push_sem); + mdp_lut_push = 0; + out = inpdw(MDP_BASE + 0x90070) & ~((0x1 << 10) | 0x7); + MDP_OUTP(MDP_BASE + 0x90070, + (mdp_lut_push_i << 10) | 0x7 | out); + mutex_unlock(&mdp_lut_push_sem); + } +} + +#define MDP_REV42_HIST_MAX_BIN 128 +#define MDP_REV41_HIST_MAX_BIN 32 + +#define MDP_HIST_DATA32_R_OFF 0x0100 +#define MDP_HIST_DATA32_G_OFF 0x0200 +#define MDP_HIST_DATA32_B_OFF 0x0300 + +#define MDP_HIST_DATA128_R_OFF 0x0400 +#define MDP_HIST_DATA128_G_OFF 0x0800 +#define MDP_HIST_DATA128_B_OFF 0x0C00 + +#define MDP_HIST_DATA_LUMA_OFF 0x0200 + +#define MDP_HIST_EXTRA_DATA0_OFF 0x0028 +#define MDP_HIST_EXTRA_DATA1_OFF 0x002C + +struct mdp_hist_mgmt *mdp_hist_mgmt_array[MDP_HIST_MGMT_MAX]; + +void __mdp_histogram_kickoff(struct mdp_hist_mgmt *mgmt) +{ + char *mdp_hist_base = MDP_BASE + mgmt->base; + if (mgmt->mdp_is_hist_data == TRUE) { + MDP_OUTP(mdp_hist_base + 0x0004, mgmt->frame_cnt); + MDP_OUTP(mdp_hist_base, 1); + } +} + +void __mdp_histogram_reset(struct mdp_hist_mgmt *mgmt) +{ + char *mdp_hist_base = MDP_BASE + mgmt->base; + MDP_OUTP(mdp_hist_base + 0x000C, 1); +} + +static void mdp_hist_read_work(struct work_struct *data); + +static int mdp_hist_init_mgmt(struct mdp_hist_mgmt *mgmt, uint32_t block) +{ + uint32_t bins, extra, index, term = 0; + init_completion(&mgmt->mdp_hist_comp); + mutex_init(&mgmt->mdp_hist_mutex); + mutex_init(&mgmt->mdp_do_hist_mutex); + mgmt->block = block; + mgmt->base = mdp_block2base(block); + mgmt->mdp_is_hist_start = FALSE; + mgmt->mdp_is_hist_data = FALSE; + mgmt->mdp_is_hist_valid = FALSE; + mgmt->mdp_is_hist_init = FALSE; + mgmt->frame_cnt = 0; + mgmt->bit_mask = 0; + mgmt->num_bins = 0; + switch (block) { + case MDP_BLOCK_DMA_P: + term = MDP_HISTOGRAM_TERM_DMA_P; + bins = (mdp_rev >= MDP_REV_42) ? MDP_REV42_HIST_MAX_BIN : + MDP_REV41_HIST_MAX_BIN; + extra = 2; + mgmt->base += (mdp_rev >= MDP_REV_40) ? 0x5000 : 0x4000; + index = MDP_HIST_MGMT_DMA_P; + break; + case MDP_BLOCK_DMA_S: + term = MDP_HISTOGRAM_TERM_DMA_S; + bins = MDP_REV42_HIST_MAX_BIN; + extra = 2; + mgmt->base += 0x5000; + index = MDP_HIST_MGMT_DMA_S; + break; + case MDP_BLOCK_VG_1: + term = MDP_HISTOGRAM_TERM_VG_1; + bins = MDP_REV42_HIST_MAX_BIN; + extra = 1; + mgmt->base += 0x6000; + index = MDP_HIST_MGMT_VG_1; + break; + case MDP_BLOCK_VG_2: + term = MDP_HISTOGRAM_TERM_VG_2; + bins = MDP_REV42_HIST_MAX_BIN; + extra = 1; + mgmt->base += 0x6000; + index = MDP_HIST_MGMT_VG_2; + break; + default: + term = MDP_HISTOGRAM_TERM_DMA_P; + bins = (mdp_rev >= MDP_REV_42) ? MDP_REV42_HIST_MAX_BIN : + MDP_REV41_HIST_MAX_BIN; + extra = 2; + mgmt->base += (mdp_rev >= MDP_REV_40) ? 0x5000 : 0x4000; + index = MDP_HIST_MGMT_DMA_P; + } + mgmt->irq_term = term; + + mgmt->c0 = kmalloc(bins * sizeof(uint32_t), GFP_KERNEL); + if (mgmt->c0 == NULL) + goto error; + + mgmt->c1 = kmalloc(bins * sizeof(uint32_t), GFP_KERNEL); + if (mgmt->c1 == NULL) + goto error_1; + + mgmt->c2 = kmalloc(bins * sizeof(uint32_t), GFP_KERNEL); + if (mgmt->c2 == NULL) + goto error_2; + + mgmt->extra_info = kmalloc(extra * sizeof(uint32_t), GFP_KERNEL); + if (mgmt->extra_info == NULL) + goto error_extra; + + INIT_WORK(&mgmt->mdp_histogram_worker, mdp_hist_read_work); + mgmt->hist = NULL; + + mdp_hist_mgmt_array[index] = mgmt; + return 0; + +error_extra: + kfree(mgmt->c2); +error_2: + kfree(mgmt->c1); +error_1: + kfree(mgmt->c0); +error: + return -ENOMEM; +} + +static void mdp_hist_del_mgmt(struct mdp_hist_mgmt *mgmt) +{ + kfree(mgmt->extra_info); + kfree(mgmt->c2); + kfree(mgmt->c1); + kfree(mgmt->c0); +} + +static int mdp_histogram_init(void) +{ + struct mdp_hist_mgmt *temp; + int i, ret; + mdp_hist_wq = alloc_workqueue("mdp_hist_wq", + WQ_NON_REENTRANT | WQ_UNBOUND, 0); + + for (i = 0; i < MDP_HIST_MGMT_MAX; i++) + mdp_hist_mgmt_array[i] = NULL; + + if (mdp_rev >= MDP_REV_30) { + temp = kmalloc(sizeof(struct mdp_hist_mgmt), GFP_KERNEL); + if (!temp) + goto exit; + ret = mdp_hist_init_mgmt(temp, MDP_BLOCK_DMA_P); + if (ret) { + kfree(temp); + goto exit; + } + } + + if (mdp_rev >= MDP_REV_40) { + temp = kmalloc(sizeof(struct mdp_hist_mgmt), GFP_KERNEL); + if (!temp) + goto exit_list; + ret = mdp_hist_init_mgmt(temp, MDP_BLOCK_VG_1); + if (ret) + goto exit_list; + + temp = kmalloc(sizeof(struct mdp_hist_mgmt), GFP_KERNEL); + if (!temp) + goto exit_list; + ret = mdp_hist_init_mgmt(temp, MDP_BLOCK_VG_2); + if (ret) + goto exit_list; + } + + if (mdp_rev >= MDP_REV_42) { + temp = kmalloc(sizeof(struct mdp_hist_mgmt), GFP_KERNEL); + if (!temp) + goto exit_list; + ret = mdp_hist_init_mgmt(temp, MDP_BLOCK_DMA_S); + if (ret) + goto exit_list; + } + + return 0; + +exit_list: + for (i = 0; i < MDP_HIST_MGMT_MAX; i++) { + temp = mdp_hist_mgmt_array[i]; + if (!temp) + continue; + mdp_hist_del_mgmt(temp); + kfree(temp); + mdp_hist_mgmt_array[i] = NULL; + } +exit: + return -ENOMEM; +} + +int mdp_histogram_block2mgmt(uint32_t block, struct mdp_hist_mgmt **mgmt) +{ + struct mdp_hist_mgmt *temp, *output; + int i, ret = 0; + + output = NULL; + + for (i = 0; i < MDP_HIST_MGMT_MAX; i++) { + temp = mdp_hist_mgmt_array[i]; + if (!temp) + continue; + + if (temp->block == block) { + output = temp; + break; + } + } + + if (output == NULL) + ret = -EINVAL; + else + *mgmt = output; + + return ret; +} + +static int mdp_histogram_enable(struct mdp_hist_mgmt *mgmt) +{ + uint32_t base; + if (mgmt->mdp_is_hist_data == TRUE) { + pr_err("%s histogram already started\n", __func__); + return -EINVAL; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_enable_irq(mgmt->irq_term); + INIT_COMPLETION(mgmt->mdp_hist_comp); + + base = (uint32_t) (MDP_BASE + mgmt->base); + MDP_OUTP(base + 0x0018, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE); + MDP_OUTP(base + 0x0010, 1); + MDP_OUTP(base + 0x001C, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE); + + MDP_OUTP(base + 0x0004, mgmt->frame_cnt); + if (mgmt->block != MDP_BLOCK_VG_1 && mgmt->block != MDP_BLOCK_VG_2) + MDP_OUTP(base + 0x0008, mgmt->bit_mask); + mgmt->mdp_is_hist_data = TRUE; + mgmt->mdp_is_hist_valid = TRUE; + mgmt->mdp_is_hist_init = FALSE; + __mdp_histogram_reset(mgmt); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return 0; +} + +static int mdp_histogram_disable(struct mdp_hist_mgmt *mgmt) +{ + uint32_t base, status; + if (mgmt->mdp_is_hist_data == FALSE) { + pr_err("%s histogram already stopped\n", __func__); + return -EINVAL; + } + + mgmt->mdp_is_hist_data = FALSE; + mgmt->mdp_is_hist_valid = FALSE; + mgmt->mdp_is_hist_init = FALSE; + + base = (uint32_t) (MDP_BASE + mgmt->base); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + if (mdp_rev >= MDP_REV_42) + MDP_OUTP(base + 0x0020, 1); + status = inpdw(base + 0x001C); + status &= ~(INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE); + MDP_OUTP(base + 0x001C, status); + + MDP_OUTP(base + 0x0018, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + complete(&mgmt->mdp_hist_comp); + mdp_disable_irq(mgmt->irq_term); + return 0; +} + +/*call when spanning mgmt_array only*/ +int _mdp_histogram_ctrl(boolean en, struct mdp_hist_mgmt *mgmt) +{ + int ret = 0; + + mutex_lock(&mgmt->mdp_hist_mutex); + if (mgmt->mdp_is_hist_start == TRUE) { + if (en) + ret = mdp_histogram_enable(mgmt); + else + ret = mdp_histogram_disable(mgmt); + } + mutex_unlock(&mgmt->mdp_hist_mutex); + + if (en == false) + cancel_work_sync(&mgmt->mdp_histogram_worker); + + return ret; +} + +int mdp_histogram_ctrl(boolean en, uint32_t block) +{ + struct mdp_hist_mgmt *mgmt = NULL; + int ret = 0; + + ret = mdp_histogram_block2mgmt(block, &mgmt); + if (ret) + goto error; + + ret = _mdp_histogram_ctrl(en, mgmt); +error: + return ret; +} + +int mdp_histogram_ctrl_all(boolean en) +{ + struct mdp_hist_mgmt *temp; + int i, ret = 0, ret_temp = 0; + + for (i = 0; i < MDP_HIST_MGMT_MAX; i++) { + temp = mdp_hist_mgmt_array[i]; + if (!temp) + continue; + + ret_temp = _mdp_histogram_ctrl(en, temp); + if (ret_temp) + ret = ret_temp; + } + return ret; +} + +int mdp_histogram_start(struct mdp_histogram_start_req *req) +{ + struct mdp_hist_mgmt *mgmt = NULL; + int ret; + + ret = mdp_histogram_block2mgmt(req->block, &mgmt); + if (ret) { + ret = -ENOTTY; + goto error; + } + + mutex_lock(&mgmt->mdp_hist_mutex); + if (mgmt->mdp_is_hist_start == TRUE) { + pr_err("%s histogram already started\n", __func__); + ret = -EPERM; + goto error_lock; + } + + mgmt->block = req->block; + mgmt->frame_cnt = req->frame_cnt; + mgmt->bit_mask = req->bit_mask; + mgmt->num_bins = req->num_bins; + mgmt->hist = NULL; + + ret = mdp_histogram_enable(mgmt); + + mgmt->mdp_is_hist_start = TRUE; + +error_lock: + mutex_unlock(&mgmt->mdp_hist_mutex); +error: + return ret; +} + +int mdp_histogram_stop(struct fb_info *info, uint32_t block) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *) info->par; + struct mdp_hist_mgmt *mgmt = NULL; + int ret; + + ret = mdp_histogram_block2mgmt(block, &mgmt); + if (ret) { + ret = -ENOTTY; + goto error; + } + + mutex_lock(&mgmt->mdp_hist_mutex); + if (mgmt->mdp_is_hist_start == FALSE) { + pr_err("%s histogram already stopped\n", __func__); + ret = -EPERM; + goto error_lock; + } + + mgmt->mdp_is_hist_start = FALSE; + + if (!mfd->panel_power_on) { + mgmt->mdp_is_hist_data = FALSE; + complete(&mgmt->mdp_hist_comp); + ret = -EINVAL; + goto error_lock; + } + + ret = mdp_histogram_disable(mgmt); + + mutex_unlock(&mgmt->mdp_hist_mutex); + cancel_work_sync(&mgmt->mdp_histogram_worker); + return ret; + +error_lock: + mutex_unlock(&mgmt->mdp_hist_mutex); +error: + return ret; +} + +/*call from within mdp_hist_mutex context*/ +static int _mdp_histogram_read_dma_data(struct mdp_hist_mgmt *mgmt) +{ + char *mdp_hist_base; + uint32_t r_data_offset, g_data_offset, b_data_offset; + int i, ret = 0; + + mdp_hist_base = MDP_BASE + mgmt->base; + + r_data_offset = (32 == mgmt->num_bins) ? MDP_HIST_DATA32_R_OFF : + MDP_HIST_DATA128_R_OFF; + g_data_offset = (32 == mgmt->num_bins) ? MDP_HIST_DATA32_G_OFF : + MDP_HIST_DATA128_G_OFF; + b_data_offset = (32 == mgmt->num_bins) ? MDP_HIST_DATA32_B_OFF : + MDP_HIST_DATA128_B_OFF; + + if (mgmt->c0 == NULL || mgmt->c1 == NULL || mgmt->c2 == NULL) { + ret = -ENOMEM; + goto hist_err; + } + + if (!mgmt->hist) { + pr_err("%s: mgmt->hist not set, mgmt->hist = 0x%08x", + __func__, (uint32_t) mgmt->hist); + return -EINVAL; + } + + if (mgmt->hist->bin_cnt != mgmt->num_bins) { + pr_err("%s, bins config = %d, bin requested = %d", __func__, + mgmt->num_bins, mgmt->hist->bin_cnt); + return -EINVAL; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < mgmt->num_bins; i++) { + mgmt->c0[i] = inpdw(mdp_hist_base + r_data_offset + (4*i)); + mgmt->c1[i] = inpdw(mdp_hist_base + g_data_offset + (4*i)); + mgmt->c2[i] = inpdw(mdp_hist_base + b_data_offset + (4*i)); + } + + if (mdp_rev >= MDP_REV_42) { + if (mgmt->extra_info) { + mgmt->extra_info[0] = inpdw(mdp_hist_base + + MDP_HIST_EXTRA_DATA0_OFF); + mgmt->extra_info[1] = inpdw(mdp_hist_base + + MDP_HIST_EXTRA_DATA0_OFF + 4); + } else + ret = -ENOMEM; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + if (!ret) + return ret; + +hist_err: + pr_err("%s: invalid hist buffer\n", __func__); + return ret; +} + +/*call from within mdp_hist_mutex context*/ +static int _mdp_histogram_read_vg_data(struct mdp_hist_mgmt *mgmt) +{ + char *mdp_hist_base; + int i, ret = 0; + + mdp_hist_base = MDP_BASE + mgmt->base; + + if (mgmt->c0 == NULL) { + ret = -ENOMEM; + goto hist_err; + } + + if (!mgmt->hist) { + pr_err("%s: mgmt->hist not set", __func__); + return -EINVAL; + } + + if (mgmt->hist->bin_cnt != mgmt->num_bins) { + pr_err("%s, bins config = %d, bin requested = %d", __func__, + mgmt->num_bins, mgmt->hist->bin_cnt); + return -EINVAL; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < mgmt->num_bins; i++) + mgmt->c0[i] = inpdw(mdp_hist_base + MDP_HIST_DATA_LUMA_OFF + + (4*i)); + + if (mdp_rev >= MDP_REV_42) { + if (mgmt->extra_info) { + mgmt->extra_info[0] = inpdw(mdp_hist_base + + MDP_HIST_EXTRA_DATA0_OFF); + } else + ret = -ENOMEM; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + if (!ret) + return ret; + +hist_err: + pr_err("%s: invalid hist buffer\n", __func__); + return ret; +} + +static void mdp_hist_read_work(struct work_struct *data) +{ + struct mdp_hist_mgmt *mgmt = container_of(data, struct mdp_hist_mgmt, + mdp_histogram_worker); + int ret = 0; + mutex_lock(&mgmt->mdp_hist_mutex); + if (mgmt->mdp_is_hist_data == FALSE) { + pr_debug("%s, Histogram disabled before read.\n", __func__); + ret = -EINVAL; + goto error; + } + + switch (mgmt->block) { + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + ret = _mdp_histogram_read_dma_data(mgmt); + break; + case MDP_BLOCK_VG_1: + case MDP_BLOCK_VG_2: + ret = _mdp_histogram_read_vg_data(mgmt); + break; + default: + pr_err("%s, invalid MDP block = %d\n", __func__, mgmt->block); + ret = -EINVAL; + goto error; + } + + /* + * if read was triggered by an underrun or failed copying, + * don't wake up readers + */ + if (!ret && mgmt->mdp_is_hist_valid && mgmt->mdp_is_hist_init) { + mgmt->hist = NULL; + complete(&mgmt->mdp_hist_comp); + } + + if (mgmt->mdp_is_hist_valid == FALSE) + mgmt->mdp_is_hist_valid = TRUE; + if (mgmt->mdp_is_hist_init == FALSE) + mgmt->mdp_is_hist_init = TRUE; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + if (!ret) + __mdp_histogram_kickoff(mgmt); + else + __mdp_histogram_reset(mgmt); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + +error: + mutex_unlock(&mgmt->mdp_hist_mutex); +} + +/*call from within mdp_hist_mutex*/ +static int _mdp_copy_hist_data(struct mdp_histogram_data *hist, + struct mdp_hist_mgmt *mgmt) +{ + int ret; + + if (hist->c0) { + ret = copy_to_user(hist->c0, mgmt->c0, + sizeof(uint32_t) * (hist->bin_cnt)); + if (ret) + goto err; + } + if (hist->c1) { + ret = copy_to_user(hist->c1, mgmt->c1, + sizeof(uint32_t) * (hist->bin_cnt)); + if (ret) + goto err; + } + if (hist->c2) { + ret = copy_to_user(hist->c2, mgmt->c2, + sizeof(uint32_t) * (hist->bin_cnt)); + if (ret) + goto err; + } + if (hist->extra_info) { + ret = copy_to_user(hist->extra_info, mgmt->extra_info, + sizeof(uint32_t) * ((hist->block > MDP_BLOCK_VG_2) ? 2 : 1)); + if (ret) + goto err; + } +err: + return ret; +} + +static int mdp_do_histogram(struct fb_info *info, + struct mdp_histogram_data *hist) +{ + struct mdp_hist_mgmt *mgmt = NULL; + int ret = 0; + + ret = mdp_histogram_block2mgmt(hist->block, &mgmt); + if (ret) { + pr_info("%s - %d", __func__, __LINE__); + ret = -EINVAL; + return ret; + } + + mutex_lock(&mgmt->mdp_do_hist_mutex); + if (!mgmt->frame_cnt || (mgmt->num_bins == 0)) { + pr_info("%s - frame_cnt = %d, num_bins = %d", __func__, + mgmt->frame_cnt, mgmt->num_bins); + ret = -EINVAL; + goto error; +} + if ((mdp_rev <= MDP_REV_41 && hist->bin_cnt > MDP_REV41_HIST_MAX_BIN) + || (mdp_rev == MDP_REV_42 && + hist->bin_cnt > MDP_REV42_HIST_MAX_BIN)) { + pr_info("%s - mdp_rev = %d, num_bins = %d", __func__, mdp_rev, + hist->bin_cnt); + ret = -EINVAL; + goto error; +} + mutex_lock(&mgmt->mdp_hist_mutex); + if (!mgmt->mdp_is_hist_data) { + pr_info("%s - hist_data = false!", __func__); + ret = -EINVAL; + goto error_lock; + } + + if (!mgmt->mdp_is_hist_start) { + pr_err("%s histogram not started\n", __func__); + ret = -EPERM; + goto error_lock; + } + + if (mgmt->hist != NULL) { + pr_err("%s; histogram attempted to be read twice\n", __func__); + ret = -EPERM; + goto error_lock; + } + mgmt->hist = hist; + mutex_unlock(&mgmt->mdp_hist_mutex); + + if (wait_for_completion_killable(&mgmt->mdp_hist_comp)) { + pr_err("%s(): histogram bin collection killed", __func__); + ret = -EINVAL; + goto error; + } + + mutex_lock(&mgmt->mdp_hist_mutex); + if (mgmt->mdp_is_hist_data && mgmt->mdp_is_hist_init) + ret = _mdp_copy_hist_data(hist, mgmt); + else + ret = -ENODATA; +error_lock: + mutex_unlock(&mgmt->mdp_hist_mutex); +error: + mutex_unlock(&mgmt->mdp_do_hist_mutex); + return ret; +} +#endif + +/* Returns < 0 on error, 0 on timeout, or > 0 on successful wait */ + +int mdp_ppp_pipe_wait(void) +{ + int ret = 1; + boolean wait; + unsigned long flag; + + /* wait 5 seconds for the operation to complete before declaring + the MDP hung */ + spin_lock_irqsave(&mdp_spin_lock, flag); + wait = mdp_ppp_waiting; + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (wait == TRUE) { + ret = wait_for_completion_interruptible_timeout(&mdp_ppp_comp, + 5 * HZ); + + if (!ret) + printk(KERN_ERR "%s: Timed out waiting for the MDP.\n", + __func__); + } + + return ret; +} + +static DEFINE_SPINLOCK(mdp_lock); +static int mdp_irq_mask; +static int mdp_irq_enabled; + +/* + * mdp_enable_irq: can not be called from isr + */ +void mdp_enable_irq(uint32 term) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&mdp_lock, irq_flags); + if (mdp_irq_mask & term) { + printk(KERN_ERR "%s: MDP IRQ term-0x%x is already set, mask=%x irq=%d\n", + __func__, term, mdp_irq_mask, mdp_irq_enabled); + } else { + mdp_irq_mask |= term; + if (mdp_irq_mask && !mdp_irq_enabled) { + mdp_irq_enabled = 1; + enable_irq(mdp_irq); + } + } + spin_unlock_irqrestore(&mdp_lock, irq_flags); +} + +/* + * mdp_disable_irq: can not be called from isr + */ +void mdp_disable_irq(uint32 term) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&mdp_lock, irq_flags); + if (!(mdp_irq_mask & term)) { + printk(KERN_ERR "%s: MDP IRQ term-0x%x is NOT set, mask=%x irq=%d\n", + __func__, term, mdp_irq_mask, mdp_irq_enabled); + } else { + mdp_irq_mask &= ~term; + if (!mdp_irq_mask && mdp_irq_enabled) { + mdp_irq_enabled = 0; + disable_irq(mdp_irq); + } + } + spin_unlock_irqrestore(&mdp_lock, irq_flags); +} + +void mdp_disable_irq_nosync(uint32 term) +{ + spin_lock(&mdp_lock); + if (!(mdp_irq_mask & term)) { + printk(KERN_ERR "%s: MDP IRQ term-0x%x is NOT set, mask=%x irq=%d\n", + __func__, term, mdp_irq_mask, mdp_irq_enabled); + } else { + mdp_irq_mask &= ~term; + if (!mdp_irq_mask && mdp_irq_enabled) { + mdp_irq_enabled = 0; + disable_irq_nosync(mdp_irq); + } + } + spin_unlock(&mdp_lock); +} + +void mdp_pipe_kickoff(uint32 term, struct msm_fb_data_type *mfd) +{ + unsigned long flag; + /* complete all the writes before starting */ + wmb(); + + /* kick off PPP engine */ + if (term == MDP_PPP_TERM) { + if (mdp_debug[MDP_PPP_BLOCK]) + jiffies_to_timeval(jiffies, &mdp_ppp_timeval); + + /* let's turn on PPP block */ + mdp_pipe_ctrl(MDP_PPP_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + mdp_enable_irq(term); + INIT_COMPLETION(mdp_ppp_comp); + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_ppp_waiting = TRUE; + spin_unlock_irqrestore(&mdp_spin_lock, flag); + outpdw(MDP_BASE + 0x30, 0x1000); + wait_for_completion_killable(&mdp_ppp_comp); + mdp_disable_irq(term); + + if (mdp_debug[MDP_PPP_BLOCK]) { + struct timeval now; + + jiffies_to_timeval(jiffies, &now); + mdp_ppp_timeval.tv_usec = + now.tv_usec - mdp_ppp_timeval.tv_usec; + MSM_FB_DEBUG("MDP-PPP: %d\n", + (int)mdp_ppp_timeval.tv_usec); + } + } else if (term == MDP_DMA2_TERM) { + if (mdp_debug[MDP_DMA2_BLOCK]) { + MSM_FB_DEBUG("MDP-DMA2: %d\n", + (int)mdp_dma2_timeval.tv_usec); + jiffies_to_timeval(jiffies, &mdp_dma2_timeval); + } + /* DMA update timestamp */ + mdp_dma2_last_update_time = ktime_get_real(); + /* let's turn on DMA2 block */ +#ifdef CONFIG_FB_MSM_MDP22 + outpdw(MDP_CMD_DEBUG_ACCESS_BASE + 0x0044, 0x0);/* start DMA */ +#else + mdp_lut_enable(); + +#ifdef CONFIG_FB_MSM_MDP40 + outpdw(MDP_BASE + 0x000c, 0x0); /* start DMA */ +#else + outpdw(MDP_BASE + 0x0044, 0x0); /* start DMA */ + +#ifdef CONFIG_FB_MSM_MDP303 + +#ifdef CONFIG_FB_MSM_MIPI_DSI + mipi_dsi_cmd_mdp_start(); +#endif + +#endif + +#endif +#endif +#ifdef CONFIG_FB_MSM_MDP40 + } else if (term == MDP_DMA_S_TERM) { + mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + outpdw(MDP_BASE + 0x0010, 0x0); /* start DMA */ + } else if (term == MDP_DMA_E_TERM) { + mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + outpdw(MDP_BASE + 0x0014, 0x0); /* start DMA */ + } else if (term == MDP_OVERLAY0_TERM) { + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_lut_enable(); + outpdw(MDP_BASE + 0x0004, 0); + } else if (term == MDP_OVERLAY1_TERM) { + mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_lut_enable(); + outpdw(MDP_BASE + 0x0008, 0); + } else if (term == MDP_OVERLAY2_TERM) { + mdp_pipe_ctrl(MDP_OVERLAY2_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_lut_enable(); + outpdw(MDP_BASE + 0x00D0, 0); + } +#else + } else if (term == MDP_DMA_S_TERM) { + mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + outpdw(MDP_BASE + 0x0048, 0x0); /* start DMA */ + } else if (term == MDP_DMA_E_TERM) { + mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + outpdw(MDP_BASE + 0x004C, 0x0); + } +#endif +} + +static int mdp_clk_rate; +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; + +static void mdp_pipe_ctrl_workqueue_handler(struct work_struct *work) +{ + mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} +void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state, + boolean isr) +{ + boolean mdp_all_blocks_off = TRUE; + int i; + unsigned long flag; + struct msm_fb_panel_data *pdata; + + /* + * It is assumed that if isr = TRUE then start = OFF + * if start = ON when isr = TRUE it could happen that the usercontext + * could turn off the clocks while the interrupt is updating the + * power to ON + */ + WARN_ON(isr == TRUE && state == MDP_BLOCK_POWER_ON); + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (MDP_BLOCK_POWER_ON == state) { + atomic_inc(&mdp_block_power_cnt[block]); + + if (MDP_DMA2_BLOCK == block) + mdp_in_processing = TRUE; + } else { + atomic_dec(&mdp_block_power_cnt[block]); + + if (atomic_read(&mdp_block_power_cnt[block]) < 0) { + /* + * Master has to serve a request to power off MDP always + * It also has a timer to power off. So, in case of + * timer expires first and DMA2 finishes later, + * master has to power off two times + * There shouldn't be multiple power-off request for + * other blocks + */ + if (block != MDP_MASTER_BLOCK) { + MSM_FB_INFO("mdp_block_power_cnt[block=%d] \ + multiple power-off request\n", block); + } + atomic_set(&mdp_block_power_cnt[block], 0); + } + + if (MDP_DMA2_BLOCK == block) + mdp_in_processing = FALSE; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + /* + * If it's in isr, we send our request to workqueue. + * Otherwise, processing happens in the current context + */ + if (isr) { + if (mdp_current_clk_on) { + /* checking all blocks power state */ + for (i = 0; i < MDP_MAX_BLOCK; i++) { + if (atomic_read(&mdp_block_power_cnt[i]) > 0) { + mdp_all_blocks_off = FALSE; + break; + } + } + + if (mdp_all_blocks_off) { + /* send workqueue to turn off mdp power */ + queue_delayed_work(mdp_pipe_ctrl_wq, + &mdp_pipe_ctrl_worker, + mdp_timer_duration); + } + } + } else { + down(&mdp_pipe_ctrl_mutex); + /* checking all blocks power state */ + for (i = 0; i < MDP_MAX_BLOCK; i++) { + if (atomic_read(&mdp_block_power_cnt[i]) > 0) { + mdp_all_blocks_off = FALSE; + break; + } + } + + /* + * find out whether a delayable work item is currently + * pending + */ + + if (delayed_work_pending(&mdp_pipe_ctrl_worker)) { + /* + * try to cancel the current work if it fails to + * stop (which means del_timer can't delete it + * from the list, it's about to expire and run), + * we have to let it run. queue_delayed_work won't + * accept the next job which is same as + * queue_delayed_work(mdp_timer_duration = 0) + */ + cancel_delayed_work(&mdp_pipe_ctrl_worker); + } + + if ((mdp_all_blocks_off) && (mdp_current_clk_on)) { + mutex_lock(&mdp_suspend_mutex); + if (block == MDP_MASTER_BLOCK || mdp_suspended) { + mdp_current_clk_on = FALSE; + mb(); + /* turn off MDP clks */ + mdp_vsync_clk_disable(); + for (i = 0; i < pdev_list_cnt; i++) { + pdata = (struct msm_fb_panel_data *) + pdev_list[i]->dev.platform_data; + if (pdata && pdata->clk_func) + pdata->clk_func(0); + } + if (mdp_clk != NULL) { + mdp_clk_rate = clk_get_rate(mdp_clk); + clk_disable_unprepare(mdp_clk); + if (mdp_hw_revision <= + MDP4_REVISION_V2_1 && + mdp_clk_rate > 122880000) { + clk_set_rate(mdp_clk, + 122880000); + } + MSM_FB_DEBUG("MDP CLK OFF\n"); + } + if (mdp_pclk != NULL) { + clk_disable_unprepare(mdp_pclk); + MSM_FB_DEBUG("MDP PCLK OFF\n"); + } + if (mdp_lut_clk != NULL) + clk_disable_unprepare(mdp_lut_clk); + } else { + /* send workqueue to turn off mdp power */ + queue_delayed_work(mdp_pipe_ctrl_wq, + &mdp_pipe_ctrl_worker, + mdp_timer_duration); + } + mutex_unlock(&mdp_suspend_mutex); + } else if ((!mdp_all_blocks_off) && (!mdp_current_clk_on)) { + mdp_current_clk_on = TRUE; + /* turn on MDP clks */ + for (i = 0; i < pdev_list_cnt; i++) { + pdata = (struct msm_fb_panel_data *) + pdev_list[i]->dev.platform_data; + if (pdata && pdata->clk_func) + pdata->clk_func(1); + } + if (mdp_clk != NULL) { + if (mdp_hw_revision <= + MDP4_REVISION_V2_1 && + mdp_clk_rate > 122880000) { + clk_set_rate(mdp_clk, + mdp_clk_rate); + } + clk_prepare_enable(mdp_clk); + MSM_FB_DEBUG("MDP CLK ON\n"); + } + if (mdp_pclk != NULL) { + clk_prepare_enable(mdp_pclk); + MSM_FB_DEBUG("MDP PCLK ON\n"); + } + if (mdp_lut_clk != NULL) + clk_prepare_enable(mdp_lut_clk); + mdp_vsync_clk_enable(); + } + up(&mdp_pipe_ctrl_mutex); + } +} + +void mdp_histogram_handle_isr(struct mdp_hist_mgmt *mgmt) +{ + uint32 isr, mask; + char *base_addr = MDP_BASE + mgmt->base; + isr = inpdw(base_addr + MDP_HIST_INTR_STATUS_OFF); + mask = inpdw(base_addr + MDP_HIST_INTR_ENABLE_OFF); + outpdw(base_addr + MDP_HIST_INTR_CLEAR_OFF, isr); + mb(); + isr &= mask; + if (isr & INTR_HIST_RESET_SEQ_DONE) + __mdp_histogram_kickoff(mgmt); + + if (isr & INTR_HIST_DONE) { + if ((waitqueue_active(&mgmt->mdp_hist_comp.wait)) + && (mgmt->hist != NULL)) { + if (!queue_work(mdp_hist_wq, + &mgmt->mdp_histogram_worker)) { + pr_err("%s %d- can't queue hist_read\n", + __func__, mgmt->block); + } + } else { + __mdp_histogram_reset(mgmt); + } + } +} + +#ifndef CONFIG_FB_MSM_MDP40 +irqreturn_t mdp_isr(int irq, void *ptr) +{ + uint32 mdp_interrupt = 0; + struct mdp_dma_data *dma; + unsigned long flag; + struct mdp_hist_mgmt *mgmt = NULL; + char *base_addr; + int i, ret; + /* Ensure all the register write are complete */ + mb(); + + mdp_is_in_isr = TRUE; + + mdp_interrupt = inp32(MDP_INTR_STATUS); + outp32(MDP_INTR_CLEAR, mdp_interrupt); + + mdp_interrupt &= mdp_intr_mask; + + if (mdp_interrupt & TV_ENC_UNDERRUN) { + mdp_interrupt &= ~(TV_ENC_UNDERRUN); + mdp_tv_underflow_cnt++; + } + + if (!mdp_interrupt) + goto out; + + /* DMA3 TV-Out Start */ + if (mdp_interrupt & TV_OUT_DMA3_START) { + /* let's disable TV out interrupt */ + mdp_intr_mask &= ~TV_OUT_DMA3_START; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + + dma = &dma3_data; + if (dma->waiting) { + dma->waiting = FALSE; + complete(&dma->comp); + } + } + + if (mdp_rev >= MDP_REV_30) { + /* Only DMA_P histogram exists for this MDP rev*/ + if (mdp_interrupt & MDP_HIST_DONE) { + ret = mdp_histogram_block2mgmt(MDP_BLOCK_DMA_P, &mgmt); + if (!ret) + mdp_histogram_handle_isr(mgmt); + outp32(MDP_INTR_CLEAR, MDP_HIST_DONE); + } + + /* LCDC UnderFlow */ + if (mdp_interrupt & LCDC_UNDERFLOW) { + mdp_lcdc_underflow_cnt++; + /*when underflow happens HW resets all the histogram + registers that were set before so restore them back + to normal.*/ + for (i = 0; i < MDP_HIST_MGMT_MAX; i++) { + mgmt = mdp_hist_mgmt_array[i]; + if (!mgmt) + continue; + + base_addr = MDP_BASE + mgmt->base; + outpdw(base_addr + 0x010, 1); + outpdw(base_addr + 0x01C, INTR_HIST_DONE | + INTR_HIST_RESET_SEQ_DONE); + mgmt->mdp_is_hist_valid = FALSE; + __mdp_histogram_reset(mgmt); + } + } + + /* LCDC Frame Start */ + if (mdp_interrupt & LCDC_FRAME_START) { + dma = &dma2_data; + spin_lock_irqsave(&mdp_spin_lock, flag); + /* let's disable LCDC interrupt */ + mdp_intr_mask &= ~LCDC_FRAME_START; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + if (dma->waiting) { + dma->waiting = FALSE; + complete(&dma->comp); + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + } + + /* DMA2 LCD-Out Complete */ + if (mdp_interrupt & MDP_DMA_S_DONE) { + dma = &dma_s_data; + dma->busy = FALSE; + mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_OFF, + TRUE); + complete(&dma->comp); + } + + /* DMA_E LCD-Out Complete */ + if (mdp_interrupt & MDP_DMA_E_DONE) { + dma = &dma_s_data; + dma->busy = FALSE; + mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_OFF, + TRUE); + complete(&dma->comp); + } + } + + /* DMA2 LCD-Out Complete */ + if (mdp_interrupt & MDP_DMA_P_DONE) { + struct timeval now; + + mdp_dma2_last_update_time = ktime_sub(ktime_get_real(), + mdp_dma2_last_update_time); + if (mdp_debug[MDP_DMA2_BLOCK]) { + jiffies_to_timeval(jiffies, &now); + mdp_dma2_timeval.tv_usec = + now.tv_usec - mdp_dma2_timeval.tv_usec; + } +#ifndef CONFIG_FB_MSM_MDP303 + dma = &dma2_data; + spin_lock_irqsave(&mdp_spin_lock, flag); + dma->busy = FALSE; + spin_unlock_irqrestore(&mdp_spin_lock, flag); + mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); + complete(&dma->comp); +#else + if (mdp_prim_panel_type == MIPI_CMD_PANEL) { + dma = &dma2_data; + spin_lock_irqsave(&mdp_spin_lock, flag); + dma->busy = FALSE; + spin_unlock_irqrestore(&mdp_spin_lock, flag); + mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, + TRUE); + complete(&dma->comp); + } +#endif + } + + /* PPP Complete */ + if (mdp_interrupt & MDP_PPP_DONE) { +#ifdef CONFIG_FB_MSM_MDP31 + MDP_OUTP(MDP_BASE + 0x00100, 0xFFFF); +#endif + mdp_pipe_ctrl(MDP_PPP_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); + spin_lock_irqsave(&mdp_spin_lock, flag); + if (mdp_ppp_waiting) { + mdp_ppp_waiting = FALSE; + complete(&mdp_ppp_comp); + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + } + +out: +mdp_is_in_isr = FALSE; + + return IRQ_HANDLED; +} +#endif + +static void mdp_drv_init(void) +{ + int i; + + for (i = 0; i < MDP_MAX_BLOCK; i++) { + mdp_debug[i] = 0; + } + + /* initialize spin lock and workqueue */ + spin_lock_init(&mdp_spin_lock); + mdp_dma_wq = create_singlethread_workqueue("mdp_dma_wq"); + mdp_vsync_wq = create_singlethread_workqueue("mdp_vsync_wq"); + mdp_pipe_ctrl_wq = create_singlethread_workqueue("mdp_pipe_ctrl_wq"); + INIT_DELAYED_WORK(&mdp_pipe_ctrl_worker, + mdp_pipe_ctrl_workqueue_handler); + + /* initialize semaphore */ + init_completion(&mdp_ppp_comp); + sema_init(&mdp_ppp_mutex, 1); + sema_init(&mdp_pipe_ctrl_mutex, 1); + + dma2_data.busy = FALSE; + dma2_data.dmap_busy = FALSE; + dma2_data.waiting = FALSE; + init_completion(&dma2_data.comp); + init_completion(&dma2_data.dmap_comp); + sema_init(&dma2_data.mutex, 1); + mutex_init(&dma2_data.ov_mutex); + + dma3_data.busy = FALSE; + dma3_data.waiting = FALSE; + init_completion(&dma3_data.comp); + sema_init(&dma3_data.mutex, 1); + + dma_s_data.busy = FALSE; + dma_s_data.waiting = FALSE; + init_completion(&dma_s_data.comp); + sema_init(&dma_s_data.mutex, 1); + +#ifndef CONFIG_FB_MSM_MDP303 + dma_e_data.busy = FALSE; + dma_e_data.waiting = FALSE; + init_completion(&dma_e_data.comp); + mutex_init(&dma_e_data.ov_mutex); +#endif +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL + dma_wb_data.busy = FALSE; + dma_wb_data.waiting = FALSE; + init_completion(&dma_wb_data.comp); + mutex_init(&dma_wb_data.ov_mutex); +#endif + + /* initializing mdp power block counter to 0 */ + for (i = 0; i < MDP_MAX_BLOCK; i++) { + atomic_set(&mdp_block_power_cnt[i], 0); + } + +#ifdef MSM_FB_ENABLE_DBGFS + { + struct dentry *root; + char sub_name[] = "mdp"; + + root = msm_fb_get_debugfs_root(); + if (root != NULL) { + mdp_dir = debugfs_create_dir(sub_name, root); + + if (mdp_dir) { + msm_fb_debugfs_file_create(mdp_dir, + "dma2_update_time_in_usec", + (u32 *) &mdp_dma2_update_time_in_usec); + msm_fb_debugfs_file_create(mdp_dir, + "vs_rdcnt_slow", + (u32 *) &mdp_lcd_rd_cnt_offset_slow); + msm_fb_debugfs_file_create(mdp_dir, + "vs_rdcnt_fast", + (u32 *) &mdp_lcd_rd_cnt_offset_fast); + msm_fb_debugfs_file_create(mdp_dir, + "mdp_usec_diff_threshold", + (u32 *) &mdp_usec_diff_threshold); + msm_fb_debugfs_file_create(mdp_dir, + "mdp_current_clk_on", + (u32 *) &mdp_current_clk_on); +#ifdef CONFIG_FB_MSM_LCDC + msm_fb_debugfs_file_create(mdp_dir, + "lcdc_start_x", + (u32 *) &first_pixel_start_x); + msm_fb_debugfs_file_create(mdp_dir, + "lcdc_start_y", + (u32 *) &first_pixel_start_y); +#endif + } + } + } +#endif +} + +static int mdp_probe(struct platform_device *pdev); +static int mdp_remove(struct platform_device *pdev); + +static int mdp_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int mdp_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static struct dev_pm_ops mdp_dev_pm_ops = { + .runtime_suspend = mdp_runtime_suspend, + .runtime_resume = mdp_runtime_resume, +}; + + +static struct platform_driver mdp_driver = { + .probe = mdp_probe, + .remove = mdp_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = mdp_suspend, + .resume = NULL, +#endif + .shutdown = NULL, + .driver = { + /* + * Driver name must match the device name added in + * platform.c. + */ + .name = "mdp", + .pm = &mdp_dev_pm_ops, + }, +}; + +static int mdp_off(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + mdp_histogram_ctrl_all(FALSE); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + ret = panel_next_off(pdev); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + if (mdp_rev >= MDP_REV_41 && mfd->panel.type == MIPI_CMD_PANEL) + mdp_dsi_cmd_overlay_suspend(mfd); + return ret; +} + +static int mdp_on(struct platform_device *pdev) +{ + int ret = 0; + +#ifdef CONFIG_FB_MSM_MDP40 + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + if (is_mdp4_hw_reset()) { + mdp_vsync_cfg_regs(mfd, FALSE); + mdp4_hw_init(); + outpdw(MDP_BASE + 0x0038, mdp4_display_intf); + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +#endif + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + ret = panel_next_on(pdev); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + +#ifdef CONFIG_FB_MSM_MDP40 + if (mfd->panel.type == MIPI_CMD_PANEL) + mdp4_dsi_cmd_overlay_restore(); + else if (mfd->panel.type == MDDI_PANEL) + mdp4_mddi_overlay_restore(); +#endif + + mdp_histogram_ctrl_all(TRUE); + + return ret; +} + +static int mdp_resource_initialized; +static struct msm_panel_common_pdata *mdp_pdata; + +uint32 mdp_hw_revision; + +/* + * mdp_hw_revision: + * 0 == V1 + * 1 == V2 + * 2 == V2.1 + * + */ +void mdp_hw_version(void) +{ + char *cp; + uint32 *hp; + + if (mdp_pdata == NULL) + return; + + mdp_hw_revision = MDP4_REVISION_NONE; + if (mdp_pdata->hw_revision_addr == 0) + return; + + /* tlmmgpio2 shadow */ + cp = (char *)ioremap(mdp_pdata->hw_revision_addr, 0x16); + + if (cp == NULL) + return; + + hp = (uint32 *)cp; /* HW_REVISION_NUMBER */ + mdp_hw_revision = *hp; + iounmap(cp); + + mdp_hw_revision >>= 28; /* bit 31:28 */ + mdp_hw_revision &= 0x0f; + + MSM_FB_DEBUG("%s: mdp_hw_revision=%x\n", + __func__, mdp_hw_revision); +} + +#ifdef CONFIG_FB_MSM_MDP40 +static void configure_mdp_core_clk_table(uint32 min_clk_rate) +{ + uint8 count; + uint32 current_rate; + if (mdp_clk && mdp_pdata && mdp_pdata->mdp_core_clk_table) { + min_clk_rate = clk_round_rate(mdp_clk, min_clk_rate); + if (clk_set_rate(mdp_clk, min_clk_rate) < 0) + printk(KERN_ERR "%s: clk_set_rate failed\n", + __func__); + else { + count = 0; + current_rate = clk_get_rate(mdp_clk); + while (count < mdp_pdata->num_mdp_clk) { + if (mdp_pdata->mdp_core_clk_table[count] + < current_rate) { + mdp_pdata-> + mdp_core_clk_table[count] = + current_rate; + } + count++; + } + } + } +} +#endif + +#ifdef CONFIG_MSM_BUS_SCALING +static uint32_t mdp_bus_scale_handle; +int mdp_bus_scale_update_request(uint32_t index) +{ + if (!mdp_pdata && (!mdp_pdata->mdp_bus_scale_table + || index > (mdp_pdata->mdp_bus_scale_table->num_usecases - 1))) { + printk(KERN_ERR "%s invalid table or index\n", __func__); + return -EINVAL; + } + if (mdp_bus_scale_handle < 1) { + pr_debug("%s invalid bus handle\n", __func__); + return -EINVAL; + } + return msm_bus_scale_client_update_request(mdp_bus_scale_handle, + index); +} +#endif +DEFINE_MUTEX(mdp_clk_lock); +int mdp_set_core_clk(uint16 perf_level) +{ + int ret = -EINVAL; + if (mdp_clk && mdp_pdata + && mdp_pdata->mdp_core_clk_table) { + if (perf_level > mdp_pdata->num_mdp_clk) + printk(KERN_ERR "%s invalid perf level\n", __func__); + else { + mutex_lock(&mdp_clk_lock); + ret = clk_set_rate(mdp_clk, + mdp_pdata-> + mdp_core_clk_table[mdp_pdata->num_mdp_clk + - perf_level]); + mutex_unlock(&mdp_clk_lock); + if (ret) { + printk(KERN_ERR "%s unable to set mdp_core_clk rate\n", + __func__); + } + } + } + return ret; +} + +unsigned long mdp_get_core_clk(void) +{ + unsigned long clk_rate = 0; + if (mdp_clk) { + mutex_lock(&mdp_clk_lock); + clk_rate = clk_get_rate(mdp_clk); + mutex_unlock(&mdp_clk_lock); + } + + return clk_rate; +} + +unsigned long mdp_perf_level2clk_rate(uint32 perf_level) +{ + unsigned long clk_rate = 0; + + if (mdp_pdata && mdp_pdata->mdp_core_clk_table) { + if (perf_level > mdp_pdata->num_mdp_clk) { + printk(KERN_ERR "%s invalid perf level\n", __func__); + clk_rate = mdp_get_core_clk(); + } else { + clk_rate = mdp_pdata-> + mdp_core_clk_table[mdp_pdata->num_mdp_clk + - perf_level]; + } + } else + clk_rate = mdp_get_core_clk(); + + return clk_rate; +} + +static int mdp_irq_clk_setup(struct platform_device *pdev, + char cont_splashScreen) +{ + int ret; + +#ifdef CONFIG_FB_MSM_MDP40 + ret = request_irq(mdp_irq, mdp4_isr, IRQF_DISABLED, "MDP", 0); +#else + ret = request_irq(mdp_irq, mdp_isr, IRQF_DISABLED, "MDP", 0); +#endif + if (ret) { + printk(KERN_ERR "mdp request_irq() failed!\n"); + return ret; + } + disable_irq(mdp_irq); + + footswitch = regulator_get(&pdev->dev, "vdd"); + if (IS_ERR(footswitch)) + footswitch = NULL; + else { + regulator_enable(footswitch); + mdp_footswitch_on = 1; + + if (mdp_rev == MDP_REV_42 && !cont_splashScreen) { + regulator_disable(footswitch); + msleep(20); + regulator_enable(footswitch); + } + } + + mdp_clk = clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(mdp_clk)) { + ret = PTR_ERR(mdp_clk); + printk(KERN_ERR "can't get mdp_clk error:%d!\n", ret); + free_irq(mdp_irq, 0); + return ret; + } + + mdp_pclk = clk_get(&pdev->dev, "iface_clk"); + if (IS_ERR(mdp_pclk)) + mdp_pclk = NULL; + + if (mdp_rev >= MDP_REV_42) { + mdp_lut_clk = clk_get(&pdev->dev, "lut_clk"); + if (IS_ERR(mdp_lut_clk)) { + ret = PTR_ERR(mdp_lut_clk); + pr_err("can't get mdp_clk error:%d!\n", ret); + clk_put(mdp_clk); + free_irq(mdp_irq, 0); + return ret; + } + } else { + mdp_lut_clk = NULL; + } + +#ifdef CONFIG_FB_MSM_MDP40 + /* + * mdp_clk should greater than mdp_pclk always + */ + if (mdp_pdata && mdp_pdata->mdp_core_clk_rate) { + if (cont_splashScreen) + mdp_clk_rate = clk_get_rate(mdp_clk); + else + mdp_clk_rate = mdp_pdata->mdp_core_clk_rate; + + mutex_lock(&mdp_clk_lock); + clk_set_rate(mdp_clk, mdp_clk_rate); + if (mdp_lut_clk != NULL) + clk_set_rate(mdp_lut_clk, mdp_clk_rate); + mutex_unlock(&mdp_clk_lock); + } + + MSM_FB_DEBUG("mdp_clk: mdp_clk=%d\n", (int)clk_get_rate(mdp_clk)); +#endif + return 0; +} + +static int mdp_probe(struct platform_device *pdev) +{ + struct platform_device *msm_fb_dev = NULL; + struct msm_fb_data_type *mfd; + struct msm_fb_panel_data *pdata = NULL; + int rc; + resource_size_t size ; + unsigned long flag; +#ifdef CONFIG_FB_MSM_MDP40 + int intf, if_no; +#endif +#if defined(CONFIG_FB_MSM_MIPI_DSI) && defined(CONFIG_FB_MSM_MDP40) + struct mipi_panel_info *mipi; +#endif + static int contSplash_update_done; + + if ((pdev->id == 0) && (pdev->num_resources > 0)) { + mdp_init_pdev = pdev; + mdp_pdata = pdev->dev.platform_data; + + size = resource_size(&pdev->resource[0]); + msm_mdp_base = ioremap(pdev->resource[0].start, size); + + MSM_FB_DEBUG("MDP HW Base phy_Address = 0x%x virt = 0x%x\n", + (int)pdev->resource[0].start, (int)msm_mdp_base); + + if (unlikely(!msm_mdp_base)) + return -ENOMEM; + + mdp_irq = platform_get_irq(pdev, 0); + if (mdp_irq < 0) { + pr_err("mdp: can not get mdp irq\n"); + return -ENOMEM; + } + + mdp_rev = mdp_pdata->mdp_rev; + + rc = mdp_irq_clk_setup(pdev, mdp_pdata->cont_splash_enabled); + + if (rc) + return rc; + + mdp_hw_version(); + + /* initializing mdp hw */ +#ifdef CONFIG_FB_MSM_MDP40 + if (!(mdp_pdata->cont_splash_enabled)) + mdp4_hw_init(); +#else + mdp_hw_init(); +#endif + +#ifdef CONFIG_FB_MSM_OVERLAY + mdp_hw_cursor_init(); +#endif + mdp_resource_initialized = 1; + return 0; + } + + if (!mdp_resource_initialized) + return -EPERM; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + msm_fb_dev = platform_device_alloc("msm_fb", pdev->id); + if (!msm_fb_dev) + return -ENOMEM; + + /* link to the latest pdev */ + mfd->pdev = msm_fb_dev; + mfd->mdp_rev = mdp_rev; + + if (mdp_pdata) { + if (mdp_pdata->cont_splash_enabled) { + mfd->cont_splash_done = 0; + if (!contSplash_update_done) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, + MDP_BLOCK_POWER_ON, FALSE); + contSplash_update_done = 1; + } + } else + mfd->cont_splash_done = 1; + } + + mfd->ov0_wb_buf = MDP_ALLOC(sizeof(struct mdp_buf_type)); + mfd->ov1_wb_buf = MDP_ALLOC(sizeof(struct mdp_buf_type)); + memset((void *)mfd->ov0_wb_buf, 0, sizeof(struct mdp_buf_type)); + memset((void *)mfd->ov1_wb_buf, 0, sizeof(struct mdp_buf_type)); + + if (mdp_pdata) { + mfd->ov0_wb_buf->size = mdp_pdata->ov0_wb_size; + mfd->ov1_wb_buf->size = mdp_pdata->ov1_wb_size; + mfd->mem_hid = mdp_pdata->mem_hid; + } else { + mfd->ov0_wb_buf->size = 0; + mfd->ov1_wb_buf->size = 0; + mfd->mem_hid = 0; + } + mfd->ov0_blt_state = 0; + mfd->use_ov0_blt = 0 ; + + /* initialize Post Processing data*/ + mdp_hist_lut_init(); + mdp_histogram_init(); + + /* add panel data */ + if (platform_device_add_data + (msm_fb_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + printk(KERN_ERR "mdp_probe: platform_device_add_data failed!\n"); + rc = -ENOMEM; + goto mdp_probe_err; + } + /* data chain */ + pdata = msm_fb_dev->dev.platform_data; + pdata->on = mdp_on; + pdata->off = mdp_off; + pdata->next = pdev; + + mdp_prim_panel_type = mfd->panel.type; + switch (mfd->panel.type) { + case EXT_MDDI_PANEL: + case MDDI_PANEL: + case EBI2_PANEL: + INIT_WORK(&mfd->dma_update_worker, + mdp_lcd_update_workqueue_handler); + INIT_WORK(&mfd->vsync_resync_worker, + mdp_vsync_resync_workqueue_handler); + mfd->hw_refresh = FALSE; + + if (mfd->panel.type == EXT_MDDI_PANEL) { + /* 15 fps -> 66 msec */ + mfd->refresh_timer_duration = (66 * HZ / 1000); + } else { + /* 24 fps -> 42 msec */ + mfd->refresh_timer_duration = (42 * HZ / 1000); + } + +#ifdef CONFIG_FB_MSM_MDP22 + mfd->dma_fnc = mdp_dma2_update; + mfd->dma = &dma2_data; +#else + if (mfd->panel_info.pdest == DISPLAY_1) { +#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI) + mfd->dma_fnc = mdp4_mddi_overlay; + mfd->cursor_update = mdp4_mddi_overlay_cursor; +#else + mfd->dma_fnc = mdp_dma2_update; +#endif + mfd->dma = &dma2_data; + mfd->lut_update = mdp_lut_update_nonlcdc; + mfd->do_histogram = mdp_do_histogram; + mfd->start_histogram = mdp_histogram_start; + mfd->stop_histogram = mdp_histogram_stop; + } else { + mfd->dma_fnc = mdp_dma_s_update; + mfd->dma = &dma_s_data; + } +#endif + if (mdp_pdata) + mfd->vsync_gpio = mdp_pdata->gpio; + else + mfd->vsync_gpio = -1; + +#ifdef CONFIG_FB_MSM_MDP40 + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_intr_mask |= INTR_OVERLAY0_DONE; + if (mdp_hw_revision < MDP4_REVISION_V2_1) { + /* dmas dmap switch */ + mdp_intr_mask |= INTR_DMA_S_DONE; + } + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + if (mfd->panel.type == EBI2_PANEL) + intf = EBI2_INTF; + else + intf = MDDI_INTF; + + if (mfd->panel_info.pdest == DISPLAY_1) + if_no = PRIMARY_INTF_SEL; + else + if_no = SECONDARY_INTF_SEL; + + mdp4_display_intf_sel(if_no, intf); +#endif + mdp_config_vsync(mdp_init_pdev, mfd); + break; + +#ifdef CONFIG_FB_MSM_MIPI_DSI + case MIPI_VIDEO_PANEL: +#ifndef CONFIG_FB_MSM_MDP303 + pdata->on = mdp4_dsi_video_on; + pdata->off = mdp4_dsi_video_off; + mfd->hw_refresh = TRUE; + mfd->dma_fnc = mdp4_dsi_video_overlay; + mfd->lut_update = mdp_lut_update_lcdc; + mfd->do_histogram = mdp_do_histogram; + mfd->start_histogram = mdp_histogram_start; + mfd->stop_histogram = mdp_histogram_stop; + if (mfd->panel_info.pdest == DISPLAY_1) { + if_no = PRIMARY_INTF_SEL; + mfd->dma = &dma2_data; + } else { + if_no = EXTERNAL_INTF_SEL; + mfd->dma = &dma_e_data; + } + mdp4_display_intf_sel(if_no, DSI_VIDEO_INTF); +#else + pdata->on = mdp_dsi_video_on; + pdata->off = mdp_dsi_video_off; + mfd->hw_refresh = TRUE; + mfd->dma_fnc = mdp_dsi_video_update; + mfd->do_histogram = mdp_do_histogram; + mfd->start_histogram = mdp_histogram_start; + mfd->stop_histogram = mdp_histogram_stop; + if (mfd->panel_info.pdest == DISPLAY_1) + mfd->dma = &dma2_data; + else { + printk(KERN_ERR "Invalid Selection of destination panel\n"); + rc = -ENODEV; + goto mdp_probe_err; + } + +#endif + if (mdp_rev >= MDP_REV_40) + mfd->cursor_update = mdp_hw_cursor_sync_update; + else + mfd->cursor_update = mdp_hw_cursor_update; + break; + + case MIPI_CMD_PANEL: +#ifndef CONFIG_FB_MSM_MDP303 + mfd->dma_fnc = mdp4_dsi_cmd_overlay; + mipi = &mfd->panel_info.mipi; + configure_mdp_core_clk_table((mipi->dsi_pclk_rate) * 3 / 2); + if (mfd->panel_info.pdest == DISPLAY_1) { + if_no = PRIMARY_INTF_SEL; + mfd->dma = &dma2_data; + } else { + if_no = SECONDARY_INTF_SEL; + mfd->dma = &dma_s_data; + } + mfd->lut_update = mdp_lut_update_nonlcdc; + mfd->do_histogram = mdp_do_histogram; + mfd->start_histogram = mdp_histogram_start; + mfd->stop_histogram = mdp_histogram_stop; + mdp4_display_intf_sel(if_no, DSI_CMD_INTF); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_intr_mask |= INTR_OVERLAY0_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +#else + mfd->dma_fnc = mdp_dma2_update; + mfd->do_histogram = mdp_do_histogram; + mfd->start_histogram = mdp_histogram_start; + mfd->stop_histogram = mdp_histogram_stop; + if (mfd->panel_info.pdest == DISPLAY_1) + mfd->dma = &dma2_data; + else { + printk(KERN_ERR "Invalid Selection of destination panel\n"); + rc = -ENODEV; + goto mdp_probe_err; + } + INIT_WORK(&mfd->dma_update_worker, + mdp_lcd_update_workqueue_handler); +#endif + mdp_config_vsync(mdp_init_pdev, mfd); + break; +#endif + +#ifdef CONFIG_FB_MSM_DTV + case DTV_PANEL: + pdata->on = mdp4_dtv_on; + pdata->off = mdp4_dtv_off; + mfd->hw_refresh = TRUE; + mfd->cursor_update = mdp_hw_cursor_update; + mfd->dma_fnc = mdp4_dtv_overlay; + mfd->dma = &dma_e_data; + mdp4_display_intf_sel(EXTERNAL_INTF_SEL, DTV_INTF); + break; +#endif + case HDMI_PANEL: + case LCDC_PANEL: + case LVDS_PANEL: + pdata->on = mdp_lcdc_on; + pdata->off = mdp_lcdc_off; + mfd->hw_refresh = TRUE; +#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDP40) + mfd->cursor_update = mdp_hw_cursor_sync_update; +#else + mfd->cursor_update = mdp_hw_cursor_update; +#endif +#ifndef CONFIG_FB_MSM_MDP22 + mfd->lut_update = mdp_lut_update_lcdc; + mfd->do_histogram = mdp_do_histogram; + mfd->start_histogram = mdp_histogram_start; + mfd->stop_histogram = mdp_histogram_stop; +#endif +#ifdef CONFIG_FB_MSM_OVERLAY + mfd->dma_fnc = mdp4_lcdc_overlay; +#else + mfd->dma_fnc = mdp_lcdc_update; +#endif + +#ifdef CONFIG_FB_MSM_MDP40 + configure_mdp_core_clk_table((mfd->panel_info.clk_rate) + * 23 / 20); + if (mfd->panel.type == HDMI_PANEL) { + mfd->dma = &dma_e_data; + mdp4_display_intf_sel(EXTERNAL_INTF_SEL, LCDC_RGB_INTF); + } else { + mfd->dma = &dma2_data; + mdp4_display_intf_sel(PRIMARY_INTF_SEL, LCDC_RGB_INTF); + } +#else + mfd->dma = &dma2_data; + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_intr_mask &= ~MDP_DMA_P_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + spin_unlock_irqrestore(&mdp_spin_lock, flag); +#endif + break; + + case TV_PANEL: +#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_TVOUT) + pdata->on = mdp4_atv_on; + pdata->off = mdp4_atv_off; + mfd->dma_fnc = mdp4_atv_overlay; + mfd->dma = &dma_e_data; + mdp4_display_intf_sel(EXTERNAL_INTF_SEL, TV_INTF); +#else + pdata->on = mdp_dma3_on; + pdata->off = mdp_dma3_off; + mfd->hw_refresh = TRUE; + mfd->dma_fnc = mdp_dma3_update; + mfd->dma = &dma3_data; +#endif + break; + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL + case WRITEBACK_PANEL: + { + unsigned int mdp_version; + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, + FALSE); + mdp_version = inpdw(MDP_BASE + 0x0); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, + FALSE); + if (mdp_version < 0x04030303) { + pr_err("%s: writeback panel not supprted\n", + __func__); + platform_device_put(msm_fb_dev); + return -ENODEV; + } + pdata->on = mdp4_overlay_writeback_on; + pdata->off = mdp4_overlay_writeback_off; + mfd->dma_fnc = mdp4_writeback_overlay; + mfd->dma = &dma_wb_data; + mdp4_display_intf_sel(EXTERNAL_INTF_SEL, DTV_INTF); + } + break; +#endif + default: + printk(KERN_ERR "mdp_probe: unknown device type!\n"); + rc = -ENODEV; + goto mdp_probe_err; + } +#ifdef CONFIG_FB_MSM_MDP40 + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp4_display_intf = inpdw(MDP_BASE + 0x0038); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +#endif + +#ifdef CONFIG_MSM_BUS_SCALING + if (!mdp_bus_scale_handle && mdp_pdata && + mdp_pdata->mdp_bus_scale_table) { + mdp_bus_scale_handle = + msm_bus_scale_register_client( + mdp_pdata->mdp_bus_scale_table); + if (!mdp_bus_scale_handle) { + printk(KERN_ERR "%s not able to get bus scale\n", + __func__); + return -ENOMEM; + } + } + + /* req bus bandwidth immediately */ + if (!(mfd->cont_splash_done)) + mdp_bus_scale_update_request(5); + +#endif + + /* set driver data */ + platform_set_drvdata(msm_fb_dev, mfd); + + rc = platform_device_add(msm_fb_dev); + if (rc) { + goto mdp_probe_err; + } + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + pdev_list[pdev_list_cnt++] = pdev; + mdp4_extn_disp = 0; + return 0; + + mdp_probe_err: + platform_device_put(msm_fb_dev); +#ifdef CONFIG_MSM_BUS_SCALING + if (mdp_pdata && mdp_pdata->mdp_bus_scale_table && + mdp_bus_scale_handle > 0) + msm_bus_scale_unregister_client(mdp_bus_scale_handle); +#endif + return rc; +} + +void mdp_footswitch_ctrl(boolean on) +{ + mutex_lock(&mdp_suspend_mutex); + if (!mdp_suspended || mdp4_extn_disp || !footswitch || + mdp_rev <= MDP_REV_41) { + mutex_unlock(&mdp_suspend_mutex); + return; + } + + if (on && !mdp_footswitch_on) { + pr_debug("Enable MDP FS\n"); + regulator_enable(footswitch); + mdp_footswitch_on = 1; + } else if (!on && mdp_footswitch_on) { + pr_debug("Disable MDP FS\n"); + regulator_disable(footswitch); + mdp_footswitch_on = 0; + } + + mutex_unlock(&mdp_suspend_mutex); +} + +#ifdef CONFIG_PM +static void mdp_suspend_sub(void) +{ + /* cancel pipe ctrl worker */ + cancel_delayed_work(&mdp_pipe_ctrl_worker); + + /* for workder can't be cancelled... */ + flush_workqueue(mdp_pipe_ctrl_wq); + + /* let's wait for PPP completion */ + while (atomic_read(&mdp_block_power_cnt[MDP_PPP_BLOCK]) > 0) + cpu_relax(); + + /* try to power down */ + mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + mutex_lock(&mdp_suspend_mutex); + mdp_suspended = TRUE; + mutex_unlock(&mdp_suspend_mutex); +} +#endif + +#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) +static int mdp_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (pdev->id == 0) { + mdp_suspend_sub(); + if (mdp_current_clk_on) { + printk(KERN_WARNING"MDP suspend failed\n"); + return -EBUSY; + } + } + + return 0; +} +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mdp_early_suspend(struct early_suspend *h) +{ + mdp_suspend_sub(); +#ifdef CONFIG_FB_MSM_DTV + mdp4_dtv_set_black_screen(); +#endif + mdp_footswitch_ctrl(FALSE); +} + +static void mdp_early_resume(struct early_suspend *h) +{ + mdp_footswitch_ctrl(TRUE); + mutex_lock(&mdp_suspend_mutex); + mdp_suspended = FALSE; + mutex_unlock(&mdp_suspend_mutex); +} +#endif + +static int mdp_remove(struct platform_device *pdev) +{ + if (footswitch != NULL) + regulator_put(footswitch); + iounmap(msm_mdp_base); + pm_runtime_disable(&pdev->dev); +#ifdef CONFIG_MSM_BUS_SCALING + if (mdp_pdata && mdp_pdata->mdp_bus_scale_table && + mdp_bus_scale_handle > 0) + msm_bus_scale_unregister_client(mdp_bus_scale_handle); +#endif + return 0; +} + +static int mdp_register_driver(void) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1; + early_suspend.suspend = mdp_early_suspend; + early_suspend.resume = mdp_early_resume; + register_early_suspend(&early_suspend); +#endif + + return platform_driver_register(&mdp_driver); +} + +static int __init mdp_driver_init(void) +{ + int ret; + + mdp_drv_init(); + + ret = mdp_register_driver(); + if (ret) { + printk(KERN_ERR "mdp_register_driver() failed!\n"); + return ret; + } + +#if defined(CONFIG_DEBUG_FS) + mdp_debugfs_init(); +#endif + + return 0; + +} + +module_init(mdp_driver_init); diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h new file mode 100644 index 0000000000000000000000000000000000000000..8f38b988b38377a10b276c478bf67dd975d57e7c --- /dev/null +++ b/drivers/video/msm/mdp.h @@ -0,0 +1,840 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MDP_H +#define MDP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MSM_BUS_SCALING +#include +#include +#endif + +#include + +#include +#include + +#include "msm_fb_panel.h" + +extern uint32 mdp_hw_revision; +extern ulong mdp4_display_intf; +extern spinlock_t mdp_spin_lock; +extern int mdp_rev; +extern struct mdp_csc_cfg mdp_csc_convert[4]; + +extern struct workqueue_struct *mdp_hist_wq; + +#define MDP4_REVISION_V1 0 +#define MDP4_REVISION_V2 1 +#define MDP4_REVISION_V2_1 2 +#define MDP4_REVISION_NONE 0xffffffff + +#ifdef BIT +#undef BIT +#endif + +#define BIT(x) (1<<(x)) + +#define MDPOP_NOP 0 +#define MDPOP_LR BIT(0) /* left to right flip */ +#define MDPOP_UD BIT(1) /* up and down flip */ +#define MDPOP_ROT90 BIT(2) /* rotate image to 90 degree */ +#define MDPOP_ROT180 (MDPOP_UD|MDPOP_LR) +#define MDPOP_ROT270 (MDPOP_ROT90|MDPOP_UD|MDPOP_LR) +#define MDPOP_ASCALE BIT(7) +#define MDPOP_ALPHAB BIT(8) /* enable alpha blending */ +#define MDPOP_TRANSP BIT(9) /* enable transparency */ +#define MDPOP_DITHER BIT(10) /* enable dither */ +#define MDPOP_SHARPENING BIT(11) /* enable sharpening */ +#define MDPOP_BLUR BIT(12) /* enable blur */ +#define MDPOP_FG_PM_ALPHA BIT(13) +#define MDP_ALLOC(x) kmalloc(x, GFP_KERNEL) + +struct mdp_buf_type { + struct ion_handle *ihdl; + u32 phys_addr; + u32 size; +}; + +struct mdp_table_entry { + uint32_t reg; + uint32_t val; +}; + +extern struct mdp_ccs mdp_ccs_yuv2rgb ; +extern struct mdp_ccs mdp_ccs_rgb2yuv ; +extern unsigned char hdmi_prim_display; + +/* + * MDP Image Structure + */ +typedef struct mdpImg_ { + uint32 imgType; /* Image type */ + uint32 *bmy_addr; /* bitmap or y addr */ + uint32 *cbcr_addr; /* cbcr addr */ + uint32 width; /* image width */ + uint32 mdpOp; /* image opertion (rotation,flip up/down, alpha/tp) */ + uint32 tpVal; /* transparency color */ + uint32 alpha; /* alpha percentage 0%(0x0) ~ 100%(0x100) */ + int sp_value; /* sharpening strength */ +} MDPIMG; + +#define MDP_OUTP(addr, data) outpdw((addr), (data)) + +#define MDP_BASE msm_mdp_base + +typedef enum { + MDP_BC_SCALE_POINT2_POINT4, + MDP_BC_SCALE_POINT4_POINT6, + MDP_BC_SCALE_POINT6_POINT8, + MDP_BC_SCALE_POINT8_1, + MDP_BC_SCALE_UP, + MDP_PR_SCALE_POINT2_POINT4, + MDP_PR_SCALE_POINT4_POINT6, + MDP_PR_SCALE_POINT6_POINT8, + MDP_PR_SCALE_POINT8_1, + MDP_PR_SCALE_UP, + MDP_SCALE_BLUR, + MDP_INIT_SCALE +} MDP_SCALE_MODE; + +typedef enum { + MDP_BLOCK_POWER_OFF, + MDP_BLOCK_POWER_ON +} MDP_BLOCK_POWER_STATE; + +typedef enum { + MDP_CMD_BLOCK, + MDP_OVERLAY0_BLOCK, + MDP_MASTER_BLOCK, + MDP_PPP_BLOCK, + MDP_DMA2_BLOCK, + MDP_DMA3_BLOCK, + MDP_DMA_S_BLOCK, + MDP_DMA_E_BLOCK, + MDP_OVERLAY1_BLOCK, + MDP_OVERLAY2_BLOCK, + MDP_MAX_BLOCK +} MDP_BLOCK_TYPE; + +/* Let's keep Q Factor power of 2 for optimization */ +#define MDP_SCALE_Q_FACTOR 512 + +#ifdef CONFIG_FB_MSM_MDP31 +#define MDP_MAX_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*8) +#define MDP_MIN_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/8) +#define MDP_MAX_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*8) +#define MDP_MIN_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/8) +#else +#define MDP_MAX_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*4) +#define MDP_MIN_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/4) +#define MDP_MAX_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*4) +#define MDP_MIN_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/4) +#endif + +/* SHIM Q Factor */ +#define PHI_Q_FACTOR 29 +#define PQF_PLUS_5 (PHI_Q_FACTOR + 5) /* due to 32 phases */ +#define PQF_PLUS_4 (PHI_Q_FACTOR + 4) +#define PQF_PLUS_2 (PHI_Q_FACTOR + 2) /* to get 4.0 */ +#define PQF_MINUS_2 (PHI_Q_FACTOR - 2) /* to get 0.25 */ +#define PQF_PLUS_5_PLUS_2 (PQF_PLUS_5 + 2) +#define PQF_PLUS_5_MINUS_2 (PQF_PLUS_5 - 2) + +#define MDP_CONVTP(tpVal) (((tpVal&0xF800)<<8)|((tpVal&0x7E0)<<5)|((tpVal&0x1F)<<3)) + +#define MDPOP_ROTATION (MDPOP_ROT90|MDPOP_LR|MDPOP_UD) +#define MDP_CHKBIT(val, bit) ((bit) == ((val) & (bit))) + +/* overlay interface API defines */ +typedef enum { + MORE_IBUF, + FINAL_IBUF, + COMPLETE_IBUF +} MDP_IBUF_STATE; + +struct mdp_dirty_region { + __u32 xoffset; /* source origin in the x-axis */ + __u32 yoffset; /* source origin in the y-axis */ + __u32 width; /* number of pixels in the x-axis */ + __u32 height; /* number of pixels in the y-axis */ +}; + +/* + * MDP extended data types + */ +typedef struct mdp_roi_s { + uint32 x; + uint32 y; + uint32 width; + uint32 height; + int32 lcd_x; + int32 lcd_y; + uint32 dst_width; + uint32 dst_height; +} MDP_ROI; + +typedef struct mdp_ibuf_s { + uint8 *buf; + uint32 bpp; + uint32 ibuf_type; + uint32 ibuf_width; + uint32 ibuf_height; + + MDP_ROI roi; + MDPIMG mdpImg; + + int32 dma_x; + int32 dma_y; + uint32 dma_w; + uint32 dma_h; + + uint32 vsync_enable; +} MDPIBUF; + +struct mdp_dma_data { + boolean busy; + boolean dmap_busy; + boolean waiting; + struct mutex ov_mutex; + struct semaphore mutex; + struct completion comp; + struct completion dmap_comp; +}; + +extern struct list_head mdp_hist_lut_list; +extern struct mutex mdp_hist_lut_list_mutex; +struct mdp_hist_lut_mgmt { + uint32_t block; + struct mutex lock; + struct list_head list; +}; + +struct mdp_hist_lut_info { + uint32_t block; + boolean is_enabled, has_sel_update; + int bank_sel; +}; + +struct mdp_hist_mgmt { + uint32_t block; + uint32_t irq_term; + uint32_t base; + struct completion mdp_hist_comp; + struct mutex mdp_hist_mutex; + struct mutex mdp_do_hist_mutex; + boolean mdp_is_hist_start, mdp_is_hist_data; + boolean mdp_is_hist_valid, mdp_is_hist_init; + uint8_t frame_cnt, bit_mask, num_bins; + struct work_struct mdp_histogram_worker; + struct mdp_histogram_data *hist; + uint32_t *c0, *c1, *c2; + uint32_t *extra_info; +}; + +enum { + MDP_HIST_MGMT_DMA_P = 0, + MDP_HIST_MGMT_DMA_S, + MDP_HIST_MGMT_VG_1, + MDP_HIST_MGMT_VG_2, + MDP_HIST_MGMT_MAX, +}; + +extern struct mdp_hist_mgmt *mdp_hist_mgmt_array[]; + +#define MDP_CMD_DEBUG_ACCESS_BASE (MDP_BASE+0x10000) + +#define MDP_DMA2_TERM 0x1 +#define MDP_DMA3_TERM 0x2 +#define MDP_PPP_TERM 0x4 +#define MDP_DMA_S_TERM 0x8 +#define MDP_DMA_E_TERM 0x10 +#ifdef CONFIG_FB_MSM_MDP40 +#define MDP_OVERLAY0_TERM 0x20 +#define MDP_OVERLAY1_TERM 0x40 +#endif +#define MDP_OVERLAY2_TERM 0x80 +#define MDP_HISTOGRAM_TERM_DMA_P 0x100 +#define MDP_HISTOGRAM_TERM_DMA_S 0x200 +#define MDP_HISTOGRAM_TERM_VG_1 0x400 +#define MDP_HISTOGRAM_TERM_VG_2 0x800 + +#define ACTIVE_START_X_EN BIT(31) +#define ACTIVE_START_Y_EN BIT(31) +#define ACTIVE_HIGH 0 +#define ACTIVE_LOW 1 +#define MDP_DMA_S_DONE BIT(2) +#define MDP_DMA_E_DONE BIT(3) +#define LCDC_FRAME_START BIT(15) +#define LCDC_UNDERFLOW BIT(16) + +#ifdef CONFIG_FB_MSM_MDP22 +#define MDP_DMA_P_DONE BIT(2) +#else +#define MDP_DMA_P_DONE BIT(14) +#endif + +#define MDP_PPP_DONE BIT(0) +#define TV_OUT_DMA3_DONE BIT(6) +#define TV_ENC_UNDERRUN BIT(7) +#define TV_OUT_DMA3_START BIT(13) +#define MDP_HIST_DONE BIT(20) + +/* histogram interrupts */ +#define INTR_HIST_DONE BIT(1) +#define INTR_HIST_RESET_SEQ_DONE BIT(0) + +#ifdef CONFIG_FB_MSM_MDP22 +#define MDP_ANY_INTR_MASK (MDP_PPP_DONE| \ + MDP_DMA_P_DONE| \ + TV_ENC_UNDERRUN) +#else +#define MDP_ANY_INTR_MASK (MDP_PPP_DONE| \ + MDP_DMA_P_DONE| \ + MDP_DMA_S_DONE| \ + MDP_DMA_E_DONE| \ + LCDC_UNDERFLOW| \ + MDP_HIST_DONE| \ + TV_ENC_UNDERRUN) +#endif + +#define MDP_TOP_LUMA 16 +#define MDP_TOP_CHROMA 0 +#define MDP_BOTTOM_LUMA 19 +#define MDP_BOTTOM_CHROMA 3 +#define MDP_LEFT_LUMA 22 +#define MDP_LEFT_CHROMA 6 +#define MDP_RIGHT_LUMA 25 +#define MDP_RIGHT_CHROMA 9 + +#define CLR_G 0x0 +#define CLR_B 0x1 +#define CLR_R 0x2 +#define CLR_ALPHA 0x3 + +#define CLR_Y CLR_G +#define CLR_CB CLR_B +#define CLR_CR CLR_R + +/* from lsb to msb */ +#define MDP_GET_PACK_PATTERN(a,x,y,z,bit) (((a)<<(bit*3))|((x)<<(bit*2))|((y)<= 0x0402030b); +} + +int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +void mdp4_dtv_base_swap(struct mdp4_overlay_pipe *pipe); +void mdp4_dtv_overlay(struct msm_fb_data_type *mfd); +int mdp4_dtv_on(struct platform_device *pdev); +int mdp4_dtv_off(struct platform_device *pdev); +void mdp4_atv_overlay(struct msm_fb_data_type *mfd); +int mdp4_atv_on(struct platform_device *pdev); +int mdp4_atv_off(struct platform_device *pdev); +void mdp4_dsi_video_fxn_register(cmd_fxn_t fxn); +void mdp4_dsi_video_overlay(struct msm_fb_data_type *mfd); +int mdp4_dsi_video_on(struct platform_device *pdev); +int mdp4_dsi_video_off(struct platform_device *pdev); +void mdp4_overlay0_done_dsi_video(struct mdp_dma_data *dma); +void mdp4_overlay0_done_dsi_cmd(struct mdp_dma_data *dma); +void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd); +void mdp4_overlay_dsi_state_set(int state); +int mdp4_overlay_dsi_state_get(void); +void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe); +void mdp4_overlay_reg_flush(struct mdp4_overlay_pipe *pipe, int all); +void mdp4_mixer_blend_setup(int mixer); +struct mdp4_overlay_pipe *mdp4_overlay_stage_pipe(int mixer, int stage); +void mdp4_mixer_stage_up(struct mdp4_overlay_pipe *pipe); +void mdp4_mixer_stage_down(struct mdp4_overlay_pipe *pipe); +void mdp4_mixer_pipe_cleanup(int mixer); +int mdp4_mixer_stage_can_run(struct mdp4_overlay_pipe *pipe); +void mdp4_overlayproc_cfg(struct mdp4_overlay_pipe *pipe); +void mdp4_mddi_overlay(struct msm_fb_data_type *mfd); +int mdp4_overlay_format2type(uint32 format); +int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe); +int mdp4_overlay_get(struct fb_info *info, struct mdp_overlay *req); +int mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req); +int mdp4_overlay_unset(struct fb_info *info, int ndx); +int mdp4_overlay_unset_mixer(int mixer); +int mdp4_overlay_play_wait(struct fb_info *info, + struct msmfb_overlay_data *req); +int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req); +struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(int ptype, int mixer); +void mdp4_overlay_pipe_free(struct mdp4_overlay_pipe *pipe); +void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc); +void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe); +void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv); +void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe); +int mdp4_overlay_pipe_staged(int mixer); +void mdp4_lcdc_primary_vsyn(void); +void mdp4_overlay0_done_lcdc(struct mdp_dma_data *dma); +void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma); +void mdp4_dma_s_done_mddi(void); +void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma); +void mdp4_dma_p_done_dsi(struct mdp_dma_data *dma); +void mdp4_dma_p_done_dsi_video(struct mdp_dma_data *dma); +void mdp4_dma_p_done_lcdc(void); +void mdp4_overlay1_done_dtv(void); +void mdp4_overlay1_done_atv(void); +void mdp4_primary_vsync_lcdc(void); +void mdp4_external_vsync_dtv(void); +void mdp4_overlay_lcdc_wait4vsync(struct msm_fb_data_type *mfd); +void mdp4_overlay_lcdc_start(void); +void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +void mdp4_update_perf_level(u32 perf_level); +void mdp4_set_perf_level(void); +void mdp4_mddi_overlay_dmas_restore(void); + +#ifndef CONFIG_FB_MSM_MIPI_DSI +void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd); +void mdp4_mddi_overlay_restore(void); +#else +static inline void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + /* empty */ +} +static inline void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd) +{ + /* empty */ +} +static inline void mdp4_mddi_overlay_restore(void) +{ + /* empty */ +} +static inline void mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd) +{ + /*empty*/ +} +static inline void mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd) +{ + /*empty*/ +} +static inline void mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + /* empty */ +} +static inline void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + /* empty*/ +} +#endif + +void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +void mdp4_rgb_igc_lut_setup(int num); +void mdp4_vg_igc_lut_setup(int num); +void mdp4_mixer_gc_lut_setup(int mixer_num); +void mdp4_fetch_cfg(uint32 clk); +uint32 mdp4_rgb_igc_lut_cvt(uint32 ndx); +void mdp4_vg_qseed_init(int); +int mdp4_overlay_blt(struct fb_info *info, struct msmfb_overlay_blt *req); +int mdp4_overlay_blt_offset(struct fb_info *info, + struct msmfb_overlay_blt *req); + + +#ifdef CONFIG_FB_MSM_MIPI_DSI +int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd); +int mdp4_dsi_overlay_blt_stop(struct msm_fb_data_type *mfd); +void mdp4_dsi_video_blt_start(struct msm_fb_data_type *mfd); +void mdp4_dsi_video_blt_stop(struct msm_fb_data_type *mfd); +void mdp4_dsi_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req); +int mdp4_dsi_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req); + +void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req); +int mdp4_dsi_video_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req); +void mdp4_dsi_video_base_swap(struct mdp4_overlay_pipe *pipe); + +#ifdef CONFIG_FB_MSM_MDP40 +static inline void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + /* empty */ +} +#endif +#else +int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req); +void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req); +int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd); +int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd); +void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd); +static inline int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd) +{ + return -ENODEV; +} +static inline int mdp4_dsi_overlay_blt_stop(struct msm_fb_data_type *mfd) +{ + return -ENODEV; +} +static inline void mdp4_dsi_video_blt_start(struct msm_fb_data_type *mfd) +{ +} +static inline void mdp4_dsi_video_blt_stop(struct msm_fb_data_type *mfd) +{ +} +static inline void mdp4_dsi_overlay_blt( + struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req) +{ +} +static inline int mdp4_dsi_overlay_blt_offset( + struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req) +{ + return -ENODEV; +} +static inline void mdp4_dsi_video_overlay_blt( + struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req) +{ +} +static inline int mdp4_dsi_video_overlay_blt_offset( + struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req) +{ + return -ENODEV; +} +static inline void mdp4_dsi_video_base_swap(struct mdp4_overlay_pipe *pipe) +{ + /* empty */ +} +#endif + +void mdp4_lcdc_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req); +int mdp4_lcdc_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req); +void mdp4_lcdc_overlay_blt_start(struct msm_fb_data_type *mfd); +void mdp4_lcdc_overlay_blt_stop(struct msm_fb_data_type *mfd); +void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd); +void mdp4_dtv_overlay_blt_stop(struct msm_fb_data_type *mfd); +void mdp4_overlay_panel_mode(int mixer_num, uint32 mode); +void mdp4_overlay_panel_mode_unset(int mixer_num, uint32 mode); +int mdp4_overlay_mixer_play(int mixer_num); +uint32 mdp4_overlay_panel_list(void); +void mdp4_lcdc_overlay_kickoff(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); + +void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); + +void mdp4_mddi_read_ptr_intr(void); + +void mdp4_dsi_cmd_dma_busy_check(void); + +#ifdef CONFIG_FB_MSM_MIPI_DSI +void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd); +void mdp4_dsi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd); +void mdp4_overlay_dsi_video_start(void); +void mdp4_overlay_dsi_video_vsync_push(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +void mdp4_dsi_cmd_overlay_restore(void); +void mdp_dsi_cmd_overlay_suspend(struct msm_fb_data_type *mfd); +#else +static inline void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + /* empty */ +} +static inline void mdp4_dsi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd) +{ + /* empty */ +} +static inline void mdp4_overlay_dsi_video_start(void) +{ + /* empty */ +} +static inline void mdp4_overlay_dsi_video_vsync_push( + struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe) +{ + /* empty */ +} +static inline void mdp4_dsi_cmd_overlay_restore(void) +{ + /* empty */ +} +#ifdef CONFIG_FB_MSM_MDP40 +static inline void mdp_dsi_cmd_overlay_suspend(struct msm_fb_data_type *mfd) +{ + /* empty */ +} +#endif +#endif /* MIPI_DSI */ + +void mdp4_dsi_cmd_kickoff_ui(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +void mdp4_dsi_cmd_kickoff_video(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +void mdp4_dsi_cmd_overlay_kickoff(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +void mdp4_dsi_cmd_base_swap(struct mdp4_overlay_pipe *pipe); + +void mdp4_overlay_panel_3d(int mixer_num, uint32 panel_3d); +int mdp4_overlay_3d_sbys(struct fb_info *info, struct msmfb_overlay_3d *req); +void mdp4_dsi_cmd_3d_sbys(struct msm_fb_data_type *mfd, + struct msmfb_overlay_3d *r3d); +void mdp4_dsi_video_3d_sbys(struct msm_fb_data_type *mfd, + struct msmfb_overlay_3d *r3d); + +int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info); + +void mdp_dmap_vsync_set(int enable); +int mdp_dmap_vsync_get(void); +void mdp_hw_cursor_done(void); +void mdp_hw_cursor_init(void); +int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor); +int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req); +void mdp4_overlay_resource_release(void); +void mdp4_overlay_dsi_video_wait4vsync(struct msm_fb_data_type *mfd); +void mdp4_primary_vsync_dsi_video(void); +uint32_t mdp4_ss_table_value(int8_t param, int8_t index); +void mdp4_overlay_borderfill_stage_down(struct mdp4_overlay_pipe *pipe); + +int mdp4_overlay_writeback_on(struct platform_device *pdev); +int mdp4_overlay_writeback_off(struct platform_device *pdev); +void mdp4_writeback_overlay(struct msm_fb_data_type *mfd); +void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); +void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd); +void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma); + +int mdp4_writeback_start(struct fb_info *info); +int mdp4_writeback_stop(struct fb_info *info); +int mdp4_writeback_dequeue_buffer(struct fb_info *info, + struct msmfb_data *data); +int mdp4_writeback_queue_buffer(struct fb_info *info, + struct msmfb_data *data); +void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd); +int mdp4_writeback_init(struct fb_info *info); +int mdp4_writeback_terminate(struct fb_info *info); + +uint32_t mdp_block2base(uint32_t block); +int mdp_hist_lut_config(struct mdp_hist_lut_data *data); + +void mdp4_hsic_set(struct mdp4_overlay_pipe *pipe, struct dpp_ctrl *ctrl); +void mdp4_hsic_update(struct mdp4_overlay_pipe *pipe); +int mdp4_csc_config(struct mdp_csc_cfg_data *config); +void mdp4_csc_write(struct mdp_csc_cfg *data, uint32_t base); +int mdp4_csc_enable(struct mdp_csc_cfg_data *config); +int mdp4_pcc_cfg(struct mdp_pcc_cfg_data *cfg_ptr); +int mdp4_argc_cfg(struct mdp_pgc_lut_data *pgc_ptr); +int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg); +u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num); +void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num); +void mdp4_free_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num); + +int mdp4_igc_lut_config(struct mdp_igc_lut_data *cfg); +void mdp4_iommu_unmap(struct mdp4_overlay_pipe *pipe); +void mdp4_iommu_attach(void); +int mdp4_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req, + struct mdp4_overlay_pipe **ppipe); +void mdp4_v4l2_overlay_clear(struct mdp4_overlay_pipe *pipe); +int mdp4_v4l2_overlay_play(struct fb_info *info, struct mdp4_overlay_pipe *pipe, + unsigned long srcp0_addr, unsigned long srcp1_addr, + unsigned long srcp2_addr); + +#endif /* MDP_H */ diff --git a/drivers/video/msm/mdp4_dtv.c b/drivers/video/msm/mdp4_dtv.c new file mode 100644 index 0000000000000000000000000000000000000000..7f68bbb3a231b2577d83e61d0214ac31fbee32ae --- /dev/null +++ b/drivers/video/msm/mdp4_dtv.c @@ -0,0 +1,326 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" +#include "mdp4.h" + +static int dtv_probe(struct platform_device *pdev); +static int dtv_remove(struct platform_device *pdev); + +static int dtv_off(struct platform_device *pdev); +static int dtv_on(struct platform_device *pdev); + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; + +static struct clk *tv_src_clk; +static struct clk *hdmi_clk; +static struct clk *mdp_tv_clk; + + +static int mdp4_dtv_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int mdp4_dtv_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static const struct dev_pm_ops mdp4_dtv_dev_pm_ops = { + .runtime_suspend = mdp4_dtv_runtime_suspend, + .runtime_resume = mdp4_dtv_runtime_resume, +}; + +static struct platform_driver dtv_driver = { + .probe = dtv_probe, + .remove = dtv_remove, + .suspend = NULL, + .resume = NULL, + .shutdown = NULL, + .driver = { + .name = "dtv", + .pm = &mdp4_dtv_dev_pm_ops, + }, +}; + +static struct lcdc_platform_data *dtv_pdata; +#ifdef CONFIG_MSM_BUS_SCALING +static uint32_t dtv_bus_scale_handle; +#else +static struct clk *ebi1_clk; +#endif + +static int dtv_off(struct platform_device *pdev) +{ + int ret = 0; + + ret = panel_next_off(pdev); + + pr_info("%s\n", __func__); + + clk_disable_unprepare(hdmi_clk); + if (mdp_tv_clk) + clk_disable_unprepare(mdp_tv_clk); + + if (dtv_pdata && dtv_pdata->lcdc_power_save) + dtv_pdata->lcdc_power_save(0); + + if (dtv_pdata && dtv_pdata->lcdc_gpio_config) + ret = dtv_pdata->lcdc_gpio_config(0); +#ifdef CONFIG_MSM_BUS_SCALING + if (dtv_bus_scale_handle > 0) + msm_bus_scale_client_update_request(dtv_bus_scale_handle, + 0); +#else + if (ebi1_clk) + clk_disable_unprepare(ebi1_clk); +#endif + mdp4_extn_disp = 0; + return ret; +} + +static int dtv_on(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + unsigned long panel_pixclock_freq , pm_qos_rate; + + mfd = platform_get_drvdata(pdev); + panel_pixclock_freq = mfd->fbi->var.pixclock; + + if (panel_pixclock_freq > 58000000) + /* pm_qos_rate should be in Khz */ + pm_qos_rate = panel_pixclock_freq / 1000 ; + else + pm_qos_rate = 58000; + mdp4_extn_disp = 1; +#ifdef CONFIG_MSM_BUS_SCALING + if (dtv_bus_scale_handle > 0) + msm_bus_scale_client_update_request(dtv_bus_scale_handle, + 1); +#else + if (ebi1_clk) { + clk_set_rate(ebi1_clk, pm_qos_rate * 1000); + clk_prepare_enable(ebi1_clk); + } +#endif + mfd = platform_get_drvdata(pdev); + + ret = clk_set_rate(tv_src_clk, mfd->fbi->var.pixclock); + if (ret) { + pr_info("%s: clk_set_rate(%d) failed\n", __func__, + mfd->fbi->var.pixclock); + if (mfd->fbi->var.pixclock == 27030000) + mfd->fbi->var.pixclock = 27000000; + ret = clk_set_rate(tv_src_clk, mfd->fbi->var.pixclock); + } + pr_info("%s: tv_src_clk=%dkHz, pm_qos_rate=%ldkHz, [%d]\n", __func__, + mfd->fbi->var.pixclock/1000, pm_qos_rate, ret); + mfd->panel_info.clk_rate = mfd->fbi->var.pixclock; + clk_prepare_enable(hdmi_clk); + clk_reset(hdmi_clk, CLK_RESET_ASSERT); + udelay(20); + clk_reset(hdmi_clk, CLK_RESET_DEASSERT); + + if (mdp_tv_clk) + clk_prepare_enable(mdp_tv_clk); + + if (dtv_pdata && dtv_pdata->lcdc_power_save) + dtv_pdata->lcdc_power_save(1); + if (dtv_pdata && dtv_pdata->lcdc_gpio_config) + ret = dtv_pdata->lcdc_gpio_config(1); + + ret = panel_next_on(pdev); + return ret; +} + +static int dtv_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct fb_info *fbi; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc; + + if (pdev->id == 0) { + dtv_pdata = pdev->dev.platform_data; +#ifdef CONFIG_MSM_BUS_SCALING + if (!dtv_bus_scale_handle && dtv_pdata && + dtv_pdata->bus_scale_table) { + dtv_bus_scale_handle = + msm_bus_scale_register_client( + dtv_pdata->bus_scale_table); + if (!dtv_bus_scale_handle) { + pr_err("%s not able to get bus scale\n", + __func__); + } + } +#else + ebi1_clk = clk_get(&pdev->dev, "mem_clk"); + if (IS_ERR(ebi1_clk)) { + ebi1_clk = NULL; + pr_warning("%s: Couldn't get ebi1 clock\n", __func__); + } +#endif + tv_src_clk = clk_get(&pdev->dev, "src_clk"); + if (IS_ERR(tv_src_clk)) { + pr_err("error: can't get tv_src_clk!\n"); + return IS_ERR(tv_src_clk); + } + + hdmi_clk = clk_get(&pdev->dev, "hdmi_clk"); + if (IS_ERR(hdmi_clk)) { + pr_err("error: can't get hdmi_clk!\n"); + return IS_ERR(hdmi_clk); + } + + mdp_tv_clk = clk_get(&pdev->dev, "mdp_clk"); + if (IS_ERR(mdp_tv_clk)) + mdp_tv_clk = NULL; + + return 0; + } + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + + /* + * link to the latest pdev + */ + mfd->pdev = mdp_dev; + mfd->dest = DISPLAY_LCDC; + + /* + * alloc panel device data + */ + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + pr_err("dtv_probe: platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + /* + * data chain + */ + pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data; + pdata->on = dtv_on; + pdata->off = dtv_off; + pdata->next = pdev; + + /* + * get/set panel specific fb info + */ + mfd->panel_info = pdata->panel_info; + if (hdmi_prim_display) + mfd->fb_imgType = MSMFB_DEFAULT_TYPE; + else + mfd->fb_imgType = MDP_RGB_565; + + fbi = mfd->fbi; + fbi->var.pixclock = mfd->panel_info.clk_rate; + fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch; + fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch; + fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch; + fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch; + fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width; + fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width; + + /* + * set driver data + */ + platform_set_drvdata(mdp_dev, mfd); + + /* + * register in mdp driver + */ + rc = platform_device_add(mdp_dev); + if (rc) + goto dtv_probe_err; + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + pdev_list[pdev_list_cnt++] = pdev; + return 0; + +dtv_probe_err: +#ifdef CONFIG_MSM_BUS_SCALING + if (dtv_pdata && dtv_pdata->bus_scale_table && + dtv_bus_scale_handle > 0) + msm_bus_scale_unregister_client(dtv_bus_scale_handle); +#endif + platform_device_put(mdp_dev); + return rc; +} + +static int dtv_remove(struct platform_device *pdev) +{ +#ifdef CONFIG_MSM_BUS_SCALING + if (dtv_pdata && dtv_pdata->bus_scale_table && + dtv_bus_scale_handle > 0) + msm_bus_scale_unregister_client(dtv_bus_scale_handle); +#else + if (ebi1_clk) + clk_put(ebi1_clk); +#endif + pm_runtime_disable(&pdev->dev); + return 0; +} + +static int dtv_register_driver(void) +{ + return platform_driver_register(&dtv_driver); +} + +static int __init dtv_driver_init(void) +{ + return dtv_register_driver(); +} + +module_init(dtv_driver_init); diff --git a/drivers/video/msm/mdp4_hsic.c b/drivers/video/msm/mdp4_hsic.c new file mode 100644 index 0000000000000000000000000000000000000000..9b45231d3ae838c11bf58a1328ffaf836abe087a --- /dev/null +++ b/drivers/video/msm/mdp4_hsic.c @@ -0,0 +1,534 @@ +/* Copyright (c) 2009-2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "mdp.h" +#include "mdp4.h" + +/* Definitions */ +#define MDP4_CSC_MV_OFF 0x4400 +#define MDP4_CSC_PRE_BV_OFF 0x4500 +#define MDP4_CSC_POST_BV_OFF 0x4580 +#define MDP4_CSC_PRE_LV_OFF 0x4600 +#define MDP4_CSC_POST_LV_OFF 0x4680 +#define MDP_VG1_BASE (MDP_BASE + MDP4_VIDEO_BASE) + +#define MDP_VG1_CSC_MVn(n) (MDP_VG1_BASE + MDP4_CSC_MV_OFF + 4 * (n)) +#define MDP_VG1_CSC_PRE_LVn(n) (MDP_VG1_BASE + MDP4_CSC_PRE_LV_OFF + 4 * (n)) +#define MDP_VG1_CSC_POST_LVn(n) (MDP_VG1_BASE + MDP4_CSC_POST_LV_OFF + 4 * (n)) +#define MDP_VG1_CSC_PRE_BVn(n) (MDP_VG1_BASE + MDP4_CSC_PRE_BV_OFF + 4 * (n)) +#define MDP_VG1_CSC_POST_BVn(n) (MDP_VG1_BASE + MDP4_CSC_POST_BV_OFF + 4 * (n)) + +#define Q16 (16) +#define Q16_ONE (1 << Q16) + +#define Q16_VALUE(x) ((int32_t)((uint32_t)x << Q16)) +#define Q16_PERCENT_VALUE(x, n) ((int32_t)( \ + div_s64(((int64_t)x * (int64_t)Q16_ONE), n))) + +#define Q16_WHOLE(x) ((int32_t)(x >> 16)) +#define Q16_FRAC(x) ((int32_t)(x & 0xFFFF)) +#define Q16_S1Q16_MUL(x, y) (((x >> 1) * (y >> 1)) >> 14) + +#define Q16_MUL(x, y) ((int32_t)((((int64_t)x) * ((int64_t)y)) >> Q16)) +#define Q16_NEGATE(x) (0 - (x)) + +/* + * HSIC Control min/max values + * These settings are based on the maximum/minimum allowed modifications to + * HSIC controls for layer and display color. Allowing too much variation in + * the CSC block will result in color clipping resulting in unwanted color + * shifts. + */ +#define TRIG_MAX Q16_VALUE(128) +#define CON_SAT_MAX Q16_VALUE(128) +#define INTENSITY_MAX (Q16_VALUE(2047) >> 12) + +#define HUE_MAX Q16_VALUE(100) +#define HUE_MIN Q16_VALUE(-100) +#define HUE_DEF Q16_VALUE(0) + +#define SAT_MAX Q16_VALUE(100) +#define SAT_MIN Q16_VALUE(-100) +#define SAT_DEF CON_SAT_MAX + +#define CON_MAX Q16_VALUE(100) +#define CON_MIN Q16_VALUE(-100) +#define CON_DEF CON_SAT_MAX + +#define INTEN_MAX Q16_VALUE(100) +#define INTEN_MIN Q16_VALUE(-100) +#define INTEN_DEF Q16_VALUE(0) + +enum { + DIRTY, + GENERATED, + CLEAN +}; + +/* local vars*/ +static int32_t csc_matrix_tab[3][3] = { + {0x00012a00, 0x00000000, 0x00019880}, + {0x00012a00, 0xffff9b80, 0xffff3000}, + {0x00012a00, 0x00020480, 0x00000000} +}; + +static int32_t csc_yuv2rgb_conv_tab[3][3] = { + {0x00010000, 0x00000000, 0x000123cb}, + {0x00010000, 0xffff9af9, 0xffff6b5e}, + {0x00010000, 0x00020838, 0x00000000} +}; + +static int32_t csc_rgb2yuv_conv_tab[3][3] = { + {0x00004c8b, 0x00009645, 0x00001d2f}, + {0xffffda56, 0xffffb60e, 0x00006f9d}, + {0x00009d70, 0xffff7c2a, 0xffffe666} +}; + +static uint32_t csc_pre_bv_tab[3] = {0xfffff800, 0xffffc000, 0xffffc000}; +static uint32_t csc_post_bv_tab[3] = {0x00000000, 0x00000000, 0x00000000}; + +static uint32_t csc_pre_lv_tab[6] = {0x00000000, 0x00007f80, 0x00000000, + 0x00007f80, 0x00000000, 0x00007f80}; +static uint32_t csc_post_lv_tab[6] = {0x00000000, 0x00007f80, 0x00000000, + 0x00007f80, 0x00000000, 0x00007f80}; + +/* Lookup table for Sin/Cos lookup - Q16*/ +static const int32_t trig_lut[65] = { + 0x00000000, /* sin((2*M_PI/256) * 0x00);*/ + 0x00000648, /* sin((2*M_PI/256) * 0x01);*/ + 0x00000C90, /* sin((2*M_PI/256) * 0x02);*/ + 0x000012D5, + 0x00001918, + 0x00001F56, + 0x00002590, + 0x00002BC4, + 0x000031F1, + 0x00003817, + 0x00003E34, + 0x00004447, + 0x00004A50, + 0x0000504D, + 0x0000563E, + 0x00005C22, + 0x000061F8, + 0x000067BE, + 0x00006D74, + 0x0000731A, + 0x000078AD, + 0x00007E2F, + 0x0000839C, + 0x000088F6, + 0x00008E3A, + 0x00009368, + 0x00009880, + 0x00009D80, + 0x0000A268, + 0x0000A736, + 0x0000ABEB, + 0x0000B086, + 0x0000B505, + 0x0000B968, + 0x0000BDAF, + 0x0000C1D8, + 0x0000C5E4, + 0x0000C9D1, + 0x0000CD9F, + 0x0000D14D, + 0x0000D4DB, + 0x0000D848, + 0x0000DB94, + 0x0000DEBE, + 0x0000E1C6, + 0x0000E4AA, + 0x0000E768, + 0x0000EA0A, + 0x0000EC83, + 0x0000EED9, + 0x0000F109, + 0x0000F314, + 0x0000F4FA, + 0x0000F6BA, + 0x0000F854, + 0x0000F9C8, + 0x0000FB15, + 0x0000FC3B, + 0x0000FD3B, + 0x0000FE13, + 0x0000FEC4, + 0x0000FF4E, + 0x0000FFB1, + 0x0000FFEC, + 0x00010000, /* sin((2*M_PI/256) * 0x40);*/ +}; + +void trig_values_q16(int32_t deg, int32_t *cos, int32_t *sin) +{ + int32_t angle; + int32_t quad, anglei, anglef; + int32_t v0 = 0, v1 = 0; + int32_t t1, t2; + + /* + * Scale the angle so that 256 is one complete revolution and mask it + * to this domain + * NOTE: 0xB60B == 256/360 + */ + angle = Q16_MUL(deg, 0xB60B) & 0x00FFFFFF; + + /* Obtain a quadrant number, integer, and fractional part */ + quad = angle >> 22; + anglei = (angle >> 16) & 0x3F; + anglef = angle & 0xFFFF; + + /* + * Using the integer part, obtain the lookup table entry and its + * complement. Using the quadrant, swap and negate these as + * necessary. + * (The values and all derivatives of sine and cosine functions + * can be derived from these values) + */ + switch (quad) { + case 0x0: + v0 += trig_lut[anglei]; + v1 += trig_lut[0x40-anglei]; + break; + + case 0x1: + v0 += trig_lut[0x40-anglei]; + v1 -= trig_lut[anglei]; + break; + + case 0x2: + v0 -= trig_lut[anglei]; + v1 -= trig_lut[0x40-anglei]; + break; + + case 0x3: + v0 -= trig_lut[0x40-anglei]; + v1 += trig_lut[anglei]; + break; + } + + /* + * Multiply the fractional part by 2*PI/256 to move it from lookup + * table units to radians, giving us the coefficient for first + * derivatives. + */ + t1 = Q16_S1Q16_MUL(anglef, 0x0648); + + /* + * Square this and divide by 2 to get the coefficient for second + * derivatives + */ + t2 = Q16_S1Q16_MUL(t1, t1) >> 1; + + *sin = v0 + Q16_S1Q16_MUL(v1, t1) - Q16_S1Q16_MUL(v0, t2); + + *cos = v1 - Q16_S1Q16_MUL(v0, t1) - Q16_S1Q16_MUL(v1, t2); +} + +/* Convert input Q16 value to s4.9 */ +int16_t convert_q16_s49(int32_t q16Value) +{ /* Top half is the whole number, Bottom half is fractional portion*/ + int16_t whole = Q16_WHOLE(q16Value); + int32_t fraction = Q16_FRAC(q16Value); + + /* Clamp whole to 3 bits */ + if (whole > 7) + whole = 7; + else if (whole < -7) + whole = -7; + + /* Reduce fraction to 9 bits. */ + fraction = (fraction<<9)>>Q16; + + return (int16_t) ((int16_t)whole<<9) | ((int16_t)fraction); +} + +/* Convert input Q16 value to uint16 */ +int16_t convert_q16_int16(int32_t val) +{ + int32_t rounded; + + if (val >= 0) { + /* Add 0.5 */ + rounded = val + (Q16_ONE>>1); + } else { + /* Subtract 0.5 */ + rounded = val - (Q16_ONE>>1); + } + + /* Truncate rounded value */ + return (int16_t)(rounded>>Q16); +} + +/* + * norm_q16 + * Return a Q16 value represeting a normalized value + * + * value -100% 0% +100% + * |-----------------|----------------| + * ^ ^ ^ + * q16MinValue q16DefaultValue q16MaxValue + * + */ +int32_t norm_q16(int32_t value, int32_t min, int32_t default_val, int32_t max, + int32_t range) +{ + int32_t diff, perc, mul, result; + + if (0 == value) { + result = default_val; + } else if (value > 0) { + /* value is between 0% and +100% represent 1.0 -> QRange Max */ + diff = range; + perc = Q16_PERCENT_VALUE(value, max); + mul = Q16_MUL(perc, diff); + result = default_val + mul; + } else { + /* if (value <= 0) */ + diff = -range; + perc = Q16_PERCENT_VALUE(-value, -min); + mul = Q16_MUL(perc, diff); + result = default_val + mul; + } + return result; +} + +void matrix_mul_3x3(int32_t dest[][3], int32_t a[][3], int32_t b[][3]) +{ + int32_t i, j, k; + int32_t tmp[3][3]; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + tmp[i][j] = 0; + for (k = 0; k < 3; k++) + tmp[i][j] += Q16_MUL(a[i][k], b[k][j]); + } + } + + /* in case dest = a or b*/ + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) + dest[i][j] = tmp[i][j]; + } +} + +#define CONVERT(x) (x)/*convert_q16_s49((x))*/ +void pr_params(struct mdp4_hsic_regs *regs) +{ + int i; + if (regs) { + for (i = 0; i < NUM_HSIC_PARAM; i++) { + pr_info("\t: hsic->params[%d] = 0x%08x [raw = 0x%08x]\n", + i, CONVERT(regs->params[i]), regs->params[i]); + } + } +} + +void pr_3x3_matrix(int32_t in[][3]) +{ + pr_info("\t[0x%08x\t0x%08x\t0x%08x]\n", CONVERT(in[0][0]), + CONVERT(in[0][1]), CONVERT(in[0][2])); + pr_info("\t[0x%08x\t0x%08x\t0x%08x]\n", CONVERT(in[1][0]), + CONVERT(in[1][1]), CONVERT(in[1][2])); + pr_info("\t[0x%08x\t0x%08x\t0x%08x]\n", CONVERT(in[2][0]), + CONVERT(in[2][1]), CONVERT(in[2][2])); +} + +void _hsic_get(struct mdp4_hsic_regs *regs, int32_t type, int8_t *val) +{ + if (type < 0 || type >= NUM_HSIC_PARAM) + BUG_ON(-EINVAL); + *val = regs->params[type]; + pr_info("%s: getting params[%d] = %d\n", __func__, type, *val); +} + +void _hsic_set(struct mdp4_hsic_regs *regs, int32_t type, int8_t val) +{ + if (type < 0 || type >= NUM_HSIC_PARAM) + BUG_ON(-EINVAL); + + if (regs->params[type] != Q16_VALUE(val)) { + regs->params[type] = Q16_VALUE(val); + regs->dirty = DIRTY; + } +} + +void _hsic_generate_csc_matrix(struct mdp4_overlay_pipe *pipe) +{ + int i, j; + int32_t sin, cos; + + int32_t hue_matrix[3][3]; + int32_t con_sat_matrix[3][3]; + struct mdp4_hsic_regs *regs = &(pipe->hsic_regs); + + memset(con_sat_matrix, 0x0, sizeof(con_sat_matrix)); + memset(hue_matrix, 0x0, sizeof(hue_matrix)); + + /* + * HSIC control require matrix multiplication of these two tables + * [T 0 0][1 0 0] T = Contrast C=Cos(Hue) + * [0 S 0][0 C -N] S = Saturation N=Sin(Hue) + * [0 0 S][0 N C] + */ + + con_sat_matrix[0][0] = norm_q16(regs->params[HSIC_CON], CON_MIN, + CON_DEF, CON_MAX, CON_SAT_MAX); + con_sat_matrix[1][1] = norm_q16(regs->params[HSIC_SAT], SAT_MIN, + SAT_DEF, SAT_MAX, CON_SAT_MAX); + con_sat_matrix[2][2] = con_sat_matrix[1][1]; + + hue_matrix[0][0] = TRIG_MAX; + + trig_values_q16(norm_q16(regs->params[HSIC_HUE], HUE_MIN, HUE_DEF, + HUE_MAX, TRIG_MAX), &cos, &sin); + + cos = Q16_MUL(cos, TRIG_MAX); + sin = Q16_MUL(sin, TRIG_MAX); + + hue_matrix[1][1] = cos; + hue_matrix[2][2] = cos; + hue_matrix[2][1] = sin; + hue_matrix[1][2] = Q16_NEGATE(sin); + + /* Generate YUV CSC matrix */ + matrix_mul_3x3(regs->conv_matrix, con_sat_matrix, hue_matrix); + + if (!(pipe->op_mode & MDP4_OP_SRC_DATA_YCBCR)) { + /* Convert input RGB to YUV then apply CSC matrix */ + pr_info("Pipe %d, has RGB input\n", pipe->pipe_num); + matrix_mul_3x3(regs->conv_matrix, regs->conv_matrix, + csc_rgb2yuv_conv_tab); + } + + /* Normalize the matrix */ + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) + regs->conv_matrix[i][j] = (regs->conv_matrix[i][j]>>14); + } + + /* Multiply above result by current csc table */ + matrix_mul_3x3(regs->conv_matrix, regs->conv_matrix, csc_matrix_tab); + + if (!(pipe->op_mode & MDP4_OP_SRC_DATA_YCBCR)) { + /*HACK:only "works"for src side*/ + /* Convert back to RGB */ + pr_info("Pipe %d, has RGB output\n", pipe->pipe_num); + matrix_mul_3x3(regs->conv_matrix, csc_yuv2rgb_conv_tab, + regs->conv_matrix); + } + + /* Update clamps pre and post. */ + /* TODO: different tables for different color formats? */ + for (i = 0; i < 6; i++) { + regs->pre_limit[i] = csc_pre_lv_tab[i]; + regs->post_limit[i] = csc_post_lv_tab[i]; + } + + /* update bias values, pre and post */ + for (i = 0; i < 3; i++) { + regs->pre_bias[i] = csc_pre_bv_tab[i]; + regs->post_bias[i] = csc_post_bv_tab[i] + + norm_q16(regs->params[HSIC_INT], + INTEN_MIN, INTEN_DEF, INTEN_MAX, INTENSITY_MAX); + } + + regs->dirty = GENERATED; +} + +void _hsic_update_mdp(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_hsic_regs *regs = &(pipe->hsic_regs); + int i, j, k; + + uint32_t *csc_mv; + uint32_t *pre_lv; + uint32_t *post_lv; + uint32_t *pre_bv; + uint32_t *post_bv; + + switch (pipe->pipe_num) { + case OVERLAY_PIPE_VG2: + csc_mv = (uint32_t *) (MDP_VG1_CSC_MVn(0) + + MDP4_VIDEO_OFF); + pre_lv = (uint32_t *) (MDP_VG1_CSC_PRE_LVn(0) + + MDP4_VIDEO_OFF); + post_lv = (uint32_t *) (MDP_VG1_CSC_POST_LVn(0) + + MDP4_VIDEO_OFF); + pre_bv = (uint32_t *) (MDP_VG1_CSC_PRE_BVn(0) + + MDP4_VIDEO_OFF); + post_bv = (uint32_t *) (MDP_VG1_CSC_POST_BVn(0) + + MDP4_VIDEO_OFF); + break; + case OVERLAY_PIPE_VG1: + default: + csc_mv = (uint32_t *) MDP_VG1_CSC_MVn(0); + pre_lv = (uint32_t *) MDP_VG1_CSC_PRE_LVn(0); + post_lv = (uint32_t *) MDP_VG1_CSC_POST_LVn(0); + pre_bv = (uint32_t *) MDP_VG1_CSC_PRE_BVn(0); + post_bv = (uint32_t *) MDP_VG1_CSC_POST_BVn(0); + break; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + k = (3*i) + j; + MDP_OUTP(csc_mv + k, convert_q16_s49( + regs->conv_matrix[i][j])); + } + } + + for (i = 0; i < 6; i++) { + MDP_OUTP(pre_lv + i, convert_q16_s49(regs->pre_limit[i])); + MDP_OUTP(post_lv + i, convert_q16_s49(regs->post_limit[i])); + } + + for (i = 0; i < 3; i++) { + MDP_OUTP(pre_bv + i, convert_q16_s49(regs->pre_bias[i])); + MDP_OUTP(post_bv + i, convert_q16_s49(regs->post_bias[i])); + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + regs->dirty = CLEAN; +} + +void mdp4_hsic_get(struct mdp4_overlay_pipe *pipe, struct dpp_ctrl *ctrl) +{ + int i; + for (i = 0; i < NUM_HSIC_PARAM; i++) + _hsic_get(&(pipe->hsic_regs), i, &(ctrl->hsic_params[i])); +} + +void mdp4_hsic_set(struct mdp4_overlay_pipe *pipe, struct dpp_ctrl *ctrl) +{ + int i; + for (i = 0; i < NUM_HSIC_PARAM; i++) + _hsic_set(&(pipe->hsic_regs), i, ctrl->hsic_params[i]); + + if (pipe->hsic_regs.dirty == DIRTY) + _hsic_generate_csc_matrix(pipe); +} + +void mdp4_hsic_update(struct mdp4_overlay_pipe *pipe) +{ + if (pipe->hsic_regs.dirty == GENERATED) + _hsic_update_mdp(pipe); +} diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c new file mode 100644 index 0000000000000000000000000000000000000000..819afffc4f43f1108279c6b8d2e13c1dd87ab0d7 --- /dev/null +++ b/drivers/video/msm/mdp4_overlay.c @@ -0,0 +1,3356 @@ +/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + +#define VERSION_KEY_MASK 0xFFFFFF00 + +struct mdp4_overlay_ctrl { + struct mdp4_overlay_pipe plist[OVERLAY_PIPE_MAX]; + struct mdp4_overlay_pipe *stage[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX]; + struct mdp4_overlay_pipe *baselayer[MDP4_MIXER_MAX]; + struct blend_cfg blend[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX]; + uint32 mixer_cfg[MDP4_MIXER_MAX]; + uint32 flush[MDP4_MIXER_MAX]; + uint32 cs_controller; + uint32 hw_version; + uint32 panel_3d; + uint32 panel_mode; + uint32 mixer0_played; + uint32 mixer1_played; + uint32 mixer2_played; +} mdp4_overlay_db = { + .cs_controller = CS_CONTROLLER_0, + .plist = { + { + .pipe_type = OVERLAY_TYPE_RGB, + .pipe_num = OVERLAY_PIPE_RGB1, + .pipe_ndx = 1, + }, + { + .pipe_type = OVERLAY_TYPE_RGB, + .pipe_num = OVERLAY_PIPE_RGB2, + .pipe_ndx = 2, + }, + { + .pipe_type = OVERLAY_TYPE_VIDEO, + .pipe_num = OVERLAY_PIPE_VG1, + .pipe_ndx = 3, + }, + { + .pipe_type = OVERLAY_TYPE_VIDEO, + .pipe_num = OVERLAY_PIPE_VG2, + .pipe_ndx = 4, + }, + { + .pipe_type = OVERLAY_TYPE_BF, + .pipe_num = OVERLAY_PIPE_RGB3, + .pipe_ndx = 5, + .mixer_num = MDP4_MIXER0, + }, + { + .pipe_type = OVERLAY_TYPE_BF, + .pipe_num = OVERLAY_PIPE_VG3, + .pipe_ndx = 6, + .mixer_num = MDP4_MIXER1, + }, + { + .pipe_type = OVERLAY_TYPE_BF, + .pipe_num = OVERLAY_PIPE_VG4, + .pipe_ndx = 7, + .mixer_num = MDP4_MIXER2, + }, + }, +}; + +static struct mdp4_overlay_ctrl *ctrl = &mdp4_overlay_db; +static int new_perf_level; +static struct ion_client *display_iclient; +static struct mdp4_iommu_pipe_info mdp_iommu[MDP4_MIXER_MAX][OVERLAY_PIPE_MAX]; + +int mdp4_overlay_iommu_map_buf(int mem_id, + struct mdp4_overlay_pipe *pipe, unsigned int plane, + unsigned long *start, unsigned long *len, + struct ion_handle **srcp_ihdl) +{ + struct mdp4_iommu_pipe_info *iom_pipe_info; + + if (!display_iclient) + return -EINVAL; + + *srcp_ihdl = ion_import_fd(display_iclient, mem_id); + if (IS_ERR_OR_NULL(*srcp_ihdl)) { + pr_err("ion_import_fd() failed\n"); + return PTR_ERR(*srcp_ihdl); + } + pr_debug("%s(): ion_hdl %p, ion_buf %p\n", __func__, *srcp_ihdl, + ion_share(display_iclient, *srcp_ihdl)); + pr_debug("mixer %u, pipe %u, plane %u\n", pipe->mixer_num, + pipe->pipe_ndx, plane); + if (ion_map_iommu(display_iclient, *srcp_ihdl, + DISPLAY_DOMAIN, GEN_POOL, SZ_4K, 0, start, + len, 0, ION_IOMMU_UNMAP_DELAYED)) { + ion_free(display_iclient, *srcp_ihdl); + pr_err("ion_map_iommu() failed\n"); + return -EINVAL; + } + + iom_pipe_info = &mdp_iommu[pipe->mixer_num][pipe->pipe_ndx - 1]; + if (!iom_pipe_info->ihdl[plane]) { + iom_pipe_info->ihdl[plane] = *srcp_ihdl; + } else { + if (iom_pipe_info->prev_ihdl[plane]) { + ion_unmap_iommu(display_iclient, + iom_pipe_info->prev_ihdl[plane], + DISPLAY_DOMAIN, GEN_POOL); + ion_free(display_iclient, + iom_pipe_info->prev_ihdl[plane]); + pr_debug("Previous: mixer %u, pipe %u, plane %u, " + "prev_ihdl %p\n", pipe->mixer_num, + pipe->pipe_ndx, plane, + iom_pipe_info->prev_ihdl[plane]); + } + + iom_pipe_info->prev_ihdl[plane] = iom_pipe_info->ihdl[plane]; + iom_pipe_info->ihdl[plane] = *srcp_ihdl; + } + pr_debug("mem_id %d, start 0x%lx, len 0x%lx\n", + mem_id, *start, *len); + return 0; +} + +void mdp4_iommu_unmap(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_iommu_pipe_info *iom_pipe_info; + unsigned char i, j; + + if (!display_iclient) + return; + + for (j = 0; j < OVERLAY_PIPE_MAX; j++) { + iom_pipe_info = &mdp_iommu[pipe->mixer_num][j]; + for (i = 0; i < MDP4_MAX_PLANE; i++) { + if (iom_pipe_info->prev_ihdl[i]) { + pr_debug("%s(): mixer %u, pipe %u, plane %u, " + "prev_ihdl %p\n", __func__, + pipe->mixer_num, j + 1, i, + iom_pipe_info->prev_ihdl[i]); + ion_unmap_iommu(display_iclient, + iom_pipe_info->prev_ihdl[i], + DISPLAY_DOMAIN, GEN_POOL); + ion_free(display_iclient, + iom_pipe_info->prev_ihdl[i]); + iom_pipe_info->prev_ihdl[i] = NULL; + } + + if (iom_pipe_info->mark_unmap) { + if (iom_pipe_info->ihdl[i]) { + if (pipe->mixer_num == MDP4_MIXER1) + mdp4_overlay_dtv_wait4vsync(); + pr_debug("%s(): mixer %u, pipe %u, plane %u, " + "ihdl %p\n", __func__, + pipe->mixer_num, j + 1, i, + iom_pipe_info->ihdl[i]); + ion_unmap_iommu(display_iclient, + iom_pipe_info->ihdl[i], + DISPLAY_DOMAIN, GEN_POOL); + ion_free(display_iclient, + iom_pipe_info->ihdl[i]); + iom_pipe_info->ihdl[i] = NULL; + } + } + } + iom_pipe_info->mark_unmap = 0; + } +} + +int mdp4_overlay_mixer_play(int mixer_num) +{ + if (mixer_num == MDP4_MIXER2) + return ctrl->mixer2_played; + else if (mixer_num == MDP4_MIXER1) + return ctrl->mixer1_played; + else + return ctrl->mixer0_played; +} + +void mdp4_overlay_panel_3d(int mixer_num, uint32 panel_3d) +{ + ctrl->panel_3d = panel_3d; +} + +void mdp4_overlay_panel_mode(int mixer_num, uint32 mode) +{ + ctrl->panel_mode |= mode; +} + +void mdp4_overlay_panel_mode_unset(int mixer_num, uint32 mode) +{ + ctrl->panel_mode &= ~mode; +} + +uint32 mdp4_overlay_panel_list(void) +{ + return ctrl->panel_mode; +} + +void mdp4_overlay_cfg_init(void) +{ + if (ctrl->hw_version == 0) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + ctrl->hw_version = inpdw(MDP_BASE + 0x0); /* MDP_HW_VERSION */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } + + if (ctrl->hw_version >= 0x0402030b) { + /* MDP_LAYERMIXER_IN_CFG_UPDATE_METHOD */ + outpdw(MDP_BASE + 0x100fc, 0x01); + } +} + +void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv) +{ + uint32 dmae_cfg_reg; + + if (atv) + dmae_cfg_reg = DMA_DEFLKR_EN; + else + dmae_cfg_reg = 0; + + if (mfd->fb_imgType == MDP_BGR_565) + dmae_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dmae_cfg_reg |= DMA_PACK_PATTERN_RGB; + + + if (mfd->panel_info.bpp == 18) { + dmae_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + } else if (mfd->panel_info.bpp == 16) { + dmae_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + } else { + dmae_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 16BPP */ + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* dma2 config register */ + MDP_OUTP(MDP_BASE + 0xb0000, dmae_cfg_reg); + if (atv) { + MDP_OUTP(MDP_BASE + 0xb0070, 0xeb0010); + MDP_OUTP(MDP_BASE + 0xb0074, 0xf00010); + MDP_OUTP(MDP_BASE + 0xb0078, 0xf00010); + MDP_OUTP(MDP_BASE + 0xb3000, 0x80); + MDP_OUTP(MDP_BASE + 0xb3010, 0x1800040); + MDP_OUTP(MDP_BASE + 0xb3014, 0x1000080); + MDP_OUTP(MDP_BASE + 0xb4004, 0x67686970); + } else { + mdp_vid_quant_set(); + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +#ifdef CONFIG_FB_MSM_HDMI_3D +void unfill_black_screen(void) { return; } +#else +void unfill_black_screen(void) +{ + uint32 temp_src_format; + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* + * VG2 Constant Color + */ + temp_src_format = inpdw(MDP_BASE + 0x30050); + MDP_OUTP(MDP_BASE + 0x30050, temp_src_format&(~BIT(22))); + /* + * MDP_OVERLAY_REG_FLUSH + */ + MDP_OUTP(MDP_BASE + 0x18000, BIT(3)); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return; +} +#endif + +#ifdef CONFIG_FB_MSM_HDMI_3D +void fill_black_screen(void) { return; } +#else +void fill_black_screen(void) +{ + /*Black color*/ + uint32 color = 0x00000000; + uint32 temp_src_format; + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* + * VG2 Constant Color + */ + MDP_OUTP(MDP_BASE + 0x31008, color); + /* + * MDP_VG2_SRC_FORMAT + */ + temp_src_format = inpdw(MDP_BASE + 0x30050); + MDP_OUTP(MDP_BASE + 0x30050, temp_src_format | BIT(22)); + /* + * MDP_OVERLAY_REG_FLUSH + */ + MDP_OUTP(MDP_BASE + 0x18000, BIT(3)); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return; +} +#endif + +void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe) +{ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + MDP_OUTP(MDP_BASE + 0xb0004, + (pipe->src_height << 16 | pipe->src_width)); + if (pipe->blt_addr) { + uint32 off, bpp; +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + MDP_OUTP(MDP_BASE + 0xb0008, pipe->blt_addr + off); + /* RGB888, output of overlay blending */ + MDP_OUTP(MDP_BASE + 0xb000c, pipe->src_width * bpp); + } else { + /* dma_e source */ + MDP_OUTP(MDP_BASE + 0xb0008, pipe->srcp0_addr); + MDP_OUTP(MDP_BASE + 0xb000c, pipe->srcp0_ystride); + } + /* dma_e dest */ + MDP_OUTP(MDP_BASE + 0xb0010, (pipe->dst_y << 16 | pipe->dst_x)); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc) +{ + uint32 dma2_cfg_reg; + uint32 mask, curr; + + dma2_cfg_reg = DMA_DITHER_EN; +#ifdef BLT_RGB565 + /* RGB888 is 0 */ + dma2_cfg_reg |= DMA_BUF_FORMAT_RGB565; /* blt only */ +#endif + + if (mfd->fb_imgType == MDP_BGR_565) + dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; + + + if (mfd->panel_info.bpp == 18) { + dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + } else if (mfd->panel_info.bpp == 16) { + dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + } else { + dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 16BPP */ + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + +#ifndef CONFIG_FB_MSM_LCDC_CHIMEI_WXGA_PANEL + if (lcdc) + dma2_cfg_reg |= DMA_PACK_ALIGN_MSB; +#endif + + /* dma2 config register */ + curr = inpdw(MDP_BASE + 0x90000); + mask = 0x0FFFFFFF; + dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask); + MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +/* + * mdp4_overlay_dmap_xy: called form baselayer only + */ +void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, bpp; + + if (mdp_is_in_isr == FALSE) + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* dma_p source */ + MDP_OUTP(MDP_BASE + 0x90004, + (pipe->src_height << 16 | pipe->src_width)); + if (pipe->blt_addr) { +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->dmap_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + MDP_OUTP(MDP_BASE + 0x90008, pipe->blt_addr + off); + /* RGB888, output of overlay blending */ + MDP_OUTP(MDP_BASE + 0x9000c, pipe->src_width * bpp); + } else { + MDP_OUTP(MDP_BASE + 0x90008, pipe->srcp0_addr); + MDP_OUTP(MDP_BASE + 0x9000c, pipe->srcp0_ystride); + } + + /* dma_p dest */ + MDP_OUTP(MDP_BASE + 0x90010, (pipe->dst_y << 16 | pipe->dst_x)); + + if (mdp_is_in_isr == FALSE) + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +#define MDP4_VG_PHASE_STEP_DEFAULT 0x20000000 +#define MDP4_VG_PHASE_STEP_SHIFT 29 + +static int mdp4_leading_0(uint32 num) +{ + uint32 bit = 0x80000000; + int i; + + for (i = 0; i < 32; i++) { + if (bit & num) + return i; + bit >>= 1; + } + + return i; +} + +static uint32 mdp4_scale_phase_step(int f_num, uint32 src, uint32 dst) +{ + uint32 val, s; + int n; + + n = mdp4_leading_0(src); + if (n > f_num) + n = f_num; + s = src << n; /* maximum to reduce lose of resolution */ + val = s / dst; + if (n < f_num) { + n = f_num - n; + val <<= n; + val |= ((s % dst) << n) / dst; + } + + return val; +} + +static void mdp4_scale_setup(struct mdp4_overlay_pipe *pipe) +{ + pipe->phasex_step = MDP4_VG_PHASE_STEP_DEFAULT; + pipe->phasey_step = MDP4_VG_PHASE_STEP_DEFAULT; + + if (pipe->dst_h && pipe->src_h != pipe->dst_h) { + u32 upscale_max; + upscale_max = (mdp_rev >= MDP_REV_41) ? + MDP4_REV41_OR_LATER_UP_SCALING_MAX : + MDP4_REV40_UP_SCALING_MAX; + if (pipe->dst_h > pipe->src_h * upscale_max) + return; + + pipe->op_mode |= MDP4_OP_SCALEY_EN; + + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) { + if (pipe->flags & MDP_BACKEND_COMPOSITION && + pipe->alpha_enable && pipe->dst_h > pipe->src_h) + pipe->op_mode |= MDP4_OP_SCALEY_PIXEL_RPT; + else if (pipe->dst_h <= (pipe->src_h / 4)) + pipe->op_mode |= MDP4_OP_SCALEY_MN_PHASE; + else + pipe->op_mode |= MDP4_OP_SCALEY_FIR; + } else { /* RGB pipe */ + pipe->op_mode |= MDP4_OP_SCALE_RGB_ENHANCED | + MDP4_OP_SCALE_RGB_BILINEAR | + MDP4_OP_SCALE_ALPHA_BILINEAR; + } + + pipe->phasey_step = mdp4_scale_phase_step(29, + pipe->src_h, pipe->dst_h); + } + + if (pipe->dst_w && pipe->src_w != pipe->dst_w) { + u32 upscale_max; + upscale_max = (mdp_rev >= MDP_REV_41) ? + MDP4_REV41_OR_LATER_UP_SCALING_MAX : + MDP4_REV40_UP_SCALING_MAX; + + if (pipe->dst_w > pipe->src_w * upscale_max) + return; + pipe->op_mode |= MDP4_OP_SCALEX_EN; + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) { + if (pipe->flags & MDP_BACKEND_COMPOSITION && + pipe->alpha_enable && pipe->dst_w > pipe->src_w) + pipe->op_mode |= MDP4_OP_SCALEX_PIXEL_RPT; + else if (pipe->dst_w <= (pipe->src_w / 4)) + pipe->op_mode |= MDP4_OP_SCALEX_MN_PHASE; + else + pipe->op_mode |= MDP4_OP_SCALEX_FIR; + } else { /* RGB pipe */ + pipe->op_mode |= MDP4_OP_SCALE_RGB_ENHANCED | + MDP4_OP_SCALE_RGB_BILINEAR | + MDP4_OP_SCALE_ALPHA_BILINEAR; + } + + pipe->phasex_step = mdp4_scale_phase_step(29, + pipe->src_w, pipe->dst_w); + } +} + +void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe) +{ + char *rgb_base; + uint32 src_size, src_xy, dst_size, dst_xy; + uint32 format, pattern; + uint32 curr, mask; + uint32 offset = 0; + int pnum; + + pnum = pipe->pipe_num - OVERLAY_PIPE_RGB1; /* start from 0 */ + rgb_base = MDP_BASE + MDP4_RGB_BASE; + rgb_base += (MDP4_RGB_OFF * pnum); + + src_size = ((pipe->src_h << 16) | pipe->src_w); + src_xy = ((pipe->src_y << 16) | pipe->src_x); + dst_size = ((pipe->dst_h << 16) | pipe->dst_w); + dst_xy = ((pipe->dst_y << 16) | pipe->dst_x); + + if ((pipe->src_x + pipe->src_w) > 0x7FF) { + offset += pipe->src_x * pipe->bpp; + src_xy &= 0xFFFF0000; + } + + if ((pipe->src_y + pipe->src_h) > 0x7FF) { + offset += pipe->src_y * pipe->src_width * pipe->bpp; + src_xy &= 0x0000FFFF; + } + + format = mdp4_overlay_format(pipe); + pattern = mdp4_overlay_unpack_pattern(pipe); + +#ifdef MDP4_IGC_LUT_ENABLE + pipe->op_mode |= MDP4_OP_IGC_LUT_EN; +#endif + + mdp4_scale_setup(pipe); + + /* Ensure proper covert matrix loaded when color space swaps */ + curr = inpdw(rgb_base + 0x0058); + /* Don't touch bits you don't want to configure*/ + mask = 0xFFFEFFFF; + pipe->op_mode = (pipe->op_mode & mask) | (curr & ~mask); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + outpdw(rgb_base + 0x0000, src_size); /* MDP_RGB_SRC_SIZE */ + outpdw(rgb_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */ + outpdw(rgb_base + 0x0008, dst_size); /* MDP_RGB_DST_SIZE */ + outpdw(rgb_base + 0x000c, dst_xy); /* MDP_RGB_DST_XY */ + + outpdw(rgb_base + 0x0010, pipe->srcp0_addr + offset); + outpdw(rgb_base + 0x0040, pipe->srcp0_ystride); + + outpdw(rgb_base + 0x0050, format);/* MDP_RGB_SRC_FORMAT */ + outpdw(rgb_base + 0x0054, pattern);/* MDP_RGB_SRC_UNPACK_PATTERN */ + if (format & MDP4_FORMAT_SOLID_FILL) { + u32 op_mode = pipe->op_mode; + op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN); + op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN); + outpdw(rgb_base + 0x0058, op_mode);/* MDP_RGB_OP_MODE */ + } else + outpdw(rgb_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */ + outpdw(rgb_base + 0x005c, pipe->phasex_step); + outpdw(rgb_base + 0x0060, pipe->phasey_step); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + mdp4_stat.pipe[pipe->pipe_num]++; +} + + +static void mdp4_overlay_vg_get_src_offset(struct mdp4_overlay_pipe *pipe, + char *vg_base, uint32 *luma_off, uint32 *chroma_off) +{ + uint32 src_xy; + *luma_off = 0; + *chroma_off = 0; + + if (pipe->src_x && (pipe->frame_format == + MDP4_FRAME_FORMAT_LINEAR)) { + src_xy = (pipe->src_y << 16) | pipe->src_x; + src_xy &= 0xffff0000; + outpdw(vg_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */ + + switch (pipe->src_format) { + case MDP_Y_CR_CB_H2V2: + case MDP_Y_CR_CB_GH2V2: + case MDP_Y_CB_CR_H2V2: + *luma_off = pipe->src_x; + *chroma_off = pipe->src_x/2; + break; + + case MDP_Y_CBCR_H2V2_TILE: + case MDP_Y_CRCB_H2V2_TILE: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CRCB_H1V1: + case MDP_Y_CBCR_H1V1: + case MDP_Y_CRCB_H2V1: + case MDP_Y_CBCR_H2V1: + *luma_off = pipe->src_x; + *chroma_off = pipe->src_x; + break; + + case MDP_YCRYCB_H2V1: + if (pipe->src_x & 0x1) + pipe->src_x += 1; + *luma_off += pipe->src_x * 2; + break; + + case MDP_ARGB_8888: + case MDP_RGBA_8888: + case MDP_BGRA_8888: + case MDP_RGBX_8888: + case MDP_RGB_565: + case MDP_BGR_565: + case MDP_XRGB_8888: + case MDP_RGB_888: + case MDP_YCBCR_H1V1: + case MDP_YCRCB_H1V1: + *luma_off = pipe->src_x * pipe->bpp; + break; + + default: + pr_err("%s: fmt %u not supported for adjustment\n", + __func__, pipe->src_format); + break; + } + } +} + +void mdp4_overlay_vg_setup(struct mdp4_overlay_pipe *pipe) +{ + char *vg_base; + uint32 frame_size, src_size, src_xy, dst_size, dst_xy; + uint32 format, pattern, luma_offset, chroma_offset; + uint32 mask, curr, addr; + int pnum, ptype; + + pnum = pipe->pipe_num - OVERLAY_PIPE_VG1; /* start from 0 */ + vg_base = MDP_BASE + MDP4_VIDEO_BASE; + vg_base += (MDP4_VIDEO_OFF * pnum); + + frame_size = ((pipe->src_height << 16) | pipe->src_width); + src_size = ((pipe->src_h << 16) | pipe->src_w); + src_xy = ((pipe->src_y << 16) | pipe->src_x); + dst_size = ((pipe->dst_h << 16) | pipe->dst_w); + dst_xy = ((pipe->dst_y << 16) | pipe->dst_x); + + ptype = mdp4_overlay_format2type(pipe->src_format); + format = mdp4_overlay_format(pipe); + pattern = mdp4_overlay_unpack_pattern(pipe); + + /* not RGB use VG pipe, pure VG pipe */ + pipe->op_mode |= MDP4_OP_CSC_EN; + if (ptype != OVERLAY_TYPE_RGB) + pipe->op_mode |= MDP4_OP_SRC_DATA_YCBCR; + +#ifdef MDP4_IGC_LUT_ENABLE + pipe->op_mode |= MDP4_OP_IGC_LUT_EN; +#endif + + mdp4_scale_setup(pipe); + + luma_offset = 0; + chroma_offset = 0; + + if (ptype == OVERLAY_TYPE_RGB) { + if ((pipe->src_y + pipe->src_h) > 0x7FF) { + luma_offset = pipe->src_y * pipe->src_width * pipe->bpp; + src_xy &= 0x0000FFFF; + } + + if ((pipe->src_x + pipe->src_w) > 0x7FF) { + luma_offset += pipe->src_x * pipe->bpp; + src_xy &= 0xFFFF0000; + } + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + outpdw(vg_base + 0x0000, src_size); /* MDP_RGB_SRC_SIZE */ + outpdw(vg_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */ + outpdw(vg_base + 0x0008, dst_size); /* MDP_RGB_DST_SIZE */ + outpdw(vg_base + 0x000c, dst_xy); /* MDP_RGB_DST_XY */ + + if (pipe->frame_format != MDP4_FRAME_FORMAT_LINEAR) + outpdw(vg_base + 0x0048, frame_size); /* TILE frame size */ + + /* + * Adjust src X offset to avoid MDP from overfetching pixels + * present before the offset. This is required for video + * frames coming with unused green pixels along the left margin + */ + /* not RGB use VG pipe, pure VG pipe */ + if (ptype != OVERLAY_TYPE_RGB) { + mdp4_overlay_vg_get_src_offset(pipe, vg_base, &luma_offset, + &chroma_offset); + } + + /* Ensure proper covert matrix loaded when color space swaps */ + curr = inpdw(vg_base + 0x0058); + mask = 0x600; + + if ((curr & mask) != (pipe->op_mode & mask)) { + addr = ((uint32_t)vg_base) + 0x4000; + if (ptype != OVERLAY_TYPE_RGB) + mdp4_csc_write(&(mdp_csc_convert[1]), addr); + else + mdp4_csc_write(&(mdp_csc_convert[0]), addr); + + mask = 0xFFFCFFFF; + } else { + /* Don't touch bits you don't want to configure*/ + mask = 0xFFFCF1FF; + } + pipe->op_mode = (pipe->op_mode & mask) | (curr & ~mask); + + /* luma component plane */ + outpdw(vg_base + 0x0010, pipe->srcp0_addr + luma_offset); + + /* chroma component plane or planar color 1 */ + outpdw(vg_base + 0x0014, pipe->srcp1_addr + chroma_offset); + + /* planar color 2 */ + outpdw(vg_base + 0x0018, pipe->srcp2_addr + chroma_offset); + + outpdw(vg_base + 0x0040, + pipe->srcp1_ystride << 16 | pipe->srcp0_ystride); + + outpdw(vg_base + 0x0044, + pipe->srcp3_ystride << 16 | pipe->srcp2_ystride); + + outpdw(vg_base + 0x0050, format); /* MDP_RGB_SRC_FORMAT */ + outpdw(vg_base + 0x0054, pattern); /* MDP_RGB_SRC_UNPACK_PATTERN */ + if (format & MDP4_FORMAT_SOLID_FILL) { + u32 op_mode = pipe->op_mode; + op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN); + op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN); + outpdw(vg_base + 0x0058, op_mode);/* MDP_RGB_OP_MODE */ + } else + outpdw(vg_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */ + outpdw(vg_base + 0x005c, pipe->phasex_step); + outpdw(vg_base + 0x0060, pipe->phasey_step); + + if (pipe->op_mode & MDP4_OP_DITHER_EN) { + outpdw(vg_base + 0x0068, + pipe->r_bit << 4 | pipe->b_bit << 2 | pipe->g_bit); + } + + if (pipe->flags & MDP_SHARPENING) { + outpdw(vg_base + 0x8200, + mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength, + 0)); + outpdw(vg_base + 0x8204, + mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength, + 1)); + } + + if (mdp_rev > MDP_REV_41) { + /* mdp chip select controller */ + mask = 0; + if (pipe->pipe_num == OVERLAY_PIPE_VG1) + mask = 0x020; /* bit 5 */ + else if (pipe->pipe_num == OVERLAY_PIPE_VG2) + mask = 0x02000; /* bit 13 */ + if (mask) { + if (pipe->op_mode & MDP4_OP_SCALEY_MN_PHASE) + ctrl->cs_controller &= ~mask; + else + ctrl->cs_controller |= mask; + /* NOT double buffered */ + outpdw(MDP_BASE + 0x00c0, ctrl->cs_controller); + } + } + + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + mdp4_stat.pipe[pipe->pipe_num]++; +} + +int mdp4_overlay_format2type(uint32 format) +{ + switch (format) { + case MDP_RGB_565: + case MDP_RGB_888: + case MDP_BGR_565: + case MDP_XRGB_8888: + case MDP_ARGB_8888: + case MDP_RGBA_8888: + case MDP_BGRA_8888: + case MDP_RGBX_8888: + return OVERLAY_TYPE_RGB; + case MDP_YCRYCB_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_TILE: + case MDP_Y_CRCB_H2V2_TILE: + case MDP_Y_CR_CB_H2V2: + case MDP_Y_CR_CB_GH2V2: + case MDP_Y_CB_CR_H2V2: + case MDP_Y_CRCB_H1V1: + case MDP_Y_CBCR_H1V1: + case MDP_YCRCB_H1V1: + case MDP_YCBCR_H1V1: + return OVERLAY_TYPE_VIDEO; + case MDP_RGB_BORDERFILL: + return OVERLAY_TYPE_BF; + default: + mdp4_stat.err_format++; + return -ERANGE; + } + +} + +#define C3_ALPHA 3 /* alpha */ +#define C2_R_Cr 2 /* R/Cr */ +#define C1_B_Cb 1 /* B/Cb */ +#define C0_G_Y 0 /* G/luma */ +#define YUV_444_MAX_WIDTH 1280 /* Max width for YUV 444*/ + +int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe) +{ + switch (pipe->src_format) { + case MDP_RGB_565: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; + pipe->r_bit = 1; /* R, 5 bits */ + pipe->b_bit = 1; /* B, 5 bits */ + pipe->g_bit = 2; /* G, 6 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 2; + pipe->element2 = C2_R_Cr; /* R */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_RGB_888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 2; + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->bpp = 3; /* 3 bpp */ + break; + case MDP_BGR_565: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; + pipe->r_bit = 1; /* R, 5 bits */ + pipe->b_bit = 1; /* B, 5 bits */ + pipe->g_bit = 2; /* G, 6 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 2; + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_XRGB_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C1_B_Cb; /* B */ + pipe->element2 = C0_G_Y; /* G */ + pipe->element1 = C2_R_Cr; /* R */ + pipe->element0 = C3_ALPHA; /* alpha */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_ARGB_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 1; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C1_B_Cb; /* B */ + pipe->element2 = C0_G_Y; /* G */ + pipe->element1 = C2_R_Cr; /* R */ + pipe->element0 = C3_ALPHA; /* alpha */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_RGBA_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 1; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C3_ALPHA; /* alpha */ + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_RGBX_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C3_ALPHA; /* alpha */ + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_BGRA_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 1; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C3_ALPHA; /* alpha */ + pipe->element2 = C2_R_Cr; /* R */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_YCRYCB_H2V1: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C0_G_Y; /* G */ + pipe->element2 = C2_R_Cr; /* R */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->bpp = 2; /* 2 bpp */ + pipe->chroma_sample = MDP4_CHROMA_H2V1; + break; + case MDP_Y_CRCB_H2V1: + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H1V1: + case MDP_Y_CBCR_H1V1: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_PSEUDO_PLANAR; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 1; /* 2 */ + if (pipe->src_format == MDP_Y_CRCB_H2V1) { + pipe->element1 = C1_B_Cb; + pipe->element0 = C2_R_Cr; + pipe->chroma_sample = MDP4_CHROMA_H2V1; + } else if (pipe->src_format == MDP_Y_CRCB_H1V1) { + pipe->element1 = C1_B_Cb; + pipe->element0 = C2_R_Cr; + if (pipe->src_width > YUV_444_MAX_WIDTH) + pipe->chroma_sample = MDP4_CHROMA_H1V2; + else + pipe->chroma_sample = MDP4_CHROMA_RGB; + } else if (pipe->src_format == MDP_Y_CBCR_H2V1) { + pipe->element1 = C2_R_Cr; + pipe->element0 = C1_B_Cb; + pipe->chroma_sample = MDP4_CHROMA_H2V1; + } else if (pipe->src_format == MDP_Y_CBCR_H1V1) { + pipe->element1 = C2_R_Cr; + pipe->element0 = C1_B_Cb; + if (pipe->src_width > YUV_444_MAX_WIDTH) + pipe->chroma_sample = MDP4_CHROMA_H1V2; + else + pipe->chroma_sample = MDP4_CHROMA_RGB; + } else if (pipe->src_format == MDP_Y_CRCB_H2V2) { + pipe->element1 = C1_B_Cb; + pipe->element0 = C2_R_Cr; + pipe->chroma_sample = MDP4_CHROMA_420; + } else if (pipe->src_format == MDP_Y_CBCR_H2V2) { + pipe->element1 = C2_R_Cr; + pipe->element0 = C1_B_Cb; + pipe->chroma_sample = MDP4_CHROMA_420; + } + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_Y_CBCR_H2V2_TILE: + case MDP_Y_CRCB_H2V2_TILE: + pipe->frame_format = MDP4_FRAME_FORMAT_VIDEO_SUPERTILE; + pipe->fetch_plane = OVERLAY_PLANE_PSEUDO_PLANAR; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 1; /* 2 */ + if (pipe->src_format == MDP_Y_CRCB_H2V2_TILE) { + pipe->element1 = C1_B_Cb; /* B */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->chroma_sample = MDP4_CHROMA_420; + } else if (pipe->src_format == MDP_Y_CBCR_H2V2_TILE) { + pipe->element1 = C2_R_Cr; /* R */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->chroma_sample = MDP4_CHROMA_420; + } + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_Y_CR_CB_H2V2: + case MDP_Y_CR_CB_GH2V2: + case MDP_Y_CB_CR_H2V2: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_PLANAR; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->chroma_sample = MDP4_CHROMA_420; + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_YCBCR_H1V1: + case MDP_YCRCB_H1V1: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 2; + pipe->element0 = C0_G_Y; /* G */ + if (pipe->src_format == MDP_YCRCB_H1V1) { + pipe->element1 = C2_R_Cr; /* R */ + pipe->element2 = C1_B_Cb; /* B */ + } else { + pipe->element1 = C1_B_Cb; /* B */ + pipe->element2 = C2_R_Cr; /* R */ + } + pipe->bpp = 3; /* 3 bpp */ + case MDP_RGB_BORDERFILL: + pipe->alpha_enable = 0; + pipe->alpha = 0; + break; + default: + /* not likely */ + mdp4_stat.err_format++; + return -ERANGE; + } + + return 0; +} + +/* + * color_key_convert: output with 12 bits color key + */ +static uint32 color_key_convert(int start, int num, uint32 color) +{ + uint32 data; + + data = (color >> start) & ((1 << num) - 1); + + /* convert to 8 bits */ + if (num == 5) + data = ((data << 3) | (data >> 2)); + else if (num == 6) + data = ((data << 2) | (data >> 4)); + + /* convert 8 bits to 12 bits */ + data = (data << 4) | (data >> 4); + + return data; +} + +void transp_color_key(int format, uint32 transp, + uint32 *c0, uint32 *c1, uint32 *c2) +{ + int b_start, g_start, r_start; + int b_num, g_num, r_num; + + switch (format) { + case MDP_RGB_565: + b_start = 0; + g_start = 5; + r_start = 11; + r_num = 5; + g_num = 6; + b_num = 5; + break; + case MDP_RGB_888: + case MDP_XRGB_8888: + case MDP_ARGB_8888: + case MDP_BGRA_8888: + b_start = 0; + g_start = 8; + r_start = 16; + r_num = 8; + g_num = 8; + b_num = 8; + break; + case MDP_RGBA_8888: + case MDP_RGBX_8888: + b_start = 16; + g_start = 8; + r_start = 0; + r_num = 8; + g_num = 8; + b_num = 8; + break; + case MDP_BGR_565: + b_start = 11; + g_start = 5; + r_start = 0; + r_num = 5; + g_num = 6; + b_num = 5; + break; + case MDP_Y_CB_CR_H2V2: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V1: + case MDP_YCBCR_H1V1: + b_start = 8; + g_start = 16; + r_start = 0; + r_num = 8; + g_num = 8; + b_num = 8; + break; + case MDP_Y_CR_CB_H2V2: + case MDP_Y_CR_CB_GH2V2: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CRCB_H2V1: + case MDP_Y_CRCB_H1V1: + case MDP_Y_CBCR_H1V1: + case MDP_YCRCB_H1V1: + b_start = 0; + g_start = 16; + r_start = 8; + r_num = 8; + g_num = 8; + b_num = 8; + break; + default: + b_start = 0; + g_start = 8; + r_start = 16; + r_num = 8; + g_num = 8; + b_num = 8; + break; + } + + *c0 = color_key_convert(g_start, g_num, transp); + *c1 = color_key_convert(b_start, b_num, transp); + *c2 = color_key_convert(r_start, r_num, transp); +} + +uint32 mdp4_overlay_format(struct mdp4_overlay_pipe *pipe) +{ + uint32 format; + + format = 0; + + if (pipe->solid_fill) + format |= MDP4_FORMAT_SOLID_FILL; + + if (pipe->unpack_align_msb) + format |= MDP4_FORMAT_UNPACK_ALIGN_MSB; + + if (pipe->unpack_tight) + format |= MDP4_FORMAT_UNPACK_TIGHT; + + if (pipe->alpha_enable) + format |= MDP4_FORMAT_ALPHA_ENABLE; + + if (pipe->flags & MDP_SOURCE_ROTATED_90) + format |= MDP4_FORMAT_90_ROTATED; + format |= (pipe->unpack_count << 13); + format |= ((pipe->bpp - 1) << 9); + format |= (pipe->a_bit << 6); + format |= (pipe->r_bit << 4); + format |= (pipe->b_bit << 2); + format |= pipe->g_bit; + + format |= (pipe->frame_format << 29); + + if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR || + pipe->fetch_plane == OVERLAY_PLANE_PLANAR) { + /* video/graphic */ + format |= (pipe->fetch_plane << 19); + format |= (pipe->chroma_site << 28); + format |= (pipe->chroma_sample << 26); + } + + return format; +} + +uint32 mdp4_overlay_unpack_pattern(struct mdp4_overlay_pipe *pipe) +{ + return (pipe->element3 << 24) | (pipe->element2 << 16) | + (pipe->element1 << 8) | pipe->element0; +} + +/* + * mdp4_overlayproc_cfg: only be called from base layer + */ +void mdp4_overlayproc_cfg(struct mdp4_overlay_pipe *pipe) +{ + uint32 data, intf; + char *overlay_base; + uint32 curr; + + intf = 0; + if (pipe->mixer_num == MDP4_MIXER2) + overlay_base = MDP_BASE + MDP4_OVERLAYPROC2_BASE; + else if (pipe->mixer_num == MDP4_MIXER1) { + overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */ + intf = inpdw(MDP_BASE + 0x0038); /* MDP_DISP_INTF_SEL */ + intf >>= 4; + intf &= 0x03; + } else + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + + if (mdp_is_in_isr == FALSE) + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* + * BLT support both primary and external external + */ + if (pipe->blt_addr) { + int off, bpp; +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + data = pipe->src_height; + data <<= 16; + data |= pipe->src_width; + outpdw(overlay_base + 0x0008, data); /* ROI, height + width */ + if (pipe->mixer_num == MDP4_MIXER0 || + pipe->mixer_num == MDP4_MIXER1) { + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + + outpdw(overlay_base + 0x000c, pipe->blt_addr + off); + /* overlay ouput is RGB888 */ + outpdw(overlay_base + 0x0010, pipe->src_width * bpp); + outpdw(overlay_base + 0x001c, pipe->blt_addr + off); + /* MDDI - BLT + on demand */ + outpdw(overlay_base + 0x0004, 0x08); + + curr = inpdw(overlay_base + 0x0014); + curr &= 0x4; +#ifdef BLT_RGB565 + outpdw(overlay_base + 0x0014, curr | 0x1); /* RGB565 */ +#else + outpdw(overlay_base + 0x0014, curr | 0x0); /* RGB888 */ +#endif + } else if (pipe->mixer_num == MDP4_MIXER2) { + if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) { + off = 0; + bpp = 1; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * + pipe->src_width * bpp; + + outpdw(overlay_base + 0x000c, + pipe->blt_addr + off); + /* overlay ouput is RGB888 */ + outpdw(overlay_base + 0x0010, + ((pipe->src_width << 16) | + pipe->src_width)); + outpdw(overlay_base + 0x001c, + pipe->blt_addr + off); + off = pipe->src_height * pipe->src_width; + /* align chroma to 2k address */ + off = (off + 2047) & ~2047; + /* UV plane adress */ + outpdw(overlay_base + 0x0020, + pipe->blt_addr + off); + /* MDDI - BLT + on demand */ + outpdw(overlay_base + 0x0004, 0x08); + /* pseudo planar + writeback */ + curr = inpdw(overlay_base + 0x0014); + curr &= 0x4; + outpdw(overlay_base + 0x0014, curr | 0x012); + /* rgb->yuv */ + outpdw(overlay_base + 0x0200, 0x05); + } + } + } else { + data = pipe->src_height; + data <<= 16; + data |= pipe->src_width; + outpdw(overlay_base + 0x0008, data); /* ROI, height + width */ + outpdw(overlay_base + 0x000c, pipe->srcp0_addr); + outpdw(overlay_base + 0x0010, pipe->srcp0_ystride); + outpdw(overlay_base + 0x0004, 0x01); /* directout */ + } + + if (pipe->mixer_num == MDP4_MIXER1) { + if (intf == TV_INTF) { + curr = inpdw(overlay_base + 0x0014); + curr &= 0x4; + outpdw(overlay_base + 0x0014, 0x02); /* yuv422 */ + /* overlay1 CSC config */ + outpdw(overlay_base + 0x0200, 0x05); /* rgb->yuv */ + } + } + +#ifdef MDP4_IGC_LUT_ENABLE + curr = inpdw(overlay_base + 0x0014); + curr &= ~0x4; + outpdw(overlay_base + 0x0014, curr | 0x4); /* GC_LUT_EN, 888 */ +#endif + + if (mdp_is_in_isr == FALSE) + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +int mdp4_overlay_pipe_staged(int mixer) +{ + uint32 data, mask, i, off; + int p1, p2; + + if (mixer == MDP4_MIXER2) + off = 0x100F0; + else + off = 0x10100; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + data = inpdw(MDP_BASE + off); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + p1 = 0; + p2 = 0; + for (i = 0; i < 8; i++) { + mask = data & 0x0f; + if (mask) { + if (mask <= 4) + p1++; + else + p2++; + } + data >>= 4; + } + + if (mixer) + return p2; + else + return p1; +} + +int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info) +{ + + int ndx, cnt; + struct mdp4_overlay_pipe *pipe; + + if (mixer_num > MDP4_MIXER_MAX) + return -ENODEV; + + cnt = 0; + ndx = MDP4_MIXER_STAGE_BASE; + for ( ; ndx < MDP4_MIXER_STAGE_MAX; ndx++) { + pipe = ctrl->stage[mixer_num][ndx]; + if (pipe == NULL) + continue; + info->z_order = pipe->mixer_stage - MDP4_MIXER_STAGE0; + /* z_order == -1, means base layer */ + info->ptype = pipe->pipe_type; + info->pnum = pipe->pipe_num; + info->pndx = pipe->pipe_ndx; + info->mixer_num = pipe->mixer_num; + info++; + cnt++; + } + return cnt; +} + +static void mdp4_mixer_stage_commit(int mixer) +{ + struct mdp4_overlay_pipe *pipe; + int i, num; + u32 data, stage; + int off; + unsigned long flags; + + data = 0; + for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) { + pipe = ctrl->stage[mixer][i]; + if (pipe == NULL) + continue; + pr_debug("%s: mixer=%d ndx=%d stage=%d\n", __func__, + mixer, pipe->pipe_ndx, i); + stage = pipe->mixer_stage; + if (mixer >= MDP4_MIXER1) + stage += 8; + stage <<= (4 * pipe->pipe_num); + data |= stage; + } + + mdp4_mixer_blend_setup(mixer); + + off = 0; + if (data != ctrl->mixer_cfg[mixer]) { + ctrl->mixer_cfg[mixer] = data; + if (mixer >= MDP4_MIXER2) { + /* MDP_LAYERMIXER2_IN_CFG */ + off = 0x100f0; + } else { + /* mixer 0 or 1 */ + num = mixer + 1; + num &= 0x01; + data |= ctrl->mixer_cfg[num]; + off = 0x10100; + } + pr_debug("%s: mixer=%d data=%x flush=%x\n", __func__, + mixer, data, ctrl->flush[mixer]); + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + local_irq_save(flags); + if (off) + outpdw(MDP_BASE + off, data); + + if (ctrl->flush[mixer]) { + outpdw(MDP_BASE + 0x18000, ctrl->flush[mixer]); + ctrl->flush[mixer] = 0; + } + local_irq_restore(flags); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + + +void mdp4_mixer_stage_up(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_overlay_pipe *pp; + int i, mixer; + + mixer = pipe->mixer_num; + + for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) { + pp = ctrl->stage[mixer][i]; + if (pp == pipe) { + ctrl->stage[mixer][i] = NULL; + break; + } + } + + ctrl->stage[mixer][pipe->mixer_stage] = pipe; /* keep it */ + + if (!(pipe->flags & MDP_OV_PLAY_NOWAIT)) { + pr_debug("%s: mixer=%d ndx=%d stage=%d flags=%x\n", + __func__, mixer, pipe->pipe_ndx, + pipe->mixer_stage, pipe->flags); + mdp4_mixer_stage_commit(mixer); + } +} + +void mdp4_mixer_stage_down(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_overlay_pipe *pp; + int i, mixer; + + mixer = pipe->mixer_num; + + for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) { + pp = ctrl->stage[mixer][i]; + if (pp == pipe) + ctrl->stage[mixer][i] = NULL; /* clear it */ + } + + if (!(pipe->flags & MDP_OV_PLAY_NOWAIT)) { + pr_debug("%s: mixer=%d ndx=%d stage=%d flags=%x\n", + __func__, pipe->mixer_num, pipe->pipe_ndx, + pipe->mixer_stage, pipe->flags); + mdp4_mixer_stage_commit(pipe->mixer_num); + } +} +/* + * mixer0: rgb3: border color at register 0x15004, 0x15008 + * mixer1: vg3: border color at register 0x1D004, 0x1D008 + * mixer2: xxx: border color at register 0x8D004, 0x8D008 + */ +void mdp4_overlay_borderfill_stage_up(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_overlay_pipe *bspipe; + int ptype, pnum, pndx, mixer; + int format, alpha_enable, alpha; + + if (pipe->pipe_type != OVERLAY_TYPE_BF) + return; + + mixer = pipe->mixer_num; + + if (ctrl->baselayer[mixer]) + return; + + bspipe = ctrl->stage[mixer][MDP4_MIXER_STAGE_BASE]; + + /* save original base layer */ + ctrl->baselayer[mixer] = bspipe; + + pipe->alpha = 0; /* make sure bf pipe has alpha 0 */ + ptype = pipe->pipe_type; + pnum = pipe->pipe_num; + pndx = pipe->pipe_ndx; + format = pipe->src_format; + alpha_enable = pipe->alpha_enable; + alpha = pipe->alpha; + *pipe = *bspipe; /* keep base layer configuration */ + pipe->pipe_type = ptype; + pipe->pipe_num = pnum; + pipe->pipe_ndx = pndx; + pipe->src_format = format; + pipe->alpha_enable = alpha_enable; + pipe->alpha = alpha; + + /* free original base layer pipe to be sued as normal pipe */ + bspipe->pipe_used = 0; + + if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) + mdp4_dsi_video_base_swap(pipe); + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_lcdc_base_swap(pipe); + else if (ctrl->panel_mode & MDP4_PANEL_DTV) + mdp4_dtv_base_swap(pipe); + + mdp4_overlay_reg_flush(bspipe, 1); + /* borderfill pipe as base layer */ + mdp4_mixer_stage_up(pipe); +} + +void mdp4_overlay_borderfill_stage_down(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_overlay_pipe *bspipe; + int ptype, pnum, pndx, mixer; + int format, alpha_enable, alpha; + + if (pipe->pipe_type != OVERLAY_TYPE_BF) + return; + + mixer = pipe->mixer_num; + + /* retrieve original base layer */ + bspipe = ctrl->baselayer[mixer]; + if (bspipe == NULL) { + pr_err("%s: no base layer at mixer=%d\n", + __func__, mixer); + return; + } + + ptype = bspipe->pipe_type; + pnum = bspipe->pipe_num; + pndx = bspipe->pipe_ndx; + format = bspipe->src_format; + alpha_enable = bspipe->alpha_enable; + alpha = bspipe->alpha; + *bspipe = *pipe; /* restore base layer configuration */ + bspipe->pipe_type = ptype; + bspipe->pipe_num = pnum; + bspipe->pipe_ndx = pndx; + bspipe->src_format = format; + bspipe->alpha_enable = alpha_enable; + bspipe->alpha = alpha; + + bspipe->pipe_used++; /* mark base layer pipe used */ + + ctrl->baselayer[mixer] = NULL; + + /* free borderfill pipe */ + pipe->pipe_used = 0; + + mdp4_dsi_video_base_swap(bspipe); + + /* free borderfill pipe */ + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_down(pipe); + mdp4_overlay_pipe_free(pipe); + + /* stage up base layer */ + mdp4_overlay_reg_flush(bspipe, 1); + /* restore original base layer */ + mdp4_mixer_stage_up(bspipe); +} + + +static struct mdp4_overlay_pipe *mdp4_background_layer(int mixer, + struct mdp4_overlay_pipe *sp) +{ + struct mdp4_overlay_pipe *pp; + struct mdp4_overlay_pipe *kp; + int i; + + kp = ctrl->stage[mixer][MDP4_MIXER_STAGE_BASE]; + for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) { + pp = ctrl->stage[mixer][i]; + if (pp == NULL) + continue; + if (pp == sp) + break; + + if ((pp->dst_x <= sp->dst_x) && + ((pp->dst_x + pp->dst_w) >= (sp->dst_x + sp->dst_w))) { + if ((pp->dst_y <= sp->dst_y) && + ((pp->dst_y + pp->dst_h) >= + (sp->dst_y + sp->dst_h))) { + kp = pp; + } + } + } + return kp; +} + +static void mdp4_overlay_bg_solidfill(struct blend_cfg *blend) +{ + struct mdp4_overlay_pipe *pipe; + char *base; + u32 op_mode, format; + int pnum, ptype; + + pipe = blend->solidfill_pipe; + if (pipe == NULL) + return; + + if (pipe->pipe_type == OVERLAY_TYPE_BF) + return; + + ptype = mdp4_overlay_format2type(pipe->src_format); + if (ptype == OVERLAY_TYPE_RGB) { + pnum = pipe->pipe_num - OVERLAY_PIPE_RGB1; + base = MDP_BASE + MDP4_RGB_BASE; + base += MDP4_RGB_OFF * pnum; + } else { + pnum = pipe->pipe_num - OVERLAY_PIPE_VG1; + base = MDP_BASE + MDP4_VIDEO_BASE; + base += MDP4_VIDEO_OFF * pnum; + } + + format = inpdw(base + 0x50); + if (blend->solidfill) { + format |= MDP4_FORMAT_SOLID_FILL; + /* + * If solid fill is enabled, flip and scale + * have to be disabled. otherwise, h/w + * underruns. + */ + op_mode = inpdw(base + 0x0058); + op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN); + op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN); + outpdw(base + 0x0058, op_mode); + outpdw(base + 0x1008, 0); /* black */ + } else { + format &= ~MDP4_FORMAT_SOLID_FILL; + blend->solidfill_pipe = NULL; + } + + outpdw(base + 0x50, format); + + mdp4_overlay_reg_flush(pipe, 0); +} + +/* + * D(i+1) = Ks * S + Kd * D(i) + */ +void mdp4_mixer_blend_setup(int mixer) +{ + struct mdp4_overlay_pipe *d_pipe; + struct mdp4_overlay_pipe *s_pipe; + struct blend_cfg *blend; + int i, off, ptype; + int d_alpha, s_alpha; + unsigned char *overlay_base; + uint32 c0, c1, c2; + + + d_pipe = ctrl->stage[mixer][MDP4_MIXER_STAGE_BASE]; + if (d_pipe == NULL) { + pr_err("%s: Error: no bg_pipe at mixer=%d\n", __func__, mixer); + return; + } + + blend = &ctrl->blend[mixer][MDP4_MIXER_STAGE0]; + for (i = MDP4_MIXER_STAGE0; i < MDP4_MIXER_STAGE_MAX; i++) { + blend->solidfill = 0; + blend->op = (MDP4_BLEND_FG_ALPHA_FG_CONST | + MDP4_BLEND_BG_ALPHA_BG_CONST); + s_pipe = ctrl->stage[mixer][i]; + if (s_pipe == NULL) { + blend++; + d_pipe = NULL; + d_alpha = 0; + continue; + } + d_pipe = mdp4_background_layer(mixer, s_pipe); + d_alpha = d_pipe->alpha_enable; + s_alpha = s_pipe->alpha_enable; + pr_debug("%s: stage=%d: bg: ndx=%d da=%d dalpha=%x " + "fg: ndx=%d sa=%d salpha=%x is_fg=%d\n", + __func__, i-2, d_pipe->pipe_ndx, d_alpha, d_pipe->alpha, + s_pipe->pipe_ndx, s_alpha, s_pipe->alpha, s_pipe->is_fg); + + /* base on fg's alpha */ + blend->bg_alpha = 0x0ff - s_pipe->alpha; + blend->fg_alpha = s_pipe->alpha; + blend->co3_sel = 1; /* use fg alpha */ + + if (s_pipe->is_fg) { + if (s_pipe->alpha == 0xff) { + blend->solidfill = 1; + blend->solidfill_pipe = d_pipe; + } + } else if (s_alpha) { + blend->op = (MDP4_BLEND_BG_ALPHA_FG_PIXEL | + MDP4_BLEND_BG_INV_ALPHA); + } else if (d_alpha) { + ptype = mdp4_overlay_format2type(s_pipe->src_format); + if (ptype == OVERLAY_TYPE_VIDEO) { + blend->op = (MDP4_BLEND_BG_ALPHA_BG_PIXEL | + MDP4_BLEND_FG_ALPHA_BG_PIXEL | + MDP4_BLEND_FG_INV_ALPHA); + blend->co3_sel = 0; /* use bg alpha */ + } else { + /* s_pipe is rgb without alpha */ + blend->op = (MDP4_BLEND_FG_ALPHA_FG_CONST | + MDP4_BLEND_BG_ALPHA_BG_CONST); + blend->bg_alpha = 0; + } + } + + if (s_pipe->transp != MDP_TRANSP_NOP) { + if (s_pipe->is_fg) { + transp_color_key(s_pipe->src_format, + s_pipe->transp, &c0, &c1, &c2); + /* Fg blocked */ + blend->op |= MDP4_BLEND_FG_TRANSP_EN; + /* lower limit */ + blend->transp_low0 = (c1 << 16 | c0); + blend->transp_low1 = c2; + /* upper limit */ + blend->transp_high0 = (c1 << 16 | c0); + blend->transp_high1 = c2; + } else { + transp_color_key(d_pipe->src_format, + s_pipe->transp, &c0, &c1, &c2); + /* Fg blocked */ + blend->op |= MDP4_BLEND_BG_TRANSP_EN; + blend--; /* one stage back */ + /* lower limit */ + blend->transp_low0 = (c1 << 16 | c0); + blend->transp_low1 = c2; + /* upper limit */ + blend->transp_high0 = (c1 << 16 | c0); + blend->transp_high1 = c2; + blend++; /* back to original stage */ + } + } + blend++; + } + + /* mixer numer, /dev/fb0, /dev/fb1, /dev/fb2 */ + if (mixer == MDP4_MIXER2) + overlay_base = MDP_BASE + MDP4_OVERLAYPROC2_BASE;/* 0x88000 */ + else if (mixer == MDP4_MIXER1) + overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */ + else + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + blend = &ctrl->blend[mixer][MDP4_MIXER_STAGE_BASE]; + /* lower limit */ + outpdw(overlay_base + 0x180, blend->transp_low0); + outpdw(overlay_base + 0x184, blend->transp_low1); + /* upper limit */ + outpdw(overlay_base + 0x188, blend->transp_high0); + outpdw(overlay_base + 0x18c, blend->transp_high1); + blend++; /* stage0 */ + for (i = MDP4_MIXER_STAGE0; i < MDP4_MIXER_STAGE_MAX; i++) { + off = 20 * i; + off = 0x20 * (i - MDP4_MIXER_STAGE0); + if (i == MDP4_MIXER_STAGE3) + off -= 4; + + if (blend->solidfill_pipe) + mdp4_overlay_bg_solidfill(blend); + + outpdw(overlay_base + off + 0x108, blend->fg_alpha); + outpdw(overlay_base + off + 0x10c, blend->bg_alpha); + outpdw(overlay_base + off + 0x104, blend->op); + outpdw(overlay_base + (off << 5) + 0x1004, blend->co3_sel); + outpdw(overlay_base + off + 0x110, blend->transp_low0);/* low */ + outpdw(overlay_base + off + 0x114, blend->transp_low1);/* low */ + /* upper limit */ + outpdw(overlay_base + off + 0x118, blend->transp_high0); + outpdw(overlay_base + off + 0x11c, blend->transp_high1); + blend++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_overlay_reg_flush(struct mdp4_overlay_pipe *pipe, int all) +{ + int mixer; + uint32 *reg; + + mixer = pipe->mixer_num; + reg = &ctrl->flush[mixer]; + *reg |= (1 << (2 + pipe->pipe_num)); + + if (all) { + if (mixer == MDP4_MIXER0) + *reg |= 0x01; + else + *reg |= 0x02; + } +} + +void mdp4_overlay_flush_piggyback(int m0, int m1) +{ + u32 data; + + data = ctrl->flush[m0] | ctrl->flush[m1]; + ctrl->flush[m0] = data; +} + +void mdp4_overlay_reg_flush_reset(struct mdp4_overlay_pipe *pipe) +{ + int mixer; + + mixer = pipe->mixer_num; + ctrl->flush[mixer] = 0; +} + +struct mdp4_overlay_pipe *mdp4_overlay_stage_pipe(int mixer, int stage) +{ + return ctrl->stage[mixer][stage]; +} + +struct mdp4_overlay_pipe *mdp4_overlay_ndx2pipe(int ndx) +{ + struct mdp4_overlay_pipe *pipe; + + if (ndx <= 0 || ndx > OVERLAY_PIPE_MAX) + return NULL; + + pipe = &ctrl->plist[ndx - 1]; /* ndx start from 1 */ + + if (pipe->pipe_used == 0) + return NULL; + + return pipe; +} + +struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(int ptype, int mixer) +{ + int i; + struct mdp4_overlay_pipe *pipe; + + if (ptype == OVERLAY_TYPE_BF) { + if (!mdp4_overlay_borderfill_supported()) + return NULL; + } + + for (i = 0; i < OVERLAY_PIPE_MAX; i++) { + pipe = &ctrl->plist[i]; + if ((pipe->pipe_used == 0) && ((pipe->pipe_type == ptype) || + (ptype == OVERLAY_TYPE_RGB && + pipe->pipe_type == OVERLAY_TYPE_VIDEO))) { + if (ptype == OVERLAY_TYPE_BF && + mixer != pipe->mixer_num) + continue; + init_completion(&pipe->comp); + init_completion(&pipe->dmas_comp); + pr_debug("%s: pipe=%x ndx=%d num=%d\n", __func__, + (int)pipe, pipe->pipe_ndx, pipe->pipe_num); + return pipe; + } + } + + pr_err("%s: ptype=%d FAILED\n", __func__, ptype); + + return NULL; +} + + +void mdp4_overlay_pipe_free(struct mdp4_overlay_pipe *pipe) +{ + uint32 ptype, num, ndx, mixer; + struct mdp4_iommu_pipe_info *iom_pipe_info; + + pr_debug("%s: pipe=%x ndx=%d\n", __func__, (int)pipe, pipe->pipe_ndx); + + ptype = pipe->pipe_type; + num = pipe->pipe_num; + ndx = pipe->pipe_ndx; + mixer = pipe->mixer_num; + iom_pipe_info = &mdp_iommu[pipe->mixer_num][pipe->pipe_ndx - 1]; + iom_pipe_info->mark_unmap = 1; + + memset(pipe, 0, sizeof(*pipe)); + + pipe->pipe_type = ptype; + pipe->pipe_num = num; + pipe->pipe_ndx = ndx; + pipe->mixer_num = mixer; +} + +static int mdp4_overlay_validate_downscale(struct mdp_overlay *req, + struct msm_fb_data_type *mfd, uint32 perf_level, uint32 pclk_rate) +{ + __u32 panel_clk_khz, mdp_clk_khz; + __u32 num_hsync_pix_clks, mdp_clks_per_hsync, src_wh; + __u32 hsync_period_ps, mdp_period_ps, total_hsync_period_ps; + unsigned long fill_rate_y_dir, fill_rate_x_dir; + unsigned long fillratex100, mdp_pixels_produced; + unsigned long mdp_clk_hz; + + pr_debug("%s: LCDC Mode Downscale validation with MDP Core" + " Clk rate\n", __func__); + pr_debug("src_w %u, src_h %u, dst_w %u, dst_h %u\n", + req->src_rect.w, req->src_rect.h, req->dst_rect.w, + req->dst_rect.h); + + + panel_clk_khz = pclk_rate/1000; + mdp_clk_hz = mdp_perf_level2clk_rate(perf_level); + + if (!mdp_clk_hz || !req->dst_rect.w || !req->dst_rect.h) { + pr_debug("mdp_perf_level2clk_rate returned 0," + "or dst_rect height/width is 0," + "Downscale Validation incomplete\n"); + return 0; + } + + mdp_clk_khz = mdp_clk_hz/1000; + + num_hsync_pix_clks = mfd->panel_info.lcdc.h_back_porch + + mfd->panel_info.lcdc.h_front_porch + + mfd->panel_info.lcdc.h_pulse_width + + mfd->panel_info.xres; + + hsync_period_ps = 1000000000/panel_clk_khz; + mdp_period_ps = 1000000000/mdp_clk_khz; + + total_hsync_period_ps = num_hsync_pix_clks * hsync_period_ps; + mdp_clks_per_hsync = total_hsync_period_ps/mdp_period_ps; + + pr_debug("hsync_period_ps %u, mdp_period_ps %u," + "total_hsync_period_ps %u\n", hsync_period_ps, + mdp_period_ps, total_hsync_period_ps); + + src_wh = req->src_rect.w * req->src_rect.h; + if (src_wh % req->dst_rect.h) + fill_rate_y_dir = (src_wh / req->dst_rect.h) + 1; + else + fill_rate_y_dir = (src_wh / req->dst_rect.h); + + fill_rate_x_dir = (mfd->panel_info.xres - req->dst_rect.w) + + req->src_rect.w; + + if (fill_rate_y_dir >= fill_rate_x_dir) + fillratex100 = 100 * fill_rate_y_dir / mfd->panel_info.xres; + else + fillratex100 = 100 * fill_rate_x_dir / mfd->panel_info.xres; + + pr_debug("mdp_clks_per_hsync %u, fill_rate_y_dir %lu," + "fill_rate_x_dir %lu\n", mdp_clks_per_hsync, + fill_rate_y_dir, fill_rate_x_dir); + + mdp_pixels_produced = 100 * mdp_clks_per_hsync/fillratex100; + pr_debug("fillratex100 %lu, mdp_pixels_produced %lu\n", + fillratex100, mdp_pixels_produced); + if (mdp_pixels_produced <= mfd->panel_info.xres) { + mdp4_stat.err_underflow++; + return -ERANGE; + } + + return 0; +} + +static int mdp4_overlay_req2pipe(struct mdp_overlay *req, int mixer, + struct mdp4_overlay_pipe **ppipe, + struct msm_fb_data_type *mfd) +{ + struct mdp4_overlay_pipe *pipe; + struct mdp4_iommu_pipe_info *iom_pipe_info; + int ret, ptype; + + u32 upscale_max; + upscale_max = (mdp_rev >= MDP_REV_41) ? + MDP4_REV41_OR_LATER_UP_SCALING_MAX : + MDP4_REV40_UP_SCALING_MAX; + + if (mfd == NULL) { + pr_err("%s: mfd == NULL, -ENODEV\n", __func__); + return -ENODEV; + } + + if (mixer >= MDP4_MIXER_MAX) { + pr_err("%s: mixer out of range!\n", __func__); + mdp4_stat.err_mixer++; + return -ERANGE; + } + + if (req->z_order < 0 || req->z_order > 3) { + pr_err("%s: z_order=%d out of range!\n", __func__, + req->z_order); + mdp4_stat.err_zorder++; + return -ERANGE; + } + + if (req->src_rect.h == 0 || req->src_rect.w == 0) { + pr_err("%s: src img of zero size!\n", __func__); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->dst_rect.h > (req->src_rect.h * upscale_max)) { + mdp4_stat.err_scale++; + pr_err("%s: scale up, too much (h)!\n", __func__); + return -ERANGE; + } + + if (req->src_rect.h > (req->dst_rect.h * 8)) { /* too little */ + mdp4_stat.err_scale++; + pr_err("%s: scale down, too little (h)!\n", __func__); + return -ERANGE; + } + + if (req->dst_rect.w > (req->src_rect.w * upscale_max)) { + mdp4_stat.err_scale++; + pr_err("%s: scale up, too much (w)!\n", __func__); + return -ERANGE; + } + + if (req->src_rect.w > (req->dst_rect.w * 8)) { /* too little */ + mdp4_stat.err_scale++; + pr_err("%s: scale down, too little (w)!\n", __func__); + return -ERANGE; + } + + if (mdp_hw_revision == MDP4_REVISION_V1) { + /* non integer down saceling ratio smaller than 1/4 + * is not supportted + */ + if (req->src_rect.h > (req->dst_rect.h * 4)) { + if (req->src_rect.h % req->dst_rect.h) { + mdp4_stat.err_scale++; + pr_err("%s: need integer (h)!\n", __func__); + return -ERANGE; + } + } + + if (req->src_rect.w > (req->dst_rect.w * 4)) { + if (req->src_rect.w % req->dst_rect.w) { + mdp4_stat.err_scale++; + pr_err("%s: need integer (w)!\n", __func__); + return -ERANGE; + } + } + } + + if (((req->src_rect.x + req->src_rect.w) > req->src.width) || + ((req->src_rect.y + req->src_rect.h) > req->src.height)) { + mdp4_stat.err_size++; + pr_err("%s invalid src rectangle\n", __func__); + return -ERANGE; + } + + if (ctrl->panel_3d != MDP4_3D_SIDE_BY_SIDE) { + int xres; + int yres; + + xres = mfd->panel_info.xres; + yres = mfd->panel_info.yres; + + if (((req->dst_rect.x + req->dst_rect.w) > xres) || + ((req->dst_rect.y + req->dst_rect.h) > yres)) { + mdp4_stat.err_size++; + pr_err("%s invalid dst rectangle\n", __func__); + return -ERANGE; + } + } + + ptype = mdp4_overlay_format2type(req->src.format); + if (ptype < 0) { + pr_err("%s: mdp4_overlay_format2type!\n", __func__); + return ptype; + } + + if (req->flags & MDP_OV_PIPE_SHARE) + ptype = OVERLAY_TYPE_VIDEO; /* VG pipe supports both RGB+YUV */ + + if (req->id == MSMFB_NEW_REQUEST) /* new request */ + pipe = mdp4_overlay_pipe_alloc(ptype, mixer); + else + pipe = mdp4_overlay_ndx2pipe(req->id); + + if (pipe == NULL) { + pr_err("%s: pipe == NULL!\n", __func__); + return -ENOMEM; + } + + if (!display_iclient && !IS_ERR_OR_NULL(mfd->iclient)) { + display_iclient = mfd->iclient; + pr_debug("%s(): display_iclient %p\n", __func__, + display_iclient); + } + + iom_pipe_info = &mdp_iommu[pipe->mixer_num][pipe->pipe_ndx - 1]; + iom_pipe_info->mark_unmap = 0; + + pipe->src_format = req->src.format; + ret = mdp4_overlay_format2pipe(pipe); + + if (ret < 0) { + pr_err("%s: mdp4_overlay_format2pipe!\n", __func__); + return ret; + } + + /* + * base layer == 1, reserved for frame buffer + * zorder 0 == stage 0 == 2 + * zorder 1 == stage 1 == 3 + * zorder 2 == stage 2 == 4 + */ + if (req->id == MSMFB_NEW_REQUEST) { /* new request */ + pipe->pipe_used++; + pipe->mixer_num = mixer; + pr_debug("%s: zorder=%d pipe ndx=%d num=%d\n", __func__, + req->z_order, pipe->pipe_ndx, pipe->pipe_num); + + } + + pipe->mixer_stage = req->z_order + MDP4_MIXER_STAGE0; + pipe->src_width = req->src.width & 0x1fff; /* source img width */ + pipe->src_height = req->src.height & 0x1fff; /* source img height */ + pipe->src_h = req->src_rect.h & 0x07ff; + pipe->src_w = req->src_rect.w & 0x07ff; + pipe->src_y = req->src_rect.y & 0x07ff; + pipe->src_x = req->src_rect.x & 0x07ff; + pipe->dst_h = req->dst_rect.h & 0x07ff; + pipe->dst_w = req->dst_rect.w & 0x07ff; + pipe->dst_y = req->dst_rect.y & 0x07ff; + pipe->dst_x = req->dst_rect.x & 0x07ff; + + pipe->op_mode = 0; + + if (req->flags & MDP_FLIP_LR) + pipe->op_mode |= MDP4_OP_FLIP_LR; + + if (req->flags & MDP_FLIP_UD) + pipe->op_mode |= MDP4_OP_FLIP_UD; + + if (req->flags & MDP_DITHER) + pipe->op_mode |= MDP4_OP_DITHER_EN; + + if (req->flags & MDP_DEINTERLACE) + pipe->op_mode |= MDP4_OP_DEINT_EN; + + if (req->flags & MDP_DEINTERLACE_ODD) + pipe->op_mode |= MDP4_OP_DEINT_ODD_REF; + + pipe->is_fg = req->is_fg;/* control alpha and color key */ + + pipe->alpha = req->alpha & 0x0ff; + + pipe->transp = req->transp_mask; + + *ppipe = pipe; + + return 0; +} + +static int get_img(struct msmfb_data *img, struct fb_info *info, + struct mdp4_overlay_pipe *pipe, unsigned int plane, + unsigned long *start, unsigned long *len, struct file **srcp_file, + int *p_need, struct ion_handle **srcp_ihdl) +{ + struct file *file; + int put_needed, ret = 0, fb_num; +#ifdef CONFIG_ANDROID_PMEM + unsigned long vstart; +#endif + *p_need = 0; + + if (img->flags & MDP_BLIT_SRC_GEM) { + *srcp_file = NULL; + return kgsl_gem_obj_addr(img->memory_id, (int) img->priv, + start, len); + } + + if (img->flags & MDP_MEMORY_ID_TYPE_FB) { + file = fget_light(img->memory_id, &put_needed); + if (file == NULL) + return -EINVAL; + + if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { + fb_num = MINOR(file->f_dentry->d_inode->i_rdev); + if (get_fb_phys_info(start, len, fb_num, + DISPLAY_SUBSYSTEM_ID)) { + ret = -1; + } else { + *srcp_file = file; + *p_need = put_needed; + } + } else + ret = -1; + if (ret) + fput_light(file, put_needed); + return ret; + } + +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + return mdp4_overlay_iommu_map_buf(img->memory_id, pipe, plane, + start, len, srcp_ihdl); +#endif +#ifdef CONFIG_ANDROID_PMEM + if (!get_pmem_file(img->memory_id, start, &vstart, + len, srcp_file)) + return 0; + else + return -EINVAL; +#endif +} + +#ifdef CONFIG_FB_MSM_MIPI_DSI +int mdp4_overlay_3d_sbys(struct fb_info *info, struct msmfb_overlay_3d *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret = -EPERM; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + mdp4_dsi_cmd_3d_sbys(mfd, req); + ret = 0; + } else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { + mdp4_dsi_video_3d_sbys(mfd, req); + ret = 0; + } + mutex_unlock(&mfd->dma->ov_mutex); + + return ret; +} +#else +int mdp4_overlay_3d_sbys(struct fb_info *info, struct msmfb_overlay_3d *req) +{ + /* do nothing */ + return -EPERM; +} +#endif + +int mdp4_overlay_blt(struct fb_info *info, struct msmfb_overlay_blt *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mfd == NULL) + return -ENODEV; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_overlay_blt(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) + mdp4_dsi_video_overlay_blt(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_lcdc_overlay_blt(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_overlay_blt(mfd, req); + + mutex_unlock(&mfd->dma->ov_mutex); + + return 0; +} + +int mdp4_overlay_blt_offset(struct fb_info *info, struct msmfb_overlay_blt *req) +{ + int ret = 0; + + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + ret = mdp4_dsi_overlay_blt_offset(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) + ret = mdp4_dsi_video_overlay_blt_offset(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) + ret = mdp4_lcdc_overlay_blt_offset(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_overlay_blt_offset(mfd, req); + + mutex_unlock(&mfd->dma->ov_mutex); + + return ret; +} + +int mdp4_overlay_get(struct fb_info *info, struct mdp_overlay *req) +{ + struct mdp4_overlay_pipe *pipe; + + pipe = mdp4_overlay_ndx2pipe(req->id); + if (pipe == NULL) + return -ENODEV; + + *req = pipe->req_data; + + if (mdp4_overlay_borderfill_supported()) + req->flags |= MDP_BORDERFILL_SUPPORTED; + + return 0; +} + +#define OVERLAY_VGA_SIZE 0x04B000 +#define OVERLAY_720P_TILE_SIZE 0x0E6000 +#define OVERLAY_WSVGA_SIZE 0x98000 /* 1024x608, align 600 to 32bit */ + +#define OVERLAY_BUS_SCALE_TABLE_BASE 6 + +static int mdp4_overlay_is_rgb_type(int format) +{ + switch (format) { + case MDP_RGB_565: + case MDP_RGB_888: + case MDP_BGR_565: + case MDP_XRGB_8888: + case MDP_ARGB_8888: + case MDP_RGBA_8888: + case MDP_BGRA_8888: + case MDP_RGBX_8888: + return 1; + default: + return 0; + } +} + +static uint32 mdp4_overlay_get_perf_level(struct mdp_overlay *req, + struct msm_fb_data_type *mfd) +{ + int is_fg = 0, i, cnt; + + if (req->is_fg && ((req->alpha & 0x0ff) == 0xff)) + is_fg = 1; + + if (mdp4_extn_disp) + return OVERLAY_PERF_LEVEL1; + + if (req->flags & (MDP_DEINTERLACE | MDP_BACKEND_COMPOSITION)) + return OVERLAY_PERF_LEVEL1; + + for (i = 0, cnt = 0; i < OVERLAY_PIPE_MAX; i++) { + if (ctrl->plist[i].pipe_used && ++cnt > 2) + return OVERLAY_PERF_LEVEL1; + } + + if (mdp4_overlay_is_rgb_type(req->src.format) && is_fg && + ((req->src.width * req->src.height) <= OVERLAY_WSVGA_SIZE)) + return OVERLAY_PERF_LEVEL4; + else if (mdp4_overlay_is_rgb_type(req->src.format)) + return OVERLAY_PERF_LEVEL1; + + if (req->src.width*req->src.height <= OVERLAY_VGA_SIZE) { + if (mfd->mdp_rev >= MDP_REV_42) + return OVERLAY_PERF_LEVEL4; + else + return OVERLAY_PERF_LEVEL3; + + } else if (req->src.width*req->src.height <= OVERLAY_720P_TILE_SIZE) { + u32 max, min; + max = (req->dst_rect.h > req->dst_rect.w) ? + req->dst_rect.h : req->dst_rect.w; + min = (mfd->panel_info.yres > mfd->panel_info.xres) ? + mfd->panel_info.xres : mfd->panel_info.yres; + if (max > min) /* landscape mode */ + return OVERLAY_PERF_LEVEL3; + else /* potrait mode */ + return OVERLAY_PERF_LEVEL2; + } + else + return OVERLAY_PERF_LEVEL1; +} + +void mdp4_update_perf_level(u32 perf_level) +{ + static int first = 1; + + new_perf_level = perf_level; + + if (first) { + first = 0; + mdp4_set_perf_level(); + } +} + +void mdp4_set_perf_level(void) +{ + static int old_perf_level; + int cur_perf_level; + + if (mdp4_extn_disp) + cur_perf_level = OVERLAY_PERF_LEVEL1; + else + cur_perf_level = new_perf_level; + + if (old_perf_level != cur_perf_level) { + mdp_set_core_clk(cur_perf_level); + old_perf_level = cur_perf_level; + mdp_bus_scale_update_request(OVERLAY_BUS_SCALE_TABLE_BASE + - cur_perf_level); + } +} + +static void mdp4_overlay_update_blt_mode(struct msm_fb_data_type *mfd) +{ + if (mfd->use_ov0_blt == mfd->ov0_blt_state) + return; + + if (mfd->use_ov0_blt) { + if (mfd->panel_info.type == LCDC_PANEL || + mfd->panel_info.type == LVDS_PANEL) + mdp4_lcdc_overlay_blt_start(mfd); + else if (mfd->panel_info.type == MIPI_VIDEO_PANEL) + mdp4_dsi_video_blt_start(mfd); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_overlay_blt_start(mfd); + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_overlay_blt_start(mfd); + } else { + if (mfd->panel_info.type == LCDC_PANEL || + mfd->panel_info.type == LVDS_PANEL) + mdp4_lcdc_overlay_blt_stop(mfd); + else if (mfd->panel_info.type == MIPI_VIDEO_PANEL) + mdp4_dsi_video_blt_stop(mfd); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_overlay_blt_stop(mfd); + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_overlay_blt_stop(mfd); + } + mfd->ov0_blt_state = mfd->use_ov0_blt; +} + +static void mdp4_overlay1_update_blt_mode(struct msm_fb_data_type *mfd) +{ + if (mfd->ov1_blt_state == mfd->use_ov1_blt) + return; + if (mfd->use_ov1_blt) { + mdp4_allocate_writeback_buf(mfd, MDP4_MIXER1); + mdp4_dtv_overlay_blt_start(mfd); + pr_debug("%s overlay1 writeback is enabled\n", __func__); + } else { + mdp4_dtv_overlay_blt_stop(mfd); + pr_debug("%s overlay1 writeback is disabled\n", __func__); + } + mfd->ov1_blt_state = mfd->use_ov1_blt; +} + +static u32 mdp4_overlay_blt_enable(struct mdp_overlay *req, + struct msm_fb_data_type *mfd, uint32 perf_level) +{ + u32 clk_rate = mfd->panel_info.clk_rate; + u32 pull_mode = 0, use_blt = 0; + + if (mfd->panel_info.type == MIPI_VIDEO_PANEL) + clk_rate = (&mfd->panel_info.mipi)->dsi_pclk_rate; + + if ((mfd->panel_info.type == LCDC_PANEL) || + (mfd->panel_info.type == MIPI_VIDEO_PANEL) || + (mfd->panel_info.type == DTV_PANEL)) + pull_mode = 1; + + if (pull_mode && (req->src_rect.h > req->dst_rect.h || + req->src_rect.w > req->dst_rect.w)) { + if (mdp4_overlay_validate_downscale(req, mfd, perf_level, + clk_rate)) + use_blt = 1; + } + + if (mfd->panel_info.type == MDDI_PANEL) { + if ((req->src_rect.h/2) >= req->dst_rect.h || + (req->src_rect.w/2) >= req->dst_rect.w) + use_blt = 1; + } + + if (mfd->mdp_rev == MDP_REV_41) { + /* + * writeback (blt) mode to provide work around for + * dsi cmd mode interface hardware bug. + */ + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + if (req->dst_rect.x != 0) + use_blt = 1; + } + if ((mfd->panel_info.xres > 1280) && + (mfd->panel_info.type != DTV_PANEL)) + use_blt = 1; + } + return use_blt; +} + +int mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret, mixer, perf_level; + struct mdp4_overlay_pipe *pipe; + + if (mfd == NULL) { + pr_err("%s: mfd == NULL, -ENODEV\n", __func__); + return -ENODEV; + } + + if (info->node != 0 || mfd->cont_splash_done) /* primary */ + if (!mfd->panel_power_on) /* suspended */ + return -EPERM; + + if (req->src.format == MDP_FB_FORMAT) + req->src.format = mfd->fb_imgType; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) { + pr_err("%s: mutex_lock_interruptible, -EINTR\n", __func__); + return -EINTR; + } + + mixer = mfd->panel_info.pdest; /* DISPLAY_1 or DISPLAY_2 */ + + ret = mdp4_overlay_req2pipe(req, mixer, &pipe, mfd); + + if (ret < 0) { + mutex_unlock(&mfd->dma->ov_mutex); + pr_err("%s: mdp4_overlay_req2pipe, ret=%d\n", __func__, ret); + return ret; + } + + perf_level = mdp4_overlay_get_perf_level(req, mfd); + + if (mixer == MDP4_MIXER0) { + u32 use_blt = mdp4_overlay_blt_enable(req, mfd, perf_level); + mfd->use_ov0_blt &= ~(1 << (pipe->pipe_ndx-1)); + mfd->use_ov0_blt |= (use_blt << (pipe->pipe_ndx-1)); + } + + /* return id back to user */ + req->id = pipe->pipe_ndx; /* pipe_ndx start from 1 */ + pipe->req_data = *req; /* keep original req */ + + pipe->flags = req->flags; + + if (!IS_ERR_OR_NULL(mfd->iclient)) { + pr_debug("pipe->flags 0x%x\n", pipe->flags); + if (pipe->flags & MDP_SECURE_OVERLAY_SESSION) { + mfd->mem_hid &= ~BIT(ION_IOMMU_HEAP_ID); + mfd->mem_hid |= ION_SECURE; + } else { + mfd->mem_hid |= BIT(ION_IOMMU_HEAP_ID); + mfd->mem_hid &= ~ION_SECURE; + } + } + + if (pipe->flags & MDP_SHARPENING) { + bool test = ((pipe->req_data.dpp.sharp_strength > 0) && + ((req->src_rect.w > req->dst_rect.w) && + (req->src_rect.h > req->dst_rect.h))); + if (test) { + pr_debug("%s: No sharpening while downscaling.\n", + __func__); + pipe->flags &= ~MDP_SHARPENING; + } + } + + /* precompute HSIC matrices */ + if (req->flags & MDP_DPP_HSIC) + mdp4_hsic_set(pipe, &(req->dpp)); + + mdp4_stat.overlay_set[pipe->mixer_num]++; + + if (ctrl->panel_mode & MDP4_PANEL_DTV && + pipe->mixer_num == MDP4_MIXER1) { + u32 use_blt = mdp4_overlay_blt_enable(req, mfd, perf_level); + + if (hdmi_prim_display) { + if (!mdp4_overlay_is_rgb_type(req->src.format) && + pipe->pipe_type == OVERLAY_TYPE_VIDEO && + (req->src_rect.h > req->dst_rect.h || + req->src_rect.w > req->dst_rect.w)) + use_blt = 1; + } + + mdp4_overlay_dtv_set(mfd, pipe); + mfd->use_ov1_blt &= ~(1 << (pipe->pipe_ndx-1)); + mfd->use_ov1_blt |= (use_blt << (pipe->pipe_ndx-1)); + } + + if (new_perf_level != perf_level) { + u32 old_level = new_perf_level; + mdp4_update_perf_level(perf_level); + + /* change clck base on perf level */ + if (pipe->mixer_num == MDP4_MIXER0) { + if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { + if (old_level > perf_level) + mdp4_set_perf_level(); + } else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + mdp4_dsi_cmd_dma_busy_wait(mfd); + mdp4_dsi_blt_dmap_busy_wait(mfd); + mdp4_set_perf_level(); + } else if (ctrl->panel_mode & MDP4_PANEL_LCDC) { + if (old_level > perf_level) + mdp4_set_perf_level(); + } else if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + mdp4_mddi_dma_busy_wait(mfd); + mdp4_mddi_blt_dmap_busy_wait(mfd); + mdp4_set_perf_level(); + } + } else { + if (ctrl->panel_mode & MDP4_PANEL_DTV) { + mdp4_overlay_reg_flush(pipe, 0); + mdp4_overlay_dtv_ov_done_push(mfd, pipe); + } + } + } + mutex_unlock(&mfd->dma->ov_mutex); + + return 0; +} + +int mdp4_overlay_unset_mixer(int mixer) +{ + struct mdp4_overlay_pipe *pipe; + int i, cnt = 0; + + for (i = MDP4_MIXER_STAGE3; i >= MDP4_MIXER_STAGE_BASE; i--) { + pipe = ctrl->stage[mixer][i]; + if (pipe == NULL) + continue; + pipe->flags &= ~MDP_OV_PLAY_NOWAIT; + mdp4_mixer_stage_down(pipe); + mdp4_overlay_pipe_free(pipe); + cnt++; + } + + return cnt; +} + +int mdp4_overlay_unset(struct fb_info *info, int ndx) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct mdp4_overlay_pipe *pipe; + struct dpp_ctrl dpp; + int i; + + if (mfd == NULL) + return -ENODEV; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + pipe = mdp4_overlay_ndx2pipe(ndx); + + if (pipe == NULL) { + mutex_unlock(&mfd->dma->ov_mutex); + return -ENODEV; + } + + if (pipe->pipe_type == OVERLAY_TYPE_BF) { + mdp4_overlay_borderfill_stage_down(pipe); + mutex_unlock(&mfd->dma->ov_mutex); + return 0; + } + + if (pipe->mixer_num == MDP4_MIXER2) + ctrl->mixer2_played = 0; + else if (pipe->mixer_num == MDP4_MIXER1) + ctrl->mixer1_played = 0; + else { + /* mixer 0 */ + ctrl->mixer0_played = 0; +#ifdef CONFIG_FB_MSM_MIPI_DSI + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + if (mfd->panel_power_on) { + mdp4_dsi_blt_dmap_busy_wait(mfd); + } + } +#else + if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + if (mfd->panel_power_on) + mdp4_mddi_blt_dmap_busy_wait(mfd); + } +#endif + } + + { + mdp4_overlay_reg_flush(pipe, 1); + + if (mfd->use_ov0_blt || pipe->mixer_num == MDP4_MIXER1) { + /* unstage pipe forcedly */ + pipe->flags &= ~MDP_OV_PLAY_NOWAIT; + } + + mdp4_mixer_stage_down(pipe); + + if (pipe->mixer_num == MDP4_MIXER0) { +#ifdef CONFIG_FB_MSM_MIPI_DSI + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + if (mfd->panel_power_on) + mdp4_dsi_cmd_overlay_restore(); + } else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { + pipe->flags &= ~MDP_OV_PLAY_NOWAIT; + if (mfd->panel_power_on) + mdp4_overlay_dsi_video_vsync_push(mfd, + pipe); + } +#else + if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + if (mfd->panel_power_on) + mdp4_mddi_overlay_restore(); + } +#endif + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) { + pipe->flags &= ~MDP_OV_PLAY_NOWAIT; + if (mfd->panel_power_on) + mdp4_overlay_lcdc_vsync_push(mfd, pipe); + } + mfd->use_ov0_blt &= ~(1 << (pipe->pipe_ndx-1)); + mdp4_overlay_update_blt_mode(mfd); + if (!mfd->use_ov0_blt) + mdp4_free_writeback_buf(mfd, MDP4_MIXER0); + } else { /* mixer1, DTV, ATV */ + if (ctrl->panel_mode & MDP4_PANEL_DTV) { + mdp4_overlay_dtv_unset(mfd, pipe); + mfd->use_ov1_blt &= ~(1 << (pipe->pipe_ndx-1)); + mdp4_overlay1_update_blt_mode(mfd); + if (!mfd->use_ov1_blt) + mdp4_free_writeback_buf(mfd, + MDP4_MIXER1); + } + } + } + + /* Reset any HSIC settings to default */ + if (pipe->flags & MDP_DPP_HSIC) { + for (i = 0; i < NUM_HSIC_PARAM; i++) + dpp.hsic_params[i] = 0; + + mdp4_hsic_set(pipe, &dpp); + mdp4_hsic_update(pipe); + } + + mdp4_stat.overlay_unset[pipe->mixer_num]++; + + mdp4_overlay_pipe_free(pipe); + + mutex_unlock(&mfd->dma->ov_mutex); + + return 0; +} + + +struct tile_desc { + uint32 width; /* tile's width */ + uint32 height; /* tile's height */ + uint32 row_tile_w; /* tiles per row's width */ + uint32 row_tile_h; /* tiles per row's height */ +}; + +void tile_samsung(struct tile_desc *tp) +{ + /* + * each row of samsung tile consists of two tiles in height + * and two tiles in width which means width should align to + * 64 x 2 bytes and height should align to 32 x 2 bytes. + * video decoder generate two tiles in width and one tile + * in height which ends up height align to 32 X 1 bytes. + */ + tp->width = 64; /* 64 bytes */ + tp->row_tile_w = 2; /* 2 tiles per row's width */ + tp->height = 32; /* 32 bytes */ + tp->row_tile_h = 1; /* 1 tiles per row's height */ +} + +uint32 tile_mem_size(struct mdp4_overlay_pipe *pipe, struct tile_desc *tp) +{ + uint32 tile_w, tile_h; + uint32 row_num_w, row_num_h; + + + tile_w = tp->width * tp->row_tile_w; + tile_h = tp->height * tp->row_tile_h; + + row_num_w = (pipe->src_width + tile_w - 1) / tile_w; + row_num_h = (pipe->src_height + tile_h - 1) / tile_h; + return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191; +} + +int mdp4_overlay_play_wait(struct fb_info *info, struct msmfb_overlay_data *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct mdp4_overlay_pipe *pipe; + + if (mfd == NULL) + return -ENODEV; + + if (!mfd->panel_power_on) /* suspended */ + return -EPERM; + + pipe = mdp4_overlay_ndx2pipe(req->id); + + if (!pipe) { + mdp4_stat.err_play++; + return -ENODEV; + } + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + mdp4_mixer_stage_commit(pipe->mixer_num); + + if (mfd->use_ov1_blt) + mdp4_overlay1_update_blt_mode(mfd); + + mdp4_overlay_dtv_wait4vsync(); + mdp4_iommu_unmap(pipe); + + mutex_unlock(&mfd->dma->ov_mutex); + return 0; +} + +int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msmfb_data *img; + struct mdp4_overlay_pipe *pipe; + ulong start, addr; + ulong len = 0; + struct file *srcp0_file = NULL; + struct file *srcp1_file = NULL, *srcp2_file = NULL; + struct ion_handle *srcp0_ihdl = NULL; + struct ion_handle *srcp1_ihdl = NULL, *srcp2_ihdl = NULL; + int ps0_need, p_need; + uint32_t overlay_version = 0; + int ret = 0; + + if (mfd == NULL) + return -ENODEV; + + if (!mfd->panel_power_on) /* suspended */ + return -EPERM; + + pipe = mdp4_overlay_ndx2pipe(req->id); + if (pipe == NULL) { + mdp4_stat.err_play++; + return -ENODEV; + } + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + if (pipe->pipe_type == OVERLAY_TYPE_BF) { + mdp4_overlay_borderfill_stage_up(pipe); + mutex_unlock(&mfd->dma->ov_mutex); + return 0; + } + + img = &req->data; + get_img(img, info, pipe, 0, &start, &len, &srcp0_file, + &ps0_need, &srcp0_ihdl); + if (len == 0) { + mutex_unlock(&mfd->dma->ov_mutex); + pr_err("%s: pmem Error\n", __func__); + ret = -1; + goto end; + } + + addr = start + img->offset; + pipe->srcp0_addr = addr; + pipe->srcp0_ystride = pipe->src_width * pipe->bpp; + + + pr_debug("%s: mixer=%d ndx=%x addr=%x flags=%x\n", __func__, + pipe->mixer_num, pipe->pipe_ndx, (int)addr, pipe->flags); + + if ((req->version_key & VERSION_KEY_MASK) == 0xF9E8D700) + overlay_version = (req->version_key & ~VERSION_KEY_MASK); + + if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR) { + if (overlay_version > 0) { + img = &req->plane1_data; + get_img(img, info, pipe, 1, &start, &len, &srcp1_file, + &p_need, &srcp1_ihdl); + if (len == 0) { + mutex_unlock(&mfd->dma->ov_mutex); + pr_err("%s: Error to get plane1\n", __func__); + ret = -EINVAL; + goto end; + } + pipe->srcp1_addr = start + img->offset; + } else if (pipe->frame_format == + MDP4_FRAME_FORMAT_VIDEO_SUPERTILE) { + struct tile_desc tile; + + tile_samsung(&tile); + pipe->srcp1_addr = addr + tile_mem_size(pipe, &tile); + } else { + pipe->srcp1_addr = addr + (pipe->src_width * + pipe->src_height); + } + pipe->srcp0_ystride = pipe->src_width; + if ((pipe->src_format == MDP_Y_CRCB_H1V1) || + (pipe->src_format == MDP_Y_CBCR_H1V1)) { + if (pipe->src_width > YUV_444_MAX_WIDTH) + pipe->srcp1_ystride = pipe->src_width << 2; + else + pipe->srcp1_ystride = pipe->src_width << 1; + } else + pipe->srcp1_ystride = pipe->src_width; + + } else if (pipe->fetch_plane == OVERLAY_PLANE_PLANAR) { + if (overlay_version > 0) { + img = &req->plane1_data; + get_img(img, info, pipe, 1, &start, &len, &srcp1_file, + &p_need, &srcp1_ihdl); + if (len == 0) { + mutex_unlock(&mfd->dma->ov_mutex); + pr_err("%s: Error to get plane1\n", __func__); + ret = -EINVAL; + goto end; + } + pipe->srcp1_addr = start + img->offset; + + img = &req->plane2_data; + get_img(img, info, pipe, 2, &start, &len, &srcp2_file, + &p_need, &srcp2_ihdl); + if (len == 0) { + mutex_unlock(&mfd->dma->ov_mutex); + pr_err("%s: Error to get plane2\n", __func__); + ret = -EINVAL; + goto end; + } + pipe->srcp2_addr = start + img->offset; + } else { + if (pipe->src_format == MDP_Y_CR_CB_GH2V2) { + addr += (ALIGN(pipe->src_width, 16) * + pipe->src_height); + pipe->srcp1_addr = addr; + addr += ((ALIGN((pipe->src_width / 2), 16)) * + (pipe->src_height / 2)); + pipe->srcp2_addr = addr; + } else { + addr += (pipe->src_width * pipe->src_height); + pipe->srcp1_addr = addr; + addr += ((pipe->src_width / 2) * + (pipe->src_height / 2)); + pipe->srcp2_addr = addr; + } + } + /* mdp planar format expects Cb in srcp1 and Cr in p2 */ + if ((pipe->src_format == MDP_Y_CR_CB_H2V2) || + (pipe->src_format == MDP_Y_CR_CB_GH2V2)) + swap(pipe->srcp1_addr, pipe->srcp2_addr); + + if (pipe->src_format == MDP_Y_CR_CB_GH2V2) { + pipe->srcp0_ystride = ALIGN(pipe->src_width, 16); + pipe->srcp1_ystride = ALIGN(pipe->src_width / 2, 16); + pipe->srcp2_ystride = ALIGN(pipe->src_width / 2, 16); + } else { + pipe->srcp0_ystride = pipe->src_width; + pipe->srcp1_ystride = pipe->src_width / 2; + pipe->srcp2_ystride = pipe->src_width / 2; + } + } + + if (mfd->use_ov0_blt) + mdp4_overlay_update_blt_mode(mfd); + + if (mfd->use_ov1_blt) + mdp4_overlay1_update_blt_mode(mfd); + + + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) { + mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */ + } else { + if (pipe->flags & MDP_SHARPENING) { + pr_debug( + "%s: Sharpening/Smoothing not supported on RGB pipe\n", + __func__); + pipe->flags &= ~MDP_SHARPENING; + } + mdp4_overlay_rgb_setup(pipe); /* rgb pipe */ + } + + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + + if (pipe->mixer_num == MDP4_MIXER2) { + ctrl->mixer2_played++; +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL + if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) { + mdp4_writeback_dma_busy_wait(mfd); + mdp4_writeback_kickoff_video(mfd, pipe); + } +#endif + } else if (pipe->mixer_num == MDP4_MIXER1) { + ctrl->mixer1_played++; + /* enternal interface */ + if (ctrl->panel_mode & MDP4_PANEL_DTV) { + if (pipe->flags & MDP_OV_PLAY_NOWAIT) + mdp4_overlay_flush_piggyback(MDP4_MIXER0, + MDP4_MIXER1); + mdp4_overlay_dtv_start(); + mdp4_overlay_dtv_ov_done_push(mfd, pipe); + if (!mfd->use_ov1_blt) + mdp4_overlay1_update_blt_mode(mfd); + } + } else { + + /* primary interface */ + ctrl->mixer0_played++; + if (ctrl->panel_mode & MDP4_PANEL_LCDC) { + mdp4_overlay_reg_flush(pipe, 0); + mdp4_overlay_lcdc_start(); + mdp4_overlay_lcdc_vsync_push(mfd, pipe); + if (!mfd->use_ov0_blt && + !(pipe->flags & MDP_OV_PLAY_NOWAIT)) + mdp4_overlay_update_blt_mode(mfd); + } +#ifdef CONFIG_FB_MSM_MIPI_DSI + else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { + mdp4_overlay_reg_flush(pipe, 0); + mdp4_overlay_dsi_video_start(); + mdp4_overlay_dsi_video_vsync_push(mfd, pipe); + if (!mfd->use_ov0_blt && + !(pipe->flags & MDP_OV_PLAY_NOWAIT)) + mdp4_overlay_update_blt_mode(mfd); + } +#endif + else { + mdp4_overlay_reg_flush_reset(pipe); + /* mddi & mipi dsi cmd mode */ + if (pipe->flags & MDP_OV_PLAY_NOWAIT) { + mdp4_stat.overlay_play[pipe->mixer_num]++; + mutex_unlock(&mfd->dma->ov_mutex); + goto end; + } +#ifdef CONFIG_FB_MSM_MIPI_DSI + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + mdp4_iommu_attach(); + mdp4_dsi_cmd_dma_busy_wait(mfd); + mdp4_dsi_cmd_kickoff_video(mfd, pipe); + } +#else + if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + mdp4_mddi_dma_busy_wait(mfd); + mdp4_mddi_kickoff_video(mfd, pipe); + } +#endif + } + } + + /* write out DPP HSIC registers */ + if (pipe->flags & MDP_DPP_HSIC) + mdp4_hsic_update(pipe); + if (!(pipe->flags & MDP_OV_PLAY_NOWAIT)) + mdp4_iommu_unmap(pipe); + mdp4_stat.overlay_play[pipe->mixer_num]++; + mutex_unlock(&mfd->dma->ov_mutex); +end: +#ifdef CONFIG_ANDROID_PMEM + if (srcp0_file) + put_pmem_file(srcp0_file); + if (srcp1_file) + put_pmem_file(srcp1_file); + if (srcp2_file) + put_pmem_file(srcp2_file); +#endif + /* only source may use frame buffer */ + if (img->flags & MDP_MEMORY_ID_TYPE_FB) + fput_light(srcp0_file, ps0_need); + return ret; +} + +static struct { + char *name; + int domain; +} msm_iommu_ctx_names[] = { + /* Display */ + { + .name = "mdp_port0_cb0", + .domain = DISPLAY_DOMAIN, + }, + /* Display */ + { + .name = "mdp_port0_cb1", + .domain = DISPLAY_DOMAIN, + }, + /* Display */ + { + .name = "mdp_port1_cb0", + .domain = DISPLAY_DOMAIN, + }, + /* Display */ + { + .name = "mdp_port1_cb1", + .domain = DISPLAY_DOMAIN, + }, +}; + +static int mdp_iommu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, int flags) +{ + pr_err("MDP IOMMU page fault: iova 0x%lx", iova); + return 0; +} + +void mdp4_iommu_attach(void) +{ + static int done; + struct iommu_domain *domain; + int i; + + if (!done) { + for (i = 0; i < ARRAY_SIZE(msm_iommu_ctx_names); i++) { + int domain_idx; + struct device *ctx = msm_iommu_get_ctx( + msm_iommu_ctx_names[i].name); + + if (!ctx) + continue; + + domain_idx = msm_iommu_ctx_names[i].domain; + + domain = msm_get_iommu_domain(domain_idx); + if (!domain) + continue; + + iommu_set_fault_handler(domain, + mdp_iommu_fault_handler); + if (iommu_attach_device(domain, ctx)) { + WARN(1, "%s: could not attach domain %d to context %s." + " iommu programming will not occur.\n", + __func__, domain_idx, + msm_iommu_ctx_names[i].name); + continue; + } + } + done = 1; + } +} + +int mdp4_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req, + struct mdp4_overlay_pipe **ppipe) +{ + struct mdp4_overlay_pipe *pipe; + int err; + struct msm_fb_data_type *mfb = info->par; + + req->z_order = 0; + req->id = MSMFB_NEW_REQUEST; + req->is_fg = false; + req->alpha = 0xff; + err = mdp4_overlay_req2pipe(req, MDP4_MIXER0, &pipe, mfb); + if (err < 0) { + pr_err("%s:Could not allocate MDP overlay pipe\n", __func__); + return err; + } + + mdp4_mixer_blend_setup(pipe->mixer_num); + *ppipe = pipe; + + return 0; +} + +void mdp4_v4l2_overlay_clear(struct mdp4_overlay_pipe *pipe) +{ + mdp4_mixer_stage_down(pipe); + mdp4_overlay_pipe_free(pipe); +} + +int mdp4_v4l2_overlay_play(struct fb_info *info, struct mdp4_overlay_pipe *pipe, + unsigned long srcp0_addr, unsigned long srcp1_addr, + unsigned long srcp2_addr) +{ + struct msm_fb_data_type *mfd = info->par; + int err; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + switch (pipe->src_format) { + case MDP_Y_CR_CB_H2V2: + /* YUV420 */ + pipe->srcp0_addr = srcp0_addr; + pipe->srcp0_ystride = pipe->src_width; + /* + * For YUV420, the luma plane is 1 byte per pixel times + * num of pixels in the image Also, the planes are + * switched in MDP, srcp2 is actually first chroma plane + */ + pipe->srcp2_addr = srcp1_addr ? srcp1_addr : + pipe->srcp0_addr + (pipe->src_width * pipe->src_height); + pipe->srcp2_ystride = pipe->src_width/2; + /* + * The chroma planes are half the size of the luma + * planes + */ + pipe->srcp1_addr = srcp2_addr ? srcp2_addr : + pipe->srcp2_addr + + (pipe->src_width * pipe->src_height / 4); + pipe->srcp1_ystride = pipe->src_width/2; + break; + case MDP_Y_CRCB_H2V2: + /* NV12 */ + pipe->srcp0_addr = srcp0_addr; + pipe->srcp0_ystride = pipe->src_width; + pipe->srcp1_addr = srcp1_addr ? srcp1_addr : + pipe->srcp0_addr + + (pipe->src_width * pipe->src_height); + pipe->srcp1_ystride = pipe->src_width; + break; + default: + pr_err("%s: format (%u) is not supported\n", __func__, + pipe->src_format); + err = -EINVAL; + goto done; + } + + pr_debug("%s: pipe ndx=%d stage=%d format=%x\n", __func__, + pipe->pipe_ndx, pipe->mixer_stage, pipe->src_format); + + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) + mdp4_overlay_vg_setup(pipe); + else + mdp4_overlay_rgb_setup(pipe); + + if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_overlay_reg_flush(pipe, 1); + + mdp4_mixer_stage_up(pipe); + + if (ctrl->panel_mode & MDP4_PANEL_LCDC) { + mdp4_overlay_lcdc_vsync_push(mfd, pipe); + } else { +#ifdef CONFIG_FB_MSM_MIPI_DSI + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + mdp4_dsi_cmd_dma_busy_wait(mfd); + mdp4_dsi_cmd_kickoff_video(mfd, pipe); + } +#else + if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + mdp4_mddi_dma_busy_wait(mfd); + mdp4_mddi_kickoff_video(mfd, pipe); + } +#endif + } +done: + mutex_unlock(&mfd->dma->ov_mutex); + return err; +} + diff --git a/drivers/video/msm/mdp4_overlay_atv.c b/drivers/video/msm/mdp4_overlay_atv.c new file mode 100644 index 0000000000000000000000000000000000000000..a64dd97be957d644ab3719cf6cb05e74a6f6d30e --- /dev/null +++ b/drivers/video/msm/mdp4_overlay_atv.c @@ -0,0 +1,210 @@ +/* Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + + +static struct mdp4_overlay_pipe *atv_pipe; + +int mdp4_atv_on(struct platform_device *pdev) +{ + uint8 *buf; + unsigned int buf_offset; + int bpp, ptype; + struct fb_info *fbi; + struct fb_var_screeninfo *var; + struct msm_fb_data_type *mfd; + struct mdp4_overlay_pipe *pipe; + int ret; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + fbi = mfd->fbi; + var = &fbi->var; + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf_offset = calc_fb_offset(mfd, fbi, bpp); + + if (atv_pipe == NULL) { + ptype = mdp4_overlay_format2type(mfd->fb_imgType); + pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1); + if (pipe == NULL) + return -EBUSY; + pipe->pipe_used++; + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + pipe->mixer_num = MDP4_MIXER1; + pipe->src_format = mfd->fb_imgType; + mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_ATV); + mdp4_overlay_format2pipe(pipe); + + atv_pipe = pipe; /* keep it */ + } else { + pipe = atv_pipe; + } + + printk(KERN_INFO "mdp4_atv_overlay: pipe=%x ndx=%d\n", + (int)pipe, pipe->pipe_ndx); + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* Turn the next panel on, get correct resolution + before configuring overlay pipe */ + ret = panel_next_on(pdev); + + pr_info("%s: fbi->var.yres: %d | fbi->var.xres: %d", + __func__, fbi->var.yres, fbi->var.xres); + + /* MDP4 Config */ + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->src_y = 0; + pipe->src_x = 0; + if (mfd->map_buffer) { + pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \ + buf_offset; + pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd-> + map_buffer->iova[0], pipe->srcp0_addr); + } else { + pipe->srcp0_addr = (uint32)(buf + buf_offset); + } + + pipe->srcp0_ystride = fbi->fix.line_length; + + mdp4_overlay_dmae_xy(pipe); /* dma_e */ + mdp4_overlay_dmae_cfg(mfd, 1); + + mdp4_overlay_rgb_setup(pipe); + + mdp4_overlayproc_cfg(pipe); + + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + + if (ret == 0) + mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return ret; +} + +int mdp4_atv_off(struct platform_device *pdev) +{ + int ret = 0; + + mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + ret = panel_next_off(pdev); + + /* delay to make sure the last frame finishes */ + msleep(100); + + /* dis-engage rgb2 from mixer1 */ + if (atv_pipe) { + mdp4_mixer_stage_down(atv_pipe); + mdp4_iommu_unmap(atv_pipe); + } + + return ret; +} + +/* + * mdp4_overlay1_done_atv: called from isr + */ +void mdp4_overlay1_done_atv() +{ + complete(&atv_pipe->comp); +} + +void mdp4_atv_overlay(struct msm_fb_data_type *mfd) +{ + struct fb_info *fbi = mfd->fbi; + uint8 *buf; + unsigned int buf_offset; + int bpp; + unsigned long flag; + struct mdp4_overlay_pipe *pipe; + + if (!mfd->panel_power_on) + return; + + /* no need to power on cmd block since it's lcdc mode */ + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf_offset = calc_fb_offset(mfd, fbi, bpp); + + mutex_lock(&mfd->dma->ov_mutex); + + pipe = atv_pipe; + if (mfd->map_buffer) { + pipe->srcp0_addr = (unsigned int)mfd->map_buffer->iova[0] + \ + buf_offset; + pr_debug("start 0x%lx srcp0_addr 0x%x\n", mfd-> + map_buffer->iova[0], pipe->srcp0_addr); + } else { + pipe->srcp0_addr = (uint32)(buf + buf_offset); + } + mdp4_overlay_rgb_setup(pipe); + mdp4_overlay_reg_flush(pipe, 0); + mdp4_mixer_stage_up(pipe); + + printk(KERN_INFO "mdp4_atv_overlay: pipe=%x ndx=%d\n", + (int)pipe, pipe->pipe_ndx); + + /* enable irq */ + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(MDP_OVERLAY1_TERM); + INIT_COMPLETION(atv_pipe->comp); + mfd->dma->waiting = TRUE; + outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE); + mdp_intr_mask |= INTR_OVERLAY1_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + wait_for_completion_killable(&atv_pipe->comp); + mdp_disable_irq(MDP_OVERLAY1_TERM); + + /* change mdp clk while mdp is idle` */ + mdp4_set_perf_level(); + + mdp4_stat.kickoff_atv++; + mutex_unlock(&mfd->dma->ov_mutex); +} diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c new file mode 100644 index 0000000000000000000000000000000000000000..351e2d059a0de28901f1b66de33635606a1f5102 --- /dev/null +++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c @@ -0,0 +1,718 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" +#include "mipi_dsi.h" + +static struct mdp4_overlay_pipe *dsi_pipe; +static struct msm_fb_data_type *dsi_mfd; +static int busy_wait_cnt; +static int dsi_state; +static unsigned long tout_expired; + +#define TOUT_PERIOD HZ /* 1 second */ +#define MS_100 (HZ/10) /* 100 ms */ + +static int vsync_start_y_adjust = 4; + +struct timer_list dsi_clock_timer; + +void mdp4_overlay_dsi_state_set(int state) +{ + unsigned long flag; + + spin_lock_irqsave(&mdp_spin_lock, flag); + dsi_state = state; + spin_unlock_irqrestore(&mdp_spin_lock, flag); +} + +int mdp4_overlay_dsi_state_get(void) +{ + return dsi_state; +} + +static void dsi_clock_tout(unsigned long data) +{ + if (mipi_dsi_clk_on) { + if (dsi_state == ST_DSI_PLAYING) { + mipi_dsi_turn_off_clks(); + mdp4_overlay_dsi_state_set(ST_DSI_CLK_OFF); + } + } +} + +static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp) +{ + /* + * The adreno GPU hardware requires that the pitch be aligned to + * 32 pixels for color buffers, so for the cases where the GPU + * is writing directly to fb0, the framebuffer pitch + * also needs to be 32 pixel aligned + */ + + if (fb_index == 0) + return ALIGN(xres, 32) * bpp; + else + return xres * bpp; +} + +void mdp4_mipi_vsync_enable(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe, int which) +{ + uint32 start_y, data, tear_en; + + tear_en = (1 << which); + + if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) && + (mfd->panel_info.lcd.vsync_enable)) { + + if (vsync_start_y_adjust <= pipe->dst_y) + start_y = pipe->dst_y - vsync_start_y_adjust; + else + start_y = (mfd->total_lcd_lines - 1) - + (vsync_start_y_adjust - pipe->dst_y); + if (which == 0) + MDP_OUTP(MDP_BASE + 0x210, start_y); /* primary */ + else + MDP_OUTP(MDP_BASE + 0x214, start_y); /* secondary */ + + data = inpdw(MDP_BASE + 0x20c); + data |= tear_en; + MDP_OUTP(MDP_BASE + 0x20c, data); + } else { + data = inpdw(MDP_BASE + 0x20c); + data &= ~tear_en; + MDP_OUTP(MDP_BASE + 0x20c, data); + } +} + +void mdp4_dsi_cmd_base_swap(struct mdp4_overlay_pipe *pipe) +{ + dsi_pipe = pipe; +} + +void mdp4_overlay_update_dsi_cmd(struct msm_fb_data_type *mfd) +{ + MDPIBUF *iBuf = &mfd->ibuf; + uint8 *src; + int ptype; + struct mdp4_overlay_pipe *pipe; + int bpp; + int ret; + + if (mfd->key != MFD_KEY) + return; + + dsi_mfd = mfd; /* keep it */ + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + if (dsi_pipe == NULL) { + ptype = mdp4_overlay_format2type(mfd->fb_imgType); + if (ptype < 0) + printk(KERN_INFO "%s: format2type failed\n", __func__); + pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0); + if (pipe == NULL) + printk(KERN_INFO "%s: pipe_alloc failed\n", __func__); + pipe->pipe_used++; + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + pipe->mixer_num = MDP4_MIXER0; + pipe->src_format = mfd->fb_imgType; + mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_DSI_CMD); + ret = mdp4_overlay_format2pipe(pipe); + if (ret < 0) + printk(KERN_INFO "%s: format2type failed\n", __func__); + + init_timer(&dsi_clock_timer); + dsi_clock_timer.function = dsi_clock_tout; + dsi_clock_timer.data = (unsigned long) mfd;; + dsi_clock_timer.expires = 0xffffffff; + add_timer(&dsi_clock_timer); + tout_expired = jiffies; + + dsi_pipe = pipe; /* keep it */ + + mdp4_init_writeback_buf(mfd, MDP4_MIXER0); + pipe->blt_addr = 0; + + } else { + pipe = dsi_pipe; + } + + if (pipe->pipe_used == 0 || + pipe->mixer_stage != MDP4_MIXER_STAGE_BASE) { + pr_err("%s: NOT baselayer\n", __func__); + mutex_unlock(&mfd->dma->ov_mutex); + return; + } + + /* + * configure dsi stream id + * dma_p = 0, dma_s = 1 + */ + MDP_OUTP(MDP_BASE + 0x000a0, 0x10); + /* disable dsi trigger */ + MDP_OUTP(MDP_BASE + 0x000a4, 0x00); + /* whole screen for base layer */ + src = (uint8 *) iBuf->buf; + + + { + struct fb_info *fbi; + + fbi = mfd->fbi; + if (pipe->is_3d) { + bpp = fbi->var.bits_per_pixel / 8; + pipe->src_height = pipe->src_height_3d; + pipe->src_width = pipe->src_width_3d; + pipe->src_h = pipe->src_height_3d; + pipe->src_w = pipe->src_width_3d; + pipe->dst_h = pipe->src_height_3d; + pipe->dst_w = pipe->src_width_3d; + pipe->srcp0_ystride = msm_fb_line_length(0, + pipe->src_width, bpp); + } else { + /* 2D */ + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->dst_h = fbi->var.yres; + pipe->dst_w = fbi->var.xres; + pipe->srcp0_ystride = fbi->fix.line_length; + } + pipe->src_y = 0; + pipe->src_x = 0; + pipe->dst_y = 0; + pipe->dst_x = 0; + pipe->srcp0_addr = (uint32)src; + } + + + mdp4_overlay_rgb_setup(pipe); + + mdp4_overlay_reg_flush(pipe, 1); + + mdp4_mixer_stage_up(pipe); + + mdp4_overlayproc_cfg(pipe); + + mdp4_overlay_dmap_xy(pipe); + + mdp4_overlay_dmap_cfg(mfd, 0); + + mdp4_mipi_vsync_enable(mfd, pipe, 0); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + wmb(); +} + +/* 3D side by side */ +void mdp4_dsi_cmd_3d_sbys(struct msm_fb_data_type *mfd, + struct msmfb_overlay_3d *r3d) +{ + struct fb_info *fbi; + struct mdp4_overlay_pipe *pipe; + int bpp; + uint8 *src = NULL; + + if (dsi_pipe == NULL) + return; + + dsi_pipe->is_3d = r3d->is_3d; + dsi_pipe->src_height_3d = r3d->height; + dsi_pipe->src_width_3d = r3d->width; + + pipe = dsi_pipe; + if (pipe->pipe_used == 0 || + pipe->mixer_stage != MDP4_MIXER_STAGE_BASE) { + pr_err("%s: NOT baselayer\n", __func__); + mutex_unlock(&mfd->dma->ov_mutex); + return; + } + + if (pipe->is_3d) + mdp4_overlay_panel_3d(pipe->mixer_num, MDP4_3D_SIDE_BY_SIDE); + else + mdp4_overlay_panel_3d(pipe->mixer_num, MDP4_3D_NONE); + + if (mfd->panel_power_on) { + mdp4_dsi_cmd_dma_busy_wait(mfd); + mdp4_dsi_blt_dmap_busy_wait(mfd); + } + + fbi = mfd->fbi; + if (pipe->is_3d) { + bpp = fbi->var.bits_per_pixel / 8; + pipe->src_height = pipe->src_height_3d; + pipe->src_width = pipe->src_width_3d; + pipe->src_h = pipe->src_height_3d; + pipe->src_w = pipe->src_width_3d; + pipe->dst_h = pipe->src_height_3d; + pipe->dst_w = pipe->src_width_3d; + pipe->srcp0_ystride = msm_fb_line_length(0, + pipe->src_width, bpp); + } else { + /* 2D */ + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->dst_h = fbi->var.yres; + pipe->dst_w = fbi->var.xres; + pipe->srcp0_ystride = fbi->fix.line_length; + } + pipe->src_y = 0; + pipe->src_x = 0; + pipe->dst_y = 0; + pipe->dst_x = 0; + pipe->srcp0_addr = (uint32)src; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + mdp4_overlay_rgb_setup(pipe); + + mdp4_overlay_reg_flush(pipe, 1); + + mdp4_mixer_stage_up(pipe); + + mdp4_overlayproc_cfg(pipe); + + mdp4_overlay_dmap_xy(pipe); + + mdp4_overlay_dmap_cfg(mfd, 0); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + + pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n", + __func__, dsi_pipe->blt_end, (int)dsi_pipe->blt_addr, current->pid); + + mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); + + if (mfd->ov0_wb_buf->phys_addr == 0) { + pr_info("%s: no blt_base assigned\n", __func__); + return -EBUSY; + } + + if (dsi_pipe->blt_addr == 0) { + mdp4_dsi_cmd_dma_busy_wait(mfd); + spin_lock_irqsave(&mdp_spin_lock, flag); + dsi_pipe->blt_end = 0; + dsi_pipe->blt_cnt = 0; + dsi_pipe->ov_cnt = 0; + dsi_pipe->dmap_cnt = 0; + dsi_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr; + mdp4_stat.blt_dsi_cmd++; + spin_unlock_irqrestore(&mdp_spin_lock, flag); + return 0; + } + + return -EBUSY; +} + +int mdp4_dsi_overlay_blt_stop(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + + + pr_debug("%s: blt_end=%d blt_addr=%x\n", + __func__, dsi_pipe->blt_end, (int)dsi_pipe->blt_addr); + + if ((dsi_pipe->blt_end == 0) && dsi_pipe->blt_addr) { + spin_lock_irqsave(&mdp_spin_lock, flag); + dsi_pipe->blt_end = 1; /* mark as end */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + return 0; + } + + return -EBUSY; +} + +int mdp4_dsi_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + req->offset = 0; + req->width = dsi_pipe->src_width; + req->height = dsi_pipe->src_height; + req->bpp = dsi_pipe->bpp; + + return sizeof(*req); +} + +void mdp4_dsi_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + if (req->enable) + mdp4_dsi_overlay_blt_start(mfd); + else if (req->enable == 0) + mdp4_dsi_overlay_blt_stop(mfd); + +} + +void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr, addr2; + int bpp; + char *overlay_base; + + + if (pipe->blt_addr == 0) + return; + + +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->dmap_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr = pipe->blt_addr + off; + + /* dmap */ + MDP_OUTP(MDP_BASE + 0x90008, addr); + + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr2 = pipe->blt_addr + off; + /* overlay 0 */ + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + outpdw(overlay_base + 0x000c, addr2); + outpdw(overlay_base + 0x001c, addr2); +} + + +/* + * mdp4_dmap_done_dsi: called from isr + * DAM_P_DONE only used when blt enabled + */ +void mdp4_dma_p_done_dsi(struct mdp_dma_data *dma) +{ + int diff; + + dsi_pipe->dmap_cnt++; + diff = dsi_pipe->ov_cnt - dsi_pipe->dmap_cnt; + pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n", + __func__, dsi_pipe->ov_cnt, dsi_pipe->dmap_cnt); + + if (diff <= 0) { + spin_lock(&mdp_spin_lock); + dma->dmap_busy = FALSE; + complete(&dma->dmap_comp); + spin_unlock(&mdp_spin_lock); + if (dsi_pipe->blt_end) { + dsi_pipe->blt_end = 0; + dsi_pipe->blt_addr = 0; + pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n", + __func__, dsi_pipe->ov_cnt, dsi_pipe->dmap_cnt); + mdp_intr_mask &= ~INTR_DMA_P_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + } + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); + mdp_disable_irq_nosync(MDP_DMA2_TERM); /* disable intr */ + return; + } + + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); + if (busy_wait_cnt) + busy_wait_cnt--; + + pr_debug("%s: kickoff dmap\n", __func__); + + mdp4_blt_xy_update(dsi_pipe); + /* kick off dmap */ + outpdw(MDP_BASE + 0x000c, 0x0); + mdp4_stat.kickoff_dmap++; + /* trigger dsi cmd engine */ + mipi_dsi_cmd_mdp_start(); + + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); +} + + +/* + * mdp4_overlay0_done_dsi_cmd: called from isr + */ +void mdp4_overlay0_done_dsi_cmd(struct mdp_dma_data *dma) +{ + int diff; + + if (dsi_pipe->blt_addr == 0) { + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); + if (busy_wait_cnt) + busy_wait_cnt--; + mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); + return; + } + + /* blt enabled */ + if (dsi_pipe->blt_end == 0) + dsi_pipe->ov_cnt++; + + pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n", + __func__, dsi_pipe->ov_cnt, dsi_pipe->dmap_cnt); + + if (dsi_pipe->blt_cnt == 0) { + /* first kickoff since blt enabled */ + mdp_intr_mask |= INTR_DMA_P_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + } + dsi_pipe->blt_cnt++; + + diff = dsi_pipe->ov_cnt - dsi_pipe->dmap_cnt; + if (diff >= 2) { + mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); + return; + } + + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + dma->dmap_busy = TRUE; + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); + if (busy_wait_cnt) + busy_wait_cnt--; + + pr_debug("%s: kickoff dmap\n", __func__); + + mdp4_blt_xy_update(dsi_pipe); + mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */ + /* kick off dmap */ + outpdw(MDP_BASE + 0x000c, 0x0); + mdp4_stat.kickoff_dmap++; + /* trigger dsi cmd engine */ + mipi_dsi_cmd_mdp_start(); + mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); +} + +void mdp4_dsi_cmd_overlay_restore(void) +{ + /* mutex holded by caller */ + if (dsi_mfd && dsi_pipe) { + mdp4_dsi_cmd_dma_busy_wait(dsi_mfd); + mipi_dsi_mdp_busy_wait(dsi_mfd); + mdp4_overlay_update_dsi_cmd(dsi_mfd); + + if (dsi_pipe->blt_addr) + mdp4_dsi_blt_dmap_busy_wait(dsi_mfd); + mdp4_dsi_cmd_overlay_kickoff(dsi_mfd, dsi_pipe); + } +} + +void mdp4_dsi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (mfd->dma->dmap_busy == TRUE) { + INIT_COMPLETION(mfd->dma->dmap_comp); + need_wait++; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (need_wait) { + /* wait until DMA finishes the current job */ + wait_for_completion(&mfd->dma->dmap_comp); + } +} + +/* + * mdp4_dsi_cmd_dma_busy_wait: check dsi link activity + * dsi link is a shared resource and it can only be used + * while it is in idle state. + * ov_mutex need to be acquired before call this function. + */ +void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + + + + if (dsi_clock_timer.function) { + if (time_after(jiffies, tout_expired)) { + tout_expired = jiffies + TOUT_PERIOD; + mod_timer(&dsi_clock_timer, tout_expired); + tout_expired -= MS_100; + } + } + + pr_debug("%s: start pid=%d dsi_clk_on=%d\n", + __func__, current->pid, mipi_dsi_clk_on); + + /* satrt dsi clock if necessary */ + if (mipi_dsi_clk_on == 0) { + local_bh_disable(); + mipi_dsi_turn_on_clks(); + local_bh_enable(); + } + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (mfd->dma->busy == TRUE) { + if (busy_wait_cnt == 0) + INIT_COMPLETION(mfd->dma->comp); + busy_wait_cnt++; + need_wait++; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (need_wait) { + /* wait until DMA finishes the current job */ + pr_debug("%s: pending pid=%d dsi_clk_on=%d\n", + __func__, current->pid, mipi_dsi_clk_on); + wait_for_completion(&mfd->dma->comp); + } + pr_debug("%s: done pid=%d dsi_clk_on=%d\n", + __func__, current->pid, mipi_dsi_clk_on); +} + +void mdp4_dsi_cmd_kickoff_video(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + /* + * a video kickoff may happen before UI kickoff after + * blt enabled. mdp4_overlay_update_dsi_cmd() need + * to be called before kickoff. + * vice versa for blt disabled. + */ + if (dsi_pipe->blt_addr && dsi_pipe->blt_cnt == 0) + mdp4_overlay_update_dsi_cmd(mfd); /* first time */ + else if (dsi_pipe->blt_addr == 0 && dsi_pipe->blt_cnt) { + mdp4_overlay_update_dsi_cmd(mfd); /* last time */ + dsi_pipe->blt_cnt = 0; + } + + pr_debug("%s: blt_addr=%d blt_cnt=%d\n", + __func__, (int)dsi_pipe->blt_addr, dsi_pipe->blt_cnt); + + if (dsi_pipe->blt_addr) + mdp4_dsi_blt_dmap_busy_wait(dsi_mfd); + + mdp4_dsi_cmd_overlay_kickoff(mfd, pipe); +} + +void mdp4_dsi_cmd_kickoff_ui(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + + pr_debug("%s: pid=%d\n", __func__, current->pid); + mdp4_dsi_cmd_overlay_kickoff(mfd, pipe); +} + + +void mdp4_dsi_cmd_overlay_kickoff(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + unsigned long flag; + + + /* change mdp clk */ + mdp4_set_perf_level(); + + mipi_dsi_mdp_busy_wait(mfd); + + if (dsi_pipe->blt_addr == 0) + mipi_dsi_cmd_mdp_start(); + + mdp4_overlay_dsi_state_set(ST_DSI_PLAYING); + + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(MDP_OVERLAY0_TERM); + mfd->dma->busy = TRUE; + if (dsi_pipe->blt_addr) + mfd->dma->dmap_busy = TRUE; + /* start OVERLAY pipe */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + mdp_pipe_kickoff(MDP_OVERLAY0_TERM, mfd); + mdp4_stat.kickoff_ov0++; +} + +void mdp_dsi_cmd_overlay_suspend(struct msm_fb_data_type *mfd) +{ + /* dis-engage rgb0 from mixer0 */ + if (dsi_pipe) { + if (mfd->ref_cnt == 0) { + /* adb stop */ + if (dsi_pipe->pipe_type == OVERLAY_TYPE_BF) + mdp4_overlay_borderfill_stage_down(dsi_pipe); + + /* dsi_pipe == rgb1 */ + mdp4_overlay_unset_mixer(dsi_pipe->mixer_num); + dsi_pipe = NULL; + } else { + mdp4_mixer_stage_down(dsi_pipe); + mdp4_iommu_unmap(dsi_pipe); + } + } +} + +void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd) +{ + mutex_lock(&mfd->dma->ov_mutex); + + if (mfd && mfd->panel_power_on) { + mdp4_dsi_cmd_dma_busy_wait(mfd); + + if (dsi_pipe && dsi_pipe->blt_addr) + mdp4_dsi_blt_dmap_busy_wait(mfd); + + mdp4_overlay_update_dsi_cmd(mfd); + + mdp4_iommu_attach(); + mdp4_dsi_cmd_kickoff_ui(mfd, dsi_pipe); + mdp4_iommu_unmap(dsi_pipe); + /* signal if pan function is waiting for the update completion */ + if (mfd->pan_waiting) { + mfd->pan_waiting = FALSE; + complete(&mfd->pan_comp); + } + } + mutex_unlock(&mfd->dma->ov_mutex); +} diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c new file mode 100644 index 0000000000000000000000000000000000000000..e9befef1522e7c079ed57ac64de16882b5d0168c --- /dev/null +++ b/drivers/video/msm/mdp4_overlay_dsi_video.c @@ -0,0 +1,729 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" +#include "mipi_dsi.h" + +#include + +#define DSI_VIDEO_BASE 0xE0000 + +static int first_pixel_start_x; +static int first_pixel_start_y; +static int dsi_video_enabled; + +static struct mdp4_overlay_pipe *dsi_pipe; +static struct completion dsi_video_comp; +static int blt_cfg_changed; + +static cmd_fxn_t display_on; + +static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp) +{ + /* + * The adreno GPU hardware requires that the pitch be aligned to + * 32 pixels for color buffers, so for the cases where the GPU + * is writing directly to fb0, the framebuffer pitch + * also needs to be 32 pixel aligned + */ + + if (fb_index == 0) + return ALIGN(xres, 32) * bpp; + else + return xres * bpp; +} + +void mdp4_dsi_video_fxn_register(cmd_fxn_t fxn) +{ + display_on = fxn; +} + +static void mdp4_overlay_dsi_video_wait4event(struct msm_fb_data_type *mfd, + int intr_done); + +void mdp4_dsi_video_base_swap(struct mdp4_overlay_pipe *pipe) +{ + dsi_pipe = pipe; +} + +int mdp4_dsi_video_on(struct platform_device *pdev) +{ + int dsi_width; + int dsi_height; + int dsi_bpp; + int dsi_border_clr; + int dsi_underflow_clr; + int dsi_hsync_skew; + + int hsync_period; + int hsync_ctrl; + int vsync_period; + int display_hctl; + int display_v_start; + int display_v_end; + int active_hctl; + int active_h_start; + int active_h_end; + int active_v_start; + int active_v_end; + int ctrl_polarity; + int h_back_porch; + int h_front_porch; + int v_back_porch; + int v_front_porch; + int hsync_pulse_width; + int vsync_pulse_width; + int hsync_polarity; + int vsync_polarity; + int data_en_polarity; + int hsync_start_x; + int hsync_end_x; + uint8 *buf; + unsigned int buf_offset; + int bpp, ptype; + struct fb_info *fbi; + struct fb_var_screeninfo *var; + struct msm_fb_data_type *mfd; + struct mdp4_overlay_pipe *pipe; + int ret; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + fbi = mfd->fbi; + var = &fbi->var; + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf_offset = calc_fb_offset(mfd, fbi, bpp); + + if (dsi_pipe == NULL) { + ptype = mdp4_overlay_format2type(mfd->fb_imgType); + if (ptype < 0) + printk(KERN_INFO "%s: format2type failed\n", __func__); + pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0); + if (pipe == NULL) { + printk(KERN_INFO "%s: pipe_alloc failed\n", __func__); + return -EBUSY; + } + pipe->pipe_used++; + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + pipe->mixer_num = MDP4_MIXER0; + pipe->src_format = mfd->fb_imgType; + mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_DSI_VIDEO); + ret = mdp4_overlay_format2pipe(pipe); + if (ret < 0) + printk(KERN_INFO "%s: format2type failed\n", __func__); + + dsi_pipe = pipe; /* keep it */ + init_completion(&dsi_video_comp); + + mdp4_init_writeback_buf(mfd, MDP4_MIXER0); + pipe->blt_addr = 0; + + } else { + pipe = dsi_pipe; + } + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + if (!(mfd->cont_splash_done)) { + mfd->cont_splash_done = 1; + mdp_pipe_ctrl(MDP_CMD_BLOCK, + MDP_BLOCK_POWER_OFF, FALSE); + mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE); + /* disable timing generator */ + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0); + mipi_dsi_controller_cfg(0); + } + + if (is_mdp4_hw_reset()) { + mdp4_hw_init(); + outpdw(MDP_BASE + 0x0038, mdp4_display_intf); + } + + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->src_y = 0; + pipe->src_x = 0; + pipe->srcp0_ystride = fbi->fix.line_length; + pipe->bpp = bpp; + + if (mfd->display_iova) + pipe->srcp0_addr = mfd->display_iova + buf_offset; + else + pipe->srcp0_addr = (uint32)(buf + buf_offset); + + pipe->dst_h = fbi->var.yres; + pipe->dst_w = fbi->var.xres; + + mdp4_overlay_dmap_xy(pipe); /* dma_p */ + mdp4_overlay_dmap_cfg(mfd, 1); + mdp4_overlay_rgb_setup(pipe); + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + + mdp4_overlayproc_cfg(pipe); + + /* + * DSI timing setting + */ + h_back_porch = var->left_margin; + h_front_porch = var->right_margin; + v_back_porch = var->upper_margin; + v_front_porch = var->lower_margin; + hsync_pulse_width = var->hsync_len; + vsync_pulse_width = var->vsync_len; + dsi_border_clr = mfd->panel_info.lcdc.border_clr; + dsi_underflow_clr = mfd->panel_info.lcdc.underflow_clr; + dsi_hsync_skew = mfd->panel_info.lcdc.hsync_skew; + dsi_width = mfd->panel_info.xres + + mfd->panel_info.lcdc.xres_pad; + dsi_height = mfd->panel_info.yres + + mfd->panel_info.lcdc.yres_pad; + dsi_bpp = mfd->panel_info.bpp; + + hsync_period = hsync_pulse_width + h_back_porch + dsi_width + + h_front_porch; + hsync_ctrl = (hsync_period << 16) | hsync_pulse_width; + hsync_start_x = h_back_porch + hsync_pulse_width; + hsync_end_x = hsync_period - h_front_porch - 1; + display_hctl = (hsync_end_x << 16) | hsync_start_x; + + vsync_period = + (vsync_pulse_width + v_back_porch + dsi_height + v_front_porch); + display_v_start = ((vsync_pulse_width + v_back_porch) * hsync_period) + + dsi_hsync_skew; + display_v_end = + ((vsync_period - v_front_porch) * hsync_period) + dsi_hsync_skew - 1; + + if (dsi_width != var->xres) { + active_h_start = hsync_start_x + first_pixel_start_x; + active_h_end = active_h_start + var->xres - 1; + active_hctl = + ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start; + } else { + active_hctl = 0; + } + + if (dsi_height != var->yres) { + active_v_start = + display_v_start + first_pixel_start_y * hsync_period; + active_v_end = active_v_start + (var->yres) * hsync_period - 1; + active_v_start |= ACTIVE_START_Y_EN; + } else { + active_v_start = 0; + active_v_end = 0; + } + + dsi_underflow_clr |= 0x80000000; /* enable recovery */ + hsync_polarity = 0; + vsync_polarity = 0; + data_en_polarity = 0; + + ctrl_polarity = + (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity); + + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x4, hsync_ctrl); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x8, vsync_period * hsync_period); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0xc, + vsync_pulse_width * hsync_period); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x10, display_hctl); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x14, display_v_start); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x18, display_v_end); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x1c, active_hctl); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x20, active_v_start); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x24, active_v_end); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x28, dsi_border_clr); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x2c, dsi_underflow_clr); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x30, dsi_hsync_skew); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x38, ctrl_polarity); + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + + mdp_histogram_ctrl_all(TRUE); + + ret = panel_next_on(pdev); + if (ret == 0) { + if (display_on != NULL) { + msleep(50); + display_on(pdev); + } + } + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return ret; +} + +int mdp4_dsi_video_off(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0); + dsi_video_enabled = 0; + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_histogram_ctrl_all(FALSE); + ret = panel_next_off(pdev); + + /* delay to make sure the last frame finishes */ + msleep(20); + + /* dis-engage rgb0 from mixer0 */ + if (dsi_pipe) { + if (mfd->ref_cnt == 0) { + /* adb stop */ + if (dsi_pipe->pipe_type == OVERLAY_TYPE_BF) + mdp4_overlay_borderfill_stage_down(dsi_pipe); + + /* dsi_pipe == rgb1 */ + mdp4_overlay_unset_mixer(dsi_pipe->mixer_num); + dsi_pipe = NULL; + } else { + mdp4_mixer_stage_down(dsi_pipe); + mdp4_iommu_unmap(dsi_pipe); + } + } + + return ret; +} + +/* 3D side by side */ +void mdp4_dsi_video_3d_sbys(struct msm_fb_data_type *mfd, + struct msmfb_overlay_3d *r3d) +{ + struct fb_info *fbi; + struct mdp4_overlay_pipe *pipe; + unsigned int buf_offset; + int bpp; + uint8 *buf = NULL; + + if (dsi_pipe == NULL) + return; + + dsi_pipe->is_3d = r3d->is_3d; + dsi_pipe->src_height_3d = r3d->height; + dsi_pipe->src_width_3d = r3d->width; + + pipe = dsi_pipe; + + if (pipe->is_3d) + mdp4_overlay_panel_3d(pipe->mixer_num, MDP4_3D_SIDE_BY_SIDE); + else + mdp4_overlay_panel_3d(pipe->mixer_num, MDP4_3D_NONE); + + fbi = mfd->fbi; + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf_offset = calc_fb_offset(mfd, fbi, bpp); + + if (pipe->is_3d) { + pipe->src_height = pipe->src_height_3d; + pipe->src_width = pipe->src_width_3d; + pipe->src_h = pipe->src_height_3d; + pipe->src_w = pipe->src_width_3d; + pipe->dst_h = pipe->src_height_3d; + pipe->dst_w = pipe->src_width_3d; + pipe->srcp0_ystride = msm_fb_line_length(0, + pipe->src_width, bpp); + } else { + /* 2D */ + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->dst_h = fbi->var.yres; + pipe->dst_w = fbi->var.xres; + pipe->srcp0_ystride = fbi->fix.line_length; + } + + pipe->src_y = 0; + pipe->src_x = 0; + pipe->dst_y = 0; + pipe->dst_x = 0; + + if (mfd->display_iova) + pipe->srcp0_addr = mfd->display_iova + buf_offset; + else + pipe->srcp0_addr = (uint32)(buf + buf_offset); + + mdp4_overlay_rgb_setup(pipe); + + mdp4_overlayproc_cfg(pipe); + + mdp4_overlay_dmap_xy(pipe); + + mdp4_overlay_dmap_cfg(mfd, 1); + + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + + mb(); + + /* wait for vsycn */ + mdp4_overlay_dsi_video_vsync_push(mfd, pipe); +} + +static void mdp4_dsi_video_blt_ov_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr; + int bpp; + char *overlay_base; + + + if (pipe->blt_addr == 0) + return; + + +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr = pipe->blt_addr + off; + + /* overlay 0 */ + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + outpdw(overlay_base + 0x000c, addr); + outpdw(overlay_base + 0x001c, addr); +} + +static void mdp4_dsi_video_blt_dmap_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr; + int bpp; + + if (pipe->blt_addr == 0) + return; + + +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->dmap_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr = pipe->blt_addr + off; + + /* dmap */ + MDP_OUTP(MDP_BASE + 0x90008, addr); +} + +/* + * mdp4_overlay_dsi_video_wait4event: + * INTR_DMA_P_DONE and INTR_PRIMARY_VSYNC event only + * no INTR_OVERLAY0_DONE event allowed. + */ +static void mdp4_overlay_dsi_video_wait4event(struct msm_fb_data_type *mfd, + int intr_done) +{ + unsigned long flag; + unsigned int data; + + data = inpdw(MDP_BASE + DSI_VIDEO_BASE); + data &= 0x01; + if (data == 0) /* timing generator disabled */ + return; + + spin_lock_irqsave(&mdp_spin_lock, flag); + INIT_COMPLETION(dsi_video_comp); + mfd->dma->waiting = TRUE; + outp32(MDP_INTR_CLEAR, intr_done); + mdp_intr_mask |= intr_done; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + wait_for_completion(&dsi_video_comp); + mdp_disable_irq(MDP_DMA2_TERM); +} + +static void mdp4_overlay_dsi_video_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + + pr_debug("%s: start pid=%d\n", __func__, current->pid); + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (mfd->dma->busy == TRUE) { + INIT_COMPLETION(mfd->dma->comp); + need_wait++; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (need_wait) { + /* wait until DMA finishes the current job */ + pr_debug("%s: pending pid=%d\n", __func__, current->pid); + wait_for_completion(&mfd->dma->comp); + } + pr_debug("%s: done pid=%d\n", __func__, current->pid); +} + +void mdp4_overlay_dsi_video_start(void) +{ + if (!dsi_video_enabled) { + /* enable DSI block */ + mdp4_iommu_attach(); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1); + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + dsi_video_enabled = 1; + } +} + +void mdp4_overlay_dsi_video_vsync_push(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + unsigned long flag; + + if (pipe->flags & MDP_OV_PLAY_NOWAIT) + return; + + if (dsi_pipe->blt_addr) { + mdp4_overlay_dsi_video_dma_busy_wait(mfd); + + mdp4_dsi_video_blt_ov_update(dsi_pipe); + dsi_pipe->ov_cnt++; + + spin_lock_irqsave(&mdp_spin_lock, flag); + outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE); + mdp_intr_mask |= INTR_OVERLAY0_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_enable_irq(MDP_OVERLAY0_TERM); + mfd->dma->busy = TRUE; + mb(); /* make sure all registers updated */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + outpdw(MDP_BASE + 0x0004, 0); /* kickoff overlay engine */ + mdp4_stat.kickoff_ov0++; + mb(); + mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE); + } else { + mdp4_overlay_dsi_video_wait4event(mfd, INTR_PRIMARY_VSYNC); + } + + mdp4_set_perf_level(); +} + +/* + * mdp4_primary_vsync_dsi_video: called from isr + */ +void mdp4_primary_vsync_dsi_video(void) +{ + complete_all(&dsi_video_comp); +} + + /* + * mdp4_dma_p_done_dsi_video: called from isr + */ +void mdp4_dma_p_done_dsi_video(struct mdp_dma_data *dma) +{ + if (blt_cfg_changed) { + mdp_is_in_isr = TRUE; + mdp4_overlayproc_cfg(dsi_pipe); + mdp4_overlay_dmap_xy(dsi_pipe); + mdp_is_in_isr = FALSE; + if (dsi_pipe->blt_addr) { + mdp4_dsi_video_blt_ov_update(dsi_pipe); + dsi_pipe->ov_cnt++; + outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE); + mdp_intr_mask |= INTR_OVERLAY0_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->busy = TRUE; + mdp_enable_irq(MDP_OVERLAY0_TERM); + /* kickoff overlay engine */ + outpdw(MDP_BASE + 0x0004, 0); + } + blt_cfg_changed = 0; + } + complete_all(&dsi_video_comp); +} + +/* + * mdp4_overlay1_done_dsi: called from isr + */ +void mdp4_overlay0_done_dsi_video(struct mdp_dma_data *dma) +{ + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + if (dsi_pipe->blt_addr == 0) { + spin_unlock(&mdp_spin_lock); + return; + } + mdp4_dsi_video_blt_dmap_update(dsi_pipe); + dsi_pipe->dmap_cnt++; + mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); +} + +/* + * make sure the MIPI_DSI_WRITEBACK_SIZE defined at boardfile + * has enough space h * w * 3 * 2 + */ +static void mdp4_dsi_video_do_blt(struct msm_fb_data_type *mfd, int enable) +{ + unsigned long flag; + int data; + int change = 0; + + mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); + + if (mfd->ov0_wb_buf->phys_addr == 0) { + pr_info("%s: no blt_base assigned\n", __func__); + return; + } + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (enable && dsi_pipe->blt_addr == 0) { + dsi_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr; + dsi_pipe->blt_cnt = 0; + dsi_pipe->ov_cnt = 0; + dsi_pipe->dmap_cnt = 0; + mdp4_stat.blt_dsi_video++; + change++; + } else if (enable == 0 && dsi_pipe->blt_addr) { + dsi_pipe->blt_addr = 0; + change++; + } + + if (!change) { + spin_unlock_irqrestore(&mdp_spin_lock, flag); + return; + } + + pr_debug("%s: enable=%d blt_addr=%x\n", __func__, + enable, (int)dsi_pipe->blt_addr); + blt_cfg_changed = 1; + + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + /* + * may need mutex here to sync with whom dsiable + * timing generator + */ + data = inpdw(MDP_BASE + DSI_VIDEO_BASE); + data &= 0x01; + if (data) { /* timing generator enabled */ + mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE); + mdp4_overlay_dsi_video_wait4event(mfd, INTR_PRIMARY_VSYNC); + } + + +} + +int mdp4_dsi_video_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + req->offset = 0; + req->width = dsi_pipe->src_width; + req->height = dsi_pipe->src_height; + req->bpp = dsi_pipe->bpp; + + return sizeof(*req); +} + +void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + mdp4_dsi_video_do_blt(mfd, req->enable); +} + +void mdp4_dsi_video_blt_start(struct msm_fb_data_type *mfd) +{ + mdp4_dsi_video_do_blt(mfd, 1); +} + +void mdp4_dsi_video_blt_stop(struct msm_fb_data_type *mfd) +{ + mdp4_dsi_video_do_blt(mfd, 0); +} + +void mdp4_dsi_video_overlay(struct msm_fb_data_type *mfd) +{ + struct fb_info *fbi = mfd->fbi; + uint8 *buf; + unsigned int buf_offset; + int bpp; + struct mdp4_overlay_pipe *pipe; + + if (!mfd->panel_power_on) + return; + + /* no need to power on cmd block since it's dsi video mode */ + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf_offset = calc_fb_offset(mfd, fbi, bpp); + + mutex_lock(&mfd->dma->ov_mutex); + + pipe = dsi_pipe; + if (pipe->pipe_used == 0 || + pipe->mixer_stage != MDP4_MIXER_STAGE_BASE) { + pr_err("%s: NOT baselayer\n", __func__); + mutex_unlock(&mfd->dma->ov_mutex); + return; + } + + if (mfd->display_iova) + pipe->srcp0_addr = mfd->display_iova + buf_offset; + else + pipe->srcp0_addr = (uint32)(buf + buf_offset); + + mdp4_overlay_rgb_setup(pipe); + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + mdp4_overlay_dsi_video_start(); + mdp4_overlay_dsi_video_vsync_push(mfd, pipe); + mdp4_iommu_unmap(pipe); + mutex_unlock(&mfd->dma->ov_mutex); +} diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c new file mode 100644 index 0000000000000000000000000000000000000000..534daa487e333d6cbe241c76e65765262308e2f9 --- /dev/null +++ b/drivers/video/msm/mdp4_overlay_dtv.c @@ -0,0 +1,728 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + +#define DTV_BASE 0xD0000 + +/*#define DEBUG*/ +#ifdef DEBUG +static void __mdp_outp(uint32 port, uint32 value) +{ + uint32 in_val; + + outpdw(port, value); + in_val = inpdw(port); + printk(KERN_INFO "MDP-DTV[%04x] => %08x [%08x]\n", + port-(uint32)(MDP_BASE + DTV_BASE), value, in_val); +} + +#undef MDP_OUTP +#define MDP_OUTP(port, value) __mdp_outp((uint32)(port), (value)) +#endif + +static int first_pixel_start_x; +static int first_pixel_start_y; +static int dtv_enabled; + +static struct mdp4_overlay_pipe *dtv_pipe; +static DECLARE_COMPLETION(dtv_comp); + +void mdp4_dtv_base_swap(struct mdp4_overlay_pipe *pipe) +{ + if (hdmi_prim_display) + dtv_pipe = pipe; +} + +static int mdp4_dtv_start(struct msm_fb_data_type *mfd) +{ + int dtv_width; + int dtv_height; + int dtv_bpp; + int dtv_border_clr; + int dtv_underflow_clr; + int dtv_hsync_skew; + + int hsync_period; + int hsync_ctrl; + int vsync_period; + int display_hctl; + int display_v_start; + int display_v_end; + int active_hctl; + int active_h_start; + int active_h_end; + int active_v_start; + int active_v_end; + int ctrl_polarity; + int h_back_porch; + int h_front_porch; + int v_back_porch; + int v_front_porch; + int hsync_pulse_width; + int vsync_pulse_width; + int hsync_polarity; + int vsync_polarity; + int data_en_polarity; + int hsync_start_x; + int hsync_end_x; + struct fb_info *fbi; + struct fb_var_screeninfo *var; + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (dtv_pipe == NULL) + return -EINVAL; + + fbi = mfd->fbi; + var = &fbi->var; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + if (hdmi_prim_display) { + if (is_mdp4_hw_reset()) { + mdp4_hw_init(); + outpdw(MDP_BASE + 0x0038, mdp4_display_intf); + } + } + mdp4_overlay_dmae_cfg(mfd, 0); + + /* + * DTV timing setting + */ + h_back_porch = var->left_margin; + h_front_porch = var->right_margin; + v_back_porch = var->upper_margin; + v_front_porch = var->lower_margin; + hsync_pulse_width = var->hsync_len; + vsync_pulse_width = var->vsync_len; + dtv_border_clr = mfd->panel_info.lcdc.border_clr; + dtv_underflow_clr = mfd->panel_info.lcdc.underflow_clr; + dtv_hsync_skew = mfd->panel_info.lcdc.hsync_skew; + + pr_info("%s: \n", __func__, + var->reserved[3], var->xres, var->yres, + var->right_margin, var->hsync_len, var->left_margin, + var->lower_margin, var->vsync_len, var->upper_margin, + var->pixclock/1000/1000); + + dtv_width = var->xres; + dtv_height = var->yres; + dtv_bpp = mfd->panel_info.bpp; + + hsync_period = + hsync_pulse_width + h_back_porch + dtv_width + h_front_porch; + hsync_ctrl = (hsync_period << 16) | hsync_pulse_width; + hsync_start_x = hsync_pulse_width + h_back_porch; + hsync_end_x = hsync_period - h_front_porch - 1; + display_hctl = (hsync_end_x << 16) | hsync_start_x; + + vsync_period = + (vsync_pulse_width + v_back_porch + dtv_height + + v_front_porch) * hsync_period; + display_v_start = + (vsync_pulse_width + v_back_porch) * hsync_period + dtv_hsync_skew; + display_v_end = + vsync_period - (v_front_porch * hsync_period) + dtv_hsync_skew - 1; + + if (dtv_width != var->xres) { + active_h_start = hsync_start_x + first_pixel_start_x; + active_h_end = active_h_start + var->xres - 1; + active_hctl = + ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start; + } else { + active_hctl = 0; + } + + if (dtv_height != var->yres) { + active_v_start = + display_v_start + first_pixel_start_y * hsync_period; + active_v_end = active_v_start + (var->yres) * hsync_period - 1; + active_v_start |= ACTIVE_START_Y_EN; + } else { + active_v_start = 0; + active_v_end = 0; + } + + dtv_underflow_clr |= 0x80000000; /* enable recovery */ + hsync_polarity = fbi->var.yres >= 720 ? 0 : 1; + vsync_polarity = fbi->var.yres >= 720 ? 0 : 1; + data_en_polarity = 0; + + ctrl_polarity = + (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity); + + + MDP_OUTP(MDP_BASE + DTV_BASE + 0x4, hsync_ctrl); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x8, vsync_period); + MDP_OUTP(MDP_BASE + DTV_BASE + 0xc, vsync_pulse_width * hsync_period); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x18, display_hctl); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x1c, display_v_start); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x20, display_v_end); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x40, dtv_border_clr); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x44, dtv_underflow_clr); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x48, dtv_hsync_skew); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x50, ctrl_polarity); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x2c, active_hctl); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x30, active_v_start); + MDP_OUTP(MDP_BASE + DTV_BASE + 0x38, active_v_end); + + /* Test pattern 8 x 8 pixel */ + /* MDP_OUTP(MDP_BASE + DTV_BASE + 0x4C, 0x80000808); */ + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return 0; +} + +static int mdp4_dtv_stop(struct msm_fb_data_type *mfd) +{ + if (dtv_pipe == NULL) + return -EINVAL; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + msleep(20); + MDP_OUTP(MDP_BASE + DTV_BASE, 0); + dtv_enabled = 0; + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return 0; +} + +int mdp4_dtv_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + int ret = 0; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + mdp_footswitch_ctrl(TRUE); + mdp4_overlay_panel_mode(MDP4_MIXER1, MDP4_PANEL_DTV); + + /* Allocate dtv_pipe at dtv_on*/ + if (dtv_pipe == NULL) { + if (mdp4_overlay_dtv_set(mfd, NULL)) { + pr_warn("%s: dtv_pipe is NULL, dtv_set failed\n", + __func__); + return -EINVAL; + } + } + + ret = panel_next_on(pdev); + if (ret != 0) + dev_warn(&pdev->dev, "mdp4_overlay_dtv: panel_next_on failed"); + + dev_info(&pdev->dev, "mdp4_overlay_dtv: on"); + + return ret; +} + +int mdp4_dtv_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + int ret = 0; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (dtv_pipe != NULL) { + mdp4_dtv_stop(mfd); + if (hdmi_prim_display && mfd->ref_cnt == 0) { + /* adb stop */ + if (dtv_pipe->pipe_type == OVERLAY_TYPE_BF) + mdp4_overlay_borderfill_stage_down(dtv_pipe); + + /* dtv_pipe == rgb1 */ + mdp4_overlay_unset_mixer(dtv_pipe->mixer_num); + dtv_pipe = NULL; + } else { + mdp4_mixer_stage_down(dtv_pipe); + mdp4_overlay_pipe_free(dtv_pipe); + mdp4_iommu_unmap(dtv_pipe); + dtv_pipe = NULL; + } + } + mdp4_overlay_panel_mode_unset(MDP4_MIXER1, MDP4_PANEL_DTV); + + ret = panel_next_off(pdev); + mdp_footswitch_ctrl(FALSE); + + dev_info(&pdev->dev, "mdp4_overlay_dtv: off"); + return ret; +} + +static void mdp4_overlay_dtv_alloc_pipe(struct msm_fb_data_type *mfd, + int32 ptype) +{ + int ret = 0; + struct fb_info *fbi = mfd->fbi; + struct mdp4_overlay_pipe *pipe; + + if (dtv_pipe != NULL) + return; + + pr_debug("%s: ptype=%d\n", __func__, ptype); + + pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1); + if (pipe == NULL) { + pr_err("%s: pipe_alloc failed\n", __func__); + return; + } + pipe->pipe_used++; + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + pipe->mixer_num = MDP4_MIXER1; + + if (ptype == OVERLAY_TYPE_BF) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* LSP_BORDER_COLOR */ + MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5004, + ((0x0 & 0xFFF) << 16) | /* 12-bit B */ + (0x0 & 0xFFF)); /* 12-bit G */ + /* MSP_BORDER_COLOR */ + MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008, + (0x0 & 0xFFF)); /* 12-bit R */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } else { + switch (mfd->ibuf.bpp) { + case 2: + pipe->src_format = MDP_RGB_565; + break; + case 3: + pipe->src_format = MDP_RGB_888; + break; + case 4: + default: + if (hdmi_prim_display) + pipe->src_format = MSMFB_DEFAULT_TYPE; + else + pipe->src_format = MDP_ARGB_8888; + break; + } + } + + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->src_y = 0; + pipe->src_x = 0; + pipe->srcp0_ystride = fbi->fix.line_length; + + ret = mdp4_overlay_format2pipe(pipe); + if (ret < 0) + pr_warn("%s: format2type failed\n", __func__); + + mdp4_overlay_dmae_xy(pipe); /* dma_e */ + mdp4_overlayproc_cfg(pipe); + + if (pipe->pipe_type == OVERLAY_TYPE_RGB) { + pipe->srcp0_addr = (uint32) mfd->ibuf.buf; + mdp4_overlay_rgb_setup(pipe); + } + + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + + dtv_pipe = pipe; /* keep it */ +} + +int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + if (dtv_pipe != NULL) + return 0; + + if (pipe != NULL && pipe->mixer_stage == MDP4_MIXER_STAGE_BASE && + pipe->pipe_type == OVERLAY_TYPE_RGB) + dtv_pipe = pipe; /* keep it */ + else if (!hdmi_prim_display && mdp4_overlay_borderfill_supported()) + mdp4_overlay_dtv_alloc_pipe(mfd, OVERLAY_TYPE_BF); + else + mdp4_overlay_dtv_alloc_pipe(mfd, OVERLAY_TYPE_RGB); + if (dtv_pipe == NULL) + return -ENODEV; + + mdp4_init_writeback_buf(mfd, MDP4_MIXER1); + dtv_pipe->blt_addr = 0; + + return mdp4_dtv_start(mfd); +} + +int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + int result = 0; + + if (dtv_pipe == NULL) + return result; + + pipe->flags &= ~MDP_OV_PLAY_NOWAIT; + mdp4_overlay_reg_flush(pipe, 0); + mdp4_overlay_dtv_ov_done_push(mfd, pipe); + + if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE && + pipe->pipe_type == OVERLAY_TYPE_RGB) { + result = mdp4_dtv_stop(mfd); + dtv_pipe = NULL; + } + return result; +} + +static void mdp4_dtv_blt_ov_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr; + int bpp; + char *overlay_base; + + if (pipe->blt_addr == 0) + return; +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = (pipe->ov_cnt & 0x01) ? + pipe->src_height * pipe->src_width * bpp : 0; + + addr = pipe->blt_addr + off; + pr_debug("%s overlay addr 0x%x\n", __func__, addr); + /* overlay 1 */ + overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */ + outpdw(overlay_base + 0x000c, addr); + outpdw(overlay_base + 0x001c, addr); +} + +static inline void mdp4_dtv_blt_dmae_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr; + int bpp; + + if (pipe->blt_addr == 0) + return; + +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = (pipe->dmae_cnt & 0x01) ? + pipe->src_height * pipe->src_width * bpp : 0; + addr = pipe->blt_addr + off; + MDP_OUTP(MDP_BASE + 0xb0008, addr); +} + +static inline void mdp4_overlay_dtv_ov_kick_start(void) +{ + outpdw(MDP_BASE + 0x0008, 0); +} + +static void mdp4_overlay_dtv_ov_start(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + + /* enable irq */ + if (mfd->ov_start) + return; + + if (!dtv_pipe) { + pr_debug("%s: no mixer1 base layer pipe allocated!\n", + __func__); + return; + } + + if (dtv_pipe->blt_addr) { + mdp4_dtv_blt_ov_update(dtv_pipe); + dtv_pipe->ov_cnt++; + mdp4_overlay_dtv_ov_kick_start(); + } + + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(MDP_OVERLAY1_TERM); + INIT_COMPLETION(dtv_pipe->comp); + mfd->dma->waiting = TRUE; + outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE); + mdp_intr_mask |= INTR_OVERLAY1_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + mfd->ov_start = true; +} + +static void mdp4_overlay_dtv_wait4dmae(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + + if (!dtv_pipe) { + pr_debug("%s: no mixer1 base layer pipe allocated!\n", + __func__); + return; + } + /* enable irq */ + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(MDP_DMA_E_TERM); + INIT_COMPLETION(dtv_pipe->comp); + mfd->dma->waiting = TRUE; + outp32(MDP_INTR_CLEAR, INTR_DMA_E_DONE); + mdp_intr_mask |= INTR_DMA_E_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + wait_for_completion_killable(&dtv_pipe->comp); + mdp_disable_irq(MDP_DMA_E_TERM); +} + +static void mdp4_overlay_dtv_wait4_ov_done(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + u32 data = inpdw(MDP_BASE + DTV_BASE); + + if (mfd->ov_start) + mfd->ov_start = false; + else + return; + if (!(data & 0x1) || (pipe == NULL)) + return; + if (!dtv_pipe) { + pr_debug("%s: no mixer1 base layer pipe allocated!\n", + __func__); + return; + } + + wait_for_completion_timeout(&dtv_pipe->comp, + msecs_to_jiffies(VSYNC_PERIOD*2)); + mdp_disable_irq(MDP_OVERLAY1_TERM); + + if (dtv_pipe->blt_addr) + mdp4_overlay_dtv_wait4dmae(mfd); +} + +void mdp4_overlay_dtv_start(void) +{ + if (!dtv_enabled) { + mdp4_iommu_attach(); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* enable DTV block */ + MDP_OUTP(MDP_BASE + DTV_BASE, 1); + mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + dtv_enabled = 1; + } +} + +void mdp4_overlay_dtv_ov_done_push(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + mdp4_overlay_dtv_ov_start(mfd); + if (pipe->flags & MDP_OV_PLAY_NOWAIT) + return; + + mdp4_overlay_dtv_wait4_ov_done(mfd, pipe); + + /* change mdp clk while mdp is idle` */ + mdp4_set_perf_level(); +} + +void mdp4_overlay_dtv_wait_for_ov(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + mdp4_overlay_dtv_wait4_ov_done(mfd, pipe); + mdp4_set_perf_level(); +} + +void mdp4_dma_e_done_dtv() +{ + if (!dtv_pipe) + return; + + complete(&dtv_pipe->comp); +} + +void mdp4_external_vsync_dtv() +{ + + complete_all(&dtv_comp); +} + +/* + * mdp4_overlay1_done_dtv: called from isr + */ +void mdp4_overlay1_done_dtv() +{ + if (!dtv_pipe) + return; + if (dtv_pipe->blt_addr) { + mdp4_dtv_blt_dmae_update(dtv_pipe); + dtv_pipe->dmae_cnt++; + } + complete_all(&dtv_pipe->comp); +} + +void mdp4_dtv_set_black_screen(void) +{ + char *rgb_base; + /*Black color*/ + uint32 color = 0x00000000; + uint32 temp_src_format; + + if (!dtv_pipe || !hdmi_prim_display) { + pr_err("dtv_pipe/hdmi as primary are not" + " configured yet\n"); + return; + } + rgb_base = MDP_BASE + MDP4_RGB_BASE; + rgb_base += (MDP4_RGB_OFF * dtv_pipe->pipe_num); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* + * RGB Constant Color + */ + MDP_OUTP(rgb_base + 0x1008, color); + /* + * MDP_RGB_SRC_FORMAT + */ + temp_src_format = inpdw(rgb_base + 0x0050); + MDP_OUTP(rgb_base + 0x0050, temp_src_format | BIT(22)); + mdp4_overlay_reg_flush(dtv_pipe, 1); + mdp4_mixer_stage_up(dtv_pipe); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_overlay_dtv_wait4vsync(void) +{ + unsigned long flag; + + if (!dtv_enabled) + return; + + /* enable irq */ + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(MDP_DMA_E_TERM); + INIT_COMPLETION(dtv_comp); + outp32(MDP_INTR_CLEAR, INTR_EXTERNAL_VSYNC); + mdp_intr_mask |= INTR_EXTERNAL_VSYNC; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + wait_for_completion_killable(&dtv_comp); + mdp_disable_irq(MDP_DMA_E_TERM); +} + +static void mdp4_dtv_do_blt(struct msm_fb_data_type *mfd, int enable) +{ + unsigned long flag; + int change = 0; + + if (!mfd->ov1_wb_buf->phys_addr) { + pr_debug("%s: no writeback buf assigned\n", __func__); + return; + } + + if (!dtv_pipe) { + pr_debug("%s: no mixer1 base layer pipe allocated!\n", + __func__); + return; + } + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (enable && dtv_pipe->blt_addr == 0) { + dtv_pipe->blt_addr = mfd->ov1_wb_buf->phys_addr; + change++; + dtv_pipe->ov_cnt = 0; + dtv_pipe->dmae_cnt = 0; + } else if (enable == 0 && dtv_pipe->blt_addr) { + dtv_pipe->blt_addr = 0; + change++; + } + pr_debug("%s: blt_addr=%x\n", __func__, (int)dtv_pipe->blt_addr); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (!change) + return; + + mdp4_overlay_dtv_wait4dmae(mfd); + + MDP_OUTP(MDP_BASE + DTV_BASE, 0); /* stop dtv */ + msleep(20); + mdp4_overlayproc_cfg(dtv_pipe); + mdp4_overlay_dmae_xy(dtv_pipe); + MDP_OUTP(MDP_BASE + DTV_BASE, 1); /* start dtv */ +} + +void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd) +{ + mdp4_dtv_do_blt(mfd, 1); +} + +void mdp4_dtv_overlay_blt_stop(struct msm_fb_data_type *mfd) +{ + mdp4_dtv_do_blt(mfd, 0); +} + +void mdp4_dtv_overlay(struct msm_fb_data_type *mfd) +{ + struct mdp4_overlay_pipe *pipe; + if (!mfd->panel_power_on) + return; + if (!dtv_pipe) { + pr_debug("%s: no mixer1 base layer pipe allocated!\n", + __func__); + return; + } + mutex_lock(&mfd->dma->ov_mutex); + pipe = dtv_pipe; + + if (hdmi_prim_display && (pipe->pipe_used == 0 || + pipe->mixer_stage != MDP4_MIXER_STAGE_BASE)) { + pr_err("%s: NOT baselayer\n", __func__); + mutex_unlock(&mfd->dma->ov_mutex); + return; + } + + if (pipe->pipe_type == OVERLAY_TYPE_RGB) { + pipe->srcp0_addr = (uint32) mfd->ibuf.buf; + mdp4_overlay_rgb_setup(pipe); + } + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + mdp4_overlay_dtv_start(); + mdp4_overlay_dtv_ov_done_push(mfd, pipe); + mdp4_iommu_unmap(pipe); + mutex_unlock(&mfd->dma->ov_mutex); +} diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c new file mode 100644 index 0000000000000000000000000000000000000000..6620d0231f9cf993248e45370a3a12708f0b05c1 --- /dev/null +++ b/drivers/video/msm/mdp4_overlay_lcdc.c @@ -0,0 +1,621 @@ +/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + +#ifdef CONFIG_FB_MSM_MDP40 +#define LCDC_BASE 0xC0000 +#else +#define LCDC_BASE 0xE0000 +#endif + +int first_pixel_start_x; +int first_pixel_start_y; +static int lcdc_enabled; + +static struct mdp4_overlay_pipe *lcdc_pipe; +static struct completion lcdc_comp; + +void mdp4_lcdc_base_swap(struct mdp4_overlay_pipe *pipe) +{ + lcdc_pipe = pipe; +} + +int mdp_lcdc_on(struct platform_device *pdev) +{ + int lcdc_width; + int lcdc_height; + int lcdc_bpp; + int lcdc_border_clr; + int lcdc_underflow_clr; + int lcdc_hsync_skew; + + int hsync_period; + int hsync_ctrl; + int vsync_period; + int display_hctl; + int display_v_start; + int display_v_end; + int active_hctl; + int active_h_start; + int active_h_end; + int active_v_start; + int active_v_end; + int ctrl_polarity; + int h_back_porch; + int h_front_porch; + int v_back_porch; + int v_front_porch; + int hsync_pulse_width; + int vsync_pulse_width; + int hsync_polarity; + int vsync_polarity; + int data_en_polarity; + int hsync_start_x; + int hsync_end_x; + uint8 *buf; + unsigned int buf_offset; + int bpp, ptype; + struct fb_info *fbi; + struct fb_var_screeninfo *var; + struct msm_fb_data_type *mfd; + struct mdp4_overlay_pipe *pipe; + int ret; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + fbi = mfd->fbi; + var = &fbi->var; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + if (is_mdp4_hw_reset()) { + mdp4_hw_init(); + outpdw(MDP_BASE + 0x0038, mdp4_display_intf); + } + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf_offset = calc_fb_offset(mfd, fbi, bpp); + + if (lcdc_pipe == NULL) { + ptype = mdp4_overlay_format2type(mfd->fb_imgType); + if (ptype < 0) + printk(KERN_INFO "%s: format2type failed\n", __func__); + pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0); + if (pipe == NULL) + printk(KERN_INFO "%s: pipe_alloc failed\n", __func__); + pipe->pipe_used++; + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + pipe->mixer_num = MDP4_MIXER0; + pipe->src_format = mfd->fb_imgType; + mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_LCDC); + ret = mdp4_overlay_format2pipe(pipe); + if (ret < 0) + printk(KERN_INFO "%s: format2pipe failed\n", __func__); + lcdc_pipe = pipe; /* keep it */ + init_completion(&lcdc_comp); + + mdp4_init_writeback_buf(mfd, MDP4_MIXER0); + pipe->blt_addr = 0; + + } else { + pipe = lcdc_pipe; + } + + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->src_y = 0; + pipe->src_x = 0; + + if (mfd->display_iova) + pipe->srcp0_addr = mfd->display_iova + buf_offset; + else + pipe->srcp0_addr = (uint32)(buf + buf_offset); + + pipe->srcp0_ystride = fbi->fix.line_length; + pipe->bpp = bpp; + + mdp4_overlay_dmap_xy(pipe); + mdp4_overlay_dmap_cfg(mfd, 1); + + mdp4_overlay_rgb_setup(pipe); + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + + mdp4_overlayproc_cfg(pipe); + + /* + * LCDC timing setting + */ + h_back_porch = var->left_margin; + h_front_porch = var->right_margin; + v_back_porch = var->upper_margin; + v_front_porch = var->lower_margin; + hsync_pulse_width = var->hsync_len; + vsync_pulse_width = var->vsync_len; + lcdc_border_clr = mfd->panel_info.lcdc.border_clr; + lcdc_underflow_clr = mfd->panel_info.lcdc.underflow_clr; + lcdc_hsync_skew = mfd->panel_info.lcdc.hsync_skew; + + lcdc_width = var->xres + mfd->panel_info.lcdc.xres_pad; + lcdc_height = var->yres + mfd->panel_info.lcdc.yres_pad; + lcdc_bpp = mfd->panel_info.bpp; + + hsync_period = + hsync_pulse_width + h_back_porch + lcdc_width + h_front_porch; + hsync_ctrl = (hsync_period << 16) | hsync_pulse_width; + hsync_start_x = hsync_pulse_width + h_back_porch; + hsync_end_x = hsync_period - h_front_porch - 1; + display_hctl = (hsync_end_x << 16) | hsync_start_x; + + vsync_period = + (vsync_pulse_width + v_back_porch + lcdc_height + + v_front_porch) * hsync_period; + display_v_start = + (vsync_pulse_width + v_back_porch) * hsync_period + lcdc_hsync_skew; + display_v_end = + vsync_period - (v_front_porch * hsync_period) + lcdc_hsync_skew - 1; + + if (lcdc_width != var->xres) { + active_h_start = hsync_start_x + first_pixel_start_x; + active_h_end = active_h_start + var->xres - 1; + active_hctl = + ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start; + } else { + active_hctl = 0; + } + + if (lcdc_height != var->yres) { + active_v_start = + display_v_start + first_pixel_start_y * hsync_period; + active_v_end = active_v_start + (var->yres) * hsync_period - 1; + active_v_start |= ACTIVE_START_Y_EN; + } else { + active_v_start = 0; + active_v_end = 0; + } + + +#ifdef CONFIG_FB_MSM_MDP40 + hsync_polarity = 1; + vsync_polarity = 1; + lcdc_underflow_clr |= 0x80000000; /* enable recovery */ +#else + hsync_polarity = 0; + vsync_polarity = 0; +#endif + data_en_polarity = 0; + + ctrl_polarity = + (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity); + + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x4, hsync_ctrl); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x8, vsync_period); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0xc, vsync_pulse_width * hsync_period); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x10, display_hctl); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x14, display_v_start); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x18, display_v_end); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x28, lcdc_border_clr); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x2c, lcdc_underflow_clr); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x30, lcdc_hsync_skew); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x38, ctrl_polarity); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x1c, active_hctl); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x20, active_v_start); + MDP_OUTP(MDP_BASE + LCDC_BASE + 0x24, active_v_end); + + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(2); +#endif + mdp_histogram_ctrl_all(TRUE); + + ret = panel_next_on(pdev); + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return ret; +} + +int mdp_lcdc_off(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + mutex_lock(&mfd->dma->ov_mutex); + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + MDP_OUTP(MDP_BASE + LCDC_BASE, 0); + lcdc_enabled = 0; + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + mdp_histogram_ctrl_all(FALSE); + ret = panel_next_off(pdev); + + mutex_unlock(&mfd->dma->ov_mutex); + + /* delay to make sure the last frame finishes */ + msleep(20); + + /* dis-engage rgb0 from mixer0 */ + if (lcdc_pipe) { + if (mfd->ref_cnt == 0) { + /* adb stop */ + if (lcdc_pipe->pipe_type == OVERLAY_TYPE_BF) + mdp4_overlay_borderfill_stage_down(lcdc_pipe); + + /* lcdc_pipe == rgb1 */ + mdp4_overlay_unset_mixer(lcdc_pipe->mixer_num); + lcdc_pipe = NULL; + } else { + mdp4_mixer_stage_down(lcdc_pipe); + mdp4_iommu_unmap(lcdc_pipe); + } + } + +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(0); +#endif + + return ret; +} + +static void mdp4_lcdc_blt_ov_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr; + int bpp; + char *overlay_base; + + + if (pipe->blt_addr == 0) + return; + + +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr = pipe->blt_addr + off; + + /* overlay 0 */ + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + outpdw(overlay_base + 0x000c, addr); + outpdw(overlay_base + 0x001c, addr); +} + +static void mdp4_lcdc_blt_dmap_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr; + int bpp; + + if (pipe->blt_addr == 0) + return; + + +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->dmap_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr = pipe->blt_addr + off; + + /* dmap */ + MDP_OUTP(MDP_BASE + 0x90008, addr); +} + +/* + * mdp4_overlay_lcdc_wait4event: + * INTR_DMA_P_DONE and INTR_PRIMARY_VSYNC event only + * no INTR_OVERLAY0_DONE event allowed. + */ +static void mdp4_overlay_lcdc_wait4event(struct msm_fb_data_type *mfd, + int intr_done) +{ + unsigned long flag; + unsigned int data; + + data = inpdw(MDP_BASE + LCDC_BASE); + data &= 0x01; + if (data == 0) /* timing generator disabled */ + return; + + spin_lock_irqsave(&mdp_spin_lock, flag); + INIT_COMPLETION(lcdc_comp); + mfd->dma->waiting = TRUE; + outp32(MDP_INTR_CLEAR, intr_done); + mdp_intr_mask |= intr_done; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + wait_for_completion(&lcdc_comp); + mdp_disable_irq(MDP_DMA2_TERM); +} + +static void mdp4_overlay_lcdc_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + + pr_debug("%s: start pid=%d\n", __func__, current->pid); + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (mfd->dma->busy == TRUE) { + INIT_COMPLETION(mfd->dma->comp); + need_wait++; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (need_wait) { + /* wait until DMA finishes the current job */ + pr_debug("%s: pending pid=%d\n", __func__, current->pid); + wait_for_completion(&mfd->dma->comp); + } + pr_debug("%s: done pid=%d\n", __func__, current->pid); +} + +void mdp4_overlay_lcdc_start(void) +{ + if (!lcdc_enabled) { + /* enable LCDC block */ + mdp4_iommu_attach(); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + MDP_OUTP(MDP_BASE + LCDC_BASE, 1); + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + lcdc_enabled = 1; + } +} + +void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + unsigned long flag; + + if (pipe->flags & MDP_OV_PLAY_NOWAIT) + return; + + if (lcdc_pipe->blt_addr) { + mdp4_overlay_lcdc_dma_busy_wait(mfd); + + mdp4_lcdc_blt_ov_update(lcdc_pipe); + lcdc_pipe->ov_cnt++; + + spin_lock_irqsave(&mdp_spin_lock, flag); + outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE); + mdp_intr_mask |= INTR_OVERLAY0_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_enable_irq(MDP_OVERLAY0_TERM); + mfd->dma->busy = TRUE; + mb(); /* make sure all registers updated */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + outpdw(MDP_BASE + 0x0004, 0); /* kickoff overlay engine */ + mdp4_stat.kickoff_ov0++; + mb(); + mdp4_overlay_lcdc_wait4event(mfd, INTR_DMA_P_DONE); + } else { + mdp4_overlay_lcdc_wait4event(mfd, INTR_PRIMARY_VSYNC); + } + mdp4_set_perf_level(); +} + +/* + * mdp4_primary_vsync_lcdc: called from isr + */ +void mdp4_primary_vsync_lcdc(void) +{ + complete_all(&lcdc_comp); +} + +/* + * mdp4_dma_p_done_lcdc: called from isr + */ +void mdp4_dma_p_done_lcdc(void) +{ + complete_all(&lcdc_comp); +} + +/* + * mdp4_overlay0_done_lcdc: called from isr + */ +void mdp4_overlay0_done_lcdc(struct mdp_dma_data *dma) +{ + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + if (lcdc_pipe->blt_addr == 0) { + spin_unlock(&mdp_spin_lock); + return; + } + mdp4_lcdc_blt_dmap_update(lcdc_pipe); + lcdc_pipe->dmap_cnt++; + mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); +} + +static void mdp4_overlay_lcdc_prefill(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + + if (lcdc_pipe->blt_addr) { + mdp4_overlay_lcdc_dma_busy_wait(mfd); + + mdp4_lcdc_blt_ov_update(lcdc_pipe); + lcdc_pipe->ov_cnt++; + + spin_lock_irqsave(&mdp_spin_lock, flag); + outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE); + mdp_intr_mask |= INTR_OVERLAY0_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_enable_irq(MDP_OVERLAY0_TERM); + mfd->dma->busy = TRUE; + mb(); /* make sure all registers updated */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + outpdw(MDP_BASE + 0x0004, 0); /* kickoff overlay engine */ + mdp4_stat.kickoff_ov0++; + mb(); + } +} +/* + * make sure the WRITEBACK_SIZE defined at boardfile + * has enough space h * w * 3 * 2 + */ +static void mdp4_lcdc_do_blt(struct msm_fb_data_type *mfd, int enable) +{ + unsigned long flag; + int change = 0; + + mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); + + if (!mfd->ov0_wb_buf->phys_addr) { + pr_debug("%s: no blt_base assigned\n", __func__); + return; + } + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (enable && lcdc_pipe->blt_addr == 0) { + lcdc_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr; + change++; + lcdc_pipe->blt_cnt = 0; + lcdc_pipe->ov_cnt = 0; + lcdc_pipe->dmap_cnt = 0; + mdp4_stat.blt_lcdc++; + } else if (enable == 0 && lcdc_pipe->blt_addr) { + lcdc_pipe->blt_addr = 0; + change++; + } + pr_info("%s: blt_addr=%x\n", __func__, (int)lcdc_pipe->blt_addr); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (!change) + return; + + mdp4_overlay_lcdc_wait4event(mfd, INTR_DMA_P_DONE); + MDP_OUTP(MDP_BASE + LCDC_BASE, 0); /* stop lcdc */ + msleep(20); + mdp4_overlayproc_cfg(lcdc_pipe); + mdp4_overlay_dmap_xy(lcdc_pipe); + if (lcdc_pipe->blt_addr) { + mdp4_overlay_lcdc_prefill(mfd); + mdp4_overlay_lcdc_prefill(mfd); + } + MDP_OUTP(MDP_BASE + LCDC_BASE, 1); /* start lcdc */ +} + +int mdp4_lcdc_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + req->offset = 0; + req->width = lcdc_pipe->src_width; + req->height = lcdc_pipe->src_height; + req->bpp = lcdc_pipe->bpp; + + return sizeof(*req); +} + +void mdp4_lcdc_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + mdp4_lcdc_do_blt(mfd, req->enable); +} + +void mdp4_lcdc_overlay_blt_start(struct msm_fb_data_type *mfd) +{ + mdp4_lcdc_do_blt(mfd, 1); +} + +void mdp4_lcdc_overlay_blt_stop(struct msm_fb_data_type *mfd) +{ + mdp4_lcdc_do_blt(mfd, 0); +} + +void mdp4_lcdc_overlay(struct msm_fb_data_type *mfd) +{ + struct fb_info *fbi = mfd->fbi; + uint8 *buf; + unsigned int buf_offset; + int bpp; + struct mdp4_overlay_pipe *pipe; + + if (!mfd->panel_power_on) + return; + + /* no need to power on cmd block since it's lcdc mode */ + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf_offset = calc_fb_offset(mfd, fbi, bpp); + + mutex_lock(&mfd->dma->ov_mutex); + + pipe = lcdc_pipe; + if (pipe->pipe_used == 0 || + pipe->mixer_stage != MDP4_MIXER_STAGE_BASE) { + pr_err("%s: NOT baselayer\n", __func__); + mutex_unlock(&mfd->dma->ov_mutex); + return; + } + + if (mfd->display_iova) + pipe->srcp0_addr = mfd->display_iova + buf_offset; + else + pipe->srcp0_addr = (uint32)(buf + buf_offset); + + mdp4_overlay_rgb_setup(pipe); + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe); + mdp4_overlay_lcdc_start(); + mdp4_overlay_lcdc_vsync_push(mfd, pipe); + mdp4_iommu_unmap(pipe); + mutex_unlock(&mfd->dma->ov_mutex); +} diff --git a/drivers/video/msm/mdp4_overlay_mddi.c b/drivers/video/msm/mdp4_overlay_mddi.c new file mode 100644 index 0000000000000000000000000000000000000000..e11bea8c9415c835a50a78cd99033e4d05492f37 --- /dev/null +++ b/drivers/video/msm/mdp4_overlay_mddi.c @@ -0,0 +1,726 @@ +/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + +static struct mdp4_overlay_pipe *mddi_pipe; +static struct msm_fb_data_type *mddi_mfd; +static int busy_wait_cnt; + +static int vsync_start_y_adjust = 4; + +static int dmap_vsync_enable; + +void mdp_dmap_vsync_set(int enable) +{ + dmap_vsync_enable = enable; +} + +int mdp_dmap_vsync_get(void) +{ + return dmap_vsync_enable; +} + +void mdp4_mddi_vsync_enable(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe, int which) +{ + uint32 start_y, data, tear_en; + + tear_en = (1 << which); + + if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) && + (mfd->panel_info.lcd.vsync_enable)) { + + if (mdp_hw_revision < MDP4_REVISION_V2_1) { + /* need dmas dmap switch */ + if (which == 0 && dmap_vsync_enable == 0 && + mfd->panel_info.lcd.rev < 2) /* dma_p */ + return; + } + + if (vsync_start_y_adjust <= pipe->dst_y) + start_y = pipe->dst_y - vsync_start_y_adjust; + else + start_y = (mfd->total_lcd_lines - 1) - + (vsync_start_y_adjust - pipe->dst_y); + if (which == 0) + MDP_OUTP(MDP_BASE + 0x210, start_y); /* primary */ + else + MDP_OUTP(MDP_BASE + 0x214, start_y); /* secondary */ + + data = inpdw(MDP_BASE + 0x20c); + data |= tear_en; + MDP_OUTP(MDP_BASE + 0x20c, data); + } else { + data = inpdw(MDP_BASE + 0x20c); + data &= ~tear_en; + MDP_OUTP(MDP_BASE + 0x20c, data); + } +} + +#define WHOLESCREEN + +void mdp4_overlay_update_lcd(struct msm_fb_data_type *mfd) +{ + MDPIBUF *iBuf = &mfd->ibuf; + uint8 *src; + int ptype; + uint32 mddi_ld_param; + uint16 mddi_vdo_packet_reg; + struct mdp4_overlay_pipe *pipe; + int ret; + + if (mfd->key != MFD_KEY) + return; + + mddi_mfd = mfd; /* keep it */ + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + if (mddi_pipe == NULL) { + ptype = mdp4_overlay_format2type(mfd->fb_imgType); + if (ptype < 0) + printk(KERN_INFO "%s: format2type failed\n", __func__); + pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0); + if (pipe == NULL) + printk(KERN_INFO "%s: pipe_alloc failed\n", __func__); + pipe->pipe_used++; + pipe->mixer_num = MDP4_MIXER0; + pipe->src_format = mfd->fb_imgType; + mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_MDDI); + ret = mdp4_overlay_format2pipe(pipe); + if (ret < 0) + printk(KERN_INFO "%s: format2type failed\n", __func__); + + mddi_pipe = pipe; /* keep it */ + mddi_ld_param = 0; + mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt; + + if (mdp_hw_revision == MDP4_REVISION_V2_1) { + uint32 data; + + data = inpdw(MDP_BASE + 0x0028); + data &= ~0x0300; /* bit 8, 9, MASTER4 */ + if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */ + data |= 0x0200; + else + data |= 0x0100; + + MDP_OUTP(MDP_BASE + 0x00028, data); + } + + if (mfd->panel_info.type == MDDI_PANEL) { + if (mfd->panel_info.pdest == DISPLAY_1) + mddi_ld_param = 0; + else + mddi_ld_param = 1; + } else { + mddi_ld_param = 2; + } + + MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param); + + if (mfd->panel_info.bpp == 24) + MDP_OUTP(MDP_BASE + 0x00094, + (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg); + else if (mfd->panel_info.bpp == 16) + MDP_OUTP(MDP_BASE + 0x00094, + (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg); + else + MDP_OUTP(MDP_BASE + 0x00094, + (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg); + + MDP_OUTP(MDP_BASE + 0x00098, 0x01); + mdp4_init_writeback_buf(mfd, MDP4_MIXER0); + pipe->blt_addr = 0; + } else { + pipe = mddi_pipe; + } + + /* 0 for dma_p, client_id = 0 */ + MDP_OUTP(MDP_BASE + 0x00090, 0); + + + src = (uint8 *) iBuf->buf; + +#ifdef WHOLESCREEN + + { + struct fb_info *fbi; + + fbi = mfd->fbi; + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->src_y = 0; + pipe->src_x = 0; + pipe->dst_h = fbi->var.yres; + pipe->dst_w = fbi->var.xres; + pipe->dst_y = 0; + pipe->dst_x = 0; + pipe->srcp0_addr = (uint32)src; + pipe->srcp0_ystride = fbi->fix.line_length; + } + +#else + if (mdp4_overlay_active(MDP4_MIXER0)) { + struct fb_info *fbi; + + fbi = mfd->fbi; + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->src_y = 0; + pipe->src_x = 0; + pipe->dst_h = fbi->var.yres; + pipe->dst_w = fbi->var.xres; + pipe->dst_y = 0; + pipe->dst_x = 0; + pipe->srcp0_addr = (uint32) src; + pipe->srcp0_ystride = fbi->fix.line_length; + } else { + /* starting input address */ + src += (iBuf->dma_x + iBuf->dma_y * iBuf->ibuf_width) + * iBuf->bpp; + + pipe->src_height = iBuf->dma_h; + pipe->src_width = iBuf->dma_w; + pipe->src_h = iBuf->dma_h; + pipe->src_w = iBuf->dma_w; + pipe->src_y = 0; + pipe->src_x = 0; + pipe->dst_h = iBuf->dma_h; + pipe->dst_w = iBuf->dma_w; + pipe->dst_y = iBuf->dma_y; + pipe->dst_x = iBuf->dma_x; + pipe->srcp0_addr = (uint32) src; + pipe->srcp0_ystride = iBuf->ibuf_width * iBuf->bpp; + } +#endif + + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + + mdp4_overlay_rgb_setup(pipe); + + mdp4_mixer_stage_up(pipe); + + mdp4_overlayproc_cfg(pipe); + + mdp4_overlay_dmap_xy(pipe); + + mdp4_overlay_dmap_cfg(mfd, 0); + + mdp4_mddi_vsync_enable(mfd, pipe, 0); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + + pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n", + __func__, mddi_pipe->blt_end, (int)mddi_pipe->blt_addr, current->pid); + + mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); + + if (mfd->ov0_wb_buf->phys_addr == 0) { + pr_info("%s: no blt_base assigned\n", __func__); + return -EBUSY; + } + + if (mddi_pipe->blt_addr == 0) { + mdp4_mddi_dma_busy_wait(mfd); + spin_lock_irqsave(&mdp_spin_lock, flag); + mddi_pipe->blt_end = 0; + mddi_pipe->blt_cnt = 0; + mddi_pipe->ov_cnt = 0; + mddi_pipe->dmap_cnt = 0; + mddi_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr; + mdp4_stat.blt_mddi++; + spin_unlock_irqrestore(&mdp_spin_lock, flag); + return 0; +} + + return -EBUSY; +} + +int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + + pr_debug("%s: blt_end=%d blt_addr=%x\n", + __func__, mddi_pipe->blt_end, (int)mddi_pipe->blt_addr); + + if ((mddi_pipe->blt_end == 0) && mddi_pipe->blt_addr) { + spin_lock_irqsave(&mdp_spin_lock, flag); + mddi_pipe->blt_end = 1; /* mark as end */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + return 0; + } + + return -EBUSY; +} + +int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + req->offset = 0; + req->width = mddi_pipe->src_width; + req->height = mddi_pipe->src_height; + req->bpp = mddi_pipe->bpp; + + return sizeof(*req); +} + +void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + if (req->enable) + mdp4_mddi_overlay_blt_start(mfd); + else if (req->enable == 0) + mdp4_mddi_overlay_blt_stop(mfd); + +} + +void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr, addr2; + int bpp; + char *overlay_base; + + if (pipe->blt_addr == 0) + return; + + +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->dmap_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + + addr = pipe->blt_addr + off; + + /* dmap */ + MDP_OUTP(MDP_BASE + 0x90008, addr); + + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr2 = pipe->blt_addr + off; + /* overlay 0 */ + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + outpdw(overlay_base + 0x000c, addr2); + outpdw(overlay_base + 0x001c, addr2); +} + +/* + * mdp4_dmap_done_mddi: called from isr + */ +void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma) +{ + int diff; + + mddi_pipe->dmap_cnt++; + diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt; + pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n", + __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt); + + if (diff <= 0) { + spin_lock(&mdp_spin_lock); + dma->dmap_busy = FALSE; + complete(&dma->dmap_comp); + spin_unlock(&mdp_spin_lock); + + if (mddi_pipe->blt_end) { + mddi_pipe->blt_end = 0; + mddi_pipe->blt_addr = 0; + pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n", __func__, + mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt); + mdp_intr_mask &= ~INTR_DMA_P_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + } + + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); + mdp_disable_irq_nosync(MDP_DMA2_TERM); /* disable intr */ + return; + } + + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); + if (busy_wait_cnt) + busy_wait_cnt--; + + pr_debug("%s: kickoff dmap\n", __func__); + + mdp4_blt_xy_update(mddi_pipe); + /* kick off dmap */ + outpdw(MDP_BASE + 0x000c, 0x0); + mdp4_stat.kickoff_dmap++; + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); +} + +/* + * mdp4_overlay0_done_mddi: called from isr + */ +void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma) +{ + int diff; + + if (mddi_pipe->blt_addr == 0) { + mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); + + if (busy_wait_cnt) + busy_wait_cnt--; + mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); + + return; + } + + /* blt enabled */ + if (mddi_pipe->blt_end == 0) + mddi_pipe->ov_cnt++; + + pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n", + __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt); + + if (mddi_pipe->blt_cnt == 0) { + /* first kickoff since blt enabled */ + mdp_intr_mask |= INTR_DMA_P_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + } + + mddi_pipe->blt_cnt++; + + diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt; + if (diff >= 2) { + mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); + return; + } + + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + dma->dmap_busy = TRUE; + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); + + if (busy_wait_cnt) + busy_wait_cnt--; + + pr_debug("%s: kickoff dmap\n", __func__); + + mdp4_blt_xy_update(mddi_pipe); + mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */ + /* kick off dmap */ + outpdw(MDP_BASE + 0x000c, 0x0); + mdp4_stat.kickoff_dmap++; + mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); +} + +void mdp4_mddi_overlay_restore(void) +{ + if (mddi_mfd == NULL) + return; + + pr_debug("%s: resotre, pid=%d\n", __func__, current->pid); + + if (mddi_mfd->panel_power_on == 0) + return; + if (mddi_mfd && mddi_pipe) { + mdp4_mddi_dma_busy_wait(mddi_mfd); + mdp4_overlay_update_lcd(mddi_mfd); + + if (mddi_pipe->blt_addr) + mdp4_mddi_blt_dmap_busy_wait(mddi_mfd); + mdp4_mddi_overlay_kickoff(mddi_mfd, mddi_pipe); + mddi_mfd->dma_update_flag = 1; + } + if (mdp_hw_revision < MDP4_REVISION_V2_1) /* need dmas dmap switch */ + mdp4_mddi_overlay_dmas_restore(); +} + +void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (mfd->dma->dmap_busy == TRUE) { + INIT_COMPLETION(mfd->dma->dmap_comp); + need_wait++; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (need_wait) { + /* wait until DMA finishes the current job */ + wait_for_completion(&mfd->dma->dmap_comp); + } +} + +/* + * mdp4_mddi_cmd_dma_busy_wait: check mddi link activity + * mddi link is a shared resource and it can only be used + * while it is in idle state. + * ov_mutex need to be acquired before call this function. + */ +void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + + pr_debug("%s: START, pid=%d\n", __func__, current->pid); + spin_lock_irqsave(&mdp_spin_lock, flag); + if (mfd->dma->busy == TRUE) { + if (busy_wait_cnt == 0) + INIT_COMPLETION(mfd->dma->comp); + busy_wait_cnt++; + need_wait++; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + + if (need_wait) { + /* wait until DMA finishes the current job */ + pr_debug("%s: PENDING, pid=%d\n", __func__, current->pid); + wait_for_completion(&mfd->dma->comp); + } + pr_debug("%s: DONE, pid=%d\n", __func__, current->pid); +} + +void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + /* + * a video kickoff may happen before UI kickoff after + * blt enabled. mdp4_overlay_update_lcd() need + * to be called before kickoff. + * vice versa for blt disabled. + */ + if (mddi_pipe->blt_addr && mddi_pipe->blt_cnt == 0) + mdp4_overlay_update_lcd(mfd); /* first time */ + else if (mddi_pipe->blt_addr == 0 && mddi_pipe->blt_cnt) { + mdp4_overlay_update_lcd(mfd); /* last time */ + mddi_pipe->blt_cnt = 0; + } + + pr_debug("%s: blt_addr=%d blt_cnt=%d\n", + __func__, (int)mddi_pipe->blt_addr, mddi_pipe->blt_cnt); + + if (mddi_pipe->blt_addr) + mdp4_mddi_blt_dmap_busy_wait(mddi_mfd); + mdp4_mddi_overlay_kickoff(mfd, pipe); +} + +void mdp4_mddi_kickoff_ui(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + pr_debug("%s: pid=%d\n", __func__, current->pid); + mdp4_mddi_overlay_kickoff(mfd, pipe); +} + + +void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + unsigned long flag; + /* change mdp clk while mdp is idle` */ + mdp4_set_perf_level(); + + mdp_enable_irq(MDP_OVERLAY0_TERM); + spin_lock_irqsave(&mdp_spin_lock, flag); + mfd->dma->busy = TRUE; + if (mddi_pipe->blt_addr) + mfd->dma->dmap_busy = TRUE; + spin_unlock_irqrestore(&mdp_spin_lock, flag); + /* start OVERLAY pipe */ + mdp_pipe_kickoff(MDP_OVERLAY0_TERM, mfd); + mdp4_stat.kickoff_ov0++; +} + +void mdp4_dma_s_update_lcd(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + MDPIBUF *iBuf = &mfd->ibuf; + uint32 outBpp = iBuf->bpp; + uint16 mddi_vdo_packet_reg; + uint32 dma_s_cfg_reg; + + dma_s_cfg_reg = 0; + + if (mfd->fb_imgType == MDP_RGBA_8888) + dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; /* on purpose */ + else if (mfd->fb_imgType == MDP_BGR_565) + dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dma_s_cfg_reg |= DMA_PACK_PATTERN_RGB; + + if (outBpp == 4) + dma_s_cfg_reg |= (1 << 26); /* xRGB8888 */ + else if (outBpp == 2) + dma_s_cfg_reg |= DMA_IBUF_FORMAT_RGB565; + + dma_s_cfg_reg |= DMA_DITHER_EN; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* PIXELSIZE */ + MDP_OUTP(MDP_BASE + 0xa0004, (pipe->dst_h << 16 | pipe->dst_w)); + MDP_OUTP(MDP_BASE + 0xa0008, pipe->srcp0_addr); /* ibuf address */ + MDP_OUTP(MDP_BASE + 0xa000c, pipe->srcp0_ystride);/* ystride */ + + if (mfd->panel_info.bpp == 24) { + dma_s_cfg_reg |= DMA_DSTC0G_8BITS | /* 666 18BPP */ + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + } else if (mfd->panel_info.bpp == 18) { + dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + } else { + dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + } + + MDP_OUTP(MDP_BASE + 0xa0010, (pipe->dst_y << 16) | pipe->dst_x); + + /* 1 for dma_s, client_id = 0 */ + MDP_OUTP(MDP_BASE + 0x00090, 1); + + mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt; + + if (mfd->panel_info.bpp == 24) + MDP_OUTP(MDP_BASE + 0x00094, + (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg); + else if (mfd->panel_info.bpp == 16) + MDP_OUTP(MDP_BASE + 0x00094, + (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg); + else + MDP_OUTP(MDP_BASE + 0x00094, + (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg); + + MDP_OUTP(MDP_BASE + 0x00098, 0x01); + + MDP_OUTP(MDP_BASE + 0xa0000, dma_s_cfg_reg); + + mdp4_mddi_vsync_enable(mfd, pipe, 1); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_mddi_dma_s_kickoff(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + /* change mdp clk while mdp is idle` */ + mdp4_set_perf_level(); + + mdp_enable_irq(MDP_DMA_S_TERM); + + if (mddi_pipe->blt_addr == 0) + mfd->dma->busy = TRUE; + + mfd->ibuf_flushed = TRUE; + /* start dma_s pipe */ + mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd); + mdp4_stat.kickoff_dmas++; + + /* wait until DMA finishes the current job */ + wait_for_completion(&mfd->dma->comp); + mdp_disable_irq(MDP_DMA_S_TERM); +} + +void mdp4_mddi_overlay_dmas_restore(void) +{ + /* mutex held by caller */ + if (mddi_mfd && mddi_pipe) { + mdp4_mddi_dma_busy_wait(mddi_mfd); + mdp4_dma_s_update_lcd(mddi_mfd, mddi_pipe); + mdp4_mddi_dma_s_kickoff(mddi_mfd, mddi_pipe); + mddi_mfd->dma_update_flag = 1; + } +} + +void mdp4_mddi_overlay(struct msm_fb_data_type *mfd) +{ + mutex_lock(&mfd->dma->ov_mutex); + + if (mfd && mfd->panel_power_on) { + mdp4_mddi_dma_busy_wait(mfd); + + if (mddi_pipe && mddi_pipe->blt_addr) + mdp4_mddi_blt_dmap_busy_wait(mfd); + + mdp4_overlay_update_lcd(mfd); + + if (mdp_hw_revision < MDP4_REVISION_V2_1) { + /* dmas dmap switch */ + if (mdp4_overlay_mixer_play(mddi_pipe->mixer_num) + == 0) { + mdp4_dma_s_update_lcd(mfd, mddi_pipe); + mdp4_mddi_dma_s_kickoff(mfd, mddi_pipe); + } else + mdp4_mddi_kickoff_ui(mfd, mddi_pipe); + } else /* no dams dmap switch */ + mdp4_mddi_kickoff_ui(mfd, mddi_pipe); + + /* signal if pan function is waiting for the update completion */ + if (mfd->pan_waiting) { + mfd->pan_waiting = FALSE; + complete(&mfd->pan_comp); + } + } + mutex_unlock(&mfd->dma->ov_mutex); +} + +int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + struct msm_fb_data_type *mfd = info->par; + mutex_lock(&mfd->dma->ov_mutex); + if (mfd && mfd->panel_power_on) { + mdp4_mddi_dma_busy_wait(mfd); + mdp_hw_cursor_update(info, cursor); + } + mutex_unlock(&mfd->dma->ov_mutex); + return 0; +} diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c new file mode 100644 index 0000000000000000000000000000000000000000..3ff360159a2c254c8dedf58e55f243ddbcd219dd --- /dev/null +++ b/drivers/video/msm/mdp4_overlay_writeback.c @@ -0,0 +1,596 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" +enum { + WB_OPEN, + WB_START, + WB_STOPING, + WB_STOP +}; +enum { + REGISTERED, + IN_FREE_QUEUE, + IN_BUSY_QUEUE, + WITH_CLIENT +}; + +static struct mdp4_overlay_pipe *writeback_pipe; +static struct msm_fb_data_type *writeback_mfd; +static int busy_wait_cnt; + +int mdp4_overlay_writeback_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct fb_info *fbi; + uint8 *buf; + struct mdp4_overlay_pipe *pipe; + int bpp; + int ret; + uint32 data; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + writeback_mfd = mfd; /* keep it */ + + fbi = mfd->fbi; + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf += fbi->var.xoffset * bpp + + fbi->var.yoffset * fbi->fix.line_length; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + if (writeback_pipe == NULL) { + pipe = mdp4_overlay_pipe_alloc(OVERLAY_TYPE_BF, MDP4_MIXER2); + if (pipe == NULL) + pr_info("%s: pipe_alloc failed\n", __func__); + pipe->pipe_used++; + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + pipe->mixer_num = MDP4_MIXER2; + pipe->src_format = MDP_ARGB_8888; + mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_WRITEBACK); + ret = mdp4_overlay_format2pipe(pipe); + if (ret < 0) + pr_info("%s: format2type failed\n", __func__); + + writeback_pipe = pipe; /* keep it */ + + } else { + pipe = writeback_pipe; + } + ret = panel_next_on(pdev); + + /* MDP_LAYERMIXER_WB_MUX_SEL to use mixer1 axi for mixer2 writeback */ + if (hdmi_prim_display) + data = 0x01; + else + data = 0x02; + outpdw(MDP_BASE + 0x100F4, data); + + MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5004, + ((0x0 & 0xFFF) << 16) | /* 12-bit B */ + (0x0 & 0xFFF)); /* 12-bit G */ + /* MSP_BORDER_COLOR */ + MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008, + (0x0 & 0xFFF)); /* 12-bit R */ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return ret; +} + +int mdp4_overlay_writeback_off(struct platform_device *pdev) +{ + int ret; + struct msm_fb_data_type *mfd = + (struct msm_fb_data_type *)platform_get_drvdata(pdev); + if (mfd && writeback_pipe) { + mdp4_writeback_dma_busy_wait(mfd); + mdp4_overlay_pipe_free(writeback_pipe); + mdp4_overlay_panel_mode_unset(writeback_pipe->mixer_num, + MDP4_PANEL_WRITEBACK); + writeback_pipe = NULL; + } + ret = panel_next_off(pdev); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* MDP_LAYERMIXER_WB_MUX_SEL to restore to default cfg*/ + outpdw(MDP_BASE + 0x100F4, 0x0); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return ret; +} +int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd) +{ + struct fb_info *fbi; + uint8 *buf; + unsigned int buf_offset; + struct mdp4_overlay_pipe *pipe; + int bpp; + + if (mfd->key != MFD_KEY) + return -ENODEV; + + if (!writeback_pipe) + return -EINVAL; + + fbi = mfd->fbi; + + pipe = writeback_pipe; + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + buf_offset = fbi->var.xoffset * bpp + + fbi->var.yoffset * fbi->fix.line_length; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->dst_h = fbi->var.yres; + pipe->dst_w = fbi->var.xres; + pipe->srcp0_ystride = fbi->fix.line_length; + pipe->src_y = 0; + pipe->src_x = 0; + pipe->dst_y = 0; + pipe->dst_x = 0; + + if (mfd->display_iova) + pipe->srcp0_addr = mfd->display_iova + buf_offset; + else + pipe->srcp0_addr = (uint32)(buf + buf_offset); + + mdp4_mixer_stage_up(pipe); + + mdp4_overlayproc_cfg(pipe); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + wmb(); + return 0; +} +void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (mfd->dma->busy == TRUE) { + if (busy_wait_cnt == 0) + INIT_COMPLETION(mfd->dma->comp); + busy_wait_cnt = 1; + need_wait++; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (need_wait) { + /* wait until DMA finishes the current job */ + pr_debug("%s: pending pid=%d\n", + __func__, current->pid); + wait_for_completion(&mfd->dma->comp); + } +} + +void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma) +{ + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + if (busy_wait_cnt) + busy_wait_cnt = 0; + mdp_disable_irq_nosync(MDP_OVERLAY2_TERM); + spin_unlock(&mdp_spin_lock); + complete_all(&dma->comp); + pr_debug("%s ovdone interrupt\n", __func__); + +} +void mdp4_writeback_overlay_kickoff(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + unsigned long flag; + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(MDP_OVERLAY2_TERM); + + mfd->dma->busy = TRUE; + outp32(MDP_INTR_CLEAR, INTR_OVERLAY2_DONE); + mdp_intr_mask |= INTR_OVERLAY2_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + + wmb(); /* make sure all registers updated */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + /* start OVERLAY pipe */ + mdp_pipe_kickoff(MDP_OVERLAY2_TERM, mfd); + wmb(); + pr_debug("%s: before ov done interrupt\n", __func__); +} +void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd) +{ + /* mutex holded by caller */ + if (mfd && writeback_pipe) { + mdp4_writeback_dma_busy_wait(mfd); + mdp4_overlay_writeback_update(mfd); + + mdp4_writeback_overlay_kickoff(mfd, writeback_pipe); + } +} + +void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + struct msmfb_writeback_data_list *node = NULL; + mutex_lock(&mfd->unregister_mutex); + mutex_lock(&mfd->writeback_mutex); + if (!list_empty(&mfd->writeback_free_queue) + && mfd->writeback_state != WB_STOPING + && mfd->writeback_state != WB_STOP) { + node = list_first_entry(&mfd->writeback_free_queue, + struct msmfb_writeback_data_list, active_entry); + } + if (node) { + list_del(&(node->active_entry)); + node->state = IN_BUSY_QUEUE; + mfd->writeback_active_cnt++; + } + mutex_unlock(&mfd->writeback_mutex); + + writeback_pipe->blt_addr = (ulong) (node ? node->addr : NULL); + + if (!writeback_pipe->blt_addr) { + pr_err("%s: no writeback buffer 0x%x, %p\n", __func__, + (unsigned int)writeback_pipe->blt_addr, node); + mutex_unlock(&mfd->unregister_mutex); + return; + } + + if (writeback_pipe->blt_cnt == 0) + mdp4_overlay_writeback_update(mfd); + + pr_debug("%s: pid=%d\n", __func__, current->pid); + + mdp4_writeback_overlay_kickoff(mfd, pipe); + + mutex_lock(&mfd->writeback_mutex); + list_add_tail(&node->active_entry, &mfd->writeback_busy_queue); + mutex_unlock(&mfd->writeback_mutex); + mfd->writeback_active_cnt--; + mutex_unlock(&mfd->unregister_mutex); + wake_up(&mfd->wait_q); +} + +void mdp4_writeback_kickoff_ui(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + + pr_debug("%s: pid=%d\n", __func__, current->pid); + mdp4_writeback_overlay_kickoff(mfd, pipe); +} + +void mdp4_writeback_overlay(struct msm_fb_data_type *mfd) +{ + int ret = 0; + struct msmfb_writeback_data_list *node = NULL; + + mutex_lock(&mfd->unregister_mutex); + mutex_lock(&mfd->writeback_mutex); + if (!list_empty(&mfd->writeback_free_queue) + && mfd->writeback_state != WB_STOPING + && mfd->writeback_state != WB_STOP) { + node = list_first_entry(&mfd->writeback_free_queue, + struct msmfb_writeback_data_list, active_entry); + } + if (node) { + list_del(&(node->active_entry)); + node->state = IN_BUSY_QUEUE; + mfd->writeback_active_cnt++; + } + mutex_unlock(&mfd->writeback_mutex); + + writeback_pipe->blt_addr = (ulong) (node ? node->addr : NULL); + + mutex_lock(&mfd->dma->ov_mutex); + pr_debug("%s in writeback\n", __func__); + if (writeback_pipe && !writeback_pipe->blt_addr) { + pr_err("%s: no writeback buffer 0x%x\n", __func__, + (unsigned int)writeback_pipe->blt_addr); + ret = mdp4_overlay_writeback_update(mfd); + if (ret) + pr_err("%s: update failed writeback pipe NULL\n", + __func__); + goto fail_no_blt_addr; + } + + if (mfd && mfd->panel_power_on) { + pr_debug("%s in before busy wait\n", __func__); + mdp4_writeback_dma_busy_wait(mfd); + + pr_debug("%s in before update\n", __func__); + ret = mdp4_overlay_writeback_update(mfd); + if (ret) { + pr_err("%s: update failed writeback pipe NULL\n", + __func__); + goto fail_no_blt_addr; + } + + pr_debug("%s: in writeback pan display 0x%x\n", __func__, + (unsigned int)writeback_pipe->blt_addr); + mdp4_writeback_kickoff_ui(mfd, writeback_pipe); + mdp4_iommu_unmap(writeback_pipe); + + /* signal if pan function is waiting for the + * update completion */ + if (mfd->pan_waiting) { + mfd->pan_waiting = FALSE; + complete(&mfd->pan_comp); + } + } + + mutex_lock(&mfd->writeback_mutex); + list_add_tail(&node->active_entry, &mfd->writeback_busy_queue); + mfd->writeback_active_cnt--; + mutex_unlock(&mfd->writeback_mutex); + wake_up(&mfd->wait_q); +fail_no_blt_addr: + /*NOTE: This api was removed + mdp4_overlay_resource_release();*/ + mutex_unlock(&mfd->dma->ov_mutex); + mutex_unlock(&mfd->unregister_mutex); +} +static int mdp4_overlay_writeback_register_buffer( + struct msm_fb_data_type *mfd, struct msmfb_writeback_data_list *node) +{ + if (!node) { + pr_err("Cannot register a NULL node\n"); + return -EINVAL; + } + node->state = REGISTERED; + list_add_tail(&node->registered_entry, &mfd->writeback_register_queue); + return 0; +} +static struct msmfb_writeback_data_list *get_if_registered( + struct msm_fb_data_type *mfd, struct msmfb_data *data) +{ + struct msmfb_writeback_data_list *temp; + bool found = false; + if (!list_empty(&mfd->writeback_register_queue)) { + list_for_each_entry(temp, + &mfd->writeback_register_queue, + registered_entry) { + if (temp && temp->buf_info.iova == data->iova) { + found = true; + break; + } + } + } + if (!found) { + temp = kzalloc(sizeof(struct msmfb_writeback_data_list), + GFP_KERNEL); + if (temp == NULL) { + pr_err("%s: out of memory\n", __func__); + goto register_alloc_fail; + } + + if (data->iova) + temp->addr = (void *)(data->iova + data->offset); +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + else { + struct ion_handle *srcp_ihdl; + ulong len; + srcp_ihdl = ion_import_fd(mfd->iclient, + data->memory_id); + if (IS_ERR_OR_NULL(srcp_ihdl)) { + pr_err("%s: ion import fd failed\n", __func__); + goto register_ion_fail; + } + if (ion_phys(mfd->iclient, + srcp_ihdl, + (ulong *)&temp->addr, + (size_t *)&len)) { + pr_err("%s: unable to get ion phys\n", + __func__); + goto register_ion_fail; + } + temp->addr += data->offset; + } +#else + else { + pr_err("%s: only support ion memory\n", __func__); + goto register_ion_fail; + } +#endif + memcpy(&temp->buf_info, data, sizeof(struct msmfb_data)); + if (mdp4_overlay_writeback_register_buffer(mfd, temp)) { + pr_err("%s: error registering node\n", __func__); + goto register_ion_fail; + } + } + return temp; + register_ion_fail: + kfree(temp); + register_alloc_fail: + return NULL; +} +int mdp4_writeback_start( + struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + mutex_lock(&mfd->writeback_mutex); + mfd->writeback_state = WB_START; + mutex_unlock(&mfd->writeback_mutex); + wake_up(&mfd->wait_q); + return 0; +} + +int mdp4_writeback_queue_buffer(struct fb_info *info, struct msmfb_data *data) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msmfb_writeback_data_list *node = NULL; + int rv = 0; + + mutex_lock(&mfd->writeback_mutex); + node = get_if_registered(mfd, data); + if (!node || node->state == IN_BUSY_QUEUE || + node->state == IN_FREE_QUEUE) { + pr_err("memory not registered or Buffer already with us\n"); + rv = -EINVAL; + goto exit; + } + + list_add_tail(&node->active_entry, &mfd->writeback_free_queue); + node->state = IN_FREE_QUEUE; + +exit: + mutex_unlock(&mfd->writeback_mutex); + return rv; +} +static int is_buffer_ready(struct msm_fb_data_type *mfd) +{ + int rc; + mutex_lock(&mfd->writeback_mutex); + rc = !list_empty(&mfd->writeback_busy_queue) || + (mfd->writeback_state == WB_STOPING); + mutex_unlock(&mfd->writeback_mutex); + return rc; +} +int mdp4_writeback_dequeue_buffer(struct fb_info *info, struct msmfb_data *data) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msmfb_writeback_data_list *node = NULL; + int rc = 0; + + rc = wait_event_interruptible(mfd->wait_q, is_buffer_ready(mfd)); + if (rc) { + pr_err("failed to get dequeued buffer\n"); + return -ENOBUFS; + } + mutex_lock(&mfd->writeback_mutex); + if (mfd->writeback_state == WB_STOPING) { + mfd->writeback_state = WB_STOP; + mutex_unlock(&mfd->writeback_mutex); + return -ENOBUFS; + } else if (!list_empty(&mfd->writeback_busy_queue)) { + node = list_first_entry(&mfd->writeback_busy_queue, + struct msmfb_writeback_data_list, active_entry); + } + if (node) { + list_del(&node->active_entry); + node->state = WITH_CLIENT; + memcpy(data, &node->buf_info, sizeof(struct msmfb_data)); + } else { + pr_err("node is NULL. Somebody else dequeued?\n"); + rc = -ENOBUFS; + } + mutex_unlock(&mfd->writeback_mutex); + return rc; +} + +static bool is_writeback_inactive(struct msm_fb_data_type *mfd) +{ + bool active; + mutex_lock(&mfd->writeback_mutex); + active = !mfd->writeback_active_cnt; + mutex_unlock(&mfd->writeback_mutex); + return active; +} +int mdp4_writeback_stop(struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + mutex_lock(&mfd->writeback_mutex); + mfd->writeback_state = WB_STOPING; + mutex_unlock(&mfd->writeback_mutex); + /* Wait for all pending writebacks to finish */ + wait_event_interruptible(mfd->wait_q, is_writeback_inactive(mfd)); + + /* Wake up dequeue thread in case of no UI update*/ + wake_up(&mfd->wait_q); + + return 0; +} +int mdp4_writeback_init(struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + mutex_init(&mfd->writeback_mutex); + mutex_init(&mfd->unregister_mutex); + INIT_LIST_HEAD(&mfd->writeback_free_queue); + INIT_LIST_HEAD(&mfd->writeback_busy_queue); + INIT_LIST_HEAD(&mfd->writeback_register_queue); + mfd->writeback_state = WB_OPEN; + init_waitqueue_head(&mfd->wait_q); + return 0; +} +int mdp4_writeback_terminate(struct fb_info *info) +{ + struct list_head *ptr, *next; + struct msmfb_writeback_data_list *temp; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int rc = 0; + + mutex_lock(&mfd->unregister_mutex); + mutex_lock(&mfd->writeback_mutex); + + if (mfd->writeback_state != WB_STOPING && + mfd->writeback_state != WB_STOP) { + pr_err("%s called without stopping\n", __func__); + rc = -EPERM; + goto terminate_err; + + } + + if (!list_empty(&mfd->writeback_register_queue)) { + list_for_each_safe(ptr, next, + &mfd->writeback_register_queue) { + temp = list_entry(ptr, + struct msmfb_writeback_data_list, + registered_entry); + list_del(&temp->registered_entry); + kfree(temp); + } + } + INIT_LIST_HEAD(&mfd->writeback_register_queue); + INIT_LIST_HEAD(&mfd->writeback_busy_queue); + INIT_LIST_HEAD(&mfd->writeback_free_queue); + + +terminate_err: + mutex_unlock(&mfd->writeback_mutex); + mutex_unlock(&mfd->unregister_mutex); + return rc; +} diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c new file mode 100644 index 0000000000000000000000000000000000000000..f0f90d43d1590c5887671bd94bd09510c88aa0a3 --- /dev/null +++ b/drivers/video/msm/mdp4_util.c @@ -0,0 +1,3306 @@ +/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + +struct mdp4_statistic mdp4_stat; + +unsigned is_mdp4_hw_reset(void) +{ + unsigned hw_reset = 0; + + /* Only revisions > v2.1 may be reset or powered off/on at runtime */ + if (mdp_hw_revision > MDP4_REVISION_V2_1) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + hw_reset = !inpdw(MDP_BASE + 0x003c); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } + + return hw_reset; +} + +void mdp4_sw_reset(ulong bits) +{ + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + bits &= 0x1f; /* 5 bits */ + outpdw(MDP_BASE + 0x001c, bits); /* MDP_SW_RESET */ + + while (inpdw(MDP_BASE + 0x001c) & bits) /* self clear when complete */ + ; + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + MSM_FB_DEBUG("mdp4_sw_reset: 0x%x\n", (int)bits); +} + +void mdp4_overlay_cfg(int overlayer, int blt_mode, int refresh, int direct_out) +{ + ulong bits = 0; + + if (blt_mode) + bits |= (1 << 3); + refresh &= 0x03; /* 2 bites */ + bits |= (refresh << 1); + direct_out &= 0x01; + bits |= direct_out; + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + + if (overlayer == MDP4_MIXER0) + outpdw(MDP_BASE + 0x10004, bits); /* MDP_OVERLAY0_CFG */ + else if (overlayer == MDP4_MIXER1) + outpdw(MDP_BASE + 0x18004, bits); /* MDP_OVERLAY1_CFG */ + + MSM_FB_DEBUG("mdp4_overlay_cfg: 0x%x\n", + (int)inpdw(MDP_BASE + 0x10004)); + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_display_intf_sel(int output, ulong intf) +{ + ulong bits, mask, data; + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + bits = inpdw(MDP_BASE + 0x0038); /* MDP_DISP_INTF_SEL */ + + if (intf == DSI_VIDEO_INTF) { + data = 0x40; /* bit 6 */ + intf = MDDI_LCDC_INTF; + if (output == SECONDARY_INTF_SEL) { + MSM_FB_INFO("%s: Illegal INTF selected, output=%d \ + intf=%d\n", __func__, output, (int)intf); + } + } else if (intf == DSI_CMD_INTF) { + data = 0x80; /* bit 7 */ + intf = MDDI_INTF; + if (output == EXTERNAL_INTF_SEL) { + MSM_FB_INFO("%s: Illegal INTF selected, output=%d \ + intf=%d\n", __func__, output, (int)intf); + } + } else + data = 0; + + mask = 0x03; /* 2 bits */ + intf &= 0x03; /* 2 bits */ + + switch (output) { + case EXTERNAL_INTF_SEL: + intf <<= 4; + mask <<= 4; + break; + case SECONDARY_INTF_SEL: + intf &= 0x02; /* only MDDI and EBI2 support */ + intf <<= 2; + mask <<= 2; + break; + default: + break; + } + + intf |= data; + mask |= data; + + bits &= ~mask; + bits |= intf; + + outpdw(MDP_BASE + 0x0038, bits); /* MDP_DISP_INTF_SEL */ + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + MSM_FB_DEBUG("mdp4_display_intf_sel: 0x%x\n", (int)inpdw(MDP_BASE + 0x0038)); +} + +unsigned long mdp4_display_status(void) +{ + ulong status; + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + status = inpdw(MDP_BASE + 0x0018) & 0x3ff; /* MDP_DISPLAY_STATUS */ + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return status; +} + +void mdp4_ebi2_lcd_setup(int lcd, ulong base, int ystride) +{ + /* always use memory map */ + ystride &= 0x01fff; /* 13 bits */ + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + if (lcd == EBI2_LCD0) { + outpdw(MDP_BASE + 0x0060, base);/* MDP_EBI2_LCD0 */ + outpdw(MDP_BASE + 0x0068, ystride);/* MDP_EBI2_LCD0_YSTRIDE */ + } else { + outpdw(MDP_BASE + 0x0064, base);/* MDP_EBI2_LCD1 */ + outpdw(MDP_BASE + 0x006c, ystride);/* MDP_EBI2_LCD1_YSTRIDE */ + } + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_mddi_setup(int mddi, unsigned long id) +{ + ulong bits; + + if (mddi == MDDI_EXTERNAL_SET) + bits = 0x02; + else if (mddi == MDDI_SECONDARY_SET) + bits = 0x01; + else + bits = 0; /* PRIMARY_SET */ + + id <<= 16; + + bits |= id; + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + outpdw(MDP_BASE + 0x0090, bits); /* MDP_MDDI_PARAM_WR_SEL */ + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req) +{ + + /* not implemented yet */ + return -1; +} + +void mdp4_fetch_cfg(uint32 core_clk) +{ + uint32 dmap_data, vg_data; + char *base; + int i; + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + if (mdp_rev >= MDP_REV_41 || core_clk >= 90000000) { /* 90 Mhz */ + dmap_data = 0x47; /* 16 bytes-burst x 8 req */ + vg_data = 0x47; /* 16 bytes-burs x 8 req */ + } else { + dmap_data = 0x27; /* 8 bytes-burst x 8 req */ + vg_data = 0x43; /* 16 bytes-burst x 4 req */ + } + + MSM_FB_DEBUG("mdp4_fetch_cfg: dmap=%x vg=%x\n", + dmap_data, vg_data); + + /* dma_p fetch config */ + outpdw(MDP_BASE + 0x91004, dmap_data); + /* dma_e fetch config */ + outpdw(MDP_BASE + 0xB1004, dmap_data); + + /* + * set up two vg pipes and two rgb pipes + */ + base = MDP_BASE + MDP4_VIDEO_BASE; + for (i = 0; i < 4; i++) { + outpdw(base + 0x1004, vg_data); + base += MDP4_VIDEO_OFF; + } + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_hw_init(void) +{ + ulong bits; + uint32 clk_rate; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + mdp4_update_perf_level(OVERLAY_PERF_LEVEL4); + +#ifdef MDP4_ERROR + /* + * Issue software reset on DMA_P will casue DMA_P dma engine stall + * on LCDC mode. However DMA_P does not stall at MDDI mode. + * This need further investigation. + */ + mdp4_sw_reset(0x17); +#endif + + if (mdp_rev > MDP_REV_41) { + /* mdp chip select controller */ + outpdw(MDP_BASE + 0x00c0, CS_CONTROLLER_0); + outpdw(MDP_BASE + 0x00c4, CS_CONTROLLER_1); + } + + mdp4_clear_lcdc(); + + mdp4_mixer_blend_init(0); + mdp4_mixer_blend_init(1); + mdp4_vg_qseed_init(0); + mdp4_vg_qseed_init(1); + + mdp4_vg_csc_setup(0); + mdp4_vg_csc_setup(1); + mdp4_mixer_csc_setup(1); + mdp4_mixer_csc_setup(2); + mdp4_dmap_csc_setup(); + + if (mdp_rev <= MDP_REV_41) { + mdp4_mixer_gc_lut_setup(0); + mdp4_mixer_gc_lut_setup(1); + } + + mdp4_vg_igc_lut_setup(0); + mdp4_vg_igc_lut_setup(1); + + mdp4_rgb_igc_lut_setup(0); + mdp4_rgb_igc_lut_setup(1); + + outp32(MDP_EBI2_PORTMAP_MODE, 0x3); + + /* system interrupts */ + + bits = mdp_intr_mask; + outpdw(MDP_BASE + 0x0050, bits);/* enable specififed interrupts */ + + /* For the max read pending cmd config below, if the MDP clock */ + /* is less than the AXI clock, then we must use 3 pending */ + /* pending requests. Otherwise, we should use 8 pending requests. */ + /* In the future we should do this detection automatically. */ + + /* max read pending cmd config */ + outpdw(MDP_BASE + 0x004c, 0x02222); /* 3 pending requests */ + +#ifndef CONFIG_FB_MSM_OVERLAY + /* both REFRESH_MODE and DIRECT_OUT are ignored at BLT mode */ + mdp4_overlay_cfg(MDP4_MIXER0, OVERLAY_MODE_BLT, 0, 0); + mdp4_overlay_cfg(MDP4_MIXER1, OVERLAY_MODE_BLT, 0, 0); +#endif + + clk_rate = mdp_get_core_clk(); + mdp4_fetch_cfg(clk_rate); + + mdp4_overlay_cfg_init(); + + /* Mark hardware as initialized. Only revisions > v2.1 have a register + * for tracking core reset status. */ + if (mdp_hw_revision > MDP4_REVISION_V2_1) + outpdw(MDP_BASE + 0x003c, 1); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + + +void mdp4_clear_lcdc(void) +{ + uint32 bits; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + bits = inpdw(MDP_BASE + 0xc0000); + if (bits & 0x01) { /* enabled already */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return; + } + + outpdw(MDP_BASE + 0xc0004, 0); /* vsync ctrl out */ + outpdw(MDP_BASE + 0xc0008, 0); /* vsync period */ + outpdw(MDP_BASE + 0xc000c, 0); /* vsync pusle width */ + outpdw(MDP_BASE + 0xc0010, 0); /* lcdc display HCTL */ + outpdw(MDP_BASE + 0xc0014, 0); /* lcdc display v start */ + outpdw(MDP_BASE + 0xc0018, 0); /* lcdc display v end */ + outpdw(MDP_BASE + 0xc001c, 0); /* lcdc active hctl */ + outpdw(MDP_BASE + 0xc0020, 0); /* lcdc active v start */ + outpdw(MDP_BASE + 0xc0024, 0); /* lcdc active v end */ + outpdw(MDP_BASE + 0xc0028, 0); /* lcdc board color */ + outpdw(MDP_BASE + 0xc002c, 0); /* lcdc underflow ctrl */ + outpdw(MDP_BASE + 0xc0030, 0); /* lcdc hsync skew */ + outpdw(MDP_BASE + 0xc0034, 0); /* lcdc test ctl */ + outpdw(MDP_BASE + 0xc0038, 0); /* lcdc ctl polarity */ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +irqreturn_t mdp4_isr(int irq, void *ptr) +{ + uint32 isr, mask, panel; + struct mdp_dma_data *dma; + struct mdp_hist_mgmt *mgmt = NULL; + char *base_addr; + int i, ret; + + mdp_is_in_isr = TRUE; + + /* complete all the reads before reading the interrupt + * status register - eliminate effects of speculative + * reads by the cpu + */ + rmb(); + isr = inpdw(MDP_INTR_STATUS); + if (isr == 0) + goto out; + + mdp4_stat.intr_tot++; + mask = inpdw(MDP_INTR_ENABLE); + outpdw(MDP_INTR_CLEAR, isr); + + if (isr & INTR_PRIMARY_INTF_UDERRUN) { + pr_debug("%s: UNDERRUN -- primary\n", __func__); + mdp4_stat.intr_underrun_p++; + /* When underun occurs mdp clear the histogram registers + that are set before in hw_init so restore them back so + that histogram works.*/ + for (i = 0; i < MDP_HIST_MGMT_MAX; i++) { + mgmt = mdp_hist_mgmt_array[i]; + if (!mgmt) + continue; + base_addr = MDP_BASE + mgmt->base; + MDP_OUTP(base_addr + 0x010, 1); + outpdw(base_addr + 0x01c, INTR_HIST_DONE | + INTR_HIST_RESET_SEQ_DONE); + mgmt->mdp_is_hist_valid = FALSE; + __mdp_histogram_reset(mgmt); + } + } + + if (isr & INTR_EXTERNAL_INTF_UDERRUN) { + pr_debug("%s: UNDERRUN -- external\n", __func__); + mdp4_stat.intr_underrun_e++; + } + + isr &= mask; + + if (isr == 0) + goto out; + + panel = mdp4_overlay_panel_list(); + if (isr & INTR_PRIMARY_VSYNC) { + mdp4_stat.intr_vsync_p++; + dma = &dma2_data; + spin_lock(&mdp_spin_lock); + mdp_intr_mask &= ~INTR_PRIMARY_VSYNC; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->waiting = FALSE; + if (panel & MDP4_PANEL_LCDC) + mdp4_primary_vsync_lcdc(); +#ifdef CONFIG_FB_MSM_MIPI_DSI + else if (panel & MDP4_PANEL_DSI_VIDEO) + mdp4_primary_vsync_dsi_video(); +#endif + spin_unlock(&mdp_spin_lock); + } +#ifdef CONFIG_FB_MSM_DTV + if (isr & INTR_EXTERNAL_VSYNC) { + mdp4_stat.intr_vsync_e++; + dma = &dma_e_data; + spin_lock(&mdp_spin_lock); + mdp_intr_mask &= ~INTR_EXTERNAL_VSYNC; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->waiting = FALSE; + if (panel & MDP4_PANEL_DTV) + mdp4_external_vsync_dtv(); + spin_unlock(&mdp_spin_lock); + } +#endif + +#ifdef CONFIG_FB_MSM_OVERLAY + if (isr & INTR_OVERLAY0_DONE) { + mdp4_stat.intr_overlay0++; + dma = &dma2_data; + if (panel & (MDP4_PANEL_LCDC | MDP4_PANEL_DSI_VIDEO)) { + /* disable LCDC interrupt */ + spin_lock(&mdp_spin_lock); + mdp_intr_mask &= ~INTR_OVERLAY0_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->waiting = FALSE; + spin_unlock(&mdp_spin_lock); + if (panel & MDP4_PANEL_LCDC) + mdp4_overlay0_done_lcdc(dma); +#ifdef CONFIG_FB_MSM_MIPI_DSI + else if (panel & MDP4_PANEL_DSI_VIDEO) + mdp4_overlay0_done_dsi_video(dma); +#endif + } else { /* MDDI, DSI_CMD */ +#ifdef CONFIG_FB_MSM_MIPI_DSI + if (panel & MDP4_PANEL_DSI_CMD) + mdp4_overlay0_done_dsi_cmd(dma); +#else + if (panel & MDP4_PANEL_MDDI) + mdp4_overlay0_done_mddi(dma); +#endif + } + mdp_hw_cursor_done(); + } + if (isr & INTR_OVERLAY1_DONE) { + mdp4_stat.intr_overlay1++; + /* disable DTV interrupt */ + dma = &dma_e_data; + spin_lock(&mdp_spin_lock); + mdp_intr_mask &= ~INTR_OVERLAY1_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->waiting = FALSE; + spin_unlock(&mdp_spin_lock); +#if defined(CONFIG_FB_MSM_DTV) + if (panel & MDP4_PANEL_DTV) + mdp4_overlay1_done_dtv(); +#endif +#if defined(CONFIG_FB_MSM_TVOUT) + if (panel & MDP4_PANEL_ATV) + mdp4_overlay1_done_atv(); +#endif + } +#if defined(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) + if (isr & INTR_OVERLAY2_DONE) { + mdp4_stat.intr_overlay2++; + /* disable DTV interrupt */ + dma = &dma_wb_data; + spin_lock(&mdp_spin_lock); + mdp_intr_mask &= ~INTR_OVERLAY2_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->waiting = FALSE; + spin_unlock(&mdp_spin_lock); + if (panel & MDP4_PANEL_WRITEBACK) + mdp4_overlay1_done_writeback(dma); + } +#endif +#endif /* OVERLAY */ + + if (isr & INTR_DMA_P_DONE) { + mdp4_stat.intr_dma_p++; + dma = &dma2_data; + if (panel & MDP4_PANEL_LCDC) { + /* disable LCDC interrupt */ + spin_lock(&mdp_spin_lock); + mdp_intr_mask &= ~INTR_DMA_P_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->waiting = FALSE; + mdp4_dma_p_done_lcdc(); + spin_unlock(&mdp_spin_lock); + } +#ifdef CONFIG_FB_MSM_OVERLAY +#ifdef CONFIG_FB_MSM_MIPI_DSI + else if (panel & MDP4_PANEL_DSI_VIDEO) { + /* disable LCDC interrupt */ + spin_lock(&mdp_spin_lock); + mdp_intr_mask &= ~INTR_DMA_P_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->waiting = FALSE; + mdp4_dma_p_done_dsi_video(dma); + spin_unlock(&mdp_spin_lock); + } else if (panel & MDP4_PANEL_DSI_CMD) { + mdp4_dma_p_done_dsi(dma); + } +#else + else { /* MDDI */ + mdp4_dma_p_done_mddi(dma); + mdp_pipe_ctrl(MDP_DMA2_BLOCK, + MDP_BLOCK_POWER_OFF, TRUE); + complete(&dma->comp); + } +#endif +#else + else { + spin_lock(&mdp_spin_lock); + dma->busy = FALSE; + spin_unlock(&mdp_spin_lock); + complete(&dma->comp); + } +#endif + } + if (isr & INTR_DMA_S_DONE) { + mdp4_stat.intr_dma_s++; +#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI) + dma = &dma2_data; +#else + dma = &dma_s_data; +#endif + + dma->busy = FALSE; + mdp_pipe_ctrl(MDP_DMA_S_BLOCK, + MDP_BLOCK_POWER_OFF, TRUE); + complete(&dma->comp); + } + if (isr & INTR_DMA_E_DONE) { + mdp4_stat.intr_dma_e++; + dma = &dma_e_data; + spin_lock(&mdp_spin_lock); + mdp_intr_mask &= ~INTR_DMA_E_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + dma->busy = FALSE; + mdp4_dma_e_done_dtv(); + if (dma->waiting) { + dma->waiting = FALSE; + complete(&dma->comp); + } + spin_unlock(&mdp_spin_lock); + } + if (isr & INTR_DMA_P_HISTOGRAM) { + mdp4_stat.intr_histogram++; + ret = mdp_histogram_block2mgmt(MDP_BLOCK_DMA_P, &mgmt); + if (!ret) + mdp_histogram_handle_isr(mgmt); + } + if (isr & INTR_DMA_S_HISTOGRAM) { + mdp4_stat.intr_histogram++; + ret = mdp_histogram_block2mgmt(MDP_BLOCK_DMA_S, &mgmt); + if (!ret) + mdp_histogram_handle_isr(mgmt); + } + if (isr & INTR_VG1_HISTOGRAM) { + mdp4_stat.intr_histogram++; + ret = mdp_histogram_block2mgmt(MDP_BLOCK_VG_1, &mgmt); + if (!ret) + mdp_histogram_handle_isr(mgmt); + } + if (isr & INTR_VG2_HISTOGRAM) { + mdp4_stat.intr_histogram++; + ret = mdp_histogram_block2mgmt(MDP_BLOCK_VG_2, &mgmt); + if (!ret) + mdp_histogram_handle_isr(mgmt); + } + +out: + mdp_is_in_isr = FALSE; + + return IRQ_HANDLED; +} + + +/* + * QSEED tables + */ + +static uint32 vg_qseed_table0[] = { + 0x5556aaff, 0x00000000, 0x00000000, 0x00000000 +}; + +static uint32 vg_qseed_table1[] = { + 0x00000000, 0x20000000, +}; + +static uint32 vg_qseed_table2[] = { + 0x02000000, 0x00000000, 0x01ff0ff9, 0x00000008, + 0x01fb0ff2, 0x00000013, 0x01f50fed, 0x0ffe0020, + 0x01ed0fe8, 0x0ffd002e, 0x01e30fe4, 0x0ffb003e, + 0x01d80fe1, 0x0ff9004e, 0x01cb0fde, 0x0ff70060, + 0x01bc0fdc, 0x0ff40074, 0x01ac0fdb, 0x0ff20087, + 0x019a0fdb, 0x0fef009c, 0x01870fdb, 0x0fed00b1, + 0x01740fdb, 0x0fea00c7, 0x01600fdc, 0x0fe700dd, + 0x014b0fdd, 0x0fe500f3, 0x01350fdf, 0x0fe30109, + 0x01200fe0, 0x0fe00120, 0x01090fe3, 0x0fdf0135, + 0x00f30fe5, 0x0fdd014b, 0x00dd0fe7, 0x0fdc0160, + 0x00c70fea, 0x0fdb0174, 0x00b10fed, 0x0fdb0187, + 0x009c0fef, 0x0fdb019a, 0x00870ff2, 0x0fdb01ac, + 0x00740ff4, 0x0fdc01bc, 0x00600ff7, 0x0fde01cb, + 0x004e0ff9, 0x0fe101d8, 0x003e0ffb, 0x0fe401e3, + 0x002e0ffd, 0x0fe801ed, 0x00200ffe, 0x0fed01f5, + 0x00130000, 0x0ff201fb, 0x00080000, 0x0ff901ff, + + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + + 0x02000000, 0x00000000, 0x01fc0ff9, 0x0ffe000d, + 0x01f60ff3, 0x0ffb001c, 0x01ef0fed, 0x0ff9002b, + 0x01e60fe8, 0x0ff6003c, 0x01dc0fe4, 0x0ff3004d, + 0x01d00fe0, 0x0ff1005f, 0x01c30fde, 0x0fee0071, + 0x01b50fdb, 0x0feb0085, 0x01a70fd9, 0x0fe80098, + 0x01960fd8, 0x0fe600ac, 0x01850fd7, 0x0fe300c1, + 0x01730fd7, 0x0fe100d5, 0x01610fd7, 0x0fdf00e9, + 0x014e0fd8, 0x0fdd00fd, 0x013b0fd8, 0x0fdb0112, + 0x01250fda, 0x0fda0127, 0x01120fdb, 0x0fd8013b, + 0x00fd0fdd, 0x0fd8014e, 0x00e90fdf, 0x0fd70161, + 0x00d50fe1, 0x0fd70173, 0x00c10fe3, 0x0fd70185, + 0x00ac0fe6, 0x0fd80196, 0x00980fe8, 0x0fd901a7, + 0x00850feb, 0x0fdb01b5, 0x00710fee, 0x0fde01c3, + 0x005f0ff1, 0x0fe001d0, 0x004d0ff3, 0x0fe401dc, + 0x003c0ff6, 0x0fe801e6, 0x002b0ff9, 0x0fed01ef, + 0x001c0ffb, 0x0ff301f6, 0x000d0ffe, 0x0ff901fc, + + 0x020f0034, 0x0f7a0043, 0x01e80023, 0x0fa8004d, + 0x01d30016, 0x0fbe0059, 0x01c6000a, 0x0fc90067, + 0x01bd0000, 0x0fce0075, 0x01b50ff7, 0x0fcf0085, + 0x01ae0fee, 0x0fcf0095, 0x01a70fe6, 0x0fcd00a6, + 0x019d0fe0, 0x0fcb00b8, 0x01940fd9, 0x0fc900ca, + 0x01890fd4, 0x0fc700dc, 0x017d0fcf, 0x0fc600ee, + 0x01700fcc, 0x0fc40100, 0x01620fc9, 0x0fc40111, + 0x01540fc6, 0x0fc30123, 0x01430fc5, 0x0fc40134, + 0x01340fc4, 0x0fc50143, 0x01230fc3, 0x0fc60154, + 0x01110fc4, 0x0fc90162, 0x01000fc4, 0x0fcc0170, + 0x00ee0fc6, 0x0fcf017d, 0x00dc0fc7, 0x0fd40189, + 0x00ca0fc9, 0x0fd90194, 0x00b80fcb, 0x0fe0019d, + 0x00a60fcd, 0x0fe601a7, 0x00950fcf, 0x0fee01ae, + 0x00850fcf, 0x0ff701b5, 0x00750fce, 0x000001bd, + 0x00670fc9, 0x000a01c6, 0x00590fbe, 0x001601d3, + 0x004d0fa8, 0x002301e8, 0x00430f7a, 0x0034020f, + + 0x015c005e, 0x0fde0068, 0x015c0054, 0x0fdd0073, + 0x015b004b, 0x0fdc007e, 0x015a0042, 0x0fdb0089, + 0x01590039, 0x0fda0094, 0x01560030, 0x0fda00a0, + 0x01530028, 0x0fda00ab, 0x014f0020, 0x0fda00b7, + 0x014a0019, 0x0fdb00c2, 0x01450011, 0x0fdc00ce, + 0x013e000b, 0x0fde00d9, 0x01390004, 0x0fdf00e4, + 0x01310ffe, 0x0fe200ef, 0x01290ff9, 0x0fe400fa, + 0x01200ff4, 0x0fe80104, 0x01180fef, 0x0feb010e, + 0x010e0feb, 0x0fef0118, 0x01040fe8, 0x0ff40120, + 0x00fa0fe4, 0x0ff90129, 0x00ef0fe2, 0x0ffe0131, + 0x00e40fdf, 0x00040139, 0x00d90fde, 0x000b013e, + 0x00ce0fdc, 0x00110145, 0x00c20fdb, 0x0019014a, + 0x00b70fda, 0x0020014f, 0x00ab0fda, 0x00280153, + 0x00a00fda, 0x00300156, 0x00940fda, 0x00390159, + 0x00890fdb, 0x0042015a, 0x007e0fdc, 0x004b015b, + 0x00730fdd, 0x0054015c, 0x00680fde, 0x005e015c, + + 0x01300068, 0x0ff80070, 0x01300060, 0x0ff80078, + 0x012f0059, 0x0ff80080, 0x012d0052, 0x0ff80089, + 0x012b004b, 0x0ff90091, 0x01290044, 0x0ff9009a, + 0x0126003d, 0x0ffa00a3, 0x01220037, 0x0ffb00ac, + 0x011f0031, 0x0ffc00b4, 0x011a002b, 0x0ffe00bd, + 0x01150026, 0x000000c5, 0x010f0021, 0x000200ce, + 0x010a001c, 0x000400d6, 0x01030018, 0x000600df, + 0x00fd0014, 0x000900e6, 0x00f60010, 0x000c00ee, + 0x00ee000c, 0x001000f6, 0x00e60009, 0x001400fd, + 0x00df0006, 0x00180103, 0x00d60004, 0x001c010a, + 0x00ce0002, 0x0021010f, 0x00c50000, 0x00260115, + 0x00bd0ffe, 0x002b011a, 0x00b40ffc, 0x0031011f, + 0x00ac0ffb, 0x00370122, 0x00a30ffa, 0x003d0126, + 0x009a0ff9, 0x00440129, 0x00910ff9, 0x004b012b, + 0x00890ff8, 0x0052012d, 0x00800ff8, 0x0059012f, + 0x00780ff8, 0x00600130, 0x00700ff8, 0x00680130, + + 0x01050079, 0x0003007f, 0x01040073, 0x00030086, + 0x0103006d, 0x0004008c, 0x01030066, 0x00050092, + 0x01010060, 0x00060099, 0x0100005a, 0x0007009f, + 0x00fe0054, 0x000900a5, 0x00fa004f, 0x000b00ac, + 0x00f80049, 0x000d00b2, 0x00f50044, 0x000f00b8, + 0x00f2003f, 0x001200bd, 0x00ef0039, 0x001500c3, + 0x00ea0035, 0x001800c9, 0x00e60030, 0x001c00ce, + 0x00e3002b, 0x001f00d3, 0x00dd0027, 0x002300d9, + 0x00d90023, 0x002700dd, 0x00d3001f, 0x002b00e3, + 0x00ce001c, 0x003000e6, 0x00c90018, 0x003500ea, + 0x00c30015, 0x003900ef, 0x00bd0012, 0x003f00f2, + 0x00b8000f, 0x004400f5, 0x00b2000d, 0x004900f8, + 0x00ac000b, 0x004f00fa, 0x00a50009, 0x005400fe, + 0x009f0007, 0x005a0100, 0x00990006, 0x00600101, + 0x00920005, 0x00660103, 0x008c0004, 0x006d0103, + 0x00860003, 0x00730104, 0x007f0003, 0x00790105, + + 0x00cf0088, 0x001d008c, 0x00ce0084, 0x0020008e, + 0x00cd0080, 0x00210092, 0x00cd007b, 0x00240094, + 0x00ca0077, 0x00270098, 0x00c90073, 0x0029009b, + 0x00c8006f, 0x002c009d, 0x00c6006b, 0x002f00a0, + 0x00c50067, 0x003200a2, 0x00c30062, 0x003600a5, + 0x00c0005f, 0x003900a8, 0x00c0005b, 0x003b00aa, + 0x00be0057, 0x003e00ad, 0x00ba0054, 0x004200b0, + 0x00b90050, 0x004500b2, 0x00b7004c, 0x004900b4, + 0x00b40049, 0x004c00b7, 0x00b20045, 0x005000b9, + 0x00b00042, 0x005400ba, 0x00ad003e, 0x005700be, + 0x00aa003b, 0x005b00c0, 0x00a80039, 0x005f00c0, + 0x00a50036, 0x006200c3, 0x00a20032, 0x006700c5, + 0x00a0002f, 0x006b00c6, 0x009d002c, 0x006f00c8, + 0x009b0029, 0x007300c9, 0x00980027, 0x007700ca, + 0x00940024, 0x007b00cd, 0x00920021, 0x008000cd, + 0x008e0020, 0x008400ce, 0x008c001d, 0x008800cf, + + 0x008e0083, 0x006b0084, 0x008d0083, 0x006c0084, + 0x008d0082, 0x006d0084, 0x008d0081, 0x006d0085, + 0x008d0080, 0x006e0085, 0x008c007f, 0x006f0086, + 0x008b007f, 0x00700086, 0x008b007e, 0x00710086, + 0x008b007d, 0x00720086, 0x008a007d, 0x00730086, + 0x008a007c, 0x00730087, 0x008a007b, 0x00740087, + 0x0089007b, 0x00750087, 0x008a0079, 0x00750088, + 0x008a0078, 0x00760088, 0x008a0077, 0x00770088, + 0x00880077, 0x0077008a, 0x00880076, 0x0078008a, + 0x00880075, 0x0079008a, 0x00870075, 0x007b0089, + 0x00870074, 0x007b008a, 0x00870073, 0x007c008a, + 0x00860073, 0x007d008a, 0x00860072, 0x007d008b, + 0x00860071, 0x007e008b, 0x00860070, 0x007f008b, + 0x0086006f, 0x007f008c, 0x0085006e, 0x0080008d, + 0x0085006d, 0x0081008d, 0x0084006d, 0x0082008d, + 0x0084006c, 0x0083008d, 0x0084006b, 0x0083008e, + + 0x023c0fe2, 0x00000fe2, 0x023a0fdb, 0x00000feb, + 0x02360fd3, 0x0fff0ff8, 0x022e0fcf, 0x0ffc0007, + 0x02250fca, 0x0ffa0017, 0x021a0fc6, 0x0ff70029, + 0x020c0fc4, 0x0ff4003c, 0x01fd0fc1, 0x0ff10051, + 0x01eb0fc0, 0x0fed0068, 0x01d80fc0, 0x0fe9007f, + 0x01c30fc1, 0x0fe50097, 0x01ac0fc2, 0x0fe200b0, + 0x01960fc3, 0x0fdd00ca, 0x017e0fc5, 0x0fd900e4, + 0x01650fc8, 0x0fd500fe, 0x014b0fcb, 0x0fd20118, + 0x01330fcd, 0x0fcd0133, 0x01180fd2, 0x0fcb014b, + 0x00fe0fd5, 0x0fc80165, 0x00e40fd9, 0x0fc5017e, + 0x00ca0fdd, 0x0fc30196, 0x00b00fe2, 0x0fc201ac, + 0x00970fe5, 0x0fc101c3, 0x007f0fe9, 0x0fc001d8, + 0x00680fed, 0x0fc001eb, 0x00510ff1, 0x0fc101fd, + 0x003c0ff4, 0x0fc4020c, 0x00290ff7, 0x0fc6021a, + 0x00170ffa, 0x0fca0225, 0x00070ffc, 0x0fcf022e, + 0x0ff80fff, 0x0fd30236, 0x0feb0000, 0x0fdb023a, + + 0x02780fc4, 0x00000fc4, 0x02770fbc, 0x0fff0fce, + 0x02710fb5, 0x0ffe0fdc, 0x02690fb0, 0x0ffa0fed, + 0x025f0fab, 0x0ff70fff, 0x02500fa8, 0x0ff30015, + 0x02410fa6, 0x0fef002a, 0x022f0fa4, 0x0feb0042, + 0x021a0fa4, 0x0fe5005d, 0x02040fa5, 0x0fe10076, + 0x01eb0fa7, 0x0fdb0093, 0x01d20fa9, 0x0fd600af, + 0x01b80fab, 0x0fd000cd, 0x019d0faf, 0x0fca00ea, + 0x01810fb2, 0x0fc50108, 0x01620fb7, 0x0fc10126, + 0x01440fbb, 0x0fbb0146, 0x01260fc1, 0x0fb70162, + 0x01080fc5, 0x0fb20181, 0x00ea0fca, 0x0faf019d, + 0x00cd0fd0, 0x0fab01b8, 0x00af0fd6, 0x0fa901d2, + 0x00930fdb, 0x0fa701eb, 0x00760fe1, 0x0fa50204, + 0x005d0fe5, 0x0fa4021a, 0x00420feb, 0x0fa4022f, + 0x002a0fef, 0x0fa60241, 0x00150ff3, 0x0fa80250, + 0x0fff0ff7, 0x0fab025f, 0x0fed0ffa, 0x0fb00269, + 0x0fdc0ffe, 0x0fb50271, 0x0fce0fff, 0x0fbc0277, + + 0x02a00fb0, 0x00000fb0, 0x029e0fa8, 0x0fff0fbb, + 0x02980fa1, 0x0ffd0fca, 0x028f0f9c, 0x0ff90fdc, + 0x02840f97, 0x0ff50ff0, 0x02740f94, 0x0ff10007, + 0x02640f92, 0x0fec001e, 0x02500f91, 0x0fe70038, + 0x023a0f91, 0x0fe00055, 0x02220f92, 0x0fdb0071, + 0x02080f95, 0x0fd4008f, 0x01ec0f98, 0x0fce00ae, + 0x01cf0f9b, 0x0fc700cf, 0x01b10f9f, 0x0fc100ef, + 0x01920fa4, 0x0fbb010f, 0x01710faa, 0x0fb50130, + 0x01520fae, 0x0fae0152, 0x01300fb5, 0x0faa0171, + 0x010f0fbb, 0x0fa40192, 0x00ef0fc1, 0x0f9f01b1, + 0x00cf0fc7, 0x0f9b01cf, 0x00ae0fce, 0x0f9801ec, + 0x008f0fd4, 0x0f950208, 0x00710fdb, 0x0f920222, + 0x00550fe0, 0x0f91023a, 0x00380fe7, 0x0f910250, + 0x001e0fec, 0x0f920264, 0x00070ff1, 0x0f940274, + 0x0ff00ff5, 0x0f970284, 0x0fdc0ff9, 0x0f9c028f, + 0x0fca0ffd, 0x0fa10298, 0x0fbb0fff, 0x0fa8029e, + + 0x02c80f9c, 0x00000f9c, 0x02c70f94, 0x0ffe0fa7, + 0x02c10f8c, 0x0ffc0fb7, 0x02b70f87, 0x0ff70fcb, + 0x02aa0f83, 0x0ff30fe0, 0x02990f80, 0x0fee0ff9, + 0x02870f7f, 0x0fe80012, 0x02720f7e, 0x0fe2002e, + 0x025a0f7e, 0x0fdb004d, 0x02400f80, 0x0fd5006b, + 0x02230f84, 0x0fcd008c, 0x02050f87, 0x0fc700ad, + 0x01e60f8b, 0x0fbf00d0, 0x01c60f90, 0x0fb700f3, + 0x01a30f96, 0x0fb00117, 0x01800f9c, 0x0faa013a, + 0x015d0fa2, 0x0fa2015f, 0x013a0faa, 0x0f9c0180, + 0x01170fb0, 0x0f9601a3, 0x00f30fb7, 0x0f9001c6, + 0x00d00fbf, 0x0f8b01e6, 0x00ad0fc7, 0x0f870205, + 0x008c0fcd, 0x0f840223, 0x006b0fd5, 0x0f800240, + 0x004d0fdb, 0x0f7e025a, 0x002e0fe2, 0x0f7e0272, + 0x00120fe8, 0x0f7f0287, 0x0ff90fee, 0x0f800299, + 0x0fe00ff3, 0x0f8302aa, 0x0fcb0ff7, 0x0f8702b7, + 0x0fb70ffc, 0x0f8c02c1, 0x0fa70ffe, 0x0f9402c7, + + 0x02f00f88, 0x00000f88, 0x02ee0f80, 0x0ffe0f94, + 0x02e70f78, 0x0ffc0fa5, 0x02dd0f73, 0x0ff60fba, + 0x02ce0f6f, 0x0ff20fd1, 0x02be0f6c, 0x0feb0feb, + 0x02aa0f6b, 0x0fe50006, 0x02940f6a, 0x0fde0024, + 0x02790f6c, 0x0fd60045, 0x025e0f6e, 0x0fcf0065, + 0x023f0f72, 0x0fc60089, 0x021d0f77, 0x0fbf00ad, + 0x01fd0f7b, 0x0fb600d2, 0x01da0f81, 0x0fad00f8, + 0x01b50f87, 0x0fa6011e, 0x018f0f8f, 0x0f9e0144, + 0x016b0f95, 0x0f95016b, 0x01440f9e, 0x0f8f018f, + 0x011e0fa6, 0x0f8701b5, 0x00f80fad, 0x0f8101da, + 0x00d20fb6, 0x0f7b01fd, 0x00ad0fbf, 0x0f77021d, + 0x00890fc6, 0x0f72023f, 0x00650fcf, 0x0f6e025e, + 0x00450fd6, 0x0f6c0279, 0x00240fde, 0x0f6a0294, + 0x00060fe5, 0x0f6b02aa, 0x0feb0feb, 0x0f6c02be, + 0x0fd10ff2, 0x0f6f02ce, 0x0fba0ff6, 0x0f7302dd, + 0x0fa50ffc, 0x0f7802e7, 0x0f940ffe, 0x0f8002ee, + + 0x03180f74, 0x00000f74, 0x03160f6b, 0x0ffe0f81, + 0x030e0f64, 0x0ffb0f93, 0x03030f5f, 0x0ff50fa9, + 0x02f40f5b, 0x0ff00fc1, 0x02e20f58, 0x0fe90fdd, + 0x02cd0f57, 0x0fe20ffa, 0x02b60f57, 0x0fda0019, + 0x02990f59, 0x0fd1003d, 0x027b0f5c, 0x0fc90060, + 0x02590f61, 0x0fc00086, 0x02370f66, 0x0fb700ac, + 0x02130f6b, 0x0fae00d4, 0x01ee0f72, 0x0fa400fc, + 0x01c70f79, 0x0f9b0125, 0x019f0f81, 0x0f93014d, + 0x01760f89, 0x0f890178, 0x014d0f93, 0x0f81019f, + 0x01250f9b, 0x0f7901c7, 0x00fc0fa4, 0x0f7201ee, + 0x00d40fae, 0x0f6b0213, 0x00ac0fb7, 0x0f660237, + 0x00860fc0, 0x0f610259, 0x00600fc9, 0x0f5c027b, + 0x003d0fd1, 0x0f590299, 0x00190fda, 0x0f5702b6, + 0x0ffa0fe2, 0x0f5702cd, 0x0fdd0fe9, 0x0f5802e2, + 0x0fc10ff0, 0x0f5b02f4, 0x0fa90ff5, 0x0f5f0303, + 0x0f930ffb, 0x0f64030e, 0x0f810ffe, 0x0f6b0316, + + 0x03400f60, 0x00000f60, 0x033e0f57, 0x0ffe0f6d, + 0x03370f4f, 0x0ffa0f80, 0x032a0f4b, 0x0ff30f98, + 0x031a0f46, 0x0fee0fb2, 0x03070f44, 0x0fe60fcf, + 0x02f10f44, 0x0fde0fed, 0x02d70f44, 0x0fd6000f, + 0x02b80f46, 0x0fcc0036, 0x02990f4a, 0x0fc3005a, + 0x02750f4f, 0x0fb90083, 0x02500f55, 0x0fb000ab, + 0x022a0f5b, 0x0fa500d6, 0x02020f63, 0x0f9a0101, + 0x01d80f6b, 0x0f91012c, 0x01ae0f74, 0x0f870157, + 0x01840f7c, 0x0f7c0184, 0x01570f87, 0x0f7401ae, + 0x012c0f91, 0x0f6b01d8, 0x01010f9a, 0x0f630202, + 0x00d60fa5, 0x0f5b022a, 0x00ab0fb0, 0x0f550250, + 0x00830fb9, 0x0f4f0275, 0x005a0fc3, 0x0f4a0299, + 0x00360fcc, 0x0f4602b8, 0x000f0fd6, 0x0f4402d7, + 0x0fed0fde, 0x0f4402f1, 0x0fcf0fe6, 0x0f440307, + 0x0fb20fee, 0x0f46031a, 0x0f980ff3, 0x0f4b032a, + 0x0f800ffa, 0x0f4f0337, 0x0f6d0ffe, 0x0f57033e, + + 0x02000000, 0x00000000, 0x01ff0ff9, 0x00000008, + 0x01fb0ff2, 0x00000013, 0x01f50fed, 0x0ffe0020, + 0x01ed0fe8, 0x0ffd002e, 0x01e30fe4, 0x0ffb003e, + 0x01d80fe1, 0x0ff9004e, 0x01cb0fde, 0x0ff70060, + 0x01bc0fdc, 0x0ff40074, 0x01ac0fdb, 0x0ff20087, + 0x019a0fdb, 0x0fef009c, 0x01870fdb, 0x0fed00b1, + 0x01740fdb, 0x0fea00c7, 0x01600fdc, 0x0fe700dd, + 0x014b0fdd, 0x0fe500f3, 0x01350fdf, 0x0fe30109, + 0x01200fe0, 0x0fe00120, 0x01090fe3, 0x0fdf0135, + 0x00f30fe5, 0x0fdd014b, 0x00dd0fe7, 0x0fdc0160, + 0x00c70fea, 0x0fdb0174, 0x00b10fed, 0x0fdb0187, + 0x009c0fef, 0x0fdb019a, 0x00870ff2, 0x0fdb01ac, + 0x00740ff4, 0x0fdc01bc, 0x00600ff7, 0x0fde01cb, + 0x004e0ff9, 0x0fe101d8, 0x003e0ffb, 0x0fe401e3, + 0x002e0ffd, 0x0fe801ed, 0x00200ffe, 0x0fed01f5, + 0x00130000, 0x0ff201fb, 0x00080000, 0x0ff901ff, + + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + 0x02000000, 0x00000000, 0x02000000, 0x00000000, + + 0x02000000, 0x00000000, 0x01fc0ff9, 0x0ffe000d, + 0x01f60ff3, 0x0ffb001c, 0x01ef0fed, 0x0ff9002b, + 0x01e60fe8, 0x0ff6003c, 0x01dc0fe4, 0x0ff3004d, + 0x01d00fe0, 0x0ff1005f, 0x01c30fde, 0x0fee0071, + 0x01b50fdb, 0x0feb0085, 0x01a70fd9, 0x0fe80098, + 0x01960fd8, 0x0fe600ac, 0x01850fd7, 0x0fe300c1, + 0x01730fd7, 0x0fe100d5, 0x01610fd7, 0x0fdf00e9, + 0x014e0fd8, 0x0fdd00fd, 0x013b0fd8, 0x0fdb0112, + 0x01250fda, 0x0fda0127, 0x01120fdb, 0x0fd8013b, + 0x00fd0fdd, 0x0fd8014e, 0x00e90fdf, 0x0fd70161, + 0x00d50fe1, 0x0fd70173, 0x00c10fe3, 0x0fd70185, + 0x00ac0fe6, 0x0fd80196, 0x00980fe8, 0x0fd901a7, + 0x00850feb, 0x0fdb01b5, 0x00710fee, 0x0fde01c3, + 0x005f0ff1, 0x0fe001d0, 0x004d0ff3, 0x0fe401dc, + 0x003c0ff6, 0x0fe801e6, 0x002b0ff9, 0x0fed01ef, + 0x001c0ffb, 0x0ff301f6, 0x000d0ffe, 0x0ff901fc, + + 0x020f0034, 0x0f7a0043, 0x01e80023, 0x0fa8004d, + 0x01d30016, 0x0fbe0059, 0x01c6000a, 0x0fc90067, + 0x01bd0000, 0x0fce0075, 0x01b50ff7, 0x0fcf0085, + 0x01ae0fee, 0x0fcf0095, 0x01a70fe6, 0x0fcd00a6, + 0x019d0fe0, 0x0fcb00b8, 0x01940fd9, 0x0fc900ca, + 0x01890fd4, 0x0fc700dc, 0x017d0fcf, 0x0fc600ee, + 0x01700fcc, 0x0fc40100, 0x01620fc9, 0x0fc40111, + 0x01540fc6, 0x0fc30123, 0x01430fc5, 0x0fc40134, + 0x01340fc4, 0x0fc50143, 0x01230fc3, 0x0fc60154, + 0x01110fc4, 0x0fc90162, 0x01000fc4, 0x0fcc0170, + 0x00ee0fc6, 0x0fcf017d, 0x00dc0fc7, 0x0fd40189, + 0x00ca0fc9, 0x0fd90194, 0x00b80fcb, 0x0fe0019d, + 0x00a60fcd, 0x0fe601a7, 0x00950fcf, 0x0fee01ae, + 0x00850fcf, 0x0ff701b5, 0x00750fce, 0x000001bd, + 0x00670fc9, 0x000a01c6, 0x00590fbe, 0x001601d3, + 0x004d0fa8, 0x002301e8, 0x00430f7a, 0x0034020f, + + 0x015c005e, 0x0fde0068, 0x015c0054, 0x0fdd0073, + 0x015b004b, 0x0fdc007e, 0x015a0042, 0x0fdb0089, + 0x01590039, 0x0fda0094, 0x01560030, 0x0fda00a0, + 0x01530028, 0x0fda00ab, 0x014f0020, 0x0fda00b7, + 0x014a0019, 0x0fdb00c2, 0x01450011, 0x0fdc00ce, + 0x013e000b, 0x0fde00d9, 0x01390004, 0x0fdf00e4, + 0x01310ffe, 0x0fe200ef, 0x01290ff9, 0x0fe400fa, + 0x01200ff4, 0x0fe80104, 0x01180fef, 0x0feb010e, + 0x010e0feb, 0x0fef0118, 0x01040fe8, 0x0ff40120, + 0x00fa0fe4, 0x0ff90129, 0x00ef0fe2, 0x0ffe0131, + 0x00e40fdf, 0x00040139, 0x00d90fde, 0x000b013e, + 0x00ce0fdc, 0x00110145, 0x00c20fdb, 0x0019014a, + 0x00b70fda, 0x0020014f, 0x00ab0fda, 0x00280153, + 0x00a00fda, 0x00300156, 0x00940fda, 0x00390159, + 0x00890fdb, 0x0042015a, 0x007e0fdc, 0x004b015b, + 0x00730fdd, 0x0054015c, 0x00680fde, 0x005e015c, + + 0x01300068, 0x0ff80070, 0x01300060, 0x0ff80078, + 0x012f0059, 0x0ff80080, 0x012d0052, 0x0ff80089, + 0x012b004b, 0x0ff90091, 0x01290044, 0x0ff9009a, + 0x0126003d, 0x0ffa00a3, 0x01220037, 0x0ffb00ac, + 0x011f0031, 0x0ffc00b4, 0x011a002b, 0x0ffe00bd, + 0x01150026, 0x000000c5, 0x010f0021, 0x000200ce, + 0x010a001c, 0x000400d6, 0x01030018, 0x000600df, + 0x00fd0014, 0x000900e6, 0x00f60010, 0x000c00ee, + 0x00ee000c, 0x001000f6, 0x00e60009, 0x001400fd, + 0x00df0006, 0x00180103, 0x00d60004, 0x001c010a, + 0x00ce0002, 0x0021010f, 0x00c50000, 0x00260115, + 0x00bd0ffe, 0x002b011a, 0x00b40ffc, 0x0031011f, + 0x00ac0ffb, 0x00370122, 0x00a30ffa, 0x003d0126, + 0x009a0ff9, 0x00440129, 0x00910ff9, 0x004b012b, + 0x00890ff8, 0x0052012d, 0x00800ff8, 0x0059012f, + 0x00780ff8, 0x00600130, 0x00700ff8, 0x00680130, + + 0x01050079, 0x0003007f, 0x01040073, 0x00030086, + 0x0103006d, 0x0004008c, 0x01030066, 0x00050092, + 0x01010060, 0x00060099, 0x0100005a, 0x0007009f, + 0x00fe0054, 0x000900a5, 0x00fa004f, 0x000b00ac, + 0x00f80049, 0x000d00b2, 0x00f50044, 0x000f00b8, + 0x00f2003f, 0x001200bd, 0x00ef0039, 0x001500c3, + 0x00ea0035, 0x001800c9, 0x00e60030, 0x001c00ce, + 0x00e3002b, 0x001f00d3, 0x00dd0027, 0x002300d9, + 0x00d90023, 0x002700dd, 0x00d3001f, 0x002b00e3, + 0x00ce001c, 0x003000e6, 0x00c90018, 0x003500ea, + 0x00c30015, 0x003900ef, 0x00bd0012, 0x003f00f2, + 0x00b8000f, 0x004400f5, 0x00b2000d, 0x004900f8, + 0x00ac000b, 0x004f00fa, 0x00a50009, 0x005400fe, + 0x009f0007, 0x005a0100, 0x00990006, 0x00600101, + 0x00920005, 0x00660103, 0x008c0004, 0x006d0103, + 0x00860003, 0x00730104, 0x007f0003, 0x00790105, + + 0x00cf0088, 0x001d008c, 0x00ce0084, 0x0020008e, + 0x00cd0080, 0x00210092, 0x00cd007b, 0x00240094, + 0x00ca0077, 0x00270098, 0x00c90073, 0x0029009b, + 0x00c8006f, 0x002c009d, 0x00c6006b, 0x002f00a0, + 0x00c50067, 0x003200a2, 0x00c30062, 0x003600a5, + 0x00c0005f, 0x003900a8, 0x00c0005b, 0x003b00aa, + 0x00be0057, 0x003e00ad, 0x00ba0054, 0x004200b0, + 0x00b90050, 0x004500b2, 0x00b7004c, 0x004900b4, + 0x00b40049, 0x004c00b7, 0x00b20045, 0x005000b9, + 0x00b00042, 0x005400ba, 0x00ad003e, 0x005700be, + 0x00aa003b, 0x005b00c0, 0x00a80039, 0x005f00c0, + 0x00a50036, 0x006200c3, 0x00a20032, 0x006700c5, + 0x00a0002f, 0x006b00c6, 0x009d002c, 0x006f00c8, + 0x009b0029, 0x007300c9, 0x00980027, 0x007700ca, + 0x00940024, 0x007b00cd, 0x00920021, 0x008000cd, + 0x008e0020, 0x008400ce, 0x008c001d, 0x008800cf, + + 0x008e0083, 0x006b0084, 0x008d0083, 0x006c0084, + 0x008d0082, 0x006d0084, 0x008d0081, 0x006d0085, + 0x008d0080, 0x006e0085, 0x008c007f, 0x006f0086, + 0x008b007f, 0x00700086, 0x008b007e, 0x00710086, + 0x008b007d, 0x00720086, 0x008a007d, 0x00730086, + 0x008a007c, 0x00730087, 0x008a007b, 0x00740087, + 0x0089007b, 0x00750087, 0x008a0079, 0x00750088, + 0x008a0078, 0x00760088, 0x008a0077, 0x00770088, + 0x00880077, 0x0077008a, 0x00880076, 0x0078008a, + 0x00880075, 0x0079008a, 0x00870075, 0x007b0089, + 0x00870074, 0x007b008a, 0x00870073, 0x007c008a, + 0x00860073, 0x007d008a, 0x00860072, 0x007d008b, + 0x00860071, 0x007e008b, 0x00860070, 0x007f008b, + 0x0086006f, 0x007f008c, 0x0085006e, 0x0080008d, + 0x0085006d, 0x0081008d, 0x0084006d, 0x0082008d, + 0x0084006c, 0x0083008d, 0x0084006b, 0x0083008e, + + 0x023c0fe2, 0x00000fe2, 0x023a0fdb, 0x00000feb, + 0x02360fd3, 0x0fff0ff8, 0x022e0fcf, 0x0ffc0007, + 0x02250fca, 0x0ffa0017, 0x021a0fc6, 0x0ff70029, + 0x020c0fc4, 0x0ff4003c, 0x01fd0fc1, 0x0ff10051, + 0x01eb0fc0, 0x0fed0068, 0x01d80fc0, 0x0fe9007f, + 0x01c30fc1, 0x0fe50097, 0x01ac0fc2, 0x0fe200b0, + 0x01960fc3, 0x0fdd00ca, 0x017e0fc5, 0x0fd900e4, + 0x01650fc8, 0x0fd500fe, 0x014b0fcb, 0x0fd20118, + 0x01330fcd, 0x0fcd0133, 0x01180fd2, 0x0fcb014b, + 0x00fe0fd5, 0x0fc80165, 0x00e40fd9, 0x0fc5017e, + 0x00ca0fdd, 0x0fc30196, 0x00b00fe2, 0x0fc201ac, + 0x00970fe5, 0x0fc101c3, 0x007f0fe9, 0x0fc001d8, + 0x00680fed, 0x0fc001eb, 0x00510ff1, 0x0fc101fd, + 0x003c0ff4, 0x0fc4020c, 0x00290ff7, 0x0fc6021a, + 0x00170ffa, 0x0fca0225, 0x00070ffc, 0x0fcf022e, + 0x0ff80fff, 0x0fd30236, 0x0feb0000, 0x0fdb023a, + + 0x02780fc4, 0x00000fc4, 0x02770fbc, 0x0fff0fce, + 0x02710fb5, 0x0ffe0fdc, 0x02690fb0, 0x0ffa0fed, + 0x025f0fab, 0x0ff70fff, 0x02500fa8, 0x0ff30015, + 0x02410fa6, 0x0fef002a, 0x022f0fa4, 0x0feb0042, + 0x021a0fa4, 0x0fe5005d, 0x02040fa5, 0x0fe10076, + 0x01eb0fa7, 0x0fdb0093, 0x01d20fa9, 0x0fd600af, + 0x01b80fab, 0x0fd000cd, 0x019d0faf, 0x0fca00ea, + 0x01810fb2, 0x0fc50108, 0x01620fb7, 0x0fc10126, + 0x01440fbb, 0x0fbb0146, 0x01260fc1, 0x0fb70162, + 0x01080fc5, 0x0fb20181, 0x00ea0fca, 0x0faf019d, + 0x00cd0fd0, 0x0fab01b8, 0x00af0fd6, 0x0fa901d2, + 0x00930fdb, 0x0fa701eb, 0x00760fe1, 0x0fa50204, + 0x005d0fe5, 0x0fa4021a, 0x00420feb, 0x0fa4022f, + 0x002a0fef, 0x0fa60241, 0x00150ff3, 0x0fa80250, + 0x0fff0ff7, 0x0fab025f, 0x0fed0ffa, 0x0fb00269, + 0x0fdc0ffe, 0x0fb50271, 0x0fce0fff, 0x0fbc0277, + + 0x02a00fb0, 0x00000fb0, 0x029e0fa8, 0x0fff0fbb, + 0x02980fa1, 0x0ffd0fca, 0x028f0f9c, 0x0ff90fdc, + 0x02840f97, 0x0ff50ff0, 0x02740f94, 0x0ff10007, + 0x02640f92, 0x0fec001e, 0x02500f91, 0x0fe70038, + 0x023a0f91, 0x0fe00055, 0x02220f92, 0x0fdb0071, + 0x02080f95, 0x0fd4008f, 0x01ec0f98, 0x0fce00ae, + 0x01cf0f9b, 0x0fc700cf, 0x01b10f9f, 0x0fc100ef, + 0x01920fa4, 0x0fbb010f, 0x01710faa, 0x0fb50130, + 0x01520fae, 0x0fae0152, 0x01300fb5, 0x0faa0171, + 0x010f0fbb, 0x0fa40192, 0x00ef0fc1, 0x0f9f01b1, + 0x00cf0fc7, 0x0f9b01cf, 0x00ae0fce, 0x0f9801ec, + 0x008f0fd4, 0x0f950208, 0x00710fdb, 0x0f920222, + 0x00550fe0, 0x0f91023a, 0x00380fe7, 0x0f910250, + 0x001e0fec, 0x0f920264, 0x00070ff1, 0x0f940274, + 0x0ff00ff5, 0x0f970284, 0x0fdc0ff9, 0x0f9c028f, + 0x0fca0ffd, 0x0fa10298, 0x0fbb0fff, 0x0fa8029e, + + 0x02c80f9c, 0x00000f9c, 0x02c70f94, 0x0ffe0fa7, + 0x02c10f8c, 0x0ffc0fb7, 0x02b70f87, 0x0ff70fcb, + 0x02aa0f83, 0x0ff30fe0, 0x02990f80, 0x0fee0ff9, + 0x02870f7f, 0x0fe80012, 0x02720f7e, 0x0fe2002e, + 0x025a0f7e, 0x0fdb004d, 0x02400f80, 0x0fd5006b, + 0x02230f84, 0x0fcd008c, 0x02050f87, 0x0fc700ad, + 0x01e60f8b, 0x0fbf00d0, 0x01c60f90, 0x0fb700f3, + 0x01a30f96, 0x0fb00117, 0x01800f9c, 0x0faa013a, + 0x015d0fa2, 0x0fa2015f, 0x013a0faa, 0x0f9c0180, + 0x01170fb0, 0x0f9601a3, 0x00f30fb7, 0x0f9001c6, + 0x00d00fbf, 0x0f8b01e6, 0x00ad0fc7, 0x0f870205, + 0x008c0fcd, 0x0f840223, 0x006b0fd5, 0x0f800240, + 0x004d0fdb, 0x0f7e025a, 0x002e0fe2, 0x0f7e0272, + 0x00120fe8, 0x0f7f0287, 0x0ff90fee, 0x0f800299, + 0x0fe00ff3, 0x0f8302aa, 0x0fcb0ff7, 0x0f8702b7, + 0x0fb70ffc, 0x0f8c02c1, 0x0fa70ffe, 0x0f9402c7, + + 0x02f00f88, 0x00000f88, 0x02ee0f80, 0x0ffe0f94, + 0x02e70f78, 0x0ffc0fa5, 0x02dd0f73, 0x0ff60fba, + 0x02ce0f6f, 0x0ff20fd1, 0x02be0f6c, 0x0feb0feb, + 0x02aa0f6b, 0x0fe50006, 0x02940f6a, 0x0fde0024, + 0x02790f6c, 0x0fd60045, 0x025e0f6e, 0x0fcf0065, + 0x023f0f72, 0x0fc60089, 0x021d0f77, 0x0fbf00ad, + 0x01fd0f7b, 0x0fb600d2, 0x01da0f81, 0x0fad00f8, + 0x01b50f87, 0x0fa6011e, 0x018f0f8f, 0x0f9e0144, + 0x016b0f95, 0x0f95016b, 0x01440f9e, 0x0f8f018f, + 0x011e0fa6, 0x0f8701b5, 0x00f80fad, 0x0f8101da, + 0x00d20fb6, 0x0f7b01fd, 0x00ad0fbf, 0x0f77021d, + 0x00890fc6, 0x0f72023f, 0x00650fcf, 0x0f6e025e, + 0x00450fd6, 0x0f6c0279, 0x00240fde, 0x0f6a0294, + 0x00060fe5, 0x0f6b02aa, 0x0feb0feb, 0x0f6c02be, + 0x0fd10ff2, 0x0f6f02ce, 0x0fba0ff6, 0x0f7302dd, + 0x0fa50ffc, 0x0f7802e7, 0x0f940ffe, 0x0f8002ee, + + 0x03180f74, 0x00000f74, 0x03160f6b, 0x0ffe0f81, + 0x030e0f64, 0x0ffb0f93, 0x03030f5f, 0x0ff50fa9, + 0x02f40f5b, 0x0ff00fc1, 0x02e20f58, 0x0fe90fdd, + 0x02cd0f57, 0x0fe20ffa, 0x02b60f57, 0x0fda0019, + 0x02990f59, 0x0fd1003d, 0x027b0f5c, 0x0fc90060, + 0x02590f61, 0x0fc00086, 0x02370f66, 0x0fb700ac, + 0x02130f6b, 0x0fae00d4, 0x01ee0f72, 0x0fa400fc, + 0x01c70f79, 0x0f9b0125, 0x019f0f81, 0x0f93014d, + 0x01760f89, 0x0f890178, 0x014d0f93, 0x0f81019f, + 0x01250f9b, 0x0f7901c7, 0x00fc0fa4, 0x0f7201ee, + 0x00d40fae, 0x0f6b0213, 0x00ac0fb7, 0x0f660237, + 0x00860fc0, 0x0f610259, 0x00600fc9, 0x0f5c027b, + 0x003d0fd1, 0x0f590299, 0x00190fda, 0x0f5702b6, + 0x0ffa0fe2, 0x0f5702cd, 0x0fdd0fe9, 0x0f5802e2, + 0x0fc10ff0, 0x0f5b02f4, 0x0fa90ff5, 0x0f5f0303, + 0x0f930ffb, 0x0f64030e, 0x0f810ffe, 0x0f6b0316, + + 0x03400f60, 0x00000f60, 0x033e0f57, 0x0ffe0f6d, + 0x03370f4f, 0x0ffa0f80, 0x032a0f4b, 0x0ff30f98, + 0x031a0f46, 0x0fee0fb2, 0x03070f44, 0x0fe60fcf, + 0x02f10f44, 0x0fde0fed, 0x02d70f44, 0x0fd6000f, + 0x02b80f46, 0x0fcc0036, 0x02990f4a, 0x0fc3005a, + 0x02750f4f, 0x0fb90083, 0x02500f55, 0x0fb000ab, + 0x022a0f5b, 0x0fa500d6, 0x02020f63, 0x0f9a0101, + 0x01d80f6b, 0x0f91012c, 0x01ae0f74, 0x0f870157, + 0x01840f7c, 0x0f7c0184, 0x01570f87, 0x0f7401ae, + 0x012c0f91, 0x0f6b01d8, 0x01010f9a, 0x0f630202, + 0x00d60fa5, 0x0f5b022a, 0x00ab0fb0, 0x0f550250, + 0x00830fb9, 0x0f4f0275, 0x005a0fc3, 0x0f4a0299, + 0x00360fcc, 0x0f4602b8, 0x000f0fd6, 0x0f4402d7, + 0x0fed0fde, 0x0f4402f1, 0x0fcf0fe6, 0x0f440307, + 0x0fb20fee, 0x0f46031a, 0x0f980ff3, 0x0f4b032a, + 0x0f800ffa, 0x0f4f0337, 0x0f6d0ffe, 0x0f57033e +}; + + +#define MDP4_QSEED_TABLE0_OFF 0x8100 +#define MDP4_QSEED_TABLE1_OFF 0x8200 +#define MDP4_QSEED_TABLE2_OFF 0x9000 + +void mdp4_vg_qseed_init(int vp_num) +{ + uint32 *off; + int i, voff; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + voff = MDP4_VIDEO_OFF * vp_num; + off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + + MDP4_QSEED_TABLE0_OFF); + for (i = 0; i < (sizeof(vg_qseed_table0) / sizeof(uint32)); i++) { + outpdw(off, vg_qseed_table0[i]); + off++; + /* This code is added to workaround the 1K Boundary AXI + Interleave operations from Scorpion that can potentially + corrupt the QSEED table. The idea is to complete the prevous + to the buffer before making the next write when address is + 1KB aligned to ensure the write has been committed prior to + next instruction write that can go out from the secondary AXI + port.This happens also because of the expected write sequence + from QSEED table, where LSP has to be written first then the + MSP to trigger both to write out to SRAM, if this has not been + the expectation, then corruption wouldn't have happened.*/ + + if (!((uint32)off & 0x3FF)) + wmb(); + } + + off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + + MDP4_QSEED_TABLE1_OFF); + for (i = 0; i < (sizeof(vg_qseed_table1) / sizeof(uint32)); i++) { + outpdw(off, vg_qseed_table1[i]); + off++; + if (!((uint32)off & 0x3FF)) + wmb(); + } + + off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + + MDP4_QSEED_TABLE2_OFF); + for (i = 0; i < (sizeof(vg_qseed_table2) / sizeof(uint32)); i++) { + outpdw(off, vg_qseed_table2[i]); + off++; + if (!((uint32)off & 0x3FF)) + wmb(); + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + +} + +void mdp4_mixer_blend_init(mixer_num) +{ + unsigned char *overlay_base; + int off; + + if (mixer_num) /* mixer number, /dev/fb0, /dev/fb1 */ + overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */ + else + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* stage 0 to stage 2 */ + off = 0; + outpdw(overlay_base + off + 0x104, 0x010); + outpdw(overlay_base + off + 0x108, 0xff);/* FG */ + outpdw(overlay_base + off + 0x10c, 0x00);/* BG */ + + off += 0x20; + outpdw(overlay_base + off + 0x104, 0x010); + outpdw(overlay_base + off + 0x108, 0xff);/* FG */ + outpdw(overlay_base + off + 0x10c, 0x00);/* BG */ + + off += 0x20; + outpdw(overlay_base + off + 0x104, 0x010); + outpdw(overlay_base + off + 0x108, 0xff);/* FG */ + outpdw(overlay_base + off + 0x10c, 0x00);/* BG */ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +struct mdp_csc_cfg mdp_csc_convert[4] = { + { /*RGB2RGB*/ + 0, + { + 0x0200, 0x0000, 0x0000, + 0x0000, 0x0200, 0x0000, + 0x0000, 0x0000, 0x0200, + }, + { 0x0, 0x0, 0x0, }, + { 0x0, 0x0, 0x0, }, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, }, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, }, + }, + { /*YUV2RGB*/ + 0, + { + 0x0254, 0x0000, 0x0331, + 0x0254, 0xff37, 0xfe60, + 0x0254, 0x0409, 0x0000, + }, + { 0xfff0, 0xff80, 0xff80, }, + { 0x0, 0x0, 0x0, }, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, }, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, }, + }, + { /*RGB2YUV*/ + 0, + { + 0x0083, 0x0102, 0x0032, + 0x1fb5, 0x1f6c, 0x00e1, + 0x00e1, 0x1f45, 0x1fdc + }, + { 0x0, 0x0, 0x0, }, + { 0x0010, 0x0080, 0x0080, }, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, }, + { 0x0010, 0x00eb, 0x0010, 0x00f0, 0x0010, 0x00f0, }, + }, + { /*YUV2YUV ???*/ + 0, + { + 0x0200, 0x0000, 0x0000, + 0x0000, 0x0200, 0x0000, + 0x0000, 0x0000, 0x0200, + }, + { 0x0, 0x0, 0x0, }, + { 0x0, 0x0, 0x0, }, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, }, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, }, + }, +}; + +struct mdp_csc_cfg csc_matrix[3] = { + { + (MDP_CSC_FLAG_YUV_OUT), + { + 0x0254, 0x0000, 0x0331, + 0x0254, 0xff37, 0xfe60, + 0x0254, 0x0409, 0x0000, + }, + { + 0xfff0, 0xff80, 0xff80, + }, + { + 0, 0, 0, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + }, + { + (MDP_CSC_FLAG_YUV_OUT), + { + 0x0254, 0x0000, 0x0331, + 0x0254, 0xff37, 0xfe60, + 0x0254, 0x0409, 0x0000, + }, + { + 0xfff0, 0xff80, 0xff80, + }, + { + 0, 0, 0, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + }, + { + (0), + { + 0x0200, 0x0000, 0x0000, + 0x0000, 0x0200, 0x0000, + 0x0000, 0x0000, 0x0200, + }, + { + 0x0, 0x0, 0x0, + }, + { + 0, 0, 0, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + }, +}; + + + +#define MDP4_CSC_MV_OFF 0x4400 +#define MDP4_CSC_PRE_BV_OFF 0x4500 +#define MDP4_CSC_POST_BV_OFF 0x4580 +#define MDP4_CSC_PRE_LV_OFF 0x4600 +#define MDP4_CSC_POST_LV_OFF 0x4680 + +void mdp4_vg_csc_mv_setup(int vp_num) +{ + uint32 *off; + int i, voff; + + voff = MDP4_VIDEO_OFF * vp_num; + off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + + MDP4_CSC_MV_OFF); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 9; i++) { + outpdw(off, csc_matrix[vp_num].csc_mv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_vg_csc_pre_bv_setup(int vp_num) +{ + uint32 *off; + int i, voff; + + voff = MDP4_VIDEO_OFF * vp_num; + off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + + MDP4_CSC_PRE_BV_OFF); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 3; i++) { + outpdw(off, csc_matrix[vp_num].csc_pre_bv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_vg_csc_post_bv_setup(int vp_num) +{ + uint32 *off; + int i, voff; + + voff = MDP4_VIDEO_OFF * vp_num; + off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + + MDP4_CSC_POST_BV_OFF); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 3; i++) { + outpdw(off, csc_matrix[vp_num].csc_post_bv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_vg_csc_pre_lv_setup(int vp_num) +{ + uint32 *off; + int i, voff; + + voff = MDP4_VIDEO_OFF * vp_num; + off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + + MDP4_CSC_PRE_LV_OFF); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 6; i++) { + outpdw(off, csc_matrix[vp_num].csc_pre_lv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_vg_csc_post_lv_setup(int vp_num) +{ + uint32 *off; + int i, voff; + + voff = MDP4_VIDEO_OFF * vp_num; + off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + + MDP4_CSC_POST_LV_OFF); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 6; i++) { + outpdw(off, csc_matrix[vp_num].csc_post_lv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_vg_csc_convert_setup(int vp_num) +{ + struct mdp_csc_cfg_data cfg; + + switch (vp_num) { + case 0: + cfg.block = MDP_BLOCK_VG_1; + break; + case 1: + cfg.block = MDP_BLOCK_VG_2; + break; + default: + pr_err("%s - invalid vp_num = %d", __func__, vp_num); + return; + } + cfg.csc_data = csc_matrix[vp_num]; + mdp4_csc_enable(&cfg); +} + +void mdp4_vg_csc_setup(int vp_num) +{ + /* yuv2rgb */ + mdp4_vg_csc_mv_setup(vp_num); + mdp4_vg_csc_pre_bv_setup(vp_num); + mdp4_vg_csc_post_bv_setup(vp_num); + mdp4_vg_csc_pre_lv_setup(vp_num); + mdp4_vg_csc_post_lv_setup(vp_num); + mdp4_vg_csc_convert_setup(vp_num); +} +void mdp4_vg_csc_update(struct mdp_csc *p) +{ + struct mdp4_overlay_pipe *pipe; + int vp_num; + + pipe = mdp4_overlay_ndx2pipe(p->id); + if (pipe == NULL) { + pr_err("%s: p->id = %d Error\n", __func__, p->id); + return; + } + + vp_num = pipe->pipe_num - OVERLAY_PIPE_VG1; + + if (vp_num == 0 || vp_num == 1) { + memcpy(csc_matrix[vp_num].csc_mv, p->csc_mv, sizeof(p->csc_mv)); + memcpy(csc_matrix[vp_num].csc_pre_bv, p->csc_pre_bv, + sizeof(p->csc_pre_bv)); + memcpy(csc_matrix[vp_num].csc_post_bv, p->csc_post_bv, + sizeof(p->csc_post_bv)); + memcpy(csc_matrix[vp_num].csc_pre_lv, p->csc_pre_lv, + sizeof(p->csc_pre_lv)); + memcpy(csc_matrix[vp_num].csc_post_lv, p->csc_post_lv, + sizeof(p->csc_post_lv)); + mdp4_vg_csc_setup(vp_num); + } +} +static uint32 csc_rgb2yuv_matrix_tab[9] = { + 0x0083, 0x0102, 0x0032, + 0x1fb5, 0x1f6c, 0x00e1, + 0x00e1, 0x1f45, 0x1fdc +}; + +static uint32 csc_rgb2yuv_pre_bv_tab[3] = {0, 0, 0}; + +static uint32 csc_rgb2yuv_post_bv_tab[3] = {0x0010, 0x0080, 0x0080}; + +static uint32 csc_rgb2yuv_pre_lv_tab[6] = { + 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff +}; + +static uint32 csc_rgb2yuv_post_lv_tab[6] = { + 0x0010, 0x00eb, 0x0010, + 0x00f0, 0x0010, 0x00f0 +}; + +void mdp4_mixer_csc_mv_setup(uint32 mixer) +{ + uint32 *off; + int i; + + if (mixer == MDP4_MIXER1) + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2400); + else + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2400); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 9; i++) { + outpdw(off, csc_rgb2yuv_matrix_tab[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_mixer_csc_pre_bv_setup(uint32 mixer) +{ + uint32 *off; + int i; + + if (mixer == MDP4_MIXER1) + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2500); + else + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2500); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 3; i++) { + outpdw(off, csc_rgb2yuv_pre_bv_tab[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_mixer_csc_post_bv_setup(uint32 mixer) +{ + uint32 *off; + int i; + + if (mixer == MDP4_MIXER1) + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2580); + else + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2580); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 3; i++) { + outpdw(off, csc_rgb2yuv_post_bv_tab[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_mixer_csc_pre_lv_setup(uint32 mixer) +{ + uint32 *off; + int i; + + if (mixer == MDP4_MIXER1) + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2600); + else + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2600); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 6; i++) { + outpdw(off, csc_rgb2yuv_pre_lv_tab[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_mixer_csc_post_lv_setup(uint32 mixer) +{ + uint32 *off; + int i; + + if (mixer == MDP4_MIXER1) + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2680); + else + off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2680); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 6; i++) { + outpdw(off, csc_rgb2yuv_post_lv_tab[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_mixer_csc_setup(uint32 mixer) +{ + if (mixer >= MDP4_MIXER1) { + /* rgb2yuv */ + mdp4_mixer_csc_mv_setup(mixer); + mdp4_mixer_csc_pre_bv_setup(mixer); + mdp4_mixer_csc_post_bv_setup(mixer); + mdp4_mixer_csc_pre_lv_setup(mixer); + mdp4_mixer_csc_post_lv_setup(mixer); + } +} + +#define DMA_P_BASE 0x90000 +void mdp4_dmap_csc_mv_setup(void) +{ + uint32 *off; + int i; + + off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3400); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 9; i++) { + outpdw(off, csc_matrix[2].csc_mv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_dmap_csc_pre_bv_setup(void) +{ + uint32 *off; + int i; + + off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3500); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 3; i++) { + outpdw(off, csc_matrix[2].csc_pre_bv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_dmap_csc_post_bv_setup(void) +{ + uint32 *off; + int i; + + off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3580); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 3; i++) { + outpdw(off, csc_matrix[2].csc_post_bv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_dmap_csc_pre_lv_setup(void) +{ + uint32 *off; + int i; + + off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3600); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 6; i++) { + outpdw(off, csc_matrix[2].csc_pre_lv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_dmap_csc_post_lv_setup(void) +{ + uint32 *off; + int i; + + off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3680); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < 6; i++) { + outpdw(off, csc_matrix[2].csc_post_lv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_dmap_csc_setup(void) +{ + mdp4_dmap_csc_mv_setup(); + mdp4_dmap_csc_pre_bv_setup(); + mdp4_dmap_csc_post_bv_setup(); + mdp4_dmap_csc_pre_lv_setup(); + mdp4_dmap_csc_post_lv_setup(); +} + +char gc_lut[] = { + 0x0, 0x1, 0x2, 0x2, 0x3, 0x4, 0x5, 0x6, + 0x6, 0x7, 0x8, 0x9, 0xA, 0xA, 0xB, 0xC, + 0xD, 0xD, 0xE, 0xF, 0xF, 0x10, 0x10, 0x11, + 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, + 0x16, 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x19, + 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, 0x1C, + 0x1C, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1F, + 0x1F, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, + 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, + 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, + 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, + 0x28, 0x29, 0x29, 0x29, 0x29, 0x2A, 0x2A, 0x2A, + 0x2A, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C, + 0x2C, 0x2C, 0x2D, 0x2D, 0x2D, 0x2D, 0x2E, 0x2E, + 0x2E, 0x2E, 0x2E, 0x2F, 0x2F, 0x2F, 0x2F, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, + 0x3A, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3C, + 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3D, 0x3D, 0x3D, + 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, + 0x3E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, + 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x47, + 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, + 0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A, + 0x4A, 0x4A, 0x4A, 0x4A, 0x4A, 0x4B, 0x4B, 0x4B, + 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, + 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D, 0x4D, + 0x4D, 0x4D, 0x4D, 0x4D, 0x4E, 0x4E, 0x4E, 0x4E, + 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F, + 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, + 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, + 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, + 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, + 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5A, 0x5A, + 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, + 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, + 0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, + 0x5C, 0x5C, 0x5C, 0x5C, 0x5D, 0x5D, 0x5D, 0x5D, + 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E, + 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, + 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, + 0x5F, 0x5F, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, + 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, + 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, + 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, + 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B, + 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, + 0x6B, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, + 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D, + 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, + 0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, + 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, 0x6F, 0x6F, 0x6F, + 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, + 0x6F, 0x6F, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, + 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, + 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, + 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, + 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, + 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, + 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x7A, 0x7A, + 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, + 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, + 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, + 0x7B, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, + 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, + 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, + 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, + 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, + 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB1, 0xB1, + 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, + 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, + 0xB1, 0xB1, 0xB1, 0xB1, 0xB2, 0xB2, 0xB2, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB4, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, + 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, + 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB8, + 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, + 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, + 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, + 0xBD, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xBF, 0xBF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1, + 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, + 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, + 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, + 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, + 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, + 0xC4, 0xC4, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, + 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, + 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, + 0xC5, 0xC5, 0xC5, 0xC5, 0xC6, 0xC6, 0xC6, 0xC6, + 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, + 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, + 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7, 0xC7, + 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, + 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, + 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, + 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, + 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, + 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, + 0xC8, 0xC8, 0xC8, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, + 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, + 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, + 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xCA, 0xCA, + 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, + 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, + 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, + 0xCA, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, + 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, + 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, + 0xCB, 0xCB, 0xCB, 0xCB, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCD, + 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, + 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, + 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, + 0xCD, 0xCD, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, + 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, + 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, + 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCF, 0xCF, + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, + 0xCF, 0xCF, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, + 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, + 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, + 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD1, 0xD1, 0xD1, + 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, + 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, + 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, + 0xD1, 0xD1, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, + 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, + 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, + 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD3, 0xD3, + 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, + 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, + 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, + 0xD3, 0xD3, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, + 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, + 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, + 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD5, + 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, + 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, + 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, + 0xD5, 0xD5, 0xD5, 0xD5, 0xD6, 0xD6, 0xD6, 0xD6, + 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, + 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, + 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, + 0xD6, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, + 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, + 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, + 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD8, 0xD8, + 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, + 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, + 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, + 0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, + 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, + 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, + 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, + 0xD9, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, + 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, + 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, + 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, + 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, + 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, + 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, + 0xDC, 0xDC, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, + 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, + 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, + 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDF, + 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, + 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, + 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, + 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE1, 0xE1, 0xE1, 0xE1, + 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, + 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, + 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, + 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, + 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, + 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, + 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, + 0xE2, 0xE2, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, + 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, + 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, + 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, + 0xE3, 0xE3, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, + 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, + 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, + 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, + 0xE4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, + 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, + 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, + 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, + 0xE5, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, + 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, + 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, + 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, + 0xE6, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, + 0xE7, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, + 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, + 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, + 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, + 0xE8, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, + 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, + 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, + 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, + 0xE9, 0xE9, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, + 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, + 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, + 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, + 0xEA, 0xEA, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, + 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, + 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, + 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, + 0xEB, 0xEB, 0xEB, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, + 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, + 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, + 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, + 0xEC, 0xEC, 0xEC, 0xEC, 0xED, 0xED, 0xED, 0xED, + 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, + 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, + 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, + 0xED, 0xED, 0xED, 0xED, 0xED, 0xEE, 0xEE, 0xEE, + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEF, 0xEF, + 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, + 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, + 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, + 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, + 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, + 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, + 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, + 0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, 0xF2, 0xF2, + 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, + 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, + 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, + 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF3, 0xF3, + 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, + 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, + 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, + 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, + 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, + 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, + 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, + 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, + 0xF4, 0xF4, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, + 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, + 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, + 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, + 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, + 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, + 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, + 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, + 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, + 0xF7, 0xF7, 0xF7, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF9, 0xF9, + 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, + 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, + 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, + 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, + 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, + 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, + 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, + 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, + 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, + 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, + 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, + 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, + 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, + 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFD, + 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, + 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, + 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, + 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, + 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; + +void mdp4_mixer_gc_lut_setup(int mixer_num) +{ + unsigned char *base; + uint32 data; + char val; + int i, off; + + if (mixer_num) /* mixer number, /dev/fb0, /dev/fb1 */ + base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */ + else + base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + + base += 0x4000; /* GC_LUT offset */ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + off = 0; + for (i = 0; i < 4096; i++) { + val = gc_lut[i]; + data = (val << 16 | val << 8 | val); /* R, B, and G are same */ + outpdw(base + off, data); + off += 4; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +uint32 igc_video_lut[] = { /* non linear */ + 0x0, 0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x9, + 0xA, 0xB, 0xC, 0xE, 0xF, 0x10, 0x12, 0x14, + 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F, 0x21, 0x23, + 0x25, 0x28, 0x2A, 0x2D, 0x30, 0x32, 0x35, 0x38, + 0x3B, 0x3E, 0x42, 0x45, 0x48, 0x4C, 0x4F, 0x53, + 0x57, 0x5B, 0x5F, 0x63, 0x67, 0x6B, 0x70, 0x74, + 0x79, 0x7E, 0x83, 0x88, 0x8D, 0x92, 0x97, 0x9C, + 0xA2, 0xA8, 0xAD, 0xB3, 0xB9, 0xBF, 0xC5, 0xCC, + 0xD2, 0xD8, 0xDF, 0xE6, 0xED, 0xF4, 0xFB, 0x102, + 0x109, 0x111, 0x118, 0x120, 0x128, 0x130, 0x138, 0x140, + 0x149, 0x151, 0x15A, 0x162, 0x16B, 0x174, 0x17D, 0x186, + 0x190, 0x199, 0x1A3, 0x1AC, 0x1B6, 0x1C0, 0x1CA, 0x1D5, + 0x1DF, 0x1EA, 0x1F4, 0x1FF, 0x20A, 0x215, 0x220, 0x22B, + 0x237, 0x242, 0x24E, 0x25A, 0x266, 0x272, 0x27F, 0x28B, + 0x298, 0x2A4, 0x2B1, 0x2BE, 0x2CB, 0x2D8, 0x2E6, 0x2F3, + 0x301, 0x30F, 0x31D, 0x32B, 0x339, 0x348, 0x356, 0x365, + 0x374, 0x383, 0x392, 0x3A1, 0x3B1, 0x3C0, 0x3D0, 0x3E0, + 0x3F0, 0x400, 0x411, 0x421, 0x432, 0x443, 0x454, 0x465, + 0x476, 0x487, 0x499, 0x4AB, 0x4BD, 0x4CF, 0x4E1, 0x4F3, + 0x506, 0x518, 0x52B, 0x53E, 0x551, 0x565, 0x578, 0x58C, + 0x5A0, 0x5B3, 0x5C8, 0x5DC, 0x5F0, 0x605, 0x61A, 0x62E, + 0x643, 0x659, 0x66E, 0x684, 0x699, 0x6AF, 0x6C5, 0x6DB, + 0x6F2, 0x708, 0x71F, 0x736, 0x74D, 0x764, 0x77C, 0x793, + 0x7AB, 0x7C3, 0x7DB, 0x7F3, 0x80B, 0x824, 0x83D, 0x855, + 0x86F, 0x888, 0x8A1, 0x8BB, 0x8D4, 0x8EE, 0x908, 0x923, + 0x93D, 0x958, 0x973, 0x98E, 0x9A9, 0x9C4, 0x9DF, 0x9FB, + 0xA17, 0xA33, 0xA4F, 0xA6C, 0xA88, 0xAA5, 0xAC2, 0xADF, + 0xAFC, 0xB19, 0xB37, 0xB55, 0xB73, 0xB91, 0xBAF, 0xBCE, + 0xBEC, 0xC0B, 0xC2A, 0xC4A, 0xC69, 0xC89, 0xCA8, 0xCC8, + 0xCE8, 0xD09, 0xD29, 0xD4A, 0xD6B, 0xD8C, 0xDAD, 0xDCF, + 0xDF0, 0xE12, 0xE34, 0xE56, 0xE79, 0xE9B, 0xEBE, 0xEE1, + 0xF04, 0xF27, 0xF4B, 0xF6E, 0xF92, 0xFB6, 0xFDB, 0xFFF, +}; + +void mdp4_vg_igc_lut_setup(int vp_num) +{ + unsigned char *base; + int i, voff, off; + uint32 data, val; + + voff = MDP4_VIDEO_OFF * vp_num; + base = MDP_BASE + MDP4_VIDEO_BASE + voff + 0x5000; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + off = 0; + for (i = 0; i < 256; i++) { + val = igc_video_lut[i]; + data = (val << 16 | val); /* color 0 and 1 */ + outpdw(base + off, data); + outpdw(base + off + 0x800, val); /* color 2 */ + off += 4; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +uint32 igc_rgb_lut[] = { /* linear */ + 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x101, 0x111, 0x121, 0x131, 0x141, 0x151, 0x161, 0x171, + 0x181, 0x191, 0x1A2, 0x1B2, 0x1C2, 0x1D2, 0x1E2, 0x1F2, + 0x202, 0x212, 0x222, 0x232, 0x242, 0x252, 0x262, 0x272, + 0x282, 0x292, 0x2A2, 0x2B3, 0x2C3, 0x2D3, 0x2E3, 0x2F3, + 0x303, 0x313, 0x323, 0x333, 0x343, 0x353, 0x363, 0x373, + 0x383, 0x393, 0x3A3, 0x3B3, 0x3C4, 0x3D4, 0x3E4, 0x3F4, + 0x404, 0x414, 0x424, 0x434, 0x444, 0x454, 0x464, 0x474, + 0x484, 0x494, 0x4A4, 0x4B4, 0x4C4, 0x4D5, 0x4E5, 0x4F5, + 0x505, 0x515, 0x525, 0x535, 0x545, 0x555, 0x565, 0x575, + 0x585, 0x595, 0x5A5, 0x5B5, 0x5C5, 0x5D5, 0x5E6, 0x5F6, + 0x606, 0x616, 0x626, 0x636, 0x646, 0x656, 0x666, 0x676, + 0x686, 0x696, 0x6A6, 0x6B6, 0x6C6, 0x6D6, 0x6E6, 0x6F7, + 0x707, 0x717, 0x727, 0x737, 0x747, 0x757, 0x767, 0x777, + 0x787, 0x797, 0x7A7, 0x7B7, 0x7C7, 0x7D7, 0x7E7, 0x7F7, + 0x808, 0x818, 0x828, 0x838, 0x848, 0x858, 0x868, 0x878, + 0x888, 0x898, 0x8A8, 0x8B8, 0x8C8, 0x8D8, 0x8E8, 0x8F8, + 0x908, 0x919, 0x929, 0x939, 0x949, 0x959, 0x969, 0x979, + 0x989, 0x999, 0x9A9, 0x9B9, 0x9C9, 0x9D9, 0x9E9, 0x9F9, + 0xA09, 0xA19, 0xA2A, 0xA3A, 0xA4A, 0xA5A, 0xA6A, 0xA7A, + 0xA8A, 0xA9A, 0xAAA, 0xABA, 0xACA, 0xADA, 0xAEA, 0xAFA, + 0xB0A, 0xB1A, 0xB2A, 0xB3B, 0xB4B, 0xB5B, 0xB6B, 0xB7B, + 0xB8B, 0xB9B, 0xBAB, 0xBBB, 0xBCB, 0xBDB, 0xBEB, 0xBFB, + 0xC0B, 0xC1B, 0xC2B, 0xC3B, 0xC4C, 0xC5C, 0xC6C, 0xC7C, + 0xC8C, 0xC9C, 0xCAC, 0xCBC, 0xCCC, 0xCDC, 0xCEC, 0xCFC, + 0xD0C, 0xD1C, 0xD2C, 0xD3C, 0xD4C, 0xD5D, 0xD6D, 0xD7D, + 0xD8D, 0xD9D, 0xDAD, 0xDBD, 0xDCD, 0xDDD, 0xDED, 0xDFD, + 0xE0D, 0xE1D, 0xE2D, 0xE3D, 0xE4D, 0xE5D, 0xE6E, 0xE7E, + 0xE8E, 0xE9E, 0xEAE, 0xEBE, 0xECE, 0xEDE, 0xEEE, 0xEFE, + 0xF0E, 0xF1E, 0xF2E, 0xF3E, 0xF4E, 0xF5E, 0xF6E, 0xF7F, + 0xF8F, 0xF9F, 0xFAF, 0xFBF, 0xFCF, 0xFDF, 0xFEF, 0xFFF, +}; + +void mdp4_rgb_igc_lut_setup(int num) +{ + unsigned char *base; + int i, voff, off; + uint32 data, val; + + voff = MDP4_RGB_OFF * num; + base = MDP_BASE + MDP4_RGB_BASE + voff + 0x5000; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + off = 0; + for (i = 0; i < 256; i++) { + val = igc_rgb_lut[i]; + data = (val << 16 | val); /* color 0 and 1 */ + outpdw(base + off, data); + outpdw(base + off + 0x800, val); /* color 2 */ + off += 4; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +uint32 mdp4_rgb_igc_lut_cvt(uint32 ndx) +{ + return igc_rgb_lut[ndx & 0x0ff]; +} + +uint32_t mdp4_ss_table_value(int8_t value, int8_t index) +{ + uint32_t out = 0x0; + int8_t level = -1; + uint32_t mask = 0xffffffff; + + if (value < 0) { + if (value == -128) + value = 127; + else + value = -value; + out = 0x11111111; + } else { + out = 0x88888888; + mask = 0x0fffffff; + } + + if (value == 0) + level = 0; + else { + while (value > 0 && level < 7) { + level++; + value -= 16; + } + } + + if (level == 0) { + if (index == 0) + out = 0x0; + else + out = 0x20000000; + } else { + out += (0x11111111 * level); + if (index == 1) + out &= mask; + } + + return out; +} + +static uint32_t mdp4_csc_block2base(uint32_t block) +{ + uint32_t base = 0x0; + switch (block) { + case MDP_BLOCK_OVERLAY_1: + base = 0x1A000; + break; + case MDP_BLOCK_OVERLAY_2: + base = (mdp_rev >= MDP_REV_44) ? 0x8A000 : 0x0; + break; + case MDP_BLOCK_VG_1: + base = 0x24000; + break; + case MDP_BLOCK_VG_2: + base = 0x34000; + break; + case MDP_BLOCK_DMA_P: + base = 0x93000; + break; + case MDP_BLOCK_DMA_S: + base = (mdp_rev >= MDP_REV_42) ? 0xA3000 : 0x0; + default: + break; + } + return base; +} + +int mdp4_csc_enable(struct mdp_csc_cfg_data *config) +{ + uint32_t output, base, temp, mask; + + switch (config->block) { + case MDP_BLOCK_DMA_P: + base = 0x90070; + output = (config->csc_data.flags << 3) & (0x08); + temp = (config->csc_data.flags << 10) & (0x1800); + output |= temp; + mask = 0x08 | 0x1800; + break; + case MDP_BLOCK_DMA_S: + base = 0xA0028; + output = (config->csc_data.flags << 3) & (0x08); + temp = (config->csc_data.flags << 10) & (0x1800); + output |= temp; + mask = 0x08 | 0x1800; + break; + case MDP_BLOCK_VG_1: + base = 0x20058; + output = (config->csc_data.flags << 11) & (0x800); + temp = (config->csc_data.flags << 8) & (0x600); + output |= temp; + mask = 0x800 | 0x600; + break; + case MDP_BLOCK_VG_2: + base = 0x30058; + output = (config->csc_data.flags << 11) & (0x800); + temp = (config->csc_data.flags << 8) & (0x600); + output |= temp; + mask = 0x800 | 0x600; + break; + case MDP_BLOCK_OVERLAY_1: + base = 0x18200; + output = config->csc_data.flags; + mask = 0x07; + break; + case MDP_BLOCK_OVERLAY_2: + base = 0x88200; + output = config->csc_data.flags; + mask = 0x07; + break; + default: + pr_err("%s - CSC block does not exist on MDP_BLOCK = %d\n", + __func__, config->block); + return -EINVAL; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + temp = inpdw(MDP_BASE + base) & ~mask; + output |= temp; + outpdw(MDP_BASE + base, output); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return 0; +} + +#define CSC_MV_OFF 0x400 +#define CSC_BV_OFF 0x500 +#define CSC_LV_OFF 0x600 +#define CSC_POST_OFF 0x80 + +void mdp4_csc_write(struct mdp_csc_cfg *data, uint32_t base) +{ + int i; + uint32_t *off; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + off = (uint32_t *) ((uint32_t) base + CSC_MV_OFF); + for (i = 0; i < 9; i++) { + outpdw(off, data->csc_mv[i]); + off++; + } + + off = (uint32_t *) ((uint32_t) base + CSC_BV_OFF); + for (i = 0; i < 3; i++) { + outpdw(off, data->csc_pre_bv[i]); + outpdw((uint32_t *)((uint32_t)off + CSC_POST_OFF), + data->csc_post_bv[i]); + off++; + } + + off = (uint32_t *) ((uint32_t) base + CSC_LV_OFF); + for (i = 0; i < 6; i++) { + outpdw(off, data->csc_pre_lv[i]); + outpdw((uint32_t *)((uint32_t)off + CSC_POST_OFF), + data->csc_post_lv[i]); + off++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +int mdp4_csc_config(struct mdp_csc_cfg_data *config) +{ + int ret = 0; + uint32_t base; + + base = mdp4_csc_block2base(config->block); + if (!base) { + pr_warn("%s: Block type %d isn't supported by CSC.\n", + __func__, config->block); + return -EINVAL; + } + + mdp4_csc_write(&config->csc_data, (uint32_t) (MDP_BASE + base)); + + ret = mdp4_csc_enable(config); + + return ret; +} + +void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num) +{ + struct mdp_buf_type *buf; + + if (mix_num == MDP4_MIXER0) + buf = mfd->ov0_wb_buf; + else + buf = mfd->ov1_wb_buf; + + buf->ihdl = NULL; + buf->phys_addr = 0; +} + +u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num) +{ + struct mdp_buf_type *buf; + ion_phys_addr_t addr; + size_t buffer_size; + unsigned long len; + + if (mix_num == MDP4_MIXER0) + buf = mfd->ov0_wb_buf; + else + buf = mfd->ov1_wb_buf; + + if (buf->phys_addr || !IS_ERR_OR_NULL(buf->ihdl)) + return 0; + + if (!buf->size) { + pr_err("%s:%d In valid size\n", __func__, __LINE__); + return -EINVAL; + } + + buffer_size = roundup(mfd->panel_info.xres * \ + mfd->panel_info.yres * 3 * 2, SZ_4K); + + if (!IS_ERR_OR_NULL(mfd->iclient)) { + pr_info("%s:%d ion based allocation mfd->mem_hid 0x%x\n", + __func__, __LINE__, mfd->mem_hid); + buf->ihdl = ion_alloc(mfd->iclient, buffer_size, SZ_4K, + mfd->mem_hid); + if (!IS_ERR_OR_NULL(buf->ihdl)) { + if (ion_map_iommu(mfd->iclient, buf->ihdl, + DISPLAY_DOMAIN, GEN_POOL, SZ_4K, 0, &addr, + &len, 0, 0)) { + pr_err("ion_map_iommu() failed\n"); + return -ENOMEM; + } + } else { + pr_err("%s:%d: ion_alloc failed\n", __func__, + __LINE__); + return -ENOMEM; + } + } else { + addr = allocate_contiguous_memory_nomap(buffer_size, + mfd->mem_hid, 4); + } + if (addr) { + pr_info("allocating %d bytes at %x for mdp writeback\n", + buffer_size, (u32) addr); + buf->phys_addr = addr; + return 0; + } else { + pr_err("%s cannot allocate memory for mdp writeback!\n", + __func__); + return -ENOMEM; + } +} + +void mdp4_free_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num) +{ + struct mdp_buf_type *buf; + + if (mix_num == MDP4_MIXER0) + buf = mfd->ov0_wb_buf; + else + buf = mfd->ov1_wb_buf; + + if (!IS_ERR_OR_NULL(mfd->iclient)) { + if (!IS_ERR_OR_NULL(buf->ihdl)) { + ion_unmap_iommu(mfd->iclient, buf->ihdl, + DISPLAY_DOMAIN, GEN_POOL); + ion_free(mfd->iclient, buf->ihdl); + pr_debug("%s:%d free writeback imem\n", __func__, + __LINE__); + buf->ihdl = NULL; + } + } else { + if (buf->phys_addr) { + free_contiguous_memory_by_paddr(buf->phys_addr); + pr_debug("%s:%d free writeback pmem\n", __func__, + __LINE__); + } + } + buf->phys_addr = 0; +} + +static int mdp4_update_pcc_regs(uint32_t offset, + struct mdp_pcc_cfg_data *cfg_ptr) +{ + int ret = -1; + + if (offset && cfg_ptr) { + + outpdw(offset, cfg_ptr->r.c); + outpdw(offset + 0x30, cfg_ptr->g.c); + outpdw(offset + 0x60, cfg_ptr->b.c); + offset += 4; + + outpdw(offset, cfg_ptr->r.r); + outpdw(offset + 0x30, cfg_ptr->g.r); + outpdw(offset + 0x60, cfg_ptr->b.r); + offset += 4; + + outpdw(offset, cfg_ptr->r.g); + outpdw(offset + 0x30, cfg_ptr->g.g); + outpdw(offset + 0x60, cfg_ptr->b.g); + offset += 4; + + outpdw(offset, cfg_ptr->r.b); + outpdw(offset + 0x30, cfg_ptr->g.b); + outpdw(offset + 0x60, cfg_ptr->b.b); + offset += 4; + + outpdw(offset, cfg_ptr->r.rr); + outpdw(offset + 0x30, cfg_ptr->g.rr); + outpdw(offset + 0x60, cfg_ptr->b.rr); + offset += 4; + + outpdw(offset, cfg_ptr->r.gg); + outpdw(offset + 0x30, cfg_ptr->g.gg); + outpdw(offset + 0x60, cfg_ptr->b.gg); + offset += 4; + + outpdw(offset, cfg_ptr->r.bb); + outpdw(offset + 0x30, cfg_ptr->g.bb); + outpdw(offset + 0x60, cfg_ptr->b.bb); + offset += 4; + + outpdw(offset, cfg_ptr->r.rg); + outpdw(offset + 0x30, cfg_ptr->g.rg); + outpdw(offset + 0x60, cfg_ptr->b.rg); + offset += 4; + + outpdw(offset, cfg_ptr->r.gb); + outpdw(offset + 0x30, cfg_ptr->g.gb); + outpdw(offset + 0x60, cfg_ptr->b.gb); + offset += 4; + + outpdw(offset, cfg_ptr->r.rb); + outpdw(offset + 0x30, cfg_ptr->g.rb); + outpdw(offset + 0x60, cfg_ptr->b.rb); + offset += 4; + + outpdw(offset, cfg_ptr->r.rgb_0); + outpdw(offset + 0x30, cfg_ptr->g.rgb_0); + outpdw(offset + 0x60, cfg_ptr->b.rgb_0); + offset += 4; + + outpdw(offset, cfg_ptr->r.rgb_1); + outpdw(offset + 0x30, cfg_ptr->g.rgb_1); + outpdw(offset + 0x60, cfg_ptr->b.rgb_1); + + ret = 0; + } + + return ret; +} + +static int mdp4_read_pcc_regs(uint32_t offset, + struct mdp_pcc_cfg_data *cfg_ptr) +{ + int ret = -1; + + if (offset && cfg_ptr) { + cfg_ptr->r.c = inpdw(offset); + cfg_ptr->g.c = inpdw(offset + 0x30); + cfg_ptr->b.c = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.r = inpdw(offset); + cfg_ptr->g.r = inpdw(offset + 0x30); + cfg_ptr->b.r = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.g = inpdw(offset); + cfg_ptr->g.g = inpdw(offset + 0x30); + cfg_ptr->b.g = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.b = inpdw(offset); + cfg_ptr->g.b = inpdw(offset + 0x30); + cfg_ptr->b.b = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.rr = inpdw(offset); + cfg_ptr->g.rr = inpdw(offset + 0x30); + cfg_ptr->b.rr = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.gg = inpdw(offset); + cfg_ptr->g.gg = inpdw(offset + 0x30); + cfg_ptr->b.gg = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.bb = inpdw(offset); + cfg_ptr->g.bb = inpdw(offset + 0x30); + cfg_ptr->b.bb = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.rg = inpdw(offset); + cfg_ptr->g.rg = inpdw(offset + 0x30); + cfg_ptr->b.rg = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.gb = inpdw(offset); + cfg_ptr->g.gb = inpdw(offset + 0x30); + cfg_ptr->b.gb = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.rb = inpdw(offset); + cfg_ptr->g.rb = inpdw(offset + 0x30); + cfg_ptr->b.rb = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.rgb_0 = inpdw(offset); + cfg_ptr->g.rgb_0 = inpdw(offset + 0x30); + cfg_ptr->b.rgb_0 = inpdw(offset + 0x60); + offset += 4; + + cfg_ptr->r.rgb_1 = inpdw(offset); + cfg_ptr->g.rgb_1 = inpdw(offset + 0x30); + cfg_ptr->b.rgb_1 = inpdw(offset + 0x60); + + ret = 0; + } + + return ret; +} + + +#define MDP_PCC_OFFSET 0xA000 +#define MDP_DMA_GC_OFFSET 0x8800 +#define MDP_LM_GC_OFFSET 0x4800 + +#define MDP_DMA_P_OP_MODE_OFFSET 0x70 +#define MDP_DMA_S_OP_MODE_OFFSET 0x28 +#define MDP_LM_OP_MODE_OFFSET 0x14 + +#define DMA_PCC_R2_OFFSET 0x100 + +#define MDP_GC_COLOR_OFFSET 0x100 +#define MDP_GC_PARMS_OFFSET 0x80 + +#define MDP_AR_GC_MAX_STAGES 16 + +static uint32_t mdp_pp_block2pcc(uint32_t block) +{ + uint32_t valid = 0; + + switch (block) { + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + valid = (mdp_rev >= MDP_REV_42) ? 1 : 0; + break; + + default: + break; + } + + return valid; +} + +int mdp4_pcc_cfg(struct mdp_pcc_cfg_data *cfg_ptr) +{ + int ret = -1; + uint32_t pcc_offset = 0, mdp_cfg_offset = 0; + uint32_t mdp_dma_op_mode = 0; + uint32_t blockbase; + + if (!mdp_pp_block2pcc(cfg_ptr->block)) + return ret; + + blockbase = mdp_block2base(cfg_ptr->block); + if (!blockbase) + return ret; + + blockbase += (uint32_t) MDP_BASE; + + switch (cfg_ptr->block) { + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + pcc_offset = blockbase + MDP_PCC_OFFSET; + mdp_cfg_offset = blockbase; + mdp_dma_op_mode = blockbase + + (MDP_BLOCK_DMA_P == cfg_ptr->block ? + MDP_DMA_P_OP_MODE_OFFSET + : MDP_DMA_S_OP_MODE_OFFSET); + break; + + default: + break; + } + + if (0x8 & cfg_ptr->ops) + pcc_offset += DMA_PCC_R2_OFFSET; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + switch ((0x6 & cfg_ptr->ops)>>1) { + case 0x1: + ret = mdp4_read_pcc_regs(pcc_offset, cfg_ptr); + break; + + case 0x2: + ret = mdp4_update_pcc_regs(pcc_offset, cfg_ptr); + break; + + default: + break; + } + + if (0x8 & cfg_ptr->ops) + outpdw(mdp_dma_op_mode, + ((inpdw(mdp_dma_op_mode) & ~(0x1<<10)) | + ((0x8 & cfg_ptr->ops)<<10))); + + outpdw(mdp_cfg_offset, + ((inpdw(mdp_cfg_offset) & ~(0x1<<29)) | + ((cfg_ptr->ops & 0x1)<<29))); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return ret; +} + +static uint32_t mdp_pp_block2argc(uint32_t block) +{ + uint32_t valid = 0; + + switch (block) { + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + case MDP_BLOCK_OVERLAY_0: + case MDP_BLOCK_OVERLAY_1: + valid = (mdp_rev >= MDP_REV_42) ? 1 : 0; + break; + + case MDP_BLOCK_OVERLAY_2: + valid = (mdp_rev >= MDP_REV_44) ? 1 : 0; + break; + + default: + break; + } + + return valid; +} + +static int update_ar_gc_lut(uint32_t *offset, struct mdp_pgc_lut_data *lut_data) +{ + int count = 0; + + uint32_t *c0_offset = offset; + uint32_t *c0_params_offset = (uint32_t *)((uint32_t)c0_offset + + MDP_GC_PARMS_OFFSET); + + uint32_t *c1_offset = (uint32_t *)((uint32_t)offset + + MDP_GC_COLOR_OFFSET); + + uint32_t *c1_params_offset = (uint32_t *)((uint32_t)c1_offset + + MDP_GC_PARMS_OFFSET); + + uint32_t *c2_offset = (uint32_t *)((uint32_t)offset + + 2*MDP_GC_COLOR_OFFSET); + + uint32_t *c2_params_offset = (uint32_t *)((uint32_t)c2_offset + +MDP_GC_PARMS_OFFSET); + + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (count = 0; count < MDP_AR_GC_MAX_STAGES; count++) { + if (count < lut_data->num_r_stages) { + outpdw(c0_offset+count, + ((0xfff & lut_data->r_data[count].x_start) + | 0x10000)); + + outpdw(c0_params_offset+count, + ((0x7fff & lut_data->r_data[count].slope) + | ((0xffff + & lut_data->r_data[count].offset) + << 16))); + } else + outpdw(c0_offset+count, 0); + + if (count < lut_data->num_b_stages) { + outpdw(c1_offset+count, + ((0xfff & lut_data->b_data[count].x_start) + | 0x10000)); + + outpdw(c1_params_offset+count, + ((0x7fff & lut_data->b_data[count].slope) + | ((0xffff + & lut_data->b_data[count].offset) + << 16))); + } else + outpdw(c1_offset+count, 0); + + if (count < lut_data->num_g_stages) { + outpdw(c2_offset+count, + ((0xfff & lut_data->g_data[count].x_start) + | 0x10000)); + + outpdw(c2_params_offset+count, + ((0x7fff & lut_data->g_data[count].slope) + | ((0xffff + & lut_data->g_data[count].offset) + << 16))); + } else + outpdw(c2_offset+count, 0); + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return 0; +} + +static int mdp4_argc_process_write_req(uint32_t *offset, + struct mdp_pgc_lut_data *pgc_ptr) +{ + int ret = -1; + struct mdp_ar_gc_lut_data r[MDP_AR_GC_MAX_STAGES]; + struct mdp_ar_gc_lut_data g[MDP_AR_GC_MAX_STAGES]; + struct mdp_ar_gc_lut_data b[MDP_AR_GC_MAX_STAGES]; + + ret = copy_from_user(&r[0], pgc_ptr->r_data, + pgc_ptr->num_r_stages * sizeof(struct mdp_ar_gc_lut_data)); + + if (!ret) { + ret = copy_from_user(&g[0], + pgc_ptr->g_data, + pgc_ptr->num_g_stages + * sizeof(struct mdp_ar_gc_lut_data)); + if (!ret) + ret = copy_from_user(&b[0], + pgc_ptr->b_data, + pgc_ptr->num_b_stages + * sizeof(struct mdp_ar_gc_lut_data)); + } + + if (ret) + return ret; + + pgc_ptr->r_data = &r[0]; + pgc_ptr->g_data = &g[0]; + pgc_ptr->b_data = &b[0]; + + ret = update_ar_gc_lut(offset, pgc_ptr); + return ret; +} + +int mdp4_argc_cfg(struct mdp_pgc_lut_data *pgc_ptr) +{ + int ret = -1; + uint32_t *offset = 0, *pgc_enable_offset = 0, lshift_bits = 0; + uint32_t blockbase; + + if (!mdp_pp_block2argc(pgc_ptr->block)) + return ret; + + blockbase = mdp_block2base(pgc_ptr->block); + if (!blockbase) + return ret; + + blockbase += (uint32_t) MDP_BASE; + ret = 0; + + switch (pgc_ptr->block) { + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + offset = (uint32_t *)(blockbase + MDP_DMA_GC_OFFSET); + pgc_enable_offset = (uint32_t *) blockbase; + lshift_bits = 28; + break; + + case MDP_BLOCK_OVERLAY_0: + case MDP_BLOCK_OVERLAY_1: + case MDP_BLOCK_OVERLAY_2: + offset = (uint32_t *)(blockbase + MDP_LM_GC_OFFSET); + pgc_enable_offset = (uint32_t *)(blockbase + + MDP_LM_OP_MODE_OFFSET); + lshift_bits = 2; + break; + + default: + ret = -1; + break; + } + + if (!ret) { + + switch ((0x6 & pgc_ptr->flags)>>1) { + case 0x1: + ret = -ENOTTY; + break; + + case 0x2: + ret = mdp4_argc_process_write_req(offset, pgc_ptr); + break; + + default: + break; + } + + if (!ret) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + outpdw(pgc_enable_offset, (inpdw(pgc_enable_offset) & + ~(0x1<flags) << lshift_bits)); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, + FALSE); + } + } + + return ret; +} + +static uint32_t mdp4_pp_block2igc(uint32_t block) +{ + uint32_t valid = 0; + switch (block) { + case MDP_BLOCK_VG_1: + valid = 0x1; + break; + case MDP_BLOCK_VG_2: + valid = 0x1; + break; + case MDP_BLOCK_RGB_1: + valid = 0x1; + break; + case MDP_BLOCK_RGB_2: + valid = 0x1; + break; + case MDP_BLOCK_DMA_P: + valid = (mdp_rev >= MDP_REV_40) ? 1 : 0; + break; + case MDP_BLOCK_DMA_S: + valid = (mdp_rev >= MDP_REV_40) ? 1 : 0; + break; + default: + break; + } + return valid; +} + +static int mdp4_igc_lut_write(struct mdp_igc_lut_data *cfg, uint32_t en_off, + uint32_t lut_off) +{ + int i; + uint32_t base, *off_low, *off_high; + uint32_t low[cfg->len]; + uint32_t high[cfg->len]; + + base = mdp_block2base(cfg->block); + + if (cfg->len != 256) + return -EINVAL; + + off_low = (uint32_t *)(MDP_BASE + base + lut_off); + off_high = (uint32_t *)(MDP_BASE + base + lut_off + 0x800); + if (copy_from_user(&low, cfg->c0_c1_data, cfg->len * sizeof(uint32_t))) + return -EFAULT; + if (copy_from_user(&high, cfg->c2_data, cfg->len * sizeof(uint32_t))) + return -EFAULT; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < cfg->len; i++) { + MDP_OUTP(off_low++, low[i]); + /*low address write should occur before high address write*/ + wmb(); + MDP_OUTP(off_high++, high[i]); + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return 0; +} + +static int mdp4_igc_lut_ctrl(struct mdp_igc_lut_data *cfg) +{ + uint32_t mask, out; + uint32_t base = mdp_block2base(cfg->block); + int8_t shift = 0; + + switch (cfg->block) { + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + base = base; + shift = 30; + break; + case MDP_BLOCK_VG_1: + case MDP_BLOCK_VG_2: + case MDP_BLOCK_RGB_1: + case MDP_BLOCK_RGB_2: + base += 0x58; + shift = 16; + break; + default: + return -EINVAL; + + } + out = 1<ops & 0x1)<block) { + case MDP_BLOCK_DMA_P: + case MDP_BLOCK_DMA_S: + ret = mdp4_igc_lut_write(cfg, 0x00, 0x9000); + break; + case MDP_BLOCK_VG_1: + case MDP_BLOCK_VG_2: + case MDP_BLOCK_RGB_1: + case MDP_BLOCK_RGB_2: + ret = mdp4_igc_lut_write(cfg, 0x58, 0x5000); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +int mdp4_igc_lut_config(struct mdp_igc_lut_data *cfg) +{ + int ret = 0; + + if (!mdp4_pp_block2igc(cfg->block)) { + ret = -ENOTTY; + goto error; + } + + switch ((cfg->ops & 0x6) >> 1) { + case 0x1: + pr_info("%s: IGC LUT read not supported\n", __func__); + break; + case 0x2: + ret = mdp4_igc_lut_write_cfg(cfg); + if (ret) + goto error; + break; + default: + break; + } + + ret = mdp4_igc_lut_ctrl(cfg); + +error: + return ret; +} + +#define QSEED_TABLE_1_COUNT 2 +#define QSEED_TABLE_2_COUNT 1024 + +static uint32_t mdp4_pp_block2qseed(uint32_t block) +{ + uint32_t valid = 0; + switch (block) { + case MDP_BLOCK_VG_1: + case MDP_BLOCK_VG_2: + valid = 0x1; + break; + default: + break; + } + return valid; +} + +static int mdp4_qseed_write_cfg(struct mdp_qseed_cfg_data *cfg) +{ + int i, ret = 0; + uint32_t base = (uint32_t) (MDP_BASE + mdp_block2base(cfg->block)); + uint32_t *values; + + if ((cfg->table_num != 1) && (cfg->table_num != 2)) { + ret = -ENOTTY; + goto error; + } + + if (((cfg->table_num == 1) && (cfg->len != QSEED_TABLE_1_COUNT)) || + ((cfg->table_num == 2) && (cfg->len != QSEED_TABLE_2_COUNT))) { + ret = -EINVAL; + goto error; + } + + values = kmalloc(cfg->len * sizeof(uint32_t), GFP_KERNEL); + if (!values) { + ret = -ENOMEM; + goto error; + } + + ret = copy_from_user(values, cfg->data, sizeof(uint32_t) * cfg->len); + + base += (cfg->table_num == 1) ? MDP4_QSEED_TABLE1_OFF : + MDP4_QSEED_TABLE2_OFF; + for (i = 0; i < cfg->len; i++) { + MDP_OUTP(base , values[i]); + base += sizeof(uint32_t); + } + + kfree(values); +error: + return ret; +} + +int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg) +{ + int ret = 0; + + if (!mdp4_pp_block2qseed(cfg->block)) { + ret = -ENOTTY; + goto error; + } + + if (cfg->table_num != 1) { + ret = -ENOTTY; + pr_info("%s: Only QSEED table1 supported.\n", __func__); + goto error; + } + + switch ((cfg->ops & 0x6) >> 1) { + case 0x1: + pr_info("%s: QSEED read not supported\n", __func__); + ret = -ENOTTY; + break; + case 0x2: + ret = mdp4_qseed_write_cfg(cfg); + if (ret) + goto error; + break; + default: + break; + } + +error: + return ret; +} diff --git a/drivers/video/msm/mdp4_wfd_writeback.c b/drivers/video/msm/mdp4_wfd_writeback.c new file mode 100644 index 0000000000000000000000000000000000000000..d96fc7d6f42d6856b4122c9c8df202a1519374c2 --- /dev/null +++ b/drivers/video/msm/mdp4_wfd_writeback.c @@ -0,0 +1,102 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mdp4_wfd_writeback_util.h" +#include "msm_fb.h" + +static int writeback_on(struct platform_device *pdev) +{ + return 0; +} +static int writeback_off(struct platform_device *pdev) +{ + return 0; +} +static int writeback_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc = 0; + + WRITEBACK_MSG_ERR("Inside writeback_probe\n"); + mfd = platform_get_drvdata(pdev); + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + /* + * link to the latest pdev + */ + mfd->pdev = mdp_dev; + mfd->dest = DISPLAY_LCD; + + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + pr_err("writeback_probe: " + "platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data; + pdata->on = writeback_on; + pdata->off = writeback_off; + pdata->next = pdev; + + /* + * get/set panel specific fb info + */ + mfd->panel_info = pdata->panel_info; + + mfd->fb_imgType = MDP_RGB_565; + + platform_set_drvdata(mdp_dev, mfd); + + rc = platform_device_add(mdp_dev); + if (rc) { + WRITEBACK_MSG_ERR("failed to add device"); + platform_device_put(mdp_dev); + return rc; + } + return rc; +} + +static struct platform_driver writeback_driver = { + .probe = writeback_probe, + .driver = { + .name = "writeback", + }, +}; + +static int __init writeback_driver_init(void) +{ + int rc = 0; + WRITEBACK_MSG_ERR("Inside writeback_driver_init\n"); + rc = platform_driver_register(&writeback_driver); + return rc; +} + +module_init(writeback_driver_init); diff --git a/drivers/video/msm/mdp4_wfd_writeback_panel.c b/drivers/video/msm/mdp4_wfd_writeback_panel.c new file mode 100644 index 0000000000000000000000000000000000000000..454556fbba6b72d802d06c1c07bfa03642493a08 --- /dev/null +++ b/drivers/video/msm/mdp4_wfd_writeback_panel.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mdp4_wfd_writeback_util.h" +#include "msm_fb.h" + +static int writeback_panel_probe(struct platform_device *pdev) +{ + int rc = 0; + if (pdev->id == 0) + return 0; + + if (!msm_fb_add_device(pdev)) { + WRITEBACK_MSG_ERR("Failed to add fd device\n"); + rc = -ENOMEM; + } + return rc; +} +static struct msm_fb_panel_data writeback_msm_panel_data = { + .panel_info = { + .type = WRITEBACK_PANEL, + .xres = 1280, + .yres = 720, + .pdest = DISPLAY_3, + .wait_cycle = 0, + .bpp = 24, + .fb_num = 1, + .clk_rate = 74250000, + }, +}; + +static struct platform_device writeback_panel_device = { + .name = "writeback_panel", + .id = 1, + .dev.platform_data = &writeback_msm_panel_data, +}; +static struct platform_driver writeback_panel_driver = { + .probe = writeback_panel_probe, + .driver = { + .name = "writeback_panel" + } +}; + +static int __init writeback_panel_init(void) +{ + int rc = 0; + rc = platform_driver_register(&writeback_panel_driver); + if (rc) { + WRITEBACK_MSG_ERR("Failed to register platform driver\n"); + goto fail_driver_registration; + } + rc = platform_device_register(&writeback_panel_device); + if (rc) { + WRITEBACK_MSG_ERR("Failed to register " + "writeback_panel_device\n"); + goto fail_device_registration; + } + return rc; +fail_device_registration: + platform_driver_unregister(&writeback_panel_driver); +fail_driver_registration: + return rc; +} + +module_init(writeback_panel_init); diff --git a/drivers/video/msm/mdp4_wfd_writeback_util.h b/drivers/video/msm/mdp4_wfd_writeback_util.h new file mode 100644 index 0000000000000000000000000000000000000000..582d198b2d5613b4244f835d004fa98b27e9248b --- /dev/null +++ b/drivers/video/msm/mdp4_wfd_writeback_util.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _WRITEBACK_UTIL_H_ +#define _WRITEBACK_UTIL_H_ + +#define DEBUG + +#ifdef DEBUG + #define WRITEBACK_MSG_INFO(fmt...) pr_info(fmt) + #define WRITEBACK_MSG_WARN(fmt...) pr_warning(fmt) +#else + #define WRITEBACK_MSG_INFO(fmt...) + #define WRITEBACK_MSG_WARN(fmt...) +#endif + #define WRITEBACK_MSG_ERR(fmt...) pr_err(fmt) + #define WRITEBACK_MSG_CRIT(fmt...) pr_crit(fmt) +#endif diff --git a/drivers/video/msm/mdp_csc_table.h b/drivers/video/msm/mdp_csc_table.h new file mode 100644 index 0000000000000000000000000000000000000000..a0f72c0ebd4f7cfbc8e7a9f132d8bd7e7a793cee --- /dev/null +++ b/drivers/video/msm/mdp_csc_table.h @@ -0,0 +1,641 @@ +/* drivers/video/msm/mdp_csc_table.h + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +static struct { + uint32_t reg; + uint32_t val; +} csc_matrix_config_table[] = { + /* RGB -> YUV primary forward matrix (set1). */ + { MDP_CSC_PFMVn(0), 0x83 }, + { MDP_CSC_PFMVn(1), 0x102 }, + { MDP_CSC_PFMVn(2), 0x32 }, + { MDP_CSC_PFMVn(3), 0xffffffb5 }, + { MDP_CSC_PFMVn(4), 0xffffff6c }, + { MDP_CSC_PFMVn(5), 0xe1 }, + { MDP_CSC_PFMVn(6), 0xe1 }, + { MDP_CSC_PFMVn(7), 0xffffff45 }, + { MDP_CSC_PFMVn(8), 0xffffffdc }, + + /* YUV -> RGB primary reverse matrix (set2) */ + { MDP_CSC_PRMVn(0), 0x254 }, + { MDP_CSC_PRMVn(1), 0x0 }, + { MDP_CSC_PRMVn(2), 0x331 }, + { MDP_CSC_PRMVn(3), 0x254 }, + { MDP_CSC_PRMVn(4), 0xffffff38 }, + { MDP_CSC_PRMVn(5), 0xfffffe61 }, + { MDP_CSC_PRMVn(6), 0x254 }, + { MDP_CSC_PRMVn(7), 0x409 }, + { MDP_CSC_PRMVn(8), 0x0 }, + +#ifndef CONFIG_MSM_MDP31 + /* For MDP 2.2/3.0 */ + + /* primary limit vector */ + { MDP_CSC_PLVn(0), 0x10 }, + { MDP_CSC_PLVn(1), 0xeb }, + { MDP_CSC_PLVn(2), 0x10 }, + { MDP_CSC_PLVn(3), 0xf0 }, + + /* primary bias vector */ + { MDP_CSC_PBVn(0), 0x10 }, + { MDP_CSC_PBVn(1), 0x80 }, + { MDP_CSC_PBVn(2), 0x80 }, + +#else /* CONFIG_MSM_MDP31 */ + + /* limit vectors configuration */ + /* rgb -> yuv (set1) pre-limit vector */ + { MDP_PPP_CSC_PRE_LV1n(0), 0x10 }, + { MDP_PPP_CSC_PRE_LV1n(1), 0xeb }, + { MDP_PPP_CSC_PRE_LV1n(2), 0x10 }, + { MDP_PPP_CSC_PRE_LV1n(3), 0xf0 }, + { MDP_PPP_CSC_PRE_LV1n(4), 0x10 }, + { MDP_PPP_CSC_PRE_LV1n(5), 0xf0 }, + + /* rgb -> yuv (set1) post-limit vector */ + { MDP_PPP_CSC_POST_LV1n(0), 0x0 }, + { MDP_PPP_CSC_POST_LV1n(1), 0xff }, + { MDP_PPP_CSC_POST_LV1n(2), 0x0 }, + { MDP_PPP_CSC_POST_LV1n(3), 0xff }, + { MDP_PPP_CSC_POST_LV1n(4), 0x0 }, + { MDP_PPP_CSC_POST_LV1n(5), 0xff }, + + /* yuv -> rgb (set2) pre-limit vector */ + { MDP_PPP_CSC_PRE_LV2n(0), 0x0 }, + { MDP_PPP_CSC_PRE_LV2n(1), 0xff }, + { MDP_PPP_CSC_PRE_LV2n(2), 0x0 }, + { MDP_PPP_CSC_PRE_LV2n(3), 0xff }, + { MDP_PPP_CSC_PRE_LV2n(4), 0x0 }, + { MDP_PPP_CSC_PRE_LV2n(5), 0xff }, + + /* yuv -> rgb (set2) post-limit vector */ + { MDP_PPP_CSC_POST_LV2n(0), 0x10 }, + { MDP_PPP_CSC_POST_LV2n(1), 0xeb }, + { MDP_PPP_CSC_POST_LV2n(2), 0x10 }, + { MDP_PPP_CSC_POST_LV2n(3), 0xf0 }, + { MDP_PPP_CSC_POST_LV2n(4), 0x10 }, + { MDP_PPP_CSC_POST_LV2n(5), 0xf0 }, + + /* bias vectors configuration */ + + /* XXX: why is set2 used for rgb->yuv, but set1 */ + /* used for yuv -> rgb??!? Seems to be the reverse of the + * other vectors. */ + + /* RGB -> YUV pre-bias vector... */ + { MDP_PPP_CSC_PRE_BV2n(0), 0 }, + { MDP_PPP_CSC_PRE_BV2n(1), 0 }, + { MDP_PPP_CSC_PRE_BV2n(2), 0 }, + + /* RGB -> YUV post-bias vector */ + { MDP_PPP_CSC_POST_BV2n(0), 0x10 }, + { MDP_PPP_CSC_POST_BV2n(1), 0x80 }, + { MDP_PPP_CSC_POST_BV2n(2), 0x80 }, + + /* YUV -> RGB pre-bias vector... */ + { MDP_PPP_CSC_PRE_BV1n(0), 0x1f0 }, + { MDP_PPP_CSC_PRE_BV1n(1), 0x180 }, + { MDP_PPP_CSC_PRE_BV1n(2), 0x180 }, + + /* YUV -> RGB post-bias vector */ + { MDP_PPP_CSC_POST_BV1n(0), 0 }, + { MDP_PPP_CSC_POST_BV1n(1), 0 }, + { MDP_PPP_CSC_POST_BV1n(2), 0 }, + + /* luma filter coefficients */ + { MDP_PPP_DEINT_COEFFn(0), 0x3e0 }, + { MDP_PPP_DEINT_COEFFn(1), 0x360 }, + { MDP_PPP_DEINT_COEFFn(2), 0x120 }, + { MDP_PPP_DEINT_COEFFn(3), 0x140 }, +#endif +}; + +static struct { + uint32_t reg; + uint32_t val; +} csc_color_lut[] = { + { 0x40800, 0x0 }, + { 0x40804, 0x151515 }, + { 0x40808, 0x1d1d1d }, + { 0x4080c, 0x232323 }, + { 0x40810, 0x272727 }, + { 0x40814, 0x2b2b2b }, + { 0x40818, 0x2f2f2f }, + { 0x4081c, 0x333333 }, + { 0x40820, 0x363636 }, + { 0x40824, 0x393939 }, + { 0x40828, 0x3b3b3b }, + { 0x4082c, 0x3e3e3e }, + { 0x40830, 0x404040 }, + { 0x40834, 0x434343 }, + { 0x40838, 0x454545 }, + { 0x4083c, 0x474747 }, + { 0x40840, 0x494949 }, + { 0x40844, 0x4b4b4b }, + { 0x40848, 0x4d4d4d }, + { 0x4084c, 0x4f4f4f }, + { 0x40850, 0x515151 }, + { 0x40854, 0x535353 }, + { 0x40858, 0x555555 }, + { 0x4085c, 0x565656 }, + { 0x40860, 0x585858 }, + { 0x40864, 0x5a5a5a }, + { 0x40868, 0x5b5b5b }, + { 0x4086c, 0x5d5d5d }, + { 0x40870, 0x5e5e5e }, + { 0x40874, 0x606060 }, + { 0x40878, 0x616161 }, + { 0x4087c, 0x636363 }, + { 0x40880, 0x646464 }, + { 0x40884, 0x666666 }, + { 0x40888, 0x676767 }, + { 0x4088c, 0x686868 }, + { 0x40890, 0x6a6a6a }, + { 0x40894, 0x6b6b6b }, + { 0x40898, 0x6c6c6c }, + { 0x4089c, 0x6e6e6e }, + { 0x408a0, 0x6f6f6f }, + { 0x408a4, 0x707070 }, + { 0x408a8, 0x717171 }, + { 0x408ac, 0x727272 }, + { 0x408b0, 0x747474 }, + { 0x408b4, 0x757575 }, + { 0x408b8, 0x767676 }, + { 0x408bc, 0x777777 }, + { 0x408c0, 0x787878 }, + { 0x408c4, 0x797979 }, + { 0x408c8, 0x7a7a7a }, + { 0x408cc, 0x7c7c7c }, + { 0x408d0, 0x7d7d7d }, + { 0x408d4, 0x7e7e7e }, + { 0x408d8, 0x7f7f7f }, + { 0x408dc, 0x808080 }, + { 0x408e0, 0x818181 }, + { 0x408e4, 0x828282 }, + { 0x408e8, 0x838383 }, + { 0x408ec, 0x848484 }, + { 0x408f0, 0x858585 }, + { 0x408f4, 0x868686 }, + { 0x408f8, 0x878787 }, + { 0x408fc, 0x888888 }, + { 0x40900, 0x898989 }, + { 0x40904, 0x8a8a8a }, + { 0x40908, 0x8b8b8b }, + { 0x4090c, 0x8c8c8c }, + { 0x40910, 0x8d8d8d }, + { 0x40914, 0x8e8e8e }, + { 0x40918, 0x8f8f8f }, + { 0x4091c, 0x8f8f8f }, + { 0x40920, 0x909090 }, + { 0x40924, 0x919191 }, + { 0x40928, 0x929292 }, + { 0x4092c, 0x939393 }, + { 0x40930, 0x949494 }, + { 0x40934, 0x959595 }, + { 0x40938, 0x969696 }, + { 0x4093c, 0x969696 }, + { 0x40940, 0x979797 }, + { 0x40944, 0x989898 }, + { 0x40948, 0x999999 }, + { 0x4094c, 0x9a9a9a }, + { 0x40950, 0x9b9b9b }, + { 0x40954, 0x9c9c9c }, + { 0x40958, 0x9c9c9c }, + { 0x4095c, 0x9d9d9d }, + { 0x40960, 0x9e9e9e }, + { 0x40964, 0x9f9f9f }, + { 0x40968, 0xa0a0a0 }, + { 0x4096c, 0xa0a0a0 }, + { 0x40970, 0xa1a1a1 }, + { 0x40974, 0xa2a2a2 }, + { 0x40978, 0xa3a3a3 }, + { 0x4097c, 0xa4a4a4 }, + { 0x40980, 0xa4a4a4 }, + { 0x40984, 0xa5a5a5 }, + { 0x40988, 0xa6a6a6 }, + { 0x4098c, 0xa7a7a7 }, + { 0x40990, 0xa7a7a7 }, + { 0x40994, 0xa8a8a8 }, + { 0x40998, 0xa9a9a9 }, + { 0x4099c, 0xaaaaaa }, + { 0x409a0, 0xaaaaaa }, + { 0x409a4, 0xababab }, + { 0x409a8, 0xacacac }, + { 0x409ac, 0xadadad }, + { 0x409b0, 0xadadad }, + { 0x409b4, 0xaeaeae }, + { 0x409b8, 0xafafaf }, + { 0x409bc, 0xafafaf }, + { 0x409c0, 0xb0b0b0 }, + { 0x409c4, 0xb1b1b1 }, + { 0x409c8, 0xb2b2b2 }, + { 0x409cc, 0xb2b2b2 }, + { 0x409d0, 0xb3b3b3 }, + { 0x409d4, 0xb4b4b4 }, + { 0x409d8, 0xb4b4b4 }, + { 0x409dc, 0xb5b5b5 }, + { 0x409e0, 0xb6b6b6 }, + { 0x409e4, 0xb6b6b6 }, + { 0x409e8, 0xb7b7b7 }, + { 0x409ec, 0xb8b8b8 }, + { 0x409f0, 0xb8b8b8 }, + { 0x409f4, 0xb9b9b9 }, + { 0x409f8, 0xbababa }, + { 0x409fc, 0xbababa }, + { 0x40a00, 0xbbbbbb }, + { 0x40a04, 0xbcbcbc }, + { 0x40a08, 0xbcbcbc }, + { 0x40a0c, 0xbdbdbd }, + { 0x40a10, 0xbebebe }, + { 0x40a14, 0xbebebe }, + { 0x40a18, 0xbfbfbf }, + { 0x40a1c, 0xc0c0c0 }, + { 0x40a20, 0xc0c0c0 }, + { 0x40a24, 0xc1c1c1 }, + { 0x40a28, 0xc1c1c1 }, + { 0x40a2c, 0xc2c2c2 }, + { 0x40a30, 0xc3c3c3 }, + { 0x40a34, 0xc3c3c3 }, + { 0x40a38, 0xc4c4c4 }, + { 0x40a3c, 0xc5c5c5 }, + { 0x40a40, 0xc5c5c5 }, + { 0x40a44, 0xc6c6c6 }, + { 0x40a48, 0xc6c6c6 }, + { 0x40a4c, 0xc7c7c7 }, + { 0x40a50, 0xc8c8c8 }, + { 0x40a54, 0xc8c8c8 }, + { 0x40a58, 0xc9c9c9 }, + { 0x40a5c, 0xc9c9c9 }, + { 0x40a60, 0xcacaca }, + { 0x40a64, 0xcbcbcb }, + { 0x40a68, 0xcbcbcb }, + { 0x40a6c, 0xcccccc }, + { 0x40a70, 0xcccccc }, + { 0x40a74, 0xcdcdcd }, + { 0x40a78, 0xcecece }, + { 0x40a7c, 0xcecece }, + { 0x40a80, 0xcfcfcf }, + { 0x40a84, 0xcfcfcf }, + { 0x40a88, 0xd0d0d0 }, + { 0x40a8c, 0xd0d0d0 }, + { 0x40a90, 0xd1d1d1 }, + { 0x40a94, 0xd2d2d2 }, + { 0x40a98, 0xd2d2d2 }, + { 0x40a9c, 0xd3d3d3 }, + { 0x40aa0, 0xd3d3d3 }, + { 0x40aa4, 0xd4d4d4 }, + { 0x40aa8, 0xd4d4d4 }, + { 0x40aac, 0xd5d5d5 }, + { 0x40ab0, 0xd6d6d6 }, + { 0x40ab4, 0xd6d6d6 }, + { 0x40ab8, 0xd7d7d7 }, + { 0x40abc, 0xd7d7d7 }, + { 0x40ac0, 0xd8d8d8 }, + { 0x40ac4, 0xd8d8d8 }, + { 0x40ac8, 0xd9d9d9 }, + { 0x40acc, 0xd9d9d9 }, + { 0x40ad0, 0xdadada }, + { 0x40ad4, 0xdbdbdb }, + { 0x40ad8, 0xdbdbdb }, + { 0x40adc, 0xdcdcdc }, + { 0x40ae0, 0xdcdcdc }, + { 0x40ae4, 0xdddddd }, + { 0x40ae8, 0xdddddd }, + { 0x40aec, 0xdedede }, + { 0x40af0, 0xdedede }, + { 0x40af4, 0xdfdfdf }, + { 0x40af8, 0xdfdfdf }, + { 0x40afc, 0xe0e0e0 }, + { 0x40b00, 0xe0e0e0 }, + { 0x40b04, 0xe1e1e1 }, + { 0x40b08, 0xe1e1e1 }, + { 0x40b0c, 0xe2e2e2 }, + { 0x40b10, 0xe3e3e3 }, + { 0x40b14, 0xe3e3e3 }, + { 0x40b18, 0xe4e4e4 }, + { 0x40b1c, 0xe4e4e4 }, + { 0x40b20, 0xe5e5e5 }, + { 0x40b24, 0xe5e5e5 }, + { 0x40b28, 0xe6e6e6 }, + { 0x40b2c, 0xe6e6e6 }, + { 0x40b30, 0xe7e7e7 }, + { 0x40b34, 0xe7e7e7 }, + { 0x40b38, 0xe8e8e8 }, + { 0x40b3c, 0xe8e8e8 }, + { 0x40b40, 0xe9e9e9 }, + { 0x40b44, 0xe9e9e9 }, + { 0x40b48, 0xeaeaea }, + { 0x40b4c, 0xeaeaea }, + { 0x40b50, 0xebebeb }, + { 0x40b54, 0xebebeb }, + { 0x40b58, 0xececec }, + { 0x40b5c, 0xececec }, + { 0x40b60, 0xededed }, + { 0x40b64, 0xededed }, + { 0x40b68, 0xeeeeee }, + { 0x40b6c, 0xeeeeee }, + { 0x40b70, 0xefefef }, + { 0x40b74, 0xefefef }, + { 0x40b78, 0xf0f0f0 }, + { 0x40b7c, 0xf0f0f0 }, + { 0x40b80, 0xf1f1f1 }, + { 0x40b84, 0xf1f1f1 }, + { 0x40b88, 0xf2f2f2 }, + { 0x40b8c, 0xf2f2f2 }, + { 0x40b90, 0xf2f2f2 }, + { 0x40b94, 0xf3f3f3 }, + { 0x40b98, 0xf3f3f3 }, + { 0x40b9c, 0xf4f4f4 }, + { 0x40ba0, 0xf4f4f4 }, + { 0x40ba4, 0xf5f5f5 }, + { 0x40ba8, 0xf5f5f5 }, + { 0x40bac, 0xf6f6f6 }, + { 0x40bb0, 0xf6f6f6 }, + { 0x40bb4, 0xf7f7f7 }, + { 0x40bb8, 0xf7f7f7 }, + { 0x40bbc, 0xf8f8f8 }, + { 0x40bc0, 0xf8f8f8 }, + { 0x40bc4, 0xf9f9f9 }, + { 0x40bc8, 0xf9f9f9 }, + { 0x40bcc, 0xfafafa }, + { 0x40bd0, 0xfafafa }, + { 0x40bd4, 0xfafafa }, + { 0x40bd8, 0xfbfbfb }, + { 0x40bdc, 0xfbfbfb }, + { 0x40be0, 0xfcfcfc }, + { 0x40be4, 0xfcfcfc }, + { 0x40be8, 0xfdfdfd }, + { 0x40bec, 0xfdfdfd }, + { 0x40bf0, 0xfefefe }, + { 0x40bf4, 0xfefefe }, + { 0x40bf8, 0xffffff }, + { 0x40bfc, 0xffffff }, + { 0x40c00, 0x0 }, + { 0x40c04, 0x0 }, + { 0x40c08, 0x0 }, + { 0x40c0c, 0x0 }, + { 0x40c10, 0x0 }, + { 0x40c14, 0x0 }, + { 0x40c18, 0x0 }, + { 0x40c1c, 0x0 }, + { 0x40c20, 0x0 }, + { 0x40c24, 0x0 }, + { 0x40c28, 0x0 }, + { 0x40c2c, 0x0 }, + { 0x40c30, 0x0 }, + { 0x40c34, 0x0 }, + { 0x40c38, 0x0 }, + { 0x40c3c, 0x0 }, + { 0x40c40, 0x10101 }, + { 0x40c44, 0x10101 }, + { 0x40c48, 0x10101 }, + { 0x40c4c, 0x10101 }, + { 0x40c50, 0x10101 }, + { 0x40c54, 0x10101 }, + { 0x40c58, 0x10101 }, + { 0x40c5c, 0x10101 }, + { 0x40c60, 0x10101 }, + { 0x40c64, 0x10101 }, + { 0x40c68, 0x20202 }, + { 0x40c6c, 0x20202 }, + { 0x40c70, 0x20202 }, + { 0x40c74, 0x20202 }, + { 0x40c78, 0x20202 }, + { 0x40c7c, 0x20202 }, + { 0x40c80, 0x30303 }, + { 0x40c84, 0x30303 }, + { 0x40c88, 0x30303 }, + { 0x40c8c, 0x30303 }, + { 0x40c90, 0x30303 }, + { 0x40c94, 0x40404 }, + { 0x40c98, 0x40404 }, + { 0x40c9c, 0x40404 }, + { 0x40ca0, 0x40404 }, + { 0x40ca4, 0x40404 }, + { 0x40ca8, 0x50505 }, + { 0x40cac, 0x50505 }, + { 0x40cb0, 0x50505 }, + { 0x40cb4, 0x50505 }, + { 0x40cb8, 0x60606 }, + { 0x40cbc, 0x60606 }, + { 0x40cc0, 0x60606 }, + { 0x40cc4, 0x70707 }, + { 0x40cc8, 0x70707 }, + { 0x40ccc, 0x70707 }, + { 0x40cd0, 0x70707 }, + { 0x40cd4, 0x80808 }, + { 0x40cd8, 0x80808 }, + { 0x40cdc, 0x80808 }, + { 0x40ce0, 0x90909 }, + { 0x40ce4, 0x90909 }, + { 0x40ce8, 0xa0a0a }, + { 0x40cec, 0xa0a0a }, + { 0x40cf0, 0xa0a0a }, + { 0x40cf4, 0xb0b0b }, + { 0x40cf8, 0xb0b0b }, + { 0x40cfc, 0xb0b0b }, + { 0x40d00, 0xc0c0c }, + { 0x40d04, 0xc0c0c }, + { 0x40d08, 0xd0d0d }, + { 0x40d0c, 0xd0d0d }, + { 0x40d10, 0xe0e0e }, + { 0x40d14, 0xe0e0e }, + { 0x40d18, 0xe0e0e }, + { 0x40d1c, 0xf0f0f }, + { 0x40d20, 0xf0f0f }, + { 0x40d24, 0x101010 }, + { 0x40d28, 0x101010 }, + { 0x40d2c, 0x111111 }, + { 0x40d30, 0x111111 }, + { 0x40d34, 0x121212 }, + { 0x40d38, 0x121212 }, + { 0x40d3c, 0x131313 }, + { 0x40d40, 0x131313 }, + { 0x40d44, 0x141414 }, + { 0x40d48, 0x151515 }, + { 0x40d4c, 0x151515 }, + { 0x40d50, 0x161616 }, + { 0x40d54, 0x161616 }, + { 0x40d58, 0x171717 }, + { 0x40d5c, 0x171717 }, + { 0x40d60, 0x181818 }, + { 0x40d64, 0x191919 }, + { 0x40d68, 0x191919 }, + { 0x40d6c, 0x1a1a1a }, + { 0x40d70, 0x1b1b1b }, + { 0x40d74, 0x1b1b1b }, + { 0x40d78, 0x1c1c1c }, + { 0x40d7c, 0x1c1c1c }, + { 0x40d80, 0x1d1d1d }, + { 0x40d84, 0x1e1e1e }, + { 0x40d88, 0x1f1f1f }, + { 0x40d8c, 0x1f1f1f }, + { 0x40d90, 0x202020 }, + { 0x40d94, 0x212121 }, + { 0x40d98, 0x212121 }, + { 0x40d9c, 0x222222 }, + { 0x40da0, 0x232323 }, + { 0x40da4, 0x242424 }, + { 0x40da8, 0x242424 }, + { 0x40dac, 0x252525 }, + { 0x40db0, 0x262626 }, + { 0x40db4, 0x272727 }, + { 0x40db8, 0x272727 }, + { 0x40dbc, 0x282828 }, + { 0x40dc0, 0x292929 }, + { 0x40dc4, 0x2a2a2a }, + { 0x40dc8, 0x2b2b2b }, + { 0x40dcc, 0x2c2c2c }, + { 0x40dd0, 0x2c2c2c }, + { 0x40dd4, 0x2d2d2d }, + { 0x40dd8, 0x2e2e2e }, + { 0x40ddc, 0x2f2f2f }, + { 0x40de0, 0x303030 }, + { 0x40de4, 0x313131 }, + { 0x40de8, 0x323232 }, + { 0x40dec, 0x333333 }, + { 0x40df0, 0x333333 }, + { 0x40df4, 0x343434 }, + { 0x40df8, 0x353535 }, + { 0x40dfc, 0x363636 }, + { 0x40e00, 0x373737 }, + { 0x40e04, 0x383838 }, + { 0x40e08, 0x393939 }, + { 0x40e0c, 0x3a3a3a }, + { 0x40e10, 0x3b3b3b }, + { 0x40e14, 0x3c3c3c }, + { 0x40e18, 0x3d3d3d }, + { 0x40e1c, 0x3e3e3e }, + { 0x40e20, 0x3f3f3f }, + { 0x40e24, 0x404040 }, + { 0x40e28, 0x414141 }, + { 0x40e2c, 0x424242 }, + { 0x40e30, 0x434343 }, + { 0x40e34, 0x444444 }, + { 0x40e38, 0x464646 }, + { 0x40e3c, 0x474747 }, + { 0x40e40, 0x484848 }, + { 0x40e44, 0x494949 }, + { 0x40e48, 0x4a4a4a }, + { 0x40e4c, 0x4b4b4b }, + { 0x40e50, 0x4c4c4c }, + { 0x40e54, 0x4d4d4d }, + { 0x40e58, 0x4f4f4f }, + { 0x40e5c, 0x505050 }, + { 0x40e60, 0x515151 }, + { 0x40e64, 0x525252 }, + { 0x40e68, 0x535353 }, + { 0x40e6c, 0x545454 }, + { 0x40e70, 0x565656 }, + { 0x40e74, 0x575757 }, + { 0x40e78, 0x585858 }, + { 0x40e7c, 0x595959 }, + { 0x40e80, 0x5b5b5b }, + { 0x40e84, 0x5c5c5c }, + { 0x40e88, 0x5d5d5d }, + { 0x40e8c, 0x5e5e5e }, + { 0x40e90, 0x606060 }, + { 0x40e94, 0x616161 }, + { 0x40e98, 0x626262 }, + { 0x40e9c, 0x646464 }, + { 0x40ea0, 0x656565 }, + { 0x40ea4, 0x666666 }, + { 0x40ea8, 0x686868 }, + { 0x40eac, 0x696969 }, + { 0x40eb0, 0x6a6a6a }, + { 0x40eb4, 0x6c6c6c }, + { 0x40eb8, 0x6d6d6d }, + { 0x40ebc, 0x6f6f6f }, + { 0x40ec0, 0x707070 }, + { 0x40ec4, 0x717171 }, + { 0x40ec8, 0x737373 }, + { 0x40ecc, 0x747474 }, + { 0x40ed0, 0x767676 }, + { 0x40ed4, 0x777777 }, + { 0x40ed8, 0x797979 }, + { 0x40edc, 0x7a7a7a }, + { 0x40ee0, 0x7c7c7c }, + { 0x40ee4, 0x7d7d7d }, + { 0x40ee8, 0x7f7f7f }, + { 0x40eec, 0x808080 }, + { 0x40ef0, 0x828282 }, + { 0x40ef4, 0x838383 }, + { 0x40ef8, 0x858585 }, + { 0x40efc, 0x868686 }, + { 0x40f00, 0x888888 }, + { 0x40f04, 0x898989 }, + { 0x40f08, 0x8b8b8b }, + { 0x40f0c, 0x8d8d8d }, + { 0x40f10, 0x8e8e8e }, + { 0x40f14, 0x909090 }, + { 0x40f18, 0x919191 }, + { 0x40f1c, 0x939393 }, + { 0x40f20, 0x959595 }, + { 0x40f24, 0x969696 }, + { 0x40f28, 0x989898 }, + { 0x40f2c, 0x9a9a9a }, + { 0x40f30, 0x9b9b9b }, + { 0x40f34, 0x9d9d9d }, + { 0x40f38, 0x9f9f9f }, + { 0x40f3c, 0xa1a1a1 }, + { 0x40f40, 0xa2a2a2 }, + { 0x40f44, 0xa4a4a4 }, + { 0x40f48, 0xa6a6a6 }, + { 0x40f4c, 0xa7a7a7 }, + { 0x40f50, 0xa9a9a9 }, + { 0x40f54, 0xababab }, + { 0x40f58, 0xadadad }, + { 0x40f5c, 0xafafaf }, + { 0x40f60, 0xb0b0b0 }, + { 0x40f64, 0xb2b2b2 }, + { 0x40f68, 0xb4b4b4 }, + { 0x40f6c, 0xb6b6b6 }, + { 0x40f70, 0xb8b8b8 }, + { 0x40f74, 0xbababa }, + { 0x40f78, 0xbbbbbb }, + { 0x40f7c, 0xbdbdbd }, + { 0x40f80, 0xbfbfbf }, + { 0x40f84, 0xc1c1c1 }, + { 0x40f88, 0xc3c3c3 }, + { 0x40f8c, 0xc5c5c5 }, + { 0x40f90, 0xc7c7c7 }, + { 0x40f94, 0xc9c9c9 }, + { 0x40f98, 0xcbcbcb }, + { 0x40f9c, 0xcdcdcd }, + { 0x40fa0, 0xcfcfcf }, + { 0x40fa4, 0xd1d1d1 }, + { 0x40fa8, 0xd3d3d3 }, + { 0x40fac, 0xd5d5d5 }, + { 0x40fb0, 0xd7d7d7 }, + { 0x40fb4, 0xd9d9d9 }, + { 0x40fb8, 0xdbdbdb }, + { 0x40fbc, 0xdddddd }, + { 0x40fc0, 0xdfdfdf }, + { 0x40fc4, 0xe1e1e1 }, + { 0x40fc8, 0xe3e3e3 }, + { 0x40fcc, 0xe5e5e5 }, + { 0x40fd0, 0xe7e7e7 }, + { 0x40fd4, 0xe9e9e9 }, + { 0x40fd8, 0xebebeb }, + { 0x40fdc, 0xeeeeee }, + { 0x40fe0, 0xf0f0f0 }, + { 0x40fe4, 0xf2f2f2 }, + { 0x40fe8, 0xf4f4f4 }, + { 0x40fec, 0xf6f6f6 }, + { 0x40ff0, 0xf8f8f8 }, + { 0x40ff4, 0xfbfbfb }, + { 0x40ff8, 0xfdfdfd }, + { 0x40ffc, 0xffffff }, +}; diff --git a/drivers/video/msm/mdp_cursor.c b/drivers/video/msm/mdp_cursor.c new file mode 100644 index 0000000000000000000000000000000000000000..93ff388276f5fb3b4a45a06ecb954bc9f5206d2e --- /dev/null +++ b/drivers/video/msm/mdp_cursor.c @@ -0,0 +1,264 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "mdp.h" +#include "msm_fb.h" + +static int cursor_enabled; + +#include "mdp4.h" + +#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDP40) +static struct workqueue_struct *mdp_cursor_ctrl_wq; +static struct work_struct mdp_cursor_ctrl_worker; + +/* cursor configuration */ +static void *cursor_buf_phys; +static __u32 width, height, bg_color; +static int calpha_en, transp_en, alpha; +static int sync_disabled = -1; + +void mdp_cursor_ctrl_workqueue_handler(struct work_struct *work) +{ + unsigned long flag; + + /* disable vsync */ + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_disable_irq(MDP_OVERLAY0_TERM); + spin_unlock_irqrestore(&mdp_spin_lock, flag); +} + +void mdp_hw_cursor_init(void) +{ + mdp_cursor_ctrl_wq = + create_singlethread_workqueue("mdp_cursor_ctrl_wq"); + INIT_WORK(&mdp_cursor_ctrl_worker, mdp_cursor_ctrl_workqueue_handler); +} + +void mdp_hw_cursor_done(void) +{ + /* Cursor configuration: + * + * This is done in DMA_P_DONE ISR because the following registers are + * not double buffered in hardware: + * + * MDP_DMA_P_CURSOR_SIZE, address = 0x90044 + * MDP_DMA_P_CURSOR_BLEND_CONFIG, address = 0x90060 + * MDP_DMA_P_CURSOR_BLEND_PARAM, address = 0x90064 + * MDP_DMA_P_CURSOR_BLEND_TRANS_LOW, address = 0x90068 + * MDP_DMA_P_CURSOR_BLEND_TRANS_HIG, address = 0x9006C + * + * Moving this code out of the ISR will cause the MDP to underrun! + */ + spin_lock(&mdp_spin_lock); + if (sync_disabled) { + spin_unlock(&mdp_spin_lock); + return; + } + + MDP_OUTP(MDP_BASE + 0x90044, (height << 16) | width); + MDP_OUTP(MDP_BASE + 0x90048, cursor_buf_phys); + + MDP_OUTP(MDP_BASE + 0x90060, + (transp_en << 3) | (calpha_en << 1) | + (inp32(MDP_BASE + 0x90060) & 0x1)); + + MDP_OUTP(MDP_BASE + 0x90064, (alpha << 24)); + MDP_OUTP(MDP_BASE + 0x90068, (0xffffff & bg_color)); + MDP_OUTP(MDP_BASE + 0x9006C, (0xffffff & bg_color)); + + /* enable/disable the cursor as per the last request */ + if (cursor_enabled && !(inp32(MDP_BASE + 0x90060) & (0x1))) + MDP_OUTP(MDP_BASE + 0x90060, inp32(MDP_BASE + 0x90060) | 0x1); + else if (!cursor_enabled && (inp32(MDP_BASE + 0x90060) & (0x1))) + MDP_OUTP(MDP_BASE + 0x90060, + inp32(MDP_BASE + 0x90060) & (~0x1)); + + /* enqueue the task to disable MDP interrupts */ + queue_work(mdp_cursor_ctrl_wq, &mdp_cursor_ctrl_worker); + + /* update done */ + sync_disabled = 1; + spin_unlock(&mdp_spin_lock); +} + +static void mdp_hw_cursor_enable_vsync(void) +{ + /* if the cursor registers were updated (once or more) since the + * last vsync, enable the vsync interrupt (if not already enabled) + * for the next update + */ + if (sync_disabled) { + + /* cancel pending task to disable MDP interrupts */ + if (work_pending(&mdp_cursor_ctrl_worker)) + cancel_work_sync(&mdp_cursor_ctrl_worker); + else + /* enable irq */ + mdp_enable_irq(MDP_OVERLAY0_TERM); + + sync_disabled = 0; + + /* enable vsync intr */ + outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE); + mdp_intr_mask |= INTR_OVERLAY0_DONE; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + } +} + +int mdp_hw_cursor_sync_update(struct fb_info *info, struct fb_cursor *cursor) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_image *img = &cursor->image; + unsigned long flag; + int sync_needed = 0, ret = 0; + + if ((img->width > MDP_CURSOR_WIDTH) || + (img->height > MDP_CURSOR_HEIGHT) || + (img->depth != 32)) + return -EINVAL; + + if (cursor->set & FB_CUR_SETPOS) + MDP_OUTP(MDP_BASE + 0x9004c, (img->dy << 16) | img->dx); + + if (cursor->set & FB_CUR_SETIMAGE) { + ret = copy_from_user(mfd->cursor_buf, img->data, + img->width*img->height*4); + if (ret) + return ret; + + spin_lock_irqsave(&mdp_spin_lock, flag); + if (img->bg_color == 0xffffffff) + transp_en = 0; + else + transp_en = 1; + + alpha = (img->fg_color & 0xff000000) >> 24; + + if (alpha) + calpha_en = 0x2; /* xrgb */ + else + calpha_en = 0x1; /* argb */ + + /* cursor parameters */ + height = img->height; + width = img->width; + bg_color = img->bg_color; + cursor_buf_phys = mfd->cursor_buf_phys; + + sync_needed = 1; + } else + spin_lock_irqsave(&mdp_spin_lock, flag); + + if ((cursor->enable) && (!cursor_enabled)) { + cursor_enabled = 1; + sync_needed = 1; + } else if ((!cursor->enable) && (cursor_enabled)) { + cursor_enabled = 0; + sync_needed = 1; + } + + /* if sync cursor update is needed, enable vsync */ + if (sync_needed) + mdp_hw_cursor_enable_vsync(); + + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + return 0; +} +#endif /* CONFIG_FB_MSM_OVERLAY && CONFIG_FB_MSM_MDP40 */ + +int mdp_hw_cursor_update(struct fb_info *info, struct fb_cursor *cursor) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_image *img = &cursor->image; + int calpha_en, transp_en; + int alpha; + int ret = 0; + + if ((img->width > MDP_CURSOR_WIDTH) || + (img->height > MDP_CURSOR_HEIGHT) || + (img->depth != 32)) + return -EINVAL; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + if (cursor->set & FB_CUR_SETPOS) + MDP_OUTP(MDP_BASE + 0x9004c, (img->dy << 16) | img->dx); + + if (cursor->set & FB_CUR_SETIMAGE) { + ret = copy_from_user(mfd->cursor_buf, img->data, + img->width*img->height*4); + if (ret) + return ret; + + if (img->bg_color == 0xffffffff) + transp_en = 0; + else + transp_en = 1; + + alpha = (img->fg_color & 0xff000000) >> 24; + + if (alpha) + calpha_en = 0x2; /* xrgb */ + else + calpha_en = 0x1; /* argb */ + + MDP_OUTP(MDP_BASE + 0x90044, (img->height << 16) | img->width); + MDP_OUTP(MDP_BASE + 0x90048, mfd->cursor_buf_phys); + /* order the writes the cursor_buf before updating the + * hardware */ + dma_coherent_pre_ops(); + MDP_OUTP(MDP_BASE + 0x90060, + (transp_en << 3) | (calpha_en << 1) | + (inp32(MDP_BASE + 0x90060) & 0x1)); +#ifdef CONFIG_FB_MSM_MDP40 + MDP_OUTP(MDP_BASE + 0x90064, (alpha << 24)); + MDP_OUTP(MDP_BASE + 0x90068, (0xffffff & img->bg_color)); + MDP_OUTP(MDP_BASE + 0x9006C, (0xffffff & img->bg_color)); +#else + MDP_OUTP(MDP_BASE + 0x90064, + (alpha << 24) | (0xffffff & img->bg_color)); + MDP_OUTP(MDP_BASE + 0x90068, 0); +#endif + } + + if ((cursor->enable) && (!cursor_enabled)) { + cursor_enabled = 1; + MDP_OUTP(MDP_BASE + 0x90060, inp32(MDP_BASE + 0x90060) | 0x1); + } else if ((!cursor->enable) && (cursor_enabled)) { + cursor_enabled = 0; + MDP_OUTP(MDP_BASE + 0x90060, + inp32(MDP_BASE + 0x90060) & (~0x1)); + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return 0; +} diff --git a/drivers/video/msm/mdp_debugfs.c b/drivers/video/msm/mdp_debugfs.c new file mode 100644 index 0000000000000000000000000000000000000000..254999982f14c1e0da43648adc50807b1ae843fe --- /dev/null +++ b/drivers/video/msm/mdp_debugfs.c @@ -0,0 +1,1392 @@ +/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdp.h" +#include "msm_fb.h" +#ifdef CONFIG_FB_MSM_MDP40 +#include "mdp4.h" +#endif +#include "mddihosti.h" +#include "tvenc.h" +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +#include "hdmi_msm.h" +#endif + +#define MDP_DEBUG_BUF 2048 + +static uint32 mdp_offset; +static uint32 mdp_count; + +static char debug_buf[MDP_DEBUG_BUF]; + +/* + * MDP4 + * + */ + +static int mdp_offset_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int mdp_offset_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t mdp_offset_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 off, cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + sscanf(debug_buf, "%x %d", &off, &cnt); + + if (cnt <= 0) + cnt = 1; + + mdp_offset = off; + mdp_count = cnt; + + printk(KERN_INFO "%s: offset=%x cnt=%d\n", __func__, + mdp_offset, mdp_count); + + return count; +} + +static ssize_t mdp_offset_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int len = 0; + + + if (*ppos) + return 0; /* the end */ + + len = snprintf(debug_buf, sizeof(debug_buf), "0x%08x %d\n", + mdp_offset, mdp_count); + if (len < 0) + return 0; + + if (copy_to_user(buff, debug_buf, len)) + return -EFAULT; + + *ppos += len; /* increase offset */ + + return len; +} + +static const struct file_operations mdp_off_fops = { + .open = mdp_offset_open, + .release = mdp_offset_release, + .read = mdp_offset_read, + .write = mdp_offset_write, +}; + +static int mdp_reg_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int mdp_reg_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t mdp_reg_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 off, data; + int cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x %x", &off, &data); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + outpdw(MDP_BASE + off, data); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + printk(KERN_INFO "%s: addr=%x data=%x\n", __func__, off, data); + + return count; +} + +static ssize_t mdp_reg_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int len = 0; + uint32 data; + int i, j, off, dlen, num; + char *bp, *cp; + int tot = 0; + + + if (*ppos) + return 0; /* the end */ + + j = 0; + num = 0; + bp = debug_buf; + cp = MDP_BASE + mdp_offset; + dlen = sizeof(debug_buf); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + while (j++ < 8) { + len = snprintf(bp, dlen, "0x%08x: ", (int)cp); + tot += len; + bp += len; + dlen -= len; + off = 0; + i = 0; + while (i++ < 4) { + data = inpdw(cp + off); + len = snprintf(bp, dlen, "%08x ", data); + tot += len; + bp += len; + dlen -= len; + off += 4; + num++; + if (num >= mdp_count) + break; + } + *bp++ = '\n'; + --dlen; + tot++; + cp += off; + if (num >= mdp_count) + break; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + *bp = 0; + tot++; + + if (copy_to_user(buff, debug_buf, tot)) + return -EFAULT; + + *ppos += tot; /* increase offset */ + + return tot; +} + + +static const struct file_operations mdp_reg_fops = { + .open = mdp_reg_open, + .release = mdp_reg_release, + .read = mdp_reg_read, + .write = mdp_reg_write, +}; + +#ifdef CONFIG_FB_MSM_MDP40 +static int mdp_stat_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int mdp_stat_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t mdp_stat_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + unsigned long flag; + + if (count > sizeof(debug_buf)) + return -EFAULT; + + spin_lock_irqsave(&mdp_spin_lock, flag); + memset((char *)&mdp4_stat, 0 , sizeof(mdp4_stat)); /* reset */ + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + return count; +} + +static ssize_t mdp_stat_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int len = 0; + int tot = 0; + int dlen; + char *bp; + + + if (*ppos) + return 0; /* the end */ + + bp = debug_buf; + dlen = sizeof(debug_buf); + + len = snprintf(bp, dlen, "\nmdp:\n"); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "int_total: %08lu\t", + mdp4_stat.intr_tot); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "int_overlay0: %08lu\t", + mdp4_stat.intr_overlay0); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "int_overlay1: %08lu\n", + mdp4_stat.intr_overlay1); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "int_overlay1: %08lu\n", + mdp4_stat.intr_overlay2); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "int_dmap: %08lu\t", + mdp4_stat.intr_dma_p); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "int_dmas: %08lu\t", + mdp4_stat.intr_dma_s); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "int_dmae: %08lu\n", + mdp4_stat.intr_dma_e); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "primary: vsync: %08lu\t", + mdp4_stat.intr_vsync_p); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "underrun: %08lu\n", + mdp4_stat.intr_underrun_p); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "external: vsync: %08lu\t", + mdp4_stat.intr_vsync_e); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "underrun: %08lu\n", + mdp4_stat.intr_underrun_e); + + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "histogram: %08lu\t", + mdp4_stat.intr_histogram); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "read_ptr: %08lu\n\n", + mdp4_stat.intr_rd_ptr); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "dsi:\n"); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "int_total: %08lu\tmdp_start: %08lu\n", + mdp4_stat.intr_dsi, mdp4_stat.dsi_mdp_start); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "int_cmd: %08lu\t", + mdp4_stat.intr_dsi_cmd); + + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "int_mdp: %08lu\t", + mdp4_stat.intr_dsi_mdp); + + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "int_err: %08lu\n", + mdp4_stat.intr_dsi_err); + + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "clk_on : %08lu\t", + mdp4_stat.dsi_clk_on); + + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "clk_off: %08lu\n\n", + mdp4_stat.dsi_clk_off); + + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "kickoff:\n"); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "overlay0: %08lu\t", + mdp4_stat.kickoff_ov0); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "dmap: %08lu\t", + mdp4_stat.kickoff_dmap); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "dmas: %08lu\n", + mdp4_stat.kickoff_dmas); + + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "overlay1: %08lu\t", + mdp4_stat.kickoff_ov1); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "dmae: %08lu\n\n", + mdp4_stat.kickoff_dmae); + + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "overlay0_play:\n"); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "set: %08lu\t", + mdp4_stat.overlay_set[0]); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "unset: %08lu\t", + mdp4_stat.overlay_unset[0]); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "play: %08lu\n", + mdp4_stat.overlay_play[0]); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "overlay1_play:\n"); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "set: %08lu\t", + mdp4_stat.overlay_set[1]); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "unset: %08lu\t", + mdp4_stat.overlay_unset[1]); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "play: %08lu\n\n", + mdp4_stat.overlay_play[1]); + + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "frame_push:\n"); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "rgb1: %08lu\t", + mdp4_stat.pipe[OVERLAY_PIPE_RGB1]); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "rgb2: %08lu\n", + mdp4_stat.pipe[OVERLAY_PIPE_RGB2]); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "vg1 : %08lu\t", + mdp4_stat.pipe[OVERLAY_PIPE_VG1]); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "vg2 : %08lu\n", + mdp4_stat.pipe[OVERLAY_PIPE_VG2]); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "err_mixer : %08lu\t", mdp4_stat.err_mixer); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "err_size : %08lu\n", mdp4_stat.err_size); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "err_scale : %08lu\t", mdp4_stat.err_scale); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "err_format: %08lu\n", mdp4_stat.err_format); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "err_play : %08lu\t", mdp4_stat.err_play); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "err_stage : %08lu\n", mdp4_stat.err_stage); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "err_underflow: %08lu\n\n", + mdp4_stat.err_underflow); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "writeback:\n"); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "dsi_cmd: %08lu\t", + mdp4_stat.blt_dsi_cmd); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "dsi_video: %08lu\n", + mdp4_stat.blt_dsi_video); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "lcdc: %08lu\t", + mdp4_stat.blt_lcdc); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "dtv: %08lu\t", + mdp4_stat.blt_dtv); + bp += len; + dlen -= len; + + len = snprintf(bp, dlen, "mddi: %08lu\n\n", + mdp4_stat.blt_mddi); + bp += len; + dlen -= len; + + tot = (uint32)bp - (uint32)debug_buf; + *bp = 0; + tot++; + + if (tot < 0) + return 0; + if (copy_to_user(buff, debug_buf, tot)) + return -EFAULT; + + *ppos += tot; /* increase offset */ + + return tot; +} + +static const struct file_operations mdp_stat_fops = { + .open = mdp_stat_open, + .release = mdp_stat_release, + .read = mdp_stat_read, + .write = mdp_stat_write, +}; +#endif + +/* + * MDDI + * + */ + +struct mddi_reg { + char *name; + int off; +}; + +static struct mddi_reg mddi_regs_list[] = { + {"MDDI_CMD", MDDI_CMD}, /* 0x0000 */ + {"MDDI_VERSION", MDDI_VERSION}, /* 0x0004 */ + {"MDDI_PRI_PTR", MDDI_PRI_PTR}, /* 0x0008 */ + {"MDDI_BPS", MDDI_BPS}, /* 0x0010 */ + {"MDDI_SPM", MDDI_SPM}, /* 0x0014 */ + {"MDDI_INT", MDDI_INT}, /* 0x0018 */ + {"MDDI_INTEN", MDDI_INTEN}, /* 0x001c */ + {"MDDI_REV_PTR", MDDI_REV_PTR}, /* 0x0020 */ + {"MDDI_ REV_SIZE", MDDI_REV_SIZE},/* 0x0024 */ + {"MDDI_STAT", MDDI_STAT}, /* 0x0028 */ + {"MDDI_REV_RATE_DIV", MDDI_REV_RATE_DIV}, /* 0x002c */ + {"MDDI_REV_CRC_ERR", MDDI_REV_CRC_ERR}, /* 0x0030 */ + {"MDDI_TA1_LEN", MDDI_TA1_LEN}, /* 0x0034 */ + {"MDDI_TA2_LEN", MDDI_TA2_LEN}, /* 0x0038 */ + {"MDDI_TEST", MDDI_TEST}, /* 0x0040 */ + {"MDDI_REV_PKT_CNT", MDDI_REV_PKT_CNT}, /* 0x0044 */ + {"MDDI_DRIVE_HI", MDDI_DRIVE_HI},/* 0x0048 */ + {"MDDI_DRIVE_LO", MDDI_DRIVE_LO}, /* 0x004c */ + {"MDDI_DISP_WAKE", MDDI_DISP_WAKE},/* 0x0050 */ + {"MDDI_REV_ENCAP_SZ", MDDI_REV_ENCAP_SZ}, /* 0x0054 */ + {"MDDI_RTD_VAL", MDDI_RTD_VAL}, /* 0x0058 */ + {"MDDI_PAD_CTL", MDDI_PAD_CTL}, /* 0x0068 */ + {"MDDI_DRIVER_START_CNT", MDDI_DRIVER_START_CNT}, /* 0x006c */ + {"MDDI_CORE_VER", MDDI_CORE_VER}, /* 0x008c */ + {"MDDI_FIFO_ALLOC", MDDI_FIFO_ALLOC}, /* 0x0090 */ + {"MDDI_PAD_IO_CTL", MDDI_PAD_IO_CTL}, /* 0x00a0 */ + {"MDDI_PAD_CAL", MDDI_PAD_CAL}, /* 0x00a4 */ + {0, 0} +}; + +static int mddi_reg_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int mddi_reg_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static void mddi_reg_write(int ndx, uint32 off, uint32 data) +{ + char *base; + + if (ndx) + base = (char *)msm_emdh_base; + else + base = (char *)msm_pmdh_base; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + writel(data, base + off); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + printk(KERN_INFO "%s: addr=%x data=%x\n", + __func__, (int)(base+off), (int)data); +} + +static int mddi_reg_read(int ndx) +{ + struct mddi_reg *reg; + unsigned char *base; + int data; + char *bp; + int len = 0; + int tot = 0; + int dlen; + + if (ndx) + base = msm_emdh_base; + else + base = msm_pmdh_base; + + reg = mddi_regs_list; + bp = debug_buf; + dlen = sizeof(debug_buf); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + while (reg->name) { + data = readl((u32)base + reg->off); + len = snprintf(bp, dlen, "%s:0x%08x\t\t= 0x%08x\n", + reg->name, reg->off, data); + tot += len; + bp += len; + dlen -= len; + reg++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + *bp = 0; + tot++; + + return tot; +} + +static ssize_t pmdh_reg_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 off, data; + int cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x %x", &off, &data); + + mddi_reg_write(0, off, data); + + return count; +} + +static ssize_t pmdh_reg_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int tot = 0; + + if (*ppos) + return 0; /* the end */ + + tot = mddi_reg_read(0); /* pmdh */ + + if (tot < 0) + return 0; + if (copy_to_user(buff, debug_buf, tot)) + return -EFAULT; + + *ppos += tot; /* increase offset */ + + return tot; +} + + +static const struct file_operations pmdh_fops = { + .open = mddi_reg_open, + .release = mddi_reg_release, + .read = pmdh_reg_read, + .write = pmdh_reg_write, +}; + + + +#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI) +static int vsync_reg_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int vsync_reg_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t vsync_reg_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 enable; + int cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x", &enable); + + mdp_dmap_vsync_set(enable); + + return count; +} + +static ssize_t vsync_reg_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + char *bp; + int len = 0; + int tot = 0; + int dlen; + + if (*ppos) + return 0; /* the end */ + + bp = debug_buf; + dlen = sizeof(debug_buf); + len = snprintf(bp, dlen, "%x\n", mdp_dmap_vsync_get()); + tot += len; + bp += len; + *bp = 0; + tot++; + + if (copy_to_user(buff, debug_buf, tot)) + return -EFAULT; + + *ppos += tot; /* increase offset */ + + return tot; +} + + +static const struct file_operations vsync_fops = { + .open = vsync_reg_open, + .release = vsync_reg_release, + .read = vsync_reg_read, + .write = vsync_reg_write, +}; +#endif + +static ssize_t emdh_reg_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 off, data; + int cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x %x", &off, &data); + + mddi_reg_write(1, off, data); + + return count; +} + +static ssize_t emdh_reg_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int tot = 0; + + if (*ppos) + return 0; /* the end */ + + tot = mddi_reg_read(1); /* emdh */ + + if (tot < 0) + return 0; + if (copy_to_user(buff, debug_buf, tot)) + return -EFAULT; + + *ppos += tot; /* increase offset */ + + return tot; +} + +static const struct file_operations emdh_fops = { + .open = mddi_reg_open, + .release = mddi_reg_release, + .read = emdh_reg_read, + .write = emdh_reg_write, +}; + + +uint32 dbg_offset; +uint32 dbg_count; +char *dbg_base; + + +static int dbg_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int dbg_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t dbg_base_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + return count; +} + +static ssize_t dbg_base_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int len = 0; + int tot = 0; + int dlen; + char *bp; + + + if (*ppos) + return 0; /* the end */ + + + bp = debug_buf; + dlen = sizeof(debug_buf); + + len = snprintf(bp, dlen, "mdp_base : %08x\n", + (int)msm_mdp_base); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "mddi_base : %08x\n", + (int)msm_pmdh_base); + bp += len; + dlen -= len; + len = snprintf(bp, dlen, "emdh_base : %08x\n", + (int)msm_emdh_base); + bp += len; + dlen -= len; +#ifdef CONFIG_FB_MSM_TVOUT + len = snprintf(bp, dlen, "tvenv_base: %08x\n", + (int)tvenc_base); + bp += len; + dlen -= len; +#endif + +#ifdef CONFIG_FB_MSM_MIPI_DSI + len = snprintf(bp, dlen, "mipi_dsi_base: %08x\n", + (int)mipi_dsi_base); + bp += len; + dlen -= len; +#endif + + tot = (uint32)bp - (uint32)debug_buf; + *bp = 0; + tot++; + + if (tot < 0) + return 0; + if (copy_to_user(buff, debug_buf, tot)) + return -EFAULT; + + *ppos += tot; /* increase offset */ + + return tot; +} + +static const struct file_operations dbg_base_fops = { + .open = dbg_open, + .release = dbg_release, + .read = dbg_base_read, + .write = dbg_base_write, +}; + +static ssize_t dbg_offset_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 off, cnt, num, base; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x %d %x", &off, &num, &base); + + if (cnt < 0) + cnt = 0; + + if (cnt >= 1) + dbg_offset = off; + if (cnt >= 2) + dbg_count = num; + if (cnt >= 3) + dbg_base = (char *)base; + + printk(KERN_INFO "%s: offset=%x cnt=%d base=%x\n", __func__, + dbg_offset, dbg_count, (int)dbg_base); + + return count; +} + +static ssize_t dbg_offset_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int len = 0; + + + if (*ppos) + return 0; /* the end */ + + len = snprintf(debug_buf, sizeof(debug_buf), "0x%08x %d 0x%08x\n", + dbg_offset, dbg_count, (int)dbg_base); + if (len < 0) + return 0; + + if (copy_to_user(buff, debug_buf, len)) + return -EFAULT; + + *ppos += len; /* increase offset */ + + return len; +} + +static const struct file_operations dbg_off_fops = { + .open = dbg_open, + .release = dbg_release, + .read = dbg_offset_read, + .write = dbg_offset_write, +}; + + +static ssize_t dbg_reg_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 off, data; + int cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x %x", &off, &data); + + writel(data, dbg_base + off); + + printk(KERN_INFO "%s: addr=%x data=%x\n", + __func__, (int)(dbg_base+off), (int)data); + + return count; +} + +static ssize_t dbg_reg_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int len = 0; + uint32 data; + int i, j, off, dlen, num; + char *bp, *cp; + int tot = 0; + + + if (*ppos) + return 0; /* the end */ + + if (dbg_base == 0) + return 0; /* nothing to read */ + + j = 0; + num = 0; + bp = debug_buf; + cp = (char *)(dbg_base + dbg_offset); + dlen = sizeof(debug_buf); + while (j++ < 16) { + len = snprintf(bp, dlen, "0x%08x: ", (int)cp); + tot += len; + bp += len; + dlen -= len; + off = 0; + i = 0; + while (i++ < 4) { + data = readl(cp + off); + len = snprintf(bp, dlen, "%08x ", data); + tot += len; + bp += len; + dlen -= len; + off += 4; + num++; + if (num >= dbg_count) + break; + } + data = readl((u32)cp + off); + *bp++ = '\n'; + --dlen; + tot++; + cp += off; + if (num >= dbg_count) + break; + } + *bp = 0; + tot++; + + if (copy_to_user(buff, debug_buf, tot)) + return -EFAULT; + + *ppos += tot; /* increase offset */ + + return tot; +} + + +static const struct file_operations dbg_reg_fops = { + .open = dbg_open, + .release = dbg_release, + .read = dbg_reg_read, + .write = dbg_reg_write, +}; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static uint32 hdmi_offset; +static uint32 hdmi_count; + +static int hdmi_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int hdmi_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t hdmi_offset_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 off, cnt, num; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x %d", &off, &num); + + if (cnt < 0) + cnt = 0; + + if (cnt >= 1) + hdmi_offset = off; + if (cnt >= 2) + hdmi_count = num; + + printk(KERN_INFO "%s: offset=%x cnt=%d\n", __func__, + hdmi_offset, hdmi_count); + + return count; +} + +static ssize_t hdmi_offset_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int len = 0; + + + if (*ppos) + return 0; /* the end */ + + len = snprintf(debug_buf, sizeof(debug_buf), "0x%08x %d\n", + hdmi_offset, hdmi_count); + if (len < 0) + return 0; + + if (copy_to_user(buff, debug_buf, len)) + return -EFAULT; + + *ppos += len; /* increase offset */ + + return len; +} + +static const struct file_operations hdmi_off_fops = { + .open = hdmi_open, + .release = hdmi_release, + .read = hdmi_offset_read, + .write = hdmi_offset_write, +}; + + +static ssize_t hdmi_reg_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + uint32 off, data, base; + int cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + base = hdmi_msm_get_io_base(); + if (base == 0) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x %x", &off, &data); + + writel(data, base + off); + + printk(KERN_INFO "%s: addr=%x data=%x\n", + __func__, (int)(base+off), (int)data); + + return count; +} + +static ssize_t hdmi_reg_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + int len = 0; + uint32 data; + int i, j, off, dlen, num; + char *bp, *cp; + int tot = 0; + + + if (*ppos) + return 0; /* the end */ + + if (hdmi_msm_get_io_base() == 0) + return 0; /* nothing to read */ + + j = 0; + num = 0; + bp = debug_buf; + cp = (char *)(hdmi_msm_get_io_base() + hdmi_offset); + dlen = sizeof(debug_buf); + while (j++ < 16) { + len = snprintf(bp, dlen, "0x%08x: ", (int)cp); + tot += len; + bp += len; + dlen -= len; + off = 0; + i = 0; + while (i++ < 4) { + data = readl(cp + off); + len = snprintf(bp, dlen, "%08x ", data); + tot += len; + bp += len; + dlen -= len; + off += 4; + num++; + if (num >= hdmi_count) + break; + } + data = readl((u32)cp + off); + *bp++ = '\n'; + --dlen; + tot++; + cp += off; + if (num >= hdmi_count) + break; + } + *bp = 0; + tot++; + + if (copy_to_user(buff, debug_buf, tot)) + return -EFAULT; + + *ppos += tot; /* increase offset */ + + return tot; +} + + +static const struct file_operations hdmi_reg_fops = { + .open = hdmi_open, + .release = hdmi_release, + .read = hdmi_reg_read, + .write = hdmi_reg_write, +}; +#endif + +/* + * debugfs + * + */ + +int mdp_debugfs_init(void) +{ + struct dentry *dent = debugfs_create_dir("mdp", NULL); + + if (IS_ERR(dent)) { + printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n", + __FILE__, __LINE__, PTR_ERR(dent)); + return -1; + } + + if (debugfs_create_file("off", 0644, dent, 0, &mdp_off_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -1; + } + + if (debugfs_create_file("reg", 0644, dent, 0, &mdp_reg_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -1; + } + +#ifdef CONFIG_FB_MSM_MDP40 + if (debugfs_create_file("stat", 0644, dent, 0, &mdp_stat_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -1; + } +#endif + + dent = debugfs_create_dir("mddi", NULL); + + if (IS_ERR(dent)) { + printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n", + __FILE__, __LINE__, PTR_ERR(dent)); + return -1; + } + + if (debugfs_create_file("reg", 0644, dent, 0, &pmdh_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -1; + } + +#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI) + if (debugfs_create_file("vsync", 0644, dent, 0, &vsync_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -1; + } +#endif + + dent = debugfs_create_dir("emdh", NULL); + + if (IS_ERR(dent)) { + printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n", + __FILE__, __LINE__, PTR_ERR(dent)); + return -1; + } + + if (debugfs_create_file("reg", 0644, dent, 0, &emdh_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -1; + } + + dent = debugfs_create_dir("mdp-dbg", NULL); + + if (IS_ERR(dent)) { + printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n", + __FILE__, __LINE__, PTR_ERR(dent)); + return -1; + } + + if (debugfs_create_file("base", 0644, dent, 0, &dbg_base_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -1; + } + + if (debugfs_create_file("off", 0644, dent, 0, &dbg_off_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -1; + } + + if (debugfs_create_file("reg", 0644, dent, 0, &dbg_reg_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -1; + } + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL + dent = debugfs_create_dir("hdmi", NULL); + + if (IS_ERR(dent)) { + printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n", + __FILE__, __LINE__, PTR_ERR(dent)); + return PTR_ERR(dent); + } + + if (debugfs_create_file("off", 0644, dent, 0, &hdmi_off_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: 'off' fail\n", + __FILE__, __LINE__); + return -ENOENT; + } + + if (debugfs_create_file("reg", 0644, dent, 0, &hdmi_reg_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: 'reg' fail\n", + __FILE__, __LINE__); + return -ENOENT; + } +#endif + + return 0; +} diff --git a/drivers/video/msm/mdp_dma.c b/drivers/video/msm/mdp_dma.c new file mode 100644 index 0000000000000000000000000000000000000000..18da6f2b1ba629a9e3171fd3667597e8e9f204ba --- /dev/null +++ b/drivers/video/msm/mdp_dma.c @@ -0,0 +1,612 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mddihost.h" + +static uint32 mdp_last_dma2_update_width; +static uint32 mdp_last_dma2_update_height; +static uint32 mdp_curr_dma2_update_width; +static uint32 mdp_curr_dma2_update_height; + +ktime_t mdp_dma2_last_update_time = { 0 }; + +int mdp_lcd_rd_cnt_offset_slow = 20; +int mdp_lcd_rd_cnt_offset_fast = 20; +int mdp_vsync_usec_wait_line_too_short = 5; +uint32 mdp_dma2_update_time_in_usec; +uint32 mdp_total_vdopkts; + +extern u32 msm_fb_debug_enabled; +extern struct workqueue_struct *mdp_dma_wq; + +int vsync_start_y_adjust = 4; + +static void mdp_dma2_update_lcd(struct msm_fb_data_type *mfd) +{ + MDPIBUF *iBuf = &mfd->ibuf; + int mddi_dest = FALSE; + int cmd_mode = FALSE; + uint32 outBpp = iBuf->bpp; + uint32 dma2_cfg_reg; + uint8 *src; + uint32 mddi_ld_param; + uint16 mddi_vdo_packet_reg; +#ifndef CONFIG_FB_MSM_MDP303 + struct msm_fb_panel_data *pdata = + (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; +#endif + uint32 ystride = mfd->fbi->fix.line_length; + uint32 mddi_pkt_desc; + + dma2_cfg_reg = DMA_PACK_ALIGN_LSB | + DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; + +#ifdef CONFIG_FB_MSM_MDP22 + dma2_cfg_reg |= DMA_PACK_TIGHT; +#endif + +#ifdef CONFIG_FB_MSM_MDP30 + /* + * Software workaround: On 7x25/7x27, the MDP will not + * respond if dma_w is 1 pixel. Set the update width to + * 2 pixels and adjust the x offset if needed. + */ + if (iBuf->dma_w == 1) { + iBuf->dma_w = 2; + if (iBuf->dma_x == (iBuf->ibuf_width - 2)) + iBuf->dma_x--; + } +#endif + + if (mfd->fb_imgType == MDP_BGR_565) + dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; + else if (mfd->fb_imgType == MDP_RGBA_8888) + dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; + + if (outBpp == 4) { + dma2_cfg_reg |= DMA_IBUF_C3ALPHA_EN; + dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888; + } + + if (outBpp == 2) + dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565; + + mddi_ld_param = 0; + mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt; + + if ((mfd->panel_info.type == MDDI_PANEL) || + (mfd->panel_info.type == EXT_MDDI_PANEL)) { + dma2_cfg_reg |= DMA_OUT_SEL_MDDI; + mddi_dest = TRUE; + + if (mfd->panel_info.type == MDDI_PANEL) { + mdp_total_vdopkts++; + if (mfd->panel_info.pdest == DISPLAY_1) { + dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; + mddi_ld_param = 0; +#ifdef MDDI_HOST_WINDOW_WORKAROUND + mddi_window_adjust(mfd, iBuf->dma_x, + iBuf->dma_w - 1, iBuf->dma_y, + iBuf->dma_h - 1); +#endif + } else { + dma2_cfg_reg |= + DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY; + mddi_ld_param = 1; +#ifdef MDDI_HOST_WINDOW_WORKAROUND + mddi_window_adjust(mfd, iBuf->dma_x, + iBuf->dma_w - 1, iBuf->dma_y, + iBuf->dma_h - 1); +#endif + } + } else { + dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL; + mddi_ld_param = 2; + } +#ifdef CONFIG_FB_MSM_MDP303 + } else if (mfd->panel_info.type == MIPI_CMD_PANEL) { + cmd_mode = TRUE; + dma2_cfg_reg |= DMA_OUT_SEL_DSI_CMD; +#endif + } else { + if (mfd->panel_info.pdest == DISPLAY_1) { + dma2_cfg_reg |= DMA_AHBM_LCD_SEL_PRIMARY; + outp32(MDP_EBI2_LCD0, mfd->data_port_phys); + } else { + dma2_cfg_reg |= DMA_AHBM_LCD_SEL_SECONDARY; + outp32(MDP_EBI2_LCD1, mfd->data_port_phys); + } + } + + src = (uint8 *) iBuf->buf; + /* starting input address */ + src += iBuf->dma_x * outBpp + iBuf->dma_y * ystride; + + mdp_curr_dma2_update_width = iBuf->dma_w; + mdp_curr_dma2_update_height = iBuf->dma_h; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + +#ifdef CONFIG_FB_MSM_MDP22 + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0184, + (iBuf->dma_h << 16 | iBuf->dma_w)); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0188, src); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x018C, ystride); +#else + if (cmd_mode) + MDP_OUTP(MDP_BASE + 0x90004, + (mfd->panel_info.yres << 16 | mfd->panel_info.xres)); + else + MDP_OUTP(MDP_BASE + 0x90004, (iBuf->dma_h << 16 | iBuf->dma_w)); + + MDP_OUTP(MDP_BASE + 0x90008, src); + MDP_OUTP(MDP_BASE + 0x9000c, ystride); +#endif + + if (mfd->panel_info.bpp == 18) { + mddi_pkt_desc = MDDI_VDO_PACKET_DESC; + dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + } else if (mfd->panel_info.bpp == 24) { + mddi_pkt_desc = MDDI_VDO_PACKET_DESC_24; + dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 24BPP */ + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + } else { + mddi_pkt_desc = MDDI_VDO_PACKET_DESC_16; + dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + } + +#ifndef CONFIG_FB_MSM_MDP303 + + if (mddi_dest) { +#ifdef CONFIG_FB_MSM_MDP22 + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0194, + (iBuf->dma_y << 16) | iBuf->dma_x); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0, mddi_ld_param); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4, + (mddi_pkt_desc << 16) | mddi_vdo_packet_reg); +#else + MDP_OUTP(MDP_BASE + 0x90010, (iBuf->dma_y << 16) | iBuf->dma_x); + MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param); + MDP_OUTP(MDP_BASE + 0x00094, + (mddi_pkt_desc << 16) | mddi_vdo_packet_reg); +#endif + } else { + /* setting EBI2 LCDC write window */ + pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w, + iBuf->dma_h); + } +#else + if (mfd->panel_info.type == MIPI_CMD_PANEL) { + /* dma_p = 0, dma_s = 1 */ + MDP_OUTP(MDP_BASE + 0xF1000, 0x10); + /* enable dsi trigger on dma_p */ + MDP_OUTP(MDP_BASE + 0xF1004, 0x01); + } +#endif + + /* dma2 config register */ +#ifdef MDP_HW_VSYNC + MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg); + + if ((mfd->use_mdp_vsync) && + (mfd->ibuf.vsync_enable) && (mfd->panel_info.lcd.vsync_enable)) { + uint32 start_y; + + if (vsync_start_y_adjust <= iBuf->dma_y) + start_y = iBuf->dma_y - vsync_start_y_adjust; + else + start_y = + (mfd->total_lcd_lines - 1) - (vsync_start_y_adjust - + iBuf->dma_y); + + /* + * MDP VSYNC clock must be On by now so, we don't have to + * re-enable it + */ + MDP_OUTP(MDP_BASE + 0x210, start_y); + MDP_OUTP(MDP_BASE + 0x20c, 1); /* enable prim vsync */ + } else { + MDP_OUTP(MDP_BASE + 0x20c, 0); /* disable prim vsync */ + } +#else +#ifdef CONFIG_FB_MSM_MDP22 + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0180, dma2_cfg_reg); +#else + MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg); +#endif +#endif /* MDP_HW_VSYNC */ + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +static ktime_t vt = { 0 }; +int mdp_usec_diff_threshold = 100; +int mdp_expected_usec_wait; + +enum hrtimer_restart mdp_dma2_vsync_hrtimer_handler(struct hrtimer *ht) +{ + struct msm_fb_data_type *mfd = NULL; + + mfd = container_of(ht, struct msm_fb_data_type, dma_hrtimer); + + mdp_pipe_kickoff(MDP_DMA2_TERM, mfd); + + if (msm_fb_debug_enabled) { + ktime_t t; + int usec_diff; + int actual_wait; + + t = ktime_get_real(); + + actual_wait = ktime_to_us(ktime_sub(t, vt)); + usec_diff = actual_wait - mdp_expected_usec_wait; + + if ((mdp_usec_diff_threshold < usec_diff) || (usec_diff < 0)) + MSM_FB_DEBUG + ("HRT Diff = %d usec Exp=%d usec Act=%d usec\n", + usec_diff, mdp_expected_usec_wait, actual_wait); + } + + return HRTIMER_NORESTART; +} + + +#ifdef CONFIG_FB_MSM_MDP303 +static int busy_wait_cnt; + +void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + +#ifdef DSI_CLK_CTRL + mod_timer(&dsi_clock_timer, jiffies + HZ); /* one second */ +#endif + + spin_lock_irqsave(&mdp_spin_lock, flag); +#ifdef DSI_CLK_CTRL + if (mipi_dsi_clk_on == 0) + mipi_dsi_turn_on_clks(); +#endif + + if (mfd->dma->busy == TRUE) { + if (busy_wait_cnt == 0) + INIT_COMPLETION(mfd->dma->comp); + busy_wait_cnt++; + need_wait++; + } + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + if (need_wait) { + /* wait until DMA finishes the current job */ + wait_for_completion(&mfd->dma->comp); + } +} +#endif + +static void mdp_dma_schedule(struct msm_fb_data_type *mfd, uint32 term) +{ + /* + * dma2 configure VSYNC block + * vsync supported on Primary LCD only for now + */ + int32 mdp_lcd_rd_cnt; + uint32 usec_wait_time; + uint32 start_y; + + /* + * ToDo: if we can move HRT timer callback to workqueue, we can + * move DMA2 power on under mdp_pipe_kickoff(). + * This will save a power for hrt time wait. + * However if the latency for context switch (hrt irq -> workqueue) + * is too big, we will miss the vsync timing. + */ + if (term == MDP_DMA2_TERM) + mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + mdp_dma2_update_time_in_usec = ktime_to_us(mdp_dma2_last_update_time); + + if ((!mfd->ibuf.vsync_enable) || (!mfd->panel_info.lcd.vsync_enable) + || (mfd->use_mdp_vsync)) { + mdp_pipe_kickoff(term, mfd); + return; + } + /* SW vsync logic starts here */ + + /* get current rd counter */ + mdp_lcd_rd_cnt = mdp_get_lcd_line_counter(mfd); + if (mdp_dma2_update_time_in_usec != 0) { + uint32 num, den; + + /* + * roi width boundary calculation to know the size of pixel + * width that MDP can send faster or slower than LCD read + * pointer + */ + + num = mdp_last_dma2_update_width * mdp_last_dma2_update_height; + den = + (((mfd->panel_info.lcd.refx100 * mfd->total_lcd_lines) / + 1000) * (mdp_dma2_update_time_in_usec / 100)) / 1000; + + if (den == 0) + mfd->vsync_width_boundary[mdp_last_dma2_update_width] = + mfd->panel_info.xres + 1; + else + mfd->vsync_width_boundary[mdp_last_dma2_update_width] = + (int)(num / den); + } + + if (mfd->vsync_width_boundary[mdp_last_dma2_update_width] > + mdp_curr_dma2_update_width) { + /* MDP wrp is faster than LCD rdp */ + mdp_lcd_rd_cnt += mdp_lcd_rd_cnt_offset_fast; + } else { + /* MDP wrp is slower than LCD rdp */ + mdp_lcd_rd_cnt -= mdp_lcd_rd_cnt_offset_slow; + } + + if (mdp_lcd_rd_cnt < 0) + mdp_lcd_rd_cnt = mfd->total_lcd_lines + mdp_lcd_rd_cnt; + else if (mdp_lcd_rd_cnt > mfd->total_lcd_lines) + mdp_lcd_rd_cnt = mdp_lcd_rd_cnt - mfd->total_lcd_lines - 1; + + /* get wrt pointer position */ + start_y = mfd->ibuf.dma_y; + + /* measure line difference between start_y and rd counter */ + if (start_y > mdp_lcd_rd_cnt) { + /* + * *100 for lcd_ref_hzx100 was already multiplied by 100 + * *1000000 is for usec conversion + */ + + if ((start_y - mdp_lcd_rd_cnt) <= + mdp_vsync_usec_wait_line_too_short) + usec_wait_time = 0; + else + usec_wait_time = + ((start_y - + mdp_lcd_rd_cnt) * 1000000) / + ((mfd->total_lcd_lines * + mfd->panel_info.lcd.refx100) / 100); + } else { + if ((start_y + (mfd->total_lcd_lines - mdp_lcd_rd_cnt)) <= + mdp_vsync_usec_wait_line_too_short) + usec_wait_time = 0; + else + usec_wait_time = + ((start_y + + (mfd->total_lcd_lines - + mdp_lcd_rd_cnt)) * 1000000) / + ((mfd->total_lcd_lines * + mfd->panel_info.lcd.refx100) / 100); + } + + mdp_last_dma2_update_width = mdp_curr_dma2_update_width; + mdp_last_dma2_update_height = mdp_curr_dma2_update_height; + + if (usec_wait_time == 0) { + mdp_pipe_kickoff(term, mfd); + } else { + ktime_t wait_time; + + wait_time = ns_to_ktime(usec_wait_time * 1000); + + if (msm_fb_debug_enabled) { + vt = ktime_get_real(); + mdp_expected_usec_wait = usec_wait_time; + } + hrtimer_start(&mfd->dma_hrtimer, wait_time, HRTIMER_MODE_REL); + } +} + +#ifdef MDDI_HOST_WINDOW_WORKAROUND +static void mdp_dma2_update_sub(struct msm_fb_data_type *mfd); +void mdp_dma2_update(struct msm_fb_data_type *mfd) +{ + MDPIBUF *iBuf; + uint32 upper_height; + + if (mfd->panel.type == EXT_MDDI_PANEL) { + mdp_dma2_update_sub(mfd); + return; + } + + iBuf = &mfd->ibuf; + + upper_height = + (uint32) mddi_assign_pkt_height((uint16) iBuf->dma_w, + (uint16) iBuf->dma_h, 18); + + if (upper_height >= iBuf->dma_h) { + mdp_dma2_update_sub(mfd); + } else { + uint32 lower_height; + + /* sending the upper region first */ + lower_height = iBuf->dma_h - upper_height; + iBuf->dma_h = upper_height; + mdp_dma2_update_sub(mfd); + + /* sending the lower region second */ + iBuf->dma_h = lower_height; + iBuf->dma_y += lower_height; + iBuf->vsync_enable = FALSE; + mdp_dma2_update_sub(mfd); + } +} + +static void mdp_dma2_update_sub(struct msm_fb_data_type *mfd) +#else +void mdp_dma2_update(struct msm_fb_data_type *mfd) +#endif +{ + unsigned long flag; + + down(&mfd->dma->mutex); + if ((mfd) && (!mfd->dma->busy) && (mfd->panel_power_on)) { + down(&mfd->sem); + mfd->ibuf_flushed = TRUE; + mdp_dma2_update_lcd(mfd); + + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(MDP_DMA2_TERM); + mfd->dma->busy = TRUE; + INIT_COMPLETION(mfd->dma->comp); + + spin_unlock_irqrestore(&mdp_spin_lock, flag); + /* schedule DMA to start */ + mdp_dma_schedule(mfd, MDP_DMA2_TERM); + up(&mfd->sem); + + /* wait until DMA finishes the current job */ + wait_for_completion_killable(&mfd->dma->comp); + mdp_disable_irq(MDP_DMA2_TERM); + + /* signal if pan function is waiting for the update completion */ + if (mfd->pan_waiting) { + mfd->pan_waiting = FALSE; + complete(&mfd->pan_comp); + } + } + up(&mfd->dma->mutex); +} + +void mdp_lcd_update_workqueue_handler(struct work_struct *work) +{ + struct msm_fb_data_type *mfd = NULL; + + mfd = container_of(work, struct msm_fb_data_type, dma_update_worker); + if (mfd) + mfd->dma_fnc(mfd); +} + +void mdp_set_dma_pan_info(struct fb_info *info, struct mdp_dirty_region *dirty, + boolean sync) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_info *fbi = mfd->fbi; + MDPIBUF *iBuf; + int bpp = info->var.bits_per_pixel / 8; + + down(&mfd->sem); + + iBuf = &mfd->ibuf; + + if (mfd->display_iova) + iBuf->buf = (uint8 *)mfd->display_iova; + else + iBuf->buf = (uint8 *) info->fix.smem_start; + + iBuf->buf += calc_fb_offset(mfd, fbi, bpp); + + iBuf->ibuf_width = info->var.xres_virtual; + iBuf->bpp = bpp; + + iBuf->vsync_enable = sync; + + if (dirty) { + /* + * ToDo: dirty region check inside var.xoffset+xres + * <-> var.yoffset+yres + */ + iBuf->dma_x = dirty->xoffset % info->var.xres; + iBuf->dma_y = dirty->yoffset % info->var.yres; + iBuf->dma_w = dirty->width; + iBuf->dma_h = dirty->height; + } else { + iBuf->dma_x = 0; + iBuf->dma_y = 0; + iBuf->dma_w = info->var.xres; + iBuf->dma_h = info->var.yres; + } + mfd->ibuf_flushed = FALSE; + up(&mfd->sem); +} + +void mdp_dma_pan_update(struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + MDPIBUF *iBuf; + + iBuf = &mfd->ibuf; + + if (mfd->sw_currently_refreshing) { + /* we need to wait for the pending update */ + mfd->pan_waiting = TRUE; + if (!mfd->ibuf_flushed) { + wait_for_completion_killable(&mfd->pan_comp); + } + /* waiting for this update to complete */ + mfd->pan_waiting = TRUE; + wait_for_completion_killable(&mfd->pan_comp); + } else + mfd->dma_fnc(mfd); +} + +void mdp_refresh_screen(unsigned long data) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; + + if ((mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) { + init_timer(&mfd->refresh_timer); + mfd->refresh_timer.function = mdp_refresh_screen; + mfd->refresh_timer.data = data; + + if (mfd->dma->busy) + /* come back in 1 msec */ + mfd->refresh_timer.expires = jiffies + (HZ / 1000); + else + mfd->refresh_timer.expires = + jiffies + mfd->refresh_timer_duration; + + add_timer(&mfd->refresh_timer); + + if (!mfd->dma->busy) { + if (!queue_work(mdp_dma_wq, &mfd->dma_update_worker)) { + MSM_FB_DEBUG("mdp_dma: can't queue_work! -> \ + MDP/MDDI/LCD clock speed needs to be increased\n"); + } + } + } else { + if (!mfd->hw_refresh) + complete(&mfd->refresher_comp); + } +} diff --git a/drivers/video/msm/mdp_dma_dsi_video.c b/drivers/video/msm/mdp_dma_dsi_video.c new file mode 100644 index 0000000000000000000000000000000000000000..94941a7fbfd018160068f52ab55ae955caf3c722 --- /dev/null +++ b/drivers/video/msm/mdp_dma_dsi_video.c @@ -0,0 +1,275 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + +#define DSI_VIDEO_BASE 0xF0000 +#define DMA_P_BASE 0x90000 + +static int first_pixel_start_x; +static int first_pixel_start_y; + +int mdp_dsi_video_on(struct platform_device *pdev) +{ + int dsi_width; + int dsi_height; + int dsi_bpp; + int dsi_border_clr; + int dsi_underflow_clr; + int dsi_hsync_skew; + + int hsync_period; + int hsync_ctrl; + int vsync_period; + int display_hctl; + int display_v_start; + int display_v_end; + int active_hctl; + int active_h_start; + int active_h_end; + int active_v_start; + int active_v_end; + int ctrl_polarity; + int h_back_porch; + int h_front_porch; + int v_back_porch; + int v_front_porch; + int hsync_pulse_width; + int vsync_pulse_width; + int hsync_polarity; + int vsync_polarity; + int data_en_polarity; + int hsync_start_x; + int hsync_end_x; + uint8 *buf; + uint32 dma2_cfg_reg; + + int bpp; + struct fb_info *fbi; + struct fb_var_screeninfo *var; + struct msm_fb_data_type *mfd; + int ret; + uint32_t mask, curr; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + fbi = mfd->fbi; + var = &fbi->var; + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + + buf += calc_fb_offset(mfd, fbi, bpp); + + dma2_cfg_reg = DMA_PACK_ALIGN_LSB | DMA_OUT_SEL_DSI_VIDEO; + + if (mfd->fb_imgType == MDP_BGR_565) + dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; + else if (mfd->fb_imgType == MDP_RGBA_8888) + dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; + + if (bpp == 2) + dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565; + else if (bpp == 3) + dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB888; + else + dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888; + + switch (mfd->panel_info.bpp) { + case 24: + dma2_cfg_reg |= DMA_DSTC0G_8BITS | + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + break; + case 18: + dma2_cfg_reg |= DMA_DSTC0G_6BITS | + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + break; + case 16: + dma2_cfg_reg |= DMA_DSTC0G_6BITS | + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + break; + default: + printk(KERN_ERR "mdp lcdc can't support format %d bpp!\n", + mfd->panel_info.bpp); + return -ENODEV; + } + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* starting address */ + MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x8, (uint32) buf); + + /* active window width and height */ + MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x4, ((fbi->var.yres) << 16) | + (fbi->var.xres)); + + /* buffer ystride */ + MDP_OUTP(MDP_BASE + DMA_P_BASE + 0xc, fbi->fix.line_length); + + /* x/y coordinate = always 0 for lcdc */ + MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x10, 0); + + /* dma config */ + curr = inpdw(MDP_BASE + DMA_P_BASE); + mask = 0x0FFFFFFF; + dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask); + MDP_OUTP(MDP_BASE + DMA_P_BASE, dma2_cfg_reg); + + /* + * DSI timing setting + */ + h_back_porch = var->left_margin; + h_front_porch = var->right_margin; + v_back_porch = var->upper_margin; + v_front_porch = var->lower_margin; + hsync_pulse_width = var->hsync_len; + vsync_pulse_width = var->vsync_len; + dsi_border_clr = mfd->panel_info.lcdc.border_clr; + dsi_underflow_clr = mfd->panel_info.lcdc.underflow_clr; + dsi_hsync_skew = mfd->panel_info.lcdc.hsync_skew; + dsi_width = mfd->panel_info.xres; + dsi_height = mfd->panel_info.yres; + dsi_bpp = mfd->panel_info.bpp; + hsync_period = h_back_porch + dsi_width + h_front_porch + 1; + hsync_ctrl = (hsync_period << 16) | hsync_pulse_width; + hsync_start_x = h_back_porch; + hsync_end_x = dsi_width + h_back_porch - 1; + display_hctl = (hsync_end_x << 16) | hsync_start_x; + + vsync_period = + (v_back_porch + dsi_height + v_front_porch + 1) * hsync_period; + display_v_start = v_back_porch * hsync_period + dsi_hsync_skew; + display_v_end = (dsi_height + v_back_porch) * hsync_period; + + active_h_start = hsync_start_x + first_pixel_start_x; + active_h_end = active_h_start + var->xres - 1; + active_hctl = ACTIVE_START_X_EN | + (active_h_end << 16) | active_h_start; + + active_v_start = display_v_start + + first_pixel_start_y * hsync_period; + active_v_end = active_v_start + (var->yres) * hsync_period - 1; + active_v_start |= ACTIVE_START_Y_EN; + + dsi_underflow_clr |= 0x80000000; /* enable recovery */ + hsync_polarity = 0; + vsync_polarity = 0; + data_en_polarity = 0; + + ctrl_polarity = (data_en_polarity << 2) | + (vsync_polarity << 1) | (hsync_polarity); + + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x4, hsync_ctrl); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x8, vsync_period); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0xc, vsync_pulse_width); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x10, display_hctl); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x14, display_v_start); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x18, display_v_end); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x1c, active_hctl); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x20, active_v_start); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x24, active_v_end); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x28, dsi_border_clr); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x2c, dsi_underflow_clr); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x30, dsi_hsync_skew); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE + 0x38, ctrl_polarity); + + ret = panel_next_on(pdev); + if (ret == 0) { + /* enable DSI block */ + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1); + /*Turning on DMA_P block*/ + mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + } + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return ret; +} + +int mdp_dsi_video_off(struct platform_device *pdev) +{ + int ret = 0; + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0); + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + /*Turning off DMA_P block*/ + mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + ret = panel_next_off(pdev); + /* delay to make sure the last frame finishes */ + msleep(20); + + return ret; +} + +void mdp_dsi_video_update(struct msm_fb_data_type *mfd) +{ + struct fb_info *fbi = mfd->fbi; + uint8 *buf; + int bpp; + unsigned long flag; + int irq_block = MDP_DMA2_TERM; + + if (!mfd->panel_power_on) + return; + + down(&mfd->dma->mutex); + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + + buf += calc_fb_offset(mfd, fbi, bpp); + + /* no need to power on cmd block since it's dsi mode */ + /* starting address */ + MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x8, (uint32) buf); + /* enable irq */ + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(irq_block); + INIT_COMPLETION(mfd->dma->comp); + mfd->dma->waiting = TRUE; + + outp32(MDP_INTR_CLEAR, LCDC_FRAME_START); + mdp_intr_mask |= LCDC_FRAME_START; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + + spin_unlock_irqrestore(&mdp_spin_lock, flag); + wait_for_completion_killable(&mfd->dma->comp); + mdp_disable_irq(irq_block); + up(&mfd->dma->mutex); +} diff --git a/drivers/video/msm/mdp_dma_lcdc.c b/drivers/video/msm/mdp_dma_lcdc.c new file mode 100644 index 0000000000000000000000000000000000000000..3b68e4000af6ba818a6fecd6258a73ff4d4b4567 --- /dev/null +++ b/drivers/video/msm/mdp_dma_lcdc.c @@ -0,0 +1,377 @@ +/* Copyright (c) 2008-2009, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + +#ifdef CONFIG_FB_MSM_MDP40 +#define LCDC_BASE 0xC0000 +#define DTV_BASE 0xD0000 +#define DMA_E_BASE 0xB0000 +#else +#define LCDC_BASE 0xE0000 +#endif + +#define DMA_P_BASE 0x90000 + +extern spinlock_t mdp_spin_lock; +#ifndef CONFIG_FB_MSM_MDP40 +extern uint32 mdp_intr_mask; +#endif + +int first_pixel_start_x; +int first_pixel_start_y; + +int mdp_lcdc_on(struct platform_device *pdev) +{ + int lcdc_width; + int lcdc_height; + int lcdc_bpp; + int lcdc_border_clr; + int lcdc_underflow_clr; + int lcdc_hsync_skew; + + int hsync_period; + int hsync_ctrl; + int vsync_period; + int display_hctl; + int display_v_start; + int display_v_end; + int active_hctl; + int active_h_start; + int active_h_end; + int active_v_start; + int active_v_end; + int ctrl_polarity; + int h_back_porch; + int h_front_porch; + int v_back_porch; + int v_front_porch; + int hsync_pulse_width; + int vsync_pulse_width; + int hsync_polarity; + int vsync_polarity; + int data_en_polarity; + int hsync_start_x; + int hsync_end_x; + uint8 *buf; + int bpp; + uint32 dma2_cfg_reg; + struct fb_info *fbi; + struct fb_var_screeninfo *var; + struct msm_fb_data_type *mfd; + uint32 dma_base; + uint32 timer_base = LCDC_BASE; + uint32 block = MDP_DMA2_BLOCK; + int ret; + uint32_t mask, curr; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + fbi = mfd->fbi; + var = &fbi->var; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + + buf += calc_fb_offset(mfd, fbi, bpp); + + dma2_cfg_reg = DMA_PACK_ALIGN_LSB | DMA_OUT_SEL_LCDC; + + if (mfd->fb_imgType == MDP_BGR_565) + dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; + else if (mfd->fb_imgType == MDP_RGBA_8888) + dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; + + if (bpp == 2) + dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565; + else if (bpp == 3) + dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB888; + else + dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888; + + switch (mfd->panel_info.bpp) { + case 24: + dma2_cfg_reg |= DMA_DSTC0G_8BITS | + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + break; + + case 18: + dma2_cfg_reg |= DMA_DSTC0G_6BITS | + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + break; + + case 16: + dma2_cfg_reg |= DMA_DSTC0G_6BITS | + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + break; + + default: + printk(KERN_ERR "mdp lcdc can't support format %d bpp!\n", + mfd->panel_info.bpp); + return -ENODEV; + } + + /* DMA register config */ + + dma_base = DMA_P_BASE; + +#ifdef CONFIG_FB_MSM_MDP40 + if (mfd->panel.type == HDMI_PANEL) + dma_base = DMA_E_BASE; +#endif + + /* starting address */ + MDP_OUTP(MDP_BASE + dma_base + 0x8, (uint32) buf); + /* active window width and height */ + MDP_OUTP(MDP_BASE + dma_base + 0x4, ((fbi->var.yres) << 16) | + (fbi->var.xres)); + /* buffer ystride */ + MDP_OUTP(MDP_BASE + dma_base + 0xc, fbi->fix.line_length); + /* x/y coordinate = always 0 for lcdc */ + MDP_OUTP(MDP_BASE + dma_base + 0x10, 0); + /* dma config */ + curr = inpdw(MDP_BASE + DMA_P_BASE); + mask = 0x0FFFFFFF; + dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask); + MDP_OUTP(MDP_BASE + dma_base, dma2_cfg_reg); + + /* + * LCDC timing setting + */ + h_back_porch = var->left_margin; + h_front_porch = var->right_margin; + v_back_porch = var->upper_margin; + v_front_porch = var->lower_margin; + hsync_pulse_width = var->hsync_len; + vsync_pulse_width = var->vsync_len; + lcdc_border_clr = mfd->panel_info.lcdc.border_clr; + lcdc_underflow_clr = mfd->panel_info.lcdc.underflow_clr; + lcdc_hsync_skew = mfd->panel_info.lcdc.hsync_skew; + + lcdc_width = mfd->panel_info.xres; + lcdc_height = mfd->panel_info.yres; + lcdc_bpp = mfd->panel_info.bpp; + + hsync_period = + hsync_pulse_width + h_back_porch + lcdc_width + h_front_porch; + hsync_ctrl = (hsync_period << 16) | hsync_pulse_width; + hsync_start_x = hsync_pulse_width + h_back_porch; + hsync_end_x = hsync_period - h_front_porch - 1; + display_hctl = (hsync_end_x << 16) | hsync_start_x; + + vsync_period = + (vsync_pulse_width + v_back_porch + lcdc_height + + v_front_porch) * hsync_period; + display_v_start = + (vsync_pulse_width + v_back_porch) * hsync_period + lcdc_hsync_skew; + display_v_end = + vsync_period - (v_front_porch * hsync_period) + lcdc_hsync_skew - 1; + + if (lcdc_width != var->xres) { + active_h_start = hsync_start_x + first_pixel_start_x; + active_h_end = active_h_start + var->xres - 1; + active_hctl = + ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start; + } else { + active_hctl = 0; + } + + if (lcdc_height != var->yres) { + active_v_start = + display_v_start + first_pixel_start_y * hsync_period; + active_v_end = active_v_start + (var->yres) * hsync_period - 1; + active_v_start |= ACTIVE_START_Y_EN; + } else { + active_v_start = 0; + active_v_end = 0; + } + + +#ifdef CONFIG_FB_MSM_MDP40 + if (mfd->panel.type == HDMI_PANEL) { + block = MDP_DMA_E_BLOCK; + timer_base = DTV_BASE; + hsync_polarity = 0; + vsync_polarity = 0; + } else { + hsync_polarity = 1; + vsync_polarity = 1; + } + + lcdc_underflow_clr |= 0x80000000; /* enable recovery */ +#else + hsync_polarity = 0; + vsync_polarity = 0; +#endif + data_en_polarity = 0; + + ctrl_polarity = + (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity); + + MDP_OUTP(MDP_BASE + timer_base + 0x4, hsync_ctrl); + MDP_OUTP(MDP_BASE + timer_base + 0x8, vsync_period); + MDP_OUTP(MDP_BASE + timer_base + 0xc, vsync_pulse_width * hsync_period); + if (timer_base == LCDC_BASE) { + MDP_OUTP(MDP_BASE + timer_base + 0x10, display_hctl); + MDP_OUTP(MDP_BASE + timer_base + 0x14, display_v_start); + MDP_OUTP(MDP_BASE + timer_base + 0x18, display_v_end); + MDP_OUTP(MDP_BASE + timer_base + 0x28, lcdc_border_clr); + MDP_OUTP(MDP_BASE + timer_base + 0x2c, lcdc_underflow_clr); + MDP_OUTP(MDP_BASE + timer_base + 0x30, lcdc_hsync_skew); + MDP_OUTP(MDP_BASE + timer_base + 0x38, ctrl_polarity); + MDP_OUTP(MDP_BASE + timer_base + 0x1c, active_hctl); + MDP_OUTP(MDP_BASE + timer_base + 0x20, active_v_start); + MDP_OUTP(MDP_BASE + timer_base + 0x24, active_v_end); + } else { + MDP_OUTP(MDP_BASE + timer_base + 0x18, display_hctl); + MDP_OUTP(MDP_BASE + timer_base + 0x1c, display_v_start); + MDP_OUTP(MDP_BASE + timer_base + 0x20, display_v_end); + MDP_OUTP(MDP_BASE + timer_base + 0x40, lcdc_border_clr); + MDP_OUTP(MDP_BASE + timer_base + 0x44, lcdc_underflow_clr); + MDP_OUTP(MDP_BASE + timer_base + 0x48, lcdc_hsync_skew); + MDP_OUTP(MDP_BASE + timer_base + 0x50, ctrl_polarity); + MDP_OUTP(MDP_BASE + timer_base + 0x2c, active_hctl); + MDP_OUTP(MDP_BASE + timer_base + 0x30, active_v_start); + MDP_OUTP(MDP_BASE + timer_base + 0x38, active_v_end); + } + + ret = panel_next_on(pdev); + if (ret == 0) { + /* enable LCDC block */ + MDP_OUTP(MDP_BASE + timer_base, 1); + mdp_pipe_ctrl(block, MDP_BLOCK_POWER_ON, FALSE); + } + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + return ret; +} + +int mdp_lcdc_off(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + uint32 timer_base = LCDC_BASE; + uint32 block = MDP_DMA2_BLOCK; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + +#ifdef CONFIG_FB_MSM_MDP40 + if (mfd->panel.type == HDMI_PANEL) { + block = MDP_DMA_E_BLOCK; + timer_base = DTV_BASE; + } +#endif + + down(&mfd->dma->mutex); + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + MDP_OUTP(MDP_BASE + timer_base, 0); + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_pipe_ctrl(block, MDP_BLOCK_POWER_OFF, FALSE); + + ret = panel_next_off(pdev); + up(&mfd->dma->mutex); + + /* delay to make sure the last frame finishes */ + msleep(16); + + return ret; +} + +void mdp_lcdc_update(struct msm_fb_data_type *mfd) +{ + struct fb_info *fbi = mfd->fbi; + uint8 *buf; + int bpp; + unsigned long flag; + uint32 dma_base; + int irq_block = MDP_DMA2_TERM; +#ifdef CONFIG_FB_MSM_MDP40 + int intr = INTR_DMA_P_DONE; +#endif + + if (!mfd->panel_power_on) + return; + + down(&mfd->dma->mutex); + /* no need to power on cmd block since it's lcdc mode */ + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + + buf += calc_fb_offset(mfd, fbi, bpp); + + dma_base = DMA_P_BASE; + +#ifdef CONFIG_FB_MSM_MDP40 + if (mfd->panel.type == HDMI_PANEL) { + intr = INTR_DMA_E_DONE; + irq_block = MDP_DMA_E_TERM; + dma_base = DMA_E_BASE; + } +#endif + + /* starting address */ + MDP_OUTP(MDP_BASE + dma_base + 0x8, (uint32) buf); + + /* enable LCDC irq */ + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(irq_block); + INIT_COMPLETION(mfd->dma->comp); + mfd->dma->waiting = TRUE; +#ifdef CONFIG_FB_MSM_MDP40 + outp32(MDP_INTR_CLEAR, intr); + mdp_intr_mask |= intr; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); +#else + outp32(MDP_INTR_CLEAR, LCDC_FRAME_START); + mdp_intr_mask |= LCDC_FRAME_START; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); +#endif + spin_unlock_irqrestore(&mdp_spin_lock, flag); + wait_for_completion_killable(&mfd->dma->comp); + mdp_disable_irq(irq_block); + up(&mfd->dma->mutex); +} diff --git a/drivers/video/msm/mdp_dma_s.c b/drivers/video/msm/mdp_dma_s.c new file mode 100644 index 0000000000000000000000000000000000000000..c5a2402c3651d239e2eec6c60570319ebc33377e --- /dev/null +++ b/drivers/video/msm/mdp_dma_s.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "mdp.h" +#include "msm_fb.h" + +static void mdp_dma_s_update_lcd(struct msm_fb_data_type *mfd) +{ + MDPIBUF *iBuf = &mfd->ibuf; + int mddi_dest = FALSE; + uint32 outBpp = iBuf->bpp; + uint32 dma_s_cfg_reg; + uint8 *src; + struct msm_fb_panel_data *pdata = + (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + dma_s_cfg_reg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB | + DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; + + if (mfd->fb_imgType == MDP_BGR_565) + dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dma_s_cfg_reg |= DMA_PACK_PATTERN_RGB; + + if (outBpp == 4) + dma_s_cfg_reg |= DMA_IBUF_C3ALPHA_EN; + + if (outBpp == 2) + dma_s_cfg_reg |= DMA_IBUF_FORMAT_RGB565; + + if (mfd->panel_info.pdest != DISPLAY_2) { + printk(KERN_ERR "error: non-secondary type through dma_s!\n"); + return; + } + + if (mfd->panel_info.type == MDDI_PANEL || + mfd->panel_info.type == EXT_MDDI_PANEL) { + dma_s_cfg_reg |= DMA_OUT_SEL_MDDI; + mddi_dest = TRUE; + } else { + dma_s_cfg_reg |= DMA_AHBM_LCD_SEL_SECONDARY; + outp32(MDP_EBI2_LCD1, mfd->data_port_phys); + } + + src = (uint8 *) iBuf->buf; + /* starting input address */ + src += (iBuf->dma_x + iBuf->dma_y * iBuf->ibuf_width) * outBpp; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* PIXELSIZE */ + if (mfd->panel_info.type == MDDI_PANEL) { + MDP_OUTP(MDP_BASE + 0xa0004, + (iBuf->dma_h << 16 | iBuf->dma_w)); + MDP_OUTP(MDP_BASE + 0xa0008, src); /* ibuf address */ + MDP_OUTP(MDP_BASE + 0xa000c, + iBuf->ibuf_width * outBpp);/* ystride */ + } else { + MDP_OUTP(MDP_BASE + 0xb0004, + (iBuf->dma_h << 16 | iBuf->dma_w)); + MDP_OUTP(MDP_BASE + 0xb0008, src); /* ibuf address */ + MDP_OUTP(MDP_BASE + 0xb000c, + iBuf->ibuf_width * outBpp);/* ystride */ + } + + if (mfd->panel_info.bpp == 18) { + dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + } else { + dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + } + + if (mddi_dest) { + if (mfd->panel_info.type == MDDI_PANEL) { + MDP_OUTP(MDP_BASE + 0xa0010, + (iBuf->dma_y << 16) | iBuf->dma_x); + MDP_OUTP(MDP_BASE + 0x00090, 1); + } else { + MDP_OUTP(MDP_BASE + 0xb0010, + (iBuf->dma_y << 16) | iBuf->dma_x); + MDP_OUTP(MDP_BASE + 0x00090, 2); + } + MDP_OUTP(MDP_BASE + 0x00094, + (MDDI_VDO_PACKET_DESC << 16) | + mfd->panel_info.mddi.vdopkt); + } else { + /* setting LCDC write window */ + pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w, + iBuf->dma_h); + } + + if (mfd->panel_info.type == MDDI_PANEL) + MDP_OUTP(MDP_BASE + 0xa0000, dma_s_cfg_reg); + else + MDP_OUTP(MDP_BASE + 0xb0000, dma_s_cfg_reg); + + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + if (mfd->panel_info.type == MDDI_PANEL) + mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd); + else + mdp_pipe_kickoff(MDP_DMA_E_TERM, mfd); + +} + +void mdp_dma_s_update(struct msm_fb_data_type *mfd) +{ + down(&mfd->dma->mutex); + if ((mfd) && (!mfd->dma->busy) && (mfd->panel_power_on)) { + down(&mfd->sem); + mdp_enable_irq(MDP_DMA_S_TERM); + if (mfd->panel_info.type == MDDI_PANEL) + mdp_enable_irq(MDP_DMA_S_TERM); + else + mdp_enable_irq(MDP_DMA_E_TERM); + mfd->dma->busy = TRUE; + INIT_COMPLETION(mfd->dma->comp); + mfd->ibuf_flushed = TRUE; + mdp_dma_s_update_lcd(mfd); + up(&mfd->sem); + + /* wait until DMA finishes the current job */ + wait_for_completion_killable(&mfd->dma->comp); + if (mfd->panel_info.type == MDDI_PANEL) + mdp_disable_irq(MDP_DMA_S_TERM); + else + mdp_disable_irq(MDP_DMA_E_TERM); + + /* signal if pan function is waiting for the update completion */ + if (mfd->pan_waiting) { + mfd->pan_waiting = FALSE; + complete(&mfd->pan_comp); + } + } + up(&mfd->dma->mutex); +} diff --git a/drivers/video/msm/mdp_dma_tv.c b/drivers/video/msm/mdp_dma_tv.c new file mode 100644 index 0000000000000000000000000000000000000000..ee2e323f5e29a32c91fc467e92eaae8f37526ed1 --- /dev/null +++ b/drivers/video/msm/mdp_dma_tv.c @@ -0,0 +1,140 @@ +/* Copyright (c) 2008-2009, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "mdp.h" +#include "msm_fb.h" + +extern spinlock_t mdp_spin_lock; +extern uint32 mdp_intr_mask; + +int mdp_dma3_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct fb_info *fbi; + uint8 *buf; + int bpp; + int ret = 0; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + fbi = mfd->fbi; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + + buf += calc_fb_offset(mfd, fbi, bpp); + + /* starting address[31..8] of Video frame buffer is CS0 */ + MDP_OUTP(MDP_BASE + 0xC0008, (uint32) buf >> 3); + + mdp_pipe_ctrl(MDP_DMA3_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + MDP_OUTP(MDP_BASE + 0xC0004, 0x4c60674); /* flicker filter enabled */ + MDP_OUTP(MDP_BASE + 0xC0010, 0x20); /* sobel treshold */ + + MDP_OUTP(MDP_BASE + 0xC0018, 0xeb0010); /* Y Max, Y min */ + MDP_OUTP(MDP_BASE + 0xC001C, 0xf00010); /* Cb Max, Cb min */ + MDP_OUTP(MDP_BASE + 0xC0020, 0xf00010); /* Cb Max, Cb min */ + + MDP_OUTP(MDP_BASE + 0xC000C, 0x67686970); /* add a few chars for CC */ + MDP_OUTP(MDP_BASE + 0xC0000, 0x1); /* MDP tv out enable */ + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + ret = panel_next_on(pdev); + + return ret; +} + +int mdp_dma3_off(struct platform_device *pdev) +{ + int ret = 0; + + ret = panel_next_off(pdev); + if (ret) + return ret; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + MDP_OUTP(MDP_BASE + 0xC0000, 0x0); + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + mdp_pipe_ctrl(MDP_DMA3_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + /* delay to make sure the last frame finishes */ + msleep(16); + + return ret; +} + +void mdp_dma3_update(struct msm_fb_data_type *mfd) +{ + struct fb_info *fbi = mfd->fbi; + uint8 *buf; + int bpp; + unsigned long flag; + + if (!mfd->panel_power_on) + return; + + /* no need to power on cmd block since dma3 is running */ + bpp = fbi->var.bits_per_pixel / 8; + buf = (uint8 *) fbi->fix.smem_start; + + buf += calc_fb_offset(mfd, fbi, bpp); + + MDP_OUTP(MDP_BASE + 0xC0008, (uint32) buf >> 3); + + spin_lock_irqsave(&mdp_spin_lock, flag); + mdp_enable_irq(MDP_DMA3_TERM); + INIT_COMPLETION(mfd->dma->comp); + mfd->dma->waiting = TRUE; + + outp32(MDP_INTR_CLEAR, TV_OUT_DMA3_START); + mdp_intr_mask |= TV_OUT_DMA3_START; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + wait_for_completion_killable(&mfd->dma->comp); + mdp_disable_irq(MDP_DMA3_TERM); +} diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h new file mode 100644 index 0000000000000000000000000000000000000000..f35a757d4731eacc6c6e101dbfb395cbd0ab73a0 --- /dev/null +++ b/drivers/video/msm/mdp_hw.h @@ -0,0 +1,769 @@ +/* drivers/video/msm_fb/mdp_hw.h + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MDP_HW_H_ +#define _MDP_HW_H_ + +#include +#include +#include +#include + +typedef void (*mdp_dma_start_func_t)(void *private_data, uint32_t addr, + uint32_t stride, uint32_t width, + uint32_t height, uint32_t x, uint32_t y); + +struct mdp_out_interface { + uint32_t registered:1; + void *priv; + + /* If the interface client wants to get DMA_DONE events */ + uint32_t dma_mask; + mdp_dma_start_func_t dma_start; + + struct msmfb_callback *dma_cb; + wait_queue_head_t dma_waitqueue; + + /* If the interface client wants to be notified of non-DMA irqs, + * e.g. LCDC/TV-out frame start */ + uint32_t irq_mask; + struct msmfb_callback *irq_cb; +}; + +struct mdp_info { + spinlock_t lock; + struct mdp_device mdp_dev; + char * __iomem base; + int irq; + struct clk *clk; + struct clk *ebi1_clk; + struct mdp_out_interface out_if[MSM_MDP_NUM_INTERFACES]; + int format; + int pack_pattern; + bool dma_config_dirty; +}; + +extern int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, + void *private_data, uint32_t dma_mask, + mdp_dma_start_func_t dma_start); + +extern int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, + uint32_t mask, struct msmfb_callback *cb); + +struct mdp_blit_req; +struct mdp_device; +int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, + unsigned long src_len, struct file *dst_file, + unsigned long dst_start, unsigned long dst_len); + +void mdp_ppp_dump_debug(const struct mdp_info *mdp); + +#define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) +#define mdp_readl(mdp, offset) readl(mdp->base + offset) + +#define MDP_SYNC_CONFIG_0 (0x00000) +#define MDP_SYNC_CONFIG_1 (0x00004) +#define MDP_SYNC_CONFIG_2 (0x00008) +#define MDP_SYNC_STATUS_0 (0x0000c) +#define MDP_SYNC_STATUS_1 (0x00010) +#define MDP_SYNC_STATUS_2 (0x00014) +#define MDP_SYNC_THRESH_0 (0x00018) +#define MDP_SYNC_THRESH_1 (0x0001c) +#define MDP_INTR_ENABLE (0x00020) +#define MDP_INTR_STATUS (0x00024) +#define MDP_INTR_CLEAR (0x00028) +#define MDP_DISPLAY0_START (0x00030) +#define MDP_DISPLAY1_START (0x00034) +#define MDP_DISPLAY_STATUS (0x00038) +#define MDP_EBI2_LCD0 (0x0003c) +#define MDP_EBI2_LCD1 (0x00040) +#define MDP_EBI2_PORTMAP_MODE (0x0005c) + +#ifndef CONFIG_MSM_MDP31 +#define MDP_DISPLAY0_ADDR (0x00054) +#define MDP_DISPLAY1_ADDR (0x00058) +#define MDP_PPP_CMD_MODE (0x00060) +#else +#define MDP_DISPLAY0_ADDR (0x10000) +#define MDP_DISPLAY1_ADDR (0x10004) +#define MDP_PPP_CMD_MODE (0x10060) +#endif + +#define MDP_TV_OUT_STATUS (0x00064) +#define MDP_HW_VERSION (0x00070) +#define MDP_SW_RESET (0x00074) +#define MDP_AXI_ERROR_MASTER_STOP (0x00078) +#define MDP_SEL_CLK_OR_HCLK_TEST_BUS (0x0007c) +#define MDP_PRIMARY_VSYNC_OUT_CTRL (0x00080) +#define MDP_SECONDARY_VSYNC_OUT_CTRL (0x00084) +#define MDP_EXTERNAL_VSYNC_OUT_CTRL (0x00088) +#define MDP_VSYNC_CTRL (0x0008c) +#define MDP_MDDI_PARAM_WR_SEL (0x00090) +#define MDP_MDDI_PARAM (0x00094) +#define MDP_CGC_EN (0x00100) +#define MDP_CMD_STATUS (0x10008) +#define MDP_PROFILE_EN (0x10010) +#define MDP_PROFILE_COUNT (0x10014) +#define MDP_DMA_START (0x10044) +#define MDP_FULL_BYPASS_WORD0 (0x10100) +#define MDP_FULL_BYPASS_WORD1 (0x10104) +#define MDP_COMMAND_CONFIG (0x10104) +#define MDP_FULL_BYPASS_WORD2 (0x10108) +#define MDP_FULL_BYPASS_WORD3 (0x1010c) +#define MDP_FULL_BYPASS_WORD4 (0x10110) +#define MDP_FULL_BYPASS_WORD6 (0x10118) +#define MDP_FULL_BYPASS_WORD7 (0x1011c) +#define MDP_FULL_BYPASS_WORD8 (0x10120) +#define MDP_FULL_BYPASS_WORD9 (0x10124) +#define MDP_PPP_SOURCE_CONFIG (0x10124) +#define MDP_FULL_BYPASS_WORD10 (0x10128) +#define MDP_FULL_BYPASS_WORD11 (0x1012c) +#define MDP_FULL_BYPASS_WORD12 (0x10130) +#define MDP_FULL_BYPASS_WORD13 (0x10134) +#define MDP_FULL_BYPASS_WORD14 (0x10138) +#define MDP_PPP_OPERATION_CONFIG (0x10138) +#define MDP_FULL_BYPASS_WORD15 (0x1013c) +#define MDP_FULL_BYPASS_WORD16 (0x10140) +#define MDP_FULL_BYPASS_WORD17 (0x10144) +#define MDP_FULL_BYPASS_WORD18 (0x10148) +#define MDP_FULL_BYPASS_WORD19 (0x1014c) +#define MDP_FULL_BYPASS_WORD20 (0x10150) +#define MDP_PPP_DESTINATION_CONFIG (0x10150) +#define MDP_FULL_BYPASS_WORD21 (0x10154) +#define MDP_FULL_BYPASS_WORD22 (0x10158) +#define MDP_FULL_BYPASS_WORD23 (0x1015c) +#define MDP_FULL_BYPASS_WORD24 (0x10160) +#define MDP_FULL_BYPASS_WORD25 (0x10164) +#define MDP_FULL_BYPASS_WORD26 (0x10168) +#define MDP_FULL_BYPASS_WORD27 (0x1016c) +#define MDP_FULL_BYPASS_WORD29 (0x10174) +#define MDP_FULL_BYPASS_WORD30 (0x10178) +#define MDP_FULL_BYPASS_WORD31 (0x1017c) +#define MDP_FULL_BYPASS_WORD32 (0x10180) +#define MDP_DMA_CONFIG (0x10180) +#define MDP_FULL_BYPASS_WORD33 (0x10184) +#define MDP_FULL_BYPASS_WORD34 (0x10188) +#define MDP_FULL_BYPASS_WORD35 (0x1018c) +#define MDP_FULL_BYPASS_WORD37 (0x10194) +#define MDP_FULL_BYPASS_WORD39 (0x1019c) +#define MDP_PPP_OUT_XY (0x1019c) +#define MDP_FULL_BYPASS_WORD40 (0x101a0) +#define MDP_FULL_BYPASS_WORD41 (0x101a4) +#define MDP_FULL_BYPASS_WORD43 (0x101ac) +#define MDP_FULL_BYPASS_WORD46 (0x101b8) +#define MDP_FULL_BYPASS_WORD47 (0x101bc) +#define MDP_FULL_BYPASS_WORD48 (0x101c0) +#define MDP_FULL_BYPASS_WORD49 (0x101c4) +#define MDP_FULL_BYPASS_WORD50 (0x101c8) +#define MDP_FULL_BYPASS_WORD51 (0x101cc) +#define MDP_FULL_BYPASS_WORD52 (0x101d0) +#define MDP_FULL_BYPASS_WORD53 (0x101d4) +#define MDP_FULL_BYPASS_WORD54 (0x101d8) +#define MDP_FULL_BYPASS_WORD55 (0x101dc) +#define MDP_FULL_BYPASS_WORD56 (0x101e0) +#define MDP_FULL_BYPASS_WORD57 (0x101e4) +#define MDP_FULL_BYPASS_WORD58 (0x101e8) +#define MDP_FULL_BYPASS_WORD59 (0x101ec) +#define MDP_FULL_BYPASS_WORD60 (0x101f0) +#define MDP_VSYNC_THRESHOLD (0x101f0) +#define MDP_FULL_BYPASS_WORD61 (0x101f4) +#define MDP_FULL_BYPASS_WORD62 (0x101f8) +#define MDP_FULL_BYPASS_WORD63 (0x101fc) + +#ifdef CONFIG_MSM_MDP31 +#define MDP_PPP_SRC_XY (0x10200) +#define MDP_PPP_BG_XY (0x10204) +#define MDP_PPP_SRC_IMAGE_SIZE (0x10208) +#define MDP_PPP_BG_IMAGE_SIZE (0x1020c) +#define MDP_PPP_SCALE_CONFIG (0x10230) +#define MDP_PPP_CSC_CONFIG (0x10240) +#define MDP_PPP_BLEND_BG_ALPHA_SEL (0x70010) +#endif + +#define MDP_TFETCH_TEST_MODE (0x20004) +#define MDP_TFETCH_STATUS (0x20008) +#define MDP_TFETCH_TILE_COUNT (0x20010) +#define MDP_TFETCH_FETCH_COUNT (0x20014) +#define MDP_TFETCH_CONSTANT_COLOR (0x20040) +#define MDP_BGTFETCH_TEST_MODE (0x28004) +#define MDP_BGTFETCH_STATUS (0x28008) +#define MDP_BGTFETCH_TILE_COUNT (0x28010) +#define MDP_BGTFETCH_FETCH_COUNT (0x28014) +#define MDP_BGTFETCH_CONSTANT_COLOR (0x28040) +#define MDP_CSC_BYPASS (0x40004) +#define MDP_SCALE_COEFF_LSB (0x5fffc) +#define MDP_TV_OUT_CTL (0xc0000) +#define MDP_TV_OUT_FIR_COEFF (0xc0004) +#define MDP_TV_OUT_BUF_ADDR (0xc0008) +#define MDP_TV_OUT_CC_DATA (0xc000c) +#define MDP_TV_OUT_SOBEL (0xc0010) +#define MDP_TV_OUT_Y_CLAMP (0xc0018) +#define MDP_TV_OUT_CB_CLAMP (0xc001c) +#define MDP_TV_OUT_CR_CLAMP (0xc0020) +#define MDP_TEST_MODE_CLK (0xd0000) +#define MDP_TEST_MISR_RESET_CLK (0xd0004) +#define MDP_TEST_EXPORT_MISR_CLK (0xd0008) +#define MDP_TEST_MISR_CURR_VAL_CLK (0xd000c) +#define MDP_TEST_MODE_HCLK (0xd0100) +#define MDP_TEST_MISR_RESET_HCLK (0xd0104) +#define MDP_TEST_EXPORT_MISR_HCLK (0xd0108) +#define MDP_TEST_MISR_CURR_VAL_HCLK (0xd010c) +#define MDP_TEST_MODE_DCLK (0xd0200) +#define MDP_TEST_MISR_RESET_DCLK (0xd0204) +#define MDP_TEST_EXPORT_MISR_DCLK (0xd0208) +#define MDP_TEST_MISR_CURR_VAL_DCLK (0xd020c) +#define MDP_TEST_CAPTURED_DCLK (0xd0210) +#define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214) + +#define MDP_DMA_P_START (0x00044) +#define MDP_DMA_P_CONFIG (0x90000) +#define MDP_DMA_P_SIZE (0x90004) +#define MDP_DMA_P_IBUF_ADDR (0x90008) +#define MDP_DMA_P_IBUF_Y_STRIDE (0x9000c) +#define MDP_DMA_P_OUT_XY (0x90010) +#define MDP_DMA_P_COLOR_CORRECT_CONFIG (0x90070) + +#define MDP_LCDC_EN (0xe0000) +#define MDP_LCDC_HSYNC_CTL (0xe0004) +#define MDP_LCDC_VSYNC_PERIOD (0xe0008) +#define MDP_LCDC_VSYNC_PULSE_WIDTH (0xe000c) +#define MDP_LCDC_DISPLAY_HCTL (0xe0010) +#define MDP_LCDC_DISPLAY_V_START (0xe0014) +#define MDP_LCDC_DISPLAY_V_END (0xe0018) +#define MDP_LCDC_ACTIVE_HCTL (0xe001c) +#define MDP_LCDC_ACTIVE_V_START (0xe0020) +#define MDP_LCDC_ACTIVE_V_END (0xe0024) +#define MDP_LCDC_BORDER_CLR (0xe0028) +#define MDP_LCDC_UNDERFLOW_CTL (0xe002c) +#define MDP_LCDC_HSYNC_SKEW (0xe0030) +#define MDP_LCDC_TEST_CTL (0xe0034) +#define MDP_LCDC_CTL_POLARITY (0xe0038) + +#define MDP_PPP_SCALE_STATUS (0x50000) +#define MDP_PPP_BLEND_STATUS (0x70000) + +/* MDP_SW_RESET */ +#define MDP_PPP_SW_RESET (1<<4) + +/* MDP_INTR_ENABLE */ +#define DL0_ROI_DONE (1<<0) +#define TV_OUT_DMA3_DONE (1<<6) +#define TV_ENC_UNDERRUN (1<<7) + +#ifdef CONFIG_MSM_MDP22 +#define MDP_DMA_P_DONE (1 << 2) +#else /* CONFIG_MSM_MDP31 */ +#define MDP_DMA_P_DONE (1 << 14) +#define MDP_LCDC_UNDERFLOW (1 << 16) +#define MDP_LCDC_FRAME_START (1 << 15) +#endif + +#define MDP_TOP_LUMA 16 +#define MDP_TOP_CHROMA 0 +#define MDP_BOTTOM_LUMA 19 +#define MDP_BOTTOM_CHROMA 3 +#define MDP_LEFT_LUMA 22 +#define MDP_LEFT_CHROMA 6 +#define MDP_RIGHT_LUMA 25 +#define MDP_RIGHT_CHROMA 9 + +#define CLR_G 0x0 +#define CLR_B 0x1 +#define CLR_R 0x2 +#define CLR_ALPHA 0x3 + +#define CLR_Y CLR_G +#define CLR_CB CLR_B +#define CLR_CR CLR_R + +/* from lsb to msb */ +#define MDP_GET_PACK_PATTERN(a, x, y, z, bit) \ + (((a)<<(bit*3))|((x)<<(bit*2))|((y)< + * + * Based on code from The Linux Foundation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include "mdp_hw.h" + +static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) +{ + struct mdp_info *mdp = priv; + uint32_t dma2_cfg; + uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ + + dma2_cfg = DMA_PACK_TIGHT | + DMA_PACK_ALIGN_LSB; + + dma2_cfg |= mdp->dma_format; + dma2_cfg |= mdp->dma_pack_pattern; + dma2_cfg |= DMA_DITHER_EN; + + /* 666 18BPP */ + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE); + mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR); + mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); + mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, + MDP_MDDI_PARAM); + + mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR); + mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); + mdp_writel(mdp, 0, MDP_DMA_P_START); +} + +int mdp_hw_init(struct mdp_info *mdp) +{ + int ret; + + ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, + MDP_DMA_P_DONE, mdp_dma_to_mddi); + if (ret) + return ret; + + mdp_writel(mdp, 0, MDP_INTR_ENABLE); + mdp_writel(mdp, 0, MDP_DMA_P_HIST_INTR_ENABLE); + + /* XXX: why set this? QCT says it should be > mdp_pclk, + * but they never set the clkrate of pclk */ + clk_set_rate(mdp->clk, 122880000); /* 122.88 Mhz */ + pr_info("%s: mdp_clk=%lu\n", __func__, clk_get_rate(mdp->clk)); + + /* TODO: Configure the VG/RGB pipes fetch data */ + + /* this should work for any mdp_clk freq. + * TODO: use different value for mdp_clk freqs >= 90Mhz */ + mdp_writel(mdp, 0x27, MDP_DMA_P_FETCH_CFG); /* 8 bytes-burst x 8 req */ + + mdp_writel(mdp, 0x3, MDP_EBI2_PORTMAP_MODE); + + /* 3 pending requests */ + mdp_writel(mdp, 0x02222, MDP_MAX_RD_PENDING_CMD_CONFIG); + + /* no overlay processing, sw controls everything */ + mdp_writel(mdp, 0, MDP_LAYERMIXER_IN_CFG); + mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC0_CFG); + mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC1_CFG); + + /* XXX: HACK! hardcode to do mddi on primary */ + mdp_writel(mdp, 0x2, MDP_DISP_INTF_SEL); + return 0; +} + diff --git a/drivers/video/msm/mdp_hw_init.c b/drivers/video/msm/mdp_hw_init.c new file mode 100644 index 0000000000000000000000000000000000000000..3818ed497bca7e584560a1317d3e3d03fe09eefe --- /dev/null +++ b/drivers/video/msm/mdp_hw_init.c @@ -0,0 +1,714 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "mdp.h" + +/* mdp primary csc limit vector */ +uint32 mdp_plv[] = { 0x10, 0xeb, 0x10, 0xf0 }; + +/* Color Coefficient matrix for YUV -> RGB */ +struct mdp_ccs mdp_ccs_yuv2rgb = { + MDP_CCS_YUV2RGB, + { + 0x254, + 0x000, + 0x331, + 0x254, + 0xff38, + 0xfe61, + 0x254, + 0x409, + 0x000, + }, + { +#ifdef CONFIG_FB_MSM_MDP31 + 0x1f0, + 0x180, + 0x180 +#else + 0x10, + 0x80, + 0x80 +#endif + } +}; + +/* Color Coefficient matrix for RGB -> YUV */ +struct mdp_ccs mdp_ccs_rgb2yuv = { + MDP_CCS_RGB2YUV, + { + 0x83, + 0x102, + 0x32, + 0xffb5, + 0xff6c, + 0xe1, + 0xe1, + 0xff45, + 0xffdc, + }, +#ifdef CONFIG_FB_MSM_MDP31 + { + 0x10, + 0x80, + 0x80 + } +#endif +}; + +static void mdp_load_lut_param(void) +{ + outpdw(MDP_BASE + 0x40800, 0x0); + outpdw(MDP_BASE + 0x40804, 0x151515); + outpdw(MDP_BASE + 0x40808, 0x1d1d1d); + outpdw(MDP_BASE + 0x4080c, 0x232323); + outpdw(MDP_BASE + 0x40810, 0x272727); + outpdw(MDP_BASE + 0x40814, 0x2b2b2b); + outpdw(MDP_BASE + 0x40818, 0x2f2f2f); + outpdw(MDP_BASE + 0x4081c, 0x333333); + outpdw(MDP_BASE + 0x40820, 0x363636); + outpdw(MDP_BASE + 0x40824, 0x393939); + outpdw(MDP_BASE + 0x40828, 0x3b3b3b); + outpdw(MDP_BASE + 0x4082c, 0x3e3e3e); + outpdw(MDP_BASE + 0x40830, 0x404040); + outpdw(MDP_BASE + 0x40834, 0x434343); + outpdw(MDP_BASE + 0x40838, 0x454545); + outpdw(MDP_BASE + 0x4083c, 0x474747); + outpdw(MDP_BASE + 0x40840, 0x494949); + outpdw(MDP_BASE + 0x40844, 0x4b4b4b); + outpdw(MDP_BASE + 0x40848, 0x4d4d4d); + outpdw(MDP_BASE + 0x4084c, 0x4f4f4f); + outpdw(MDP_BASE + 0x40850, 0x515151); + outpdw(MDP_BASE + 0x40854, 0x535353); + outpdw(MDP_BASE + 0x40858, 0x555555); + outpdw(MDP_BASE + 0x4085c, 0x565656); + outpdw(MDP_BASE + 0x40860, 0x585858); + outpdw(MDP_BASE + 0x40864, 0x5a5a5a); + outpdw(MDP_BASE + 0x40868, 0x5b5b5b); + outpdw(MDP_BASE + 0x4086c, 0x5d5d5d); + outpdw(MDP_BASE + 0x40870, 0x5e5e5e); + outpdw(MDP_BASE + 0x40874, 0x606060); + outpdw(MDP_BASE + 0x40878, 0x616161); + outpdw(MDP_BASE + 0x4087c, 0x636363); + outpdw(MDP_BASE + 0x40880, 0x646464); + outpdw(MDP_BASE + 0x40884, 0x666666); + outpdw(MDP_BASE + 0x40888, 0x676767); + outpdw(MDP_BASE + 0x4088c, 0x686868); + outpdw(MDP_BASE + 0x40890, 0x6a6a6a); + outpdw(MDP_BASE + 0x40894, 0x6b6b6b); + outpdw(MDP_BASE + 0x40898, 0x6c6c6c); + outpdw(MDP_BASE + 0x4089c, 0x6e6e6e); + outpdw(MDP_BASE + 0x408a0, 0x6f6f6f); + outpdw(MDP_BASE + 0x408a4, 0x707070); + outpdw(MDP_BASE + 0x408a8, 0x717171); + outpdw(MDP_BASE + 0x408ac, 0x727272); + outpdw(MDP_BASE + 0x408b0, 0x747474); + outpdw(MDP_BASE + 0x408b4, 0x757575); + outpdw(MDP_BASE + 0x408b8, 0x767676); + outpdw(MDP_BASE + 0x408bc, 0x777777); + outpdw(MDP_BASE + 0x408c0, 0x787878); + outpdw(MDP_BASE + 0x408c4, 0x797979); + outpdw(MDP_BASE + 0x408c8, 0x7a7a7a); + outpdw(MDP_BASE + 0x408cc, 0x7c7c7c); + outpdw(MDP_BASE + 0x408d0, 0x7d7d7d); + outpdw(MDP_BASE + 0x408d4, 0x7e7e7e); + outpdw(MDP_BASE + 0x408d8, 0x7f7f7f); + outpdw(MDP_BASE + 0x408dc, 0x808080); + outpdw(MDP_BASE + 0x408e0, 0x818181); + outpdw(MDP_BASE + 0x408e4, 0x828282); + outpdw(MDP_BASE + 0x408e8, 0x838383); + outpdw(MDP_BASE + 0x408ec, 0x848484); + outpdw(MDP_BASE + 0x408f0, 0x858585); + outpdw(MDP_BASE + 0x408f4, 0x868686); + outpdw(MDP_BASE + 0x408f8, 0x878787); + outpdw(MDP_BASE + 0x408fc, 0x888888); + outpdw(MDP_BASE + 0x40900, 0x898989); + outpdw(MDP_BASE + 0x40904, 0x8a8a8a); + outpdw(MDP_BASE + 0x40908, 0x8b8b8b); + outpdw(MDP_BASE + 0x4090c, 0x8c8c8c); + outpdw(MDP_BASE + 0x40910, 0x8d8d8d); + outpdw(MDP_BASE + 0x40914, 0x8e8e8e); + outpdw(MDP_BASE + 0x40918, 0x8f8f8f); + outpdw(MDP_BASE + 0x4091c, 0x8f8f8f); + outpdw(MDP_BASE + 0x40920, 0x909090); + outpdw(MDP_BASE + 0x40924, 0x919191); + outpdw(MDP_BASE + 0x40928, 0x929292); + outpdw(MDP_BASE + 0x4092c, 0x939393); + outpdw(MDP_BASE + 0x40930, 0x949494); + outpdw(MDP_BASE + 0x40934, 0x959595); + outpdw(MDP_BASE + 0x40938, 0x969696); + outpdw(MDP_BASE + 0x4093c, 0x969696); + outpdw(MDP_BASE + 0x40940, 0x979797); + outpdw(MDP_BASE + 0x40944, 0x989898); + outpdw(MDP_BASE + 0x40948, 0x999999); + outpdw(MDP_BASE + 0x4094c, 0x9a9a9a); + outpdw(MDP_BASE + 0x40950, 0x9b9b9b); + outpdw(MDP_BASE + 0x40954, 0x9c9c9c); + outpdw(MDP_BASE + 0x40958, 0x9c9c9c); + outpdw(MDP_BASE + 0x4095c, 0x9d9d9d); + outpdw(MDP_BASE + 0x40960, 0x9e9e9e); + outpdw(MDP_BASE + 0x40964, 0x9f9f9f); + outpdw(MDP_BASE + 0x40968, 0xa0a0a0); + outpdw(MDP_BASE + 0x4096c, 0xa0a0a0); + outpdw(MDP_BASE + 0x40970, 0xa1a1a1); + outpdw(MDP_BASE + 0x40974, 0xa2a2a2); + outpdw(MDP_BASE + 0x40978, 0xa3a3a3); + outpdw(MDP_BASE + 0x4097c, 0xa4a4a4); + outpdw(MDP_BASE + 0x40980, 0xa4a4a4); + outpdw(MDP_BASE + 0x40984, 0xa5a5a5); + outpdw(MDP_BASE + 0x40988, 0xa6a6a6); + outpdw(MDP_BASE + 0x4098c, 0xa7a7a7); + outpdw(MDP_BASE + 0x40990, 0xa7a7a7); + outpdw(MDP_BASE + 0x40994, 0xa8a8a8); + outpdw(MDP_BASE + 0x40998, 0xa9a9a9); + outpdw(MDP_BASE + 0x4099c, 0xaaaaaa); + outpdw(MDP_BASE + 0x409a0, 0xaaaaaa); + outpdw(MDP_BASE + 0x409a4, 0xababab); + outpdw(MDP_BASE + 0x409a8, 0xacacac); + outpdw(MDP_BASE + 0x409ac, 0xadadad); + outpdw(MDP_BASE + 0x409b0, 0xadadad); + outpdw(MDP_BASE + 0x409b4, 0xaeaeae); + outpdw(MDP_BASE + 0x409b8, 0xafafaf); + outpdw(MDP_BASE + 0x409bc, 0xafafaf); + outpdw(MDP_BASE + 0x409c0, 0xb0b0b0); + outpdw(MDP_BASE + 0x409c4, 0xb1b1b1); + outpdw(MDP_BASE + 0x409c8, 0xb2b2b2); + outpdw(MDP_BASE + 0x409cc, 0xb2b2b2); + outpdw(MDP_BASE + 0x409d0, 0xb3b3b3); + outpdw(MDP_BASE + 0x409d4, 0xb4b4b4); + outpdw(MDP_BASE + 0x409d8, 0xb4b4b4); + outpdw(MDP_BASE + 0x409dc, 0xb5b5b5); + outpdw(MDP_BASE + 0x409e0, 0xb6b6b6); + outpdw(MDP_BASE + 0x409e4, 0xb6b6b6); + outpdw(MDP_BASE + 0x409e8, 0xb7b7b7); + outpdw(MDP_BASE + 0x409ec, 0xb8b8b8); + outpdw(MDP_BASE + 0x409f0, 0xb8b8b8); + outpdw(MDP_BASE + 0x409f4, 0xb9b9b9); + outpdw(MDP_BASE + 0x409f8, 0xbababa); + outpdw(MDP_BASE + 0x409fc, 0xbababa); + outpdw(MDP_BASE + 0x40a00, 0xbbbbbb); + outpdw(MDP_BASE + 0x40a04, 0xbcbcbc); + outpdw(MDP_BASE + 0x40a08, 0xbcbcbc); + outpdw(MDP_BASE + 0x40a0c, 0xbdbdbd); + outpdw(MDP_BASE + 0x40a10, 0xbebebe); + outpdw(MDP_BASE + 0x40a14, 0xbebebe); + outpdw(MDP_BASE + 0x40a18, 0xbfbfbf); + outpdw(MDP_BASE + 0x40a1c, 0xc0c0c0); + outpdw(MDP_BASE + 0x40a20, 0xc0c0c0); + outpdw(MDP_BASE + 0x40a24, 0xc1c1c1); + outpdw(MDP_BASE + 0x40a28, 0xc1c1c1); + outpdw(MDP_BASE + 0x40a2c, 0xc2c2c2); + outpdw(MDP_BASE + 0x40a30, 0xc3c3c3); + outpdw(MDP_BASE + 0x40a34, 0xc3c3c3); + outpdw(MDP_BASE + 0x40a38, 0xc4c4c4); + outpdw(MDP_BASE + 0x40a3c, 0xc5c5c5); + outpdw(MDP_BASE + 0x40a40, 0xc5c5c5); + outpdw(MDP_BASE + 0x40a44, 0xc6c6c6); + outpdw(MDP_BASE + 0x40a48, 0xc6c6c6); + outpdw(MDP_BASE + 0x40a4c, 0xc7c7c7); + outpdw(MDP_BASE + 0x40a50, 0xc8c8c8); + outpdw(MDP_BASE + 0x40a54, 0xc8c8c8); + outpdw(MDP_BASE + 0x40a58, 0xc9c9c9); + outpdw(MDP_BASE + 0x40a5c, 0xc9c9c9); + outpdw(MDP_BASE + 0x40a60, 0xcacaca); + outpdw(MDP_BASE + 0x40a64, 0xcbcbcb); + outpdw(MDP_BASE + 0x40a68, 0xcbcbcb); + outpdw(MDP_BASE + 0x40a6c, 0xcccccc); + outpdw(MDP_BASE + 0x40a70, 0xcccccc); + outpdw(MDP_BASE + 0x40a74, 0xcdcdcd); + outpdw(MDP_BASE + 0x40a78, 0xcecece); + outpdw(MDP_BASE + 0x40a7c, 0xcecece); + outpdw(MDP_BASE + 0x40a80, 0xcfcfcf); + outpdw(MDP_BASE + 0x40a84, 0xcfcfcf); + outpdw(MDP_BASE + 0x40a88, 0xd0d0d0); + outpdw(MDP_BASE + 0x40a8c, 0xd0d0d0); + outpdw(MDP_BASE + 0x40a90, 0xd1d1d1); + outpdw(MDP_BASE + 0x40a94, 0xd2d2d2); + outpdw(MDP_BASE + 0x40a98, 0xd2d2d2); + outpdw(MDP_BASE + 0x40a9c, 0xd3d3d3); + outpdw(MDP_BASE + 0x40aa0, 0xd3d3d3); + outpdw(MDP_BASE + 0x40aa4, 0xd4d4d4); + outpdw(MDP_BASE + 0x40aa8, 0xd4d4d4); + outpdw(MDP_BASE + 0x40aac, 0xd5d5d5); + outpdw(MDP_BASE + 0x40ab0, 0xd6d6d6); + outpdw(MDP_BASE + 0x40ab4, 0xd6d6d6); + outpdw(MDP_BASE + 0x40ab8, 0xd7d7d7); + outpdw(MDP_BASE + 0x40abc, 0xd7d7d7); + outpdw(MDP_BASE + 0x40ac0, 0xd8d8d8); + outpdw(MDP_BASE + 0x40ac4, 0xd8d8d8); + outpdw(MDP_BASE + 0x40ac8, 0xd9d9d9); + outpdw(MDP_BASE + 0x40acc, 0xd9d9d9); + outpdw(MDP_BASE + 0x40ad0, 0xdadada); + outpdw(MDP_BASE + 0x40ad4, 0xdbdbdb); + outpdw(MDP_BASE + 0x40ad8, 0xdbdbdb); + outpdw(MDP_BASE + 0x40adc, 0xdcdcdc); + outpdw(MDP_BASE + 0x40ae0, 0xdcdcdc); + outpdw(MDP_BASE + 0x40ae4, 0xdddddd); + outpdw(MDP_BASE + 0x40ae8, 0xdddddd); + outpdw(MDP_BASE + 0x40aec, 0xdedede); + outpdw(MDP_BASE + 0x40af0, 0xdedede); + outpdw(MDP_BASE + 0x40af4, 0xdfdfdf); + outpdw(MDP_BASE + 0x40af8, 0xdfdfdf); + outpdw(MDP_BASE + 0x40afc, 0xe0e0e0); + outpdw(MDP_BASE + 0x40b00, 0xe0e0e0); + outpdw(MDP_BASE + 0x40b04, 0xe1e1e1); + outpdw(MDP_BASE + 0x40b08, 0xe1e1e1); + outpdw(MDP_BASE + 0x40b0c, 0xe2e2e2); + outpdw(MDP_BASE + 0x40b10, 0xe3e3e3); + outpdw(MDP_BASE + 0x40b14, 0xe3e3e3); + outpdw(MDP_BASE + 0x40b18, 0xe4e4e4); + outpdw(MDP_BASE + 0x40b1c, 0xe4e4e4); + outpdw(MDP_BASE + 0x40b20, 0xe5e5e5); + outpdw(MDP_BASE + 0x40b24, 0xe5e5e5); + outpdw(MDP_BASE + 0x40b28, 0xe6e6e6); + outpdw(MDP_BASE + 0x40b2c, 0xe6e6e6); + outpdw(MDP_BASE + 0x40b30, 0xe7e7e7); + outpdw(MDP_BASE + 0x40b34, 0xe7e7e7); + outpdw(MDP_BASE + 0x40b38, 0xe8e8e8); + outpdw(MDP_BASE + 0x40b3c, 0xe8e8e8); + outpdw(MDP_BASE + 0x40b40, 0xe9e9e9); + outpdw(MDP_BASE + 0x40b44, 0xe9e9e9); + outpdw(MDP_BASE + 0x40b48, 0xeaeaea); + outpdw(MDP_BASE + 0x40b4c, 0xeaeaea); + outpdw(MDP_BASE + 0x40b50, 0xebebeb); + outpdw(MDP_BASE + 0x40b54, 0xebebeb); + outpdw(MDP_BASE + 0x40b58, 0xececec); + outpdw(MDP_BASE + 0x40b5c, 0xececec); + outpdw(MDP_BASE + 0x40b60, 0xededed); + outpdw(MDP_BASE + 0x40b64, 0xededed); + outpdw(MDP_BASE + 0x40b68, 0xeeeeee); + outpdw(MDP_BASE + 0x40b6c, 0xeeeeee); + outpdw(MDP_BASE + 0x40b70, 0xefefef); + outpdw(MDP_BASE + 0x40b74, 0xefefef); + outpdw(MDP_BASE + 0x40b78, 0xf0f0f0); + outpdw(MDP_BASE + 0x40b7c, 0xf0f0f0); + outpdw(MDP_BASE + 0x40b80, 0xf1f1f1); + outpdw(MDP_BASE + 0x40b84, 0xf1f1f1); + outpdw(MDP_BASE + 0x40b88, 0xf2f2f2); + outpdw(MDP_BASE + 0x40b8c, 0xf2f2f2); + outpdw(MDP_BASE + 0x40b90, 0xf2f2f2); + outpdw(MDP_BASE + 0x40b94, 0xf3f3f3); + outpdw(MDP_BASE + 0x40b98, 0xf3f3f3); + outpdw(MDP_BASE + 0x40b9c, 0xf4f4f4); + outpdw(MDP_BASE + 0x40ba0, 0xf4f4f4); + outpdw(MDP_BASE + 0x40ba4, 0xf5f5f5); + outpdw(MDP_BASE + 0x40ba8, 0xf5f5f5); + outpdw(MDP_BASE + 0x40bac, 0xf6f6f6); + outpdw(MDP_BASE + 0x40bb0, 0xf6f6f6); + outpdw(MDP_BASE + 0x40bb4, 0xf7f7f7); + outpdw(MDP_BASE + 0x40bb8, 0xf7f7f7); + outpdw(MDP_BASE + 0x40bbc, 0xf8f8f8); + outpdw(MDP_BASE + 0x40bc0, 0xf8f8f8); + outpdw(MDP_BASE + 0x40bc4, 0xf9f9f9); + outpdw(MDP_BASE + 0x40bc8, 0xf9f9f9); + outpdw(MDP_BASE + 0x40bcc, 0xfafafa); + outpdw(MDP_BASE + 0x40bd0, 0xfafafa); + outpdw(MDP_BASE + 0x40bd4, 0xfafafa); + outpdw(MDP_BASE + 0x40bd8, 0xfbfbfb); + outpdw(MDP_BASE + 0x40bdc, 0xfbfbfb); + outpdw(MDP_BASE + 0x40be0, 0xfcfcfc); + outpdw(MDP_BASE + 0x40be4, 0xfcfcfc); + outpdw(MDP_BASE + 0x40be8, 0xfdfdfd); + outpdw(MDP_BASE + 0x40bec, 0xfdfdfd); + outpdw(MDP_BASE + 0x40bf0, 0xfefefe); + outpdw(MDP_BASE + 0x40bf4, 0xfefefe); + outpdw(MDP_BASE + 0x40bf8, 0xffffff); + outpdw(MDP_BASE + 0x40bfc, 0xffffff); + outpdw(MDP_BASE + 0x40c00, 0x0); + outpdw(MDP_BASE + 0x40c04, 0x0); + outpdw(MDP_BASE + 0x40c08, 0x0); + outpdw(MDP_BASE + 0x40c0c, 0x0); + outpdw(MDP_BASE + 0x40c10, 0x0); + outpdw(MDP_BASE + 0x40c14, 0x0); + outpdw(MDP_BASE + 0x40c18, 0x0); + outpdw(MDP_BASE + 0x40c1c, 0x0); + outpdw(MDP_BASE + 0x40c20, 0x0); + outpdw(MDP_BASE + 0x40c24, 0x0); + outpdw(MDP_BASE + 0x40c28, 0x0); + outpdw(MDP_BASE + 0x40c2c, 0x0); + outpdw(MDP_BASE + 0x40c30, 0x0); + outpdw(MDP_BASE + 0x40c34, 0x0); + outpdw(MDP_BASE + 0x40c38, 0x0); + outpdw(MDP_BASE + 0x40c3c, 0x0); + outpdw(MDP_BASE + 0x40c40, 0x10101); + outpdw(MDP_BASE + 0x40c44, 0x10101); + outpdw(MDP_BASE + 0x40c48, 0x10101); + outpdw(MDP_BASE + 0x40c4c, 0x10101); + outpdw(MDP_BASE + 0x40c50, 0x10101); + outpdw(MDP_BASE + 0x40c54, 0x10101); + outpdw(MDP_BASE + 0x40c58, 0x10101); + outpdw(MDP_BASE + 0x40c5c, 0x10101); + outpdw(MDP_BASE + 0x40c60, 0x10101); + outpdw(MDP_BASE + 0x40c64, 0x10101); + outpdw(MDP_BASE + 0x40c68, 0x20202); + outpdw(MDP_BASE + 0x40c6c, 0x20202); + outpdw(MDP_BASE + 0x40c70, 0x20202); + outpdw(MDP_BASE + 0x40c74, 0x20202); + outpdw(MDP_BASE + 0x40c78, 0x20202); + outpdw(MDP_BASE + 0x40c7c, 0x20202); + outpdw(MDP_BASE + 0x40c80, 0x30303); + outpdw(MDP_BASE + 0x40c84, 0x30303); + outpdw(MDP_BASE + 0x40c88, 0x30303); + outpdw(MDP_BASE + 0x40c8c, 0x30303); + outpdw(MDP_BASE + 0x40c90, 0x30303); + outpdw(MDP_BASE + 0x40c94, 0x40404); + outpdw(MDP_BASE + 0x40c98, 0x40404); + outpdw(MDP_BASE + 0x40c9c, 0x40404); + outpdw(MDP_BASE + 0x40ca0, 0x40404); + outpdw(MDP_BASE + 0x40ca4, 0x40404); + outpdw(MDP_BASE + 0x40ca8, 0x50505); + outpdw(MDP_BASE + 0x40cac, 0x50505); + outpdw(MDP_BASE + 0x40cb0, 0x50505); + outpdw(MDP_BASE + 0x40cb4, 0x50505); + outpdw(MDP_BASE + 0x40cb8, 0x60606); + outpdw(MDP_BASE + 0x40cbc, 0x60606); + outpdw(MDP_BASE + 0x40cc0, 0x60606); + outpdw(MDP_BASE + 0x40cc4, 0x70707); + outpdw(MDP_BASE + 0x40cc8, 0x70707); + outpdw(MDP_BASE + 0x40ccc, 0x70707); + outpdw(MDP_BASE + 0x40cd0, 0x70707); + outpdw(MDP_BASE + 0x40cd4, 0x80808); + outpdw(MDP_BASE + 0x40cd8, 0x80808); + outpdw(MDP_BASE + 0x40cdc, 0x80808); + outpdw(MDP_BASE + 0x40ce0, 0x90909); + outpdw(MDP_BASE + 0x40ce4, 0x90909); + outpdw(MDP_BASE + 0x40ce8, 0xa0a0a); + outpdw(MDP_BASE + 0x40cec, 0xa0a0a); + outpdw(MDP_BASE + 0x40cf0, 0xa0a0a); + outpdw(MDP_BASE + 0x40cf4, 0xb0b0b); + outpdw(MDP_BASE + 0x40cf8, 0xb0b0b); + outpdw(MDP_BASE + 0x40cfc, 0xb0b0b); + outpdw(MDP_BASE + 0x40d00, 0xc0c0c); + outpdw(MDP_BASE + 0x40d04, 0xc0c0c); + outpdw(MDP_BASE + 0x40d08, 0xd0d0d); + outpdw(MDP_BASE + 0x40d0c, 0xd0d0d); + outpdw(MDP_BASE + 0x40d10, 0xe0e0e); + outpdw(MDP_BASE + 0x40d14, 0xe0e0e); + outpdw(MDP_BASE + 0x40d18, 0xe0e0e); + outpdw(MDP_BASE + 0x40d1c, 0xf0f0f); + outpdw(MDP_BASE + 0x40d20, 0xf0f0f); + outpdw(MDP_BASE + 0x40d24, 0x101010); + outpdw(MDP_BASE + 0x40d28, 0x101010); + outpdw(MDP_BASE + 0x40d2c, 0x111111); + outpdw(MDP_BASE + 0x40d30, 0x111111); + outpdw(MDP_BASE + 0x40d34, 0x121212); + outpdw(MDP_BASE + 0x40d38, 0x121212); + outpdw(MDP_BASE + 0x40d3c, 0x131313); + outpdw(MDP_BASE + 0x40d40, 0x131313); + outpdw(MDP_BASE + 0x40d44, 0x141414); + outpdw(MDP_BASE + 0x40d48, 0x151515); + outpdw(MDP_BASE + 0x40d4c, 0x151515); + outpdw(MDP_BASE + 0x40d50, 0x161616); + outpdw(MDP_BASE + 0x40d54, 0x161616); + outpdw(MDP_BASE + 0x40d58, 0x171717); + outpdw(MDP_BASE + 0x40d5c, 0x171717); + outpdw(MDP_BASE + 0x40d60, 0x181818); + outpdw(MDP_BASE + 0x40d64, 0x191919); + outpdw(MDP_BASE + 0x40d68, 0x191919); + outpdw(MDP_BASE + 0x40d6c, 0x1a1a1a); + outpdw(MDP_BASE + 0x40d70, 0x1b1b1b); + outpdw(MDP_BASE + 0x40d74, 0x1b1b1b); + outpdw(MDP_BASE + 0x40d78, 0x1c1c1c); + outpdw(MDP_BASE + 0x40d7c, 0x1c1c1c); + outpdw(MDP_BASE + 0x40d80, 0x1d1d1d); + outpdw(MDP_BASE + 0x40d84, 0x1e1e1e); + outpdw(MDP_BASE + 0x40d88, 0x1f1f1f); + outpdw(MDP_BASE + 0x40d8c, 0x1f1f1f); + outpdw(MDP_BASE + 0x40d90, 0x202020); + outpdw(MDP_BASE + 0x40d94, 0x212121); + outpdw(MDP_BASE + 0x40d98, 0x212121); + outpdw(MDP_BASE + 0x40d9c, 0x222222); + outpdw(MDP_BASE + 0x40da0, 0x232323); + outpdw(MDP_BASE + 0x40da4, 0x242424); + outpdw(MDP_BASE + 0x40da8, 0x242424); + outpdw(MDP_BASE + 0x40dac, 0x252525); + outpdw(MDP_BASE + 0x40db0, 0x262626); + outpdw(MDP_BASE + 0x40db4, 0x272727); + outpdw(MDP_BASE + 0x40db8, 0x272727); + outpdw(MDP_BASE + 0x40dbc, 0x282828); + outpdw(MDP_BASE + 0x40dc0, 0x292929); + outpdw(MDP_BASE + 0x40dc4, 0x2a2a2a); + outpdw(MDP_BASE + 0x40dc8, 0x2b2b2b); + outpdw(MDP_BASE + 0x40dcc, 0x2c2c2c); + outpdw(MDP_BASE + 0x40dd0, 0x2c2c2c); + outpdw(MDP_BASE + 0x40dd4, 0x2d2d2d); + outpdw(MDP_BASE + 0x40dd8, 0x2e2e2e); + outpdw(MDP_BASE + 0x40ddc, 0x2f2f2f); + outpdw(MDP_BASE + 0x40de0, 0x303030); + outpdw(MDP_BASE + 0x40de4, 0x313131); + outpdw(MDP_BASE + 0x40de8, 0x323232); + outpdw(MDP_BASE + 0x40dec, 0x333333); + outpdw(MDP_BASE + 0x40df0, 0x333333); + outpdw(MDP_BASE + 0x40df4, 0x343434); + outpdw(MDP_BASE + 0x40df8, 0x353535); + outpdw(MDP_BASE + 0x40dfc, 0x363636); + outpdw(MDP_BASE + 0x40e00, 0x373737); + outpdw(MDP_BASE + 0x40e04, 0x383838); + outpdw(MDP_BASE + 0x40e08, 0x393939); + outpdw(MDP_BASE + 0x40e0c, 0x3a3a3a); + outpdw(MDP_BASE + 0x40e10, 0x3b3b3b); + outpdw(MDP_BASE + 0x40e14, 0x3c3c3c); + outpdw(MDP_BASE + 0x40e18, 0x3d3d3d); + outpdw(MDP_BASE + 0x40e1c, 0x3e3e3e); + outpdw(MDP_BASE + 0x40e20, 0x3f3f3f); + outpdw(MDP_BASE + 0x40e24, 0x404040); + outpdw(MDP_BASE + 0x40e28, 0x414141); + outpdw(MDP_BASE + 0x40e2c, 0x424242); + outpdw(MDP_BASE + 0x40e30, 0x434343); + outpdw(MDP_BASE + 0x40e34, 0x444444); + outpdw(MDP_BASE + 0x40e38, 0x464646); + outpdw(MDP_BASE + 0x40e3c, 0x474747); + outpdw(MDP_BASE + 0x40e40, 0x484848); + outpdw(MDP_BASE + 0x40e44, 0x494949); + outpdw(MDP_BASE + 0x40e48, 0x4a4a4a); + outpdw(MDP_BASE + 0x40e4c, 0x4b4b4b); + outpdw(MDP_BASE + 0x40e50, 0x4c4c4c); + outpdw(MDP_BASE + 0x40e54, 0x4d4d4d); + outpdw(MDP_BASE + 0x40e58, 0x4f4f4f); + outpdw(MDP_BASE + 0x40e5c, 0x505050); + outpdw(MDP_BASE + 0x40e60, 0x515151); + outpdw(MDP_BASE + 0x40e64, 0x525252); + outpdw(MDP_BASE + 0x40e68, 0x535353); + outpdw(MDP_BASE + 0x40e6c, 0x545454); + outpdw(MDP_BASE + 0x40e70, 0x565656); + outpdw(MDP_BASE + 0x40e74, 0x575757); + outpdw(MDP_BASE + 0x40e78, 0x585858); + outpdw(MDP_BASE + 0x40e7c, 0x595959); + outpdw(MDP_BASE + 0x40e80, 0x5b5b5b); + outpdw(MDP_BASE + 0x40e84, 0x5c5c5c); + outpdw(MDP_BASE + 0x40e88, 0x5d5d5d); + outpdw(MDP_BASE + 0x40e8c, 0x5e5e5e); + outpdw(MDP_BASE + 0x40e90, 0x606060); + outpdw(MDP_BASE + 0x40e94, 0x616161); + outpdw(MDP_BASE + 0x40e98, 0x626262); + outpdw(MDP_BASE + 0x40e9c, 0x646464); + outpdw(MDP_BASE + 0x40ea0, 0x656565); + outpdw(MDP_BASE + 0x40ea4, 0x666666); + outpdw(MDP_BASE + 0x40ea8, 0x686868); + outpdw(MDP_BASE + 0x40eac, 0x696969); + outpdw(MDP_BASE + 0x40eb0, 0x6a6a6a); + outpdw(MDP_BASE + 0x40eb4, 0x6c6c6c); + outpdw(MDP_BASE + 0x40eb8, 0x6d6d6d); + outpdw(MDP_BASE + 0x40ebc, 0x6f6f6f); + outpdw(MDP_BASE + 0x40ec0, 0x707070); + outpdw(MDP_BASE + 0x40ec4, 0x717171); + outpdw(MDP_BASE + 0x40ec8, 0x737373); + outpdw(MDP_BASE + 0x40ecc, 0x747474); + outpdw(MDP_BASE + 0x40ed0, 0x767676); + outpdw(MDP_BASE + 0x40ed4, 0x777777); + outpdw(MDP_BASE + 0x40ed8, 0x797979); + outpdw(MDP_BASE + 0x40edc, 0x7a7a7a); + outpdw(MDP_BASE + 0x40ee0, 0x7c7c7c); + outpdw(MDP_BASE + 0x40ee4, 0x7d7d7d); + outpdw(MDP_BASE + 0x40ee8, 0x7f7f7f); + outpdw(MDP_BASE + 0x40eec, 0x808080); + outpdw(MDP_BASE + 0x40ef0, 0x828282); + outpdw(MDP_BASE + 0x40ef4, 0x838383); + outpdw(MDP_BASE + 0x40ef8, 0x858585); + outpdw(MDP_BASE + 0x40efc, 0x868686); + outpdw(MDP_BASE + 0x40f00, 0x888888); + outpdw(MDP_BASE + 0x40f04, 0x898989); + outpdw(MDP_BASE + 0x40f08, 0x8b8b8b); + outpdw(MDP_BASE + 0x40f0c, 0x8d8d8d); + outpdw(MDP_BASE + 0x40f10, 0x8e8e8e); + outpdw(MDP_BASE + 0x40f14, 0x909090); + outpdw(MDP_BASE + 0x40f18, 0x919191); + outpdw(MDP_BASE + 0x40f1c, 0x939393); + outpdw(MDP_BASE + 0x40f20, 0x959595); + outpdw(MDP_BASE + 0x40f24, 0x969696); + outpdw(MDP_BASE + 0x40f28, 0x989898); + outpdw(MDP_BASE + 0x40f2c, 0x9a9a9a); + outpdw(MDP_BASE + 0x40f30, 0x9b9b9b); + outpdw(MDP_BASE + 0x40f34, 0x9d9d9d); + outpdw(MDP_BASE + 0x40f38, 0x9f9f9f); + outpdw(MDP_BASE + 0x40f3c, 0xa1a1a1); + outpdw(MDP_BASE + 0x40f40, 0xa2a2a2); + outpdw(MDP_BASE + 0x40f44, 0xa4a4a4); + outpdw(MDP_BASE + 0x40f48, 0xa6a6a6); + outpdw(MDP_BASE + 0x40f4c, 0xa7a7a7); + outpdw(MDP_BASE + 0x40f50, 0xa9a9a9); + outpdw(MDP_BASE + 0x40f54, 0xababab); + outpdw(MDP_BASE + 0x40f58, 0xadadad); + outpdw(MDP_BASE + 0x40f5c, 0xafafaf); + outpdw(MDP_BASE + 0x40f60, 0xb0b0b0); + outpdw(MDP_BASE + 0x40f64, 0xb2b2b2); + outpdw(MDP_BASE + 0x40f68, 0xb4b4b4); + outpdw(MDP_BASE + 0x40f6c, 0xb6b6b6); + outpdw(MDP_BASE + 0x40f70, 0xb8b8b8); + outpdw(MDP_BASE + 0x40f74, 0xbababa); + outpdw(MDP_BASE + 0x40f78, 0xbbbbbb); + outpdw(MDP_BASE + 0x40f7c, 0xbdbdbd); + outpdw(MDP_BASE + 0x40f80, 0xbfbfbf); + outpdw(MDP_BASE + 0x40f84, 0xc1c1c1); + outpdw(MDP_BASE + 0x40f88, 0xc3c3c3); + outpdw(MDP_BASE + 0x40f8c, 0xc5c5c5); + outpdw(MDP_BASE + 0x40f90, 0xc7c7c7); + outpdw(MDP_BASE + 0x40f94, 0xc9c9c9); + outpdw(MDP_BASE + 0x40f98, 0xcbcbcb); + outpdw(MDP_BASE + 0x40f9c, 0xcdcdcd); + outpdw(MDP_BASE + 0x40fa0, 0xcfcfcf); + outpdw(MDP_BASE + 0x40fa4, 0xd1d1d1); + outpdw(MDP_BASE + 0x40fa8, 0xd3d3d3); + outpdw(MDP_BASE + 0x40fac, 0xd5d5d5); + outpdw(MDP_BASE + 0x40fb0, 0xd7d7d7); + outpdw(MDP_BASE + 0x40fb4, 0xd9d9d9); + outpdw(MDP_BASE + 0x40fb8, 0xdbdbdb); + outpdw(MDP_BASE + 0x40fbc, 0xdddddd); + outpdw(MDP_BASE + 0x40fc0, 0xdfdfdf); + outpdw(MDP_BASE + 0x40fc4, 0xe1e1e1); + outpdw(MDP_BASE + 0x40fc8, 0xe3e3e3); + outpdw(MDP_BASE + 0x40fcc, 0xe5e5e5); + outpdw(MDP_BASE + 0x40fd0, 0xe7e7e7); + outpdw(MDP_BASE + 0x40fd4, 0xe9e9e9); + outpdw(MDP_BASE + 0x40fd8, 0xebebeb); + outpdw(MDP_BASE + 0x40fdc, 0xeeeeee); + outpdw(MDP_BASE + 0x40fe0, 0xf0f0f0); + outpdw(MDP_BASE + 0x40fe4, 0xf2f2f2); + outpdw(MDP_BASE + 0x40fe8, 0xf4f4f4); + outpdw(MDP_BASE + 0x40fec, 0xf6f6f6); + outpdw(MDP_BASE + 0x40ff0, 0xf8f8f8); + outpdw(MDP_BASE + 0x40ff4, 0xfbfbfb); + outpdw(MDP_BASE + 0x40ff8, 0xfdfdfd); + outpdw(MDP_BASE + 0x40ffc, 0xffffff); +} + +#define IRQ_EN_1__MDP_IRQ___M 0x00000800 + +void mdp_hw_init(void) +{ + int i; + + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* debug interface write access */ + outpdw(MDP_BASE + 0x60, 1); + + outp32(MDP_INTR_ENABLE, MDP_ANY_INTR_MASK); + outp32(MDP_EBI2_PORTMAP_MODE, 0x3); + outpdw(MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8, 0x0); + outpdw(MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc, 0x0); + outpdw(MDP_BASE + 0x60, 0x1); + mdp_load_lut_param(); + + /* + * clear up unused fg/main registers + */ + /* comp.plane 2&3 ystride */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0120, 0x0); + /* unpacked pattern */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x012c, 0x0); + /* unpacked pattern */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0130, 0x0); + /* unpacked pattern */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0134, 0x0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0158, 0x0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x15c, 0x0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0160, 0x0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0170, 0x0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0174, 0x0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x017c, 0x0); + + /* comp.plane 2 */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0114, 0x0); + /* comp.plane 3 */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0118, 0x0); + + /* clear up unused bg registers */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8, 0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0, 0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc, 0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0, 0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4, 0); + +#ifndef CONFIG_FB_MSM_MDP22 + MDP_OUTP(MDP_BASE + 0xE0000, 0); + MDP_OUTP(MDP_BASE + 0x100, 0xffffffff); + MDP_OUTP(MDP_BASE + 0x90070, 0); +#endif + + /* + * limit vector + * pre gets applied before color matrix conversion + * post is after ccs + */ + writel(mdp_plv[0], MDP_CSC_PRE_LV1n(0)); + writel(mdp_plv[1], MDP_CSC_PRE_LV1n(1)); + writel(mdp_plv[2], MDP_CSC_PRE_LV1n(2)); + writel(mdp_plv[3], MDP_CSC_PRE_LV1n(3)); + +#ifdef CONFIG_FB_MSM_MDP31 + writel(mdp_plv[2], MDP_CSC_PRE_LV1n(4)); + writel(mdp_plv[3], MDP_CSC_PRE_LV1n(5)); + + writel(0, MDP_CSC_POST_LV1n(0)); + writel(0xff, MDP_CSC_POST_LV1n(1)); + writel(0, MDP_CSC_POST_LV1n(2)); + writel(0xff, MDP_CSC_POST_LV1n(3)); + writel(0, MDP_CSC_POST_LV1n(4)); + writel(0xff, MDP_CSC_POST_LV1n(5)); + + writel(0, MDP_CSC_PRE_LV2n(0)); + writel(0xff, MDP_CSC_PRE_LV2n(1)); + writel(0, MDP_CSC_PRE_LV2n(2)); + writel(0xff, MDP_CSC_PRE_LV2n(3)); + writel(0, MDP_CSC_PRE_LV2n(4)); + writel(0xff, MDP_CSC_PRE_LV2n(5)); + + writel(mdp_plv[0], MDP_CSC_POST_LV2n(0)); + writel(mdp_plv[1], MDP_CSC_POST_LV2n(1)); + writel(mdp_plv[2], MDP_CSC_POST_LV2n(2)); + writel(mdp_plv[3], MDP_CSC_POST_LV2n(3)); + writel(mdp_plv[2], MDP_CSC_POST_LV2n(4)); + writel(mdp_plv[3], MDP_CSC_POST_LV2n(5)); +#endif + + /* primary forward matrix */ + for (i = 0; i < MDP_CCS_SIZE; i++) + writel(mdp_ccs_rgb2yuv.ccs[i], MDP_CSC_PFMVn(i)); + +#ifdef CONFIG_FB_MSM_MDP31 + for (i = 0; i < MDP_BV_SIZE; i++) + writel(mdp_ccs_rgb2yuv.bv[i], MDP_CSC_POST_BV2n(i)); + + writel(0, MDP_CSC_PRE_BV2n(0)); + writel(0, MDP_CSC_PRE_BV2n(1)); + writel(0, MDP_CSC_PRE_BV2n(2)); +#endif + /* primary reverse matrix */ + for (i = 0; i < MDP_CCS_SIZE; i++) + writel(mdp_ccs_yuv2rgb.ccs[i], MDP_CSC_PRMVn(i)); + + for (i = 0; i < MDP_BV_SIZE; i++) + writel(mdp_ccs_yuv2rgb.bv[i], MDP_CSC_PRE_BV1n(i)); + +#ifdef CONFIG_FB_MSM_MDP31 + writel(0, MDP_CSC_POST_BV1n(0)); + writel(0, MDP_CSC_POST_BV1n(1)); + writel(0, MDP_CSC_POST_BV1n(2)); + + outpdw(MDP_BASE + 0x30010, 0x03e0); + outpdw(MDP_BASE + 0x30014, 0x0360); + outpdw(MDP_BASE + 0x30018, 0x0120); + outpdw(MDP_BASE + 0x3001c, 0x0140); +#endif + mdp_init_scale_table(); + +#ifndef CONFIG_FB_MSM_MDP31 + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0104, + ((16 << 6) << 16) | (16) << 6); +#endif + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c new file mode 100644 index 0000000000000000000000000000000000000000..22bab96a42970080ca327dbe243eb8c2dab69225 --- /dev/null +++ b/drivers/video/msm/mdp_lcdc.c @@ -0,0 +1,432 @@ +/* drivers/video/msm/mdp_lcdc.c + * + * Copyright (c) 2009 Google Inc. + * Copyright (c) 2009 The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Author: Dima Zavin + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "mdp_hw.h" + +struct mdp_lcdc_info { + struct mdp_info *mdp; + struct clk *mdp_clk; + struct clk *pclk; + struct clk *pad_pclk; + struct msm_panel_data fb_panel_data; + struct platform_device fb_pdev; + struct msm_lcdc_platform_data *pdata; + uint32_t fb_start; + + struct msmfb_callback frame_start_cb; + wait_queue_head_t vsync_waitq; + int got_vsync; + + struct { + uint32_t clk_rate; + uint32_t hsync_ctl; + uint32_t vsync_period; + uint32_t vsync_pulse_width; + uint32_t display_hctl; + uint32_t display_vstart; + uint32_t display_vend; + uint32_t hsync_skew; + uint32_t polarity; + } parms; +}; + +static struct mdp_device *mdp_dev; + +#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data) + +static int lcdc_unblank(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + pr_info("%s: ()\n", __func__); + panel_ops->unblank(panel_ops); + + return 0; +} + +static int lcdc_blank(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + pr_info("%s: ()\n", __func__); + panel_ops->blank(panel_ops); + + return 0; +} + +static int lcdc_suspend(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + + pr_info("%s: suspending\n", __func__); + + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + clk_disable_unprepare(lcdc->pad_pclk); + clk_disable_unprepare(lcdc->pclk); + clk_disable_unprepare(lcdc->mdp_clk); + + return 0; +} + +static int lcdc_resume(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + + pr_info("%s: resuming\n", __func__); + + clk_prepare_enable(lcdc->mdp_clk); + clk_prepare_enable(lcdc->pclk); + clk_prepare_enable(lcdc->pad_pclk); + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + + return 0; +} + +static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) +{ + struct msm_panel_data *fb_panel = &lcdc->fb_panel_data; + uint32_t dma_cfg; + + clk_prepare_enable(lcdc->mdp_clk); + clk_prepare_enable(lcdc->pclk); + clk_prepare_enable(lcdc->pad_pclk); + + clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate); + clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate); + + /* write the lcdc params */ + mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL); + mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD); + mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width, + MDP_LCDC_VSYNC_PULSE_WIDTH); + mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL); + mdp_writel(lcdc->mdp, lcdc->parms.display_vstart, + MDP_LCDC_DISPLAY_V_START); + mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END); + mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW); + + mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR); + mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); + mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); + + /* config the dma_p block that drives the lcdc data */ + mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); + mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) | + (fb_panel->fb_data->xres & 0x7ff)), + MDP_DMA_P_SIZE); + + mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY); + + dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG); + dma_cfg |= (DMA_PACK_ALIGN_LSB | + DMA_PACK_PATTERN_RGB | + DMA_DITHER_EN); + dma_cfg |= DMA_OUT_SEL_LCDC; + dma_cfg &= ~DMA_DST_BITS_MASK; + + if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666) + dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + else + dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + + mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); + + /* enable the lcdc timing generation */ + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + + return 0; +} + +static void lcdc_wait_vsync(struct msm_panel_data *panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel); + int ret; + + ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2); + if (!ret && !lcdc->got_vsync) + pr_err("%s: timeout waiting for VSYNC\n", __func__); + lcdc->got_vsync = 0; +} + +static void lcdc_request_vsync(struct msm_panel_data *fb_panel, + struct msmfb_callback *vsync_cb) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + + /* the vsync callback will start the dma */ + vsync_cb->func(vsync_cb); + lcdc->got_vsync = 0; + mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START, + &lcdc->frame_start_cb); + lcdc_wait_vsync(fb_panel); +} + +static void lcdc_clear_vsync(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + lcdc->got_vsync = 0; + mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL); +} + +/* called in irq context with mdp lock held, when mdp gets the + * MDP_LCDC_FRAME_START interrupt */ +static void lcdc_frame_start(struct msmfb_callback *cb) +{ + struct mdp_lcdc_info *lcdc; + + lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb); + + lcdc->got_vsync = 1; + wake_up(&lcdc->vsync_waitq); +} + +static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) +{ + struct mdp_lcdc_info *lcdc = priv; + + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + if (mdp->dma_config_dirty) + { + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + mdelay(20); + mdp_dev->configure_dma(mdp_dev); + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + } + mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); + mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR); +} + +static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) +{ + struct msm_lcdc_timing *timing = lcdc->pdata->timing; + struct msm_fb_data *fb_data = lcdc->pdata->fb_data; + unsigned int hsync_period; + unsigned int hsync_start_x; + unsigned int hsync_end_x; + unsigned int vsync_period; + unsigned int display_vstart; + unsigned int display_vend; + + hsync_period = (timing->hsync_back_porch + + fb_data->xres + timing->hsync_front_porch); + hsync_start_x = timing->hsync_back_porch; + hsync_end_x = hsync_start_x + fb_data->xres - 1; + + vsync_period = (timing->vsync_back_porch + + fb_data->yres + timing->vsync_front_porch); + vsync_period *= hsync_period; + + display_vstart = timing->vsync_back_porch; + display_vstart *= hsync_period; + display_vstart += timing->hsync_skew; + + display_vend = (timing->vsync_back_porch + fb_data->yres) * + hsync_period; + display_vend += timing->hsync_skew - 1; + + /* register values we pre-compute at init time from the timing + * information in the panel info */ + lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) | + (timing->hsync_pulse_width & 0xfff)); + lcdc->parms.vsync_period = vsync_period & 0xffffff; + lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width * + hsync_period) & 0xffffff; + + lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) | + (hsync_start_x & 0xfff)); + lcdc->parms.display_vstart = display_vstart & 0xffffff; + lcdc->parms.display_vend = display_vend & 0xffffff; + lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff; + lcdc->parms.polarity = ((timing->hsync_act_low << 0) | + (timing->vsync_act_low << 1) | + (timing->den_act_low << 2)); + lcdc->parms.clk_rate = timing->clk_rate; +} + +static int mdp_lcdc_probe(struct platform_device *pdev) +{ + struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data; + struct mdp_lcdc_info *lcdc; + int ret = 0; + + if (!pdata) { + pr_err("%s: no LCDC platform data found\n", __func__); + return -EINVAL; + } + + lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL); + if (!lcdc) + return -ENOMEM; + + /* We don't actually own the clocks, the mdp does. */ + lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk"); + if (IS_ERR(lcdc->mdp_clk)) { + pr_err("%s: failed to get mdp_clk\n", __func__); + ret = PTR_ERR(lcdc->mdp_clk); + goto err_get_mdp_clk; + } + + lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk"); + if (IS_ERR(lcdc->pclk)) { + pr_err("%s: failed to get lcdc_pclk\n", __func__); + ret = PTR_ERR(lcdc->pclk); + goto err_get_pclk; + } + + lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk"); + if (IS_ERR(lcdc->pad_pclk)) { + pr_err("%s: failed to get lcdc_pad_pclk\n", __func__); + ret = PTR_ERR(lcdc->pad_pclk); + goto err_get_pad_pclk; + } + + init_waitqueue_head(&lcdc->vsync_waitq); + lcdc->pdata = pdata; + lcdc->frame_start_cb.func = lcdc_frame_start; + + platform_set_drvdata(pdev, lcdc); + + mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE, + lcdc_dma_start); + + precompute_timing_parms(lcdc); + + lcdc->fb_start = pdata->fb_resource->start; + lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + lcdc->fb_panel_data.suspend = lcdc_suspend; + lcdc->fb_panel_data.resume = lcdc_resume; + lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync; + lcdc->fb_panel_data.request_vsync = lcdc_request_vsync; + lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync; + lcdc->fb_panel_data.blank = lcdc_blank; + lcdc->fb_panel_data.unblank = lcdc_unblank; + lcdc->fb_panel_data.fb_data = pdata->fb_data; + lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE; + + ret = lcdc_hw_init(lcdc); + if (ret) { + pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__); + goto err_hw_init; + } + + lcdc->fb_pdev.name = "msm_panel"; + lcdc->fb_pdev.id = pdata->fb_id; + lcdc->fb_pdev.resource = pdata->fb_resource; + lcdc->fb_pdev.num_resources = 1; + lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data; + + if (pdata->panel_ops->init) + pdata->panel_ops->init(pdata->panel_ops); + + ret = platform_device_register(&lcdc->fb_pdev); + if (ret) { + pr_err("%s: Cannot register msm_panel pdev\n", __func__); + goto err_plat_dev_reg; + } + + pr_info("%s: initialized\n", __func__); + + return 0; + +err_plat_dev_reg: +err_hw_init: + platform_set_drvdata(pdev, NULL); + clk_put(lcdc->pad_pclk); +err_get_pad_pclk: + clk_put(lcdc->pclk); +err_get_pclk: + clk_put(lcdc->mdp_clk); +err_get_mdp_clk: + kfree(lcdc); + return ret; +} + +static int mdp_lcdc_remove(struct platform_device *pdev) +{ + struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + clk_put(lcdc->pclk); + clk_put(lcdc->pad_pclk); + kfree(lcdc); + + return 0; +} + +static struct platform_driver mdp_lcdc_driver = { + .probe = mdp_lcdc_probe, + .remove = mdp_lcdc_remove, + .driver = { + .name = "msm_mdp_lcdc", + .owner = THIS_MODULE, + }, +}; + +static int mdp_lcdc_add_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (mdp_dev) + return 0; + mdp_dev = container_of(dev, struct mdp_device, dev); + return platform_driver_register(&mdp_lcdc_driver); +} + +static void mdp_lcdc_remove_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (dev != &mdp_dev->dev) + return; + platform_driver_unregister(&mdp_lcdc_driver); + mdp_dev = NULL; +} + +static struct class_interface mdp_lcdc_interface = { + .add_dev = &mdp_lcdc_add_mdp_device, + .remove_dev = &mdp_lcdc_remove_mdp_device, +}; + +static int __init mdp_lcdc_init(void) +{ + return register_mdp_client(&mdp_lcdc_interface); +} + +module_init(mdp_lcdc_init); diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c new file mode 100644 index 0000000000000000000000000000000000000000..91fa68f675fe481c54a7289b8022b9a6b04a4a1d --- /dev/null +++ b/drivers/video/msm/mdp_ppp.c @@ -0,0 +1,1686 @@ +/* drivers/video/msm/src/drv/mdp/mdp_ppp.c + * + * Copyright (C) 2007 Google Incorporated + * Copyright (c) 2008-2009, 2012 The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linux/proc_fs.h" + +#include +#include + +#include +#include +#include +#include + +#include "mdp.h" +#include "msm_fb.h" + +#define MDP_IS_IMGTYPE_BAD(x) (((x) >= MDP_IMGTYPE_LIMIT) && \ + (((x) < MDP_IMGTYPE2_START) || \ + ((x) >= MDP_IMGTYPE_LIMIT2))) + +static uint32_t bytes_per_pixel[] = { + [MDP_RGB_565] = 2, + [MDP_RGB_888] = 3, + [MDP_XRGB_8888] = 4, + [MDP_ARGB_8888] = 4, + [MDP_RGBA_8888] = 4, + [MDP_BGRA_8888] = 4, + [MDP_RGBX_8888] = 4, + [MDP_Y_CBCR_H2V1] = 1, + [MDP_Y_CBCR_H2V2] = 1, + [MDP_Y_CBCR_H2V2_ADRENO] = 1, + [MDP_Y_CRCB_H2V1] = 1, + [MDP_Y_CRCB_H2V2] = 1, + [MDP_YCRYCB_H2V1] = 2, + [MDP_BGR_565] = 2 +}; + +extern uint32 mdp_plv[]; +extern struct semaphore mdp_ppp_mutex; + +int mdp_get_bytes_per_pixel(uint32_t format, + struct msm_fb_data_type *mfd) +{ + int bpp = -EINVAL; + if (format == MDP_FB_FORMAT) + format = mfd->fb_imgType; + if (format < ARRAY_SIZE(bytes_per_pixel)) + bpp = bytes_per_pixel[format]; + + if (bpp <= 0) + printk(KERN_ERR "%s incorrect format %d\n", __func__, format); + return bpp; +} + +static uint32 mdp_conv_matx_rgb2yuv(uint32 input_pixel, + uint16 *matrix_and_bias_vector, + uint32 *clamp_vector, + uint32 *look_up_table) +{ + uint8 input_C2, input_C0, input_C1; + uint32 output; + int32 comp_C2, comp_C1, comp_C0, temp; + int32 temp1, temp2, temp3; + int32 matrix[9]; + int32 bias_vector[3]; + int32 Y_low_limit, Y_high_limit, C_low_limit, C_high_limit; + int32 i; + uint32 _is_lookup_table_enabled; + + input_C2 = (input_pixel >> 16) & 0xFF; + input_C1 = (input_pixel >> 8) & 0xFF; + input_C0 = (input_pixel >> 0) & 0xFF; + + comp_C0 = input_C0; + comp_C1 = input_C1; + comp_C2 = input_C2; + + for (i = 0; i < 9; i++) + matrix[i] = + ((int32) (((int32) matrix_and_bias_vector[i]) << 20)) >> 20; + + bias_vector[0] = (int32) (matrix_and_bias_vector[9] & 0xFF); + bias_vector[1] = (int32) (matrix_and_bias_vector[10] & 0xFF); + bias_vector[2] = (int32) (matrix_and_bias_vector[11] & 0xFF); + + Y_low_limit = (int32) clamp_vector[0]; + Y_high_limit = (int32) clamp_vector[1]; + C_low_limit = (int32) clamp_vector[2]; + C_high_limit = (int32) clamp_vector[3]; + + if (look_up_table == 0) /* check for NULL point */ + _is_lookup_table_enabled = 0; + else + _is_lookup_table_enabled = 1; + + if (_is_lookup_table_enabled == 1) { + comp_C2 = (look_up_table[comp_C2] >> 16) & 0xFF; + comp_C1 = (look_up_table[comp_C1] >> 8) & 0xFF; + comp_C0 = (look_up_table[comp_C0] >> 0) & 0xFF; + } + /* + * Color Conversion + * reorder input colors + */ + temp = comp_C2; + comp_C2 = comp_C1; + comp_C1 = comp_C0; + comp_C0 = temp; + + /* matrix multiplication */ + temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] + comp_C2 * matrix[2]; + temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] + comp_C2 * matrix[5]; + temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] + comp_C2 * matrix[8]; + + comp_C0 = temp1 + 0x100; + comp_C1 = temp2 + 0x100; + comp_C2 = temp3 + 0x100; + + /* take interger part */ + comp_C0 >>= 9; + comp_C1 >>= 9; + comp_C2 >>= 9; + + /* post bias (+) */ + comp_C0 += bias_vector[0]; + comp_C1 += bias_vector[1]; + comp_C2 += bias_vector[2]; + + /* limit pixel to 8-bit */ + if (comp_C0 < 0) + comp_C0 = 0; + + if (comp_C0 > 255) + comp_C0 = 255; + + if (comp_C1 < 0) + comp_C1 = 0; + + if (comp_C1 > 255) + comp_C1 = 255; + + if (comp_C2 < 0) + comp_C2 = 0; + + if (comp_C2 > 255) + comp_C2 = 255; + + /* clamp */ + if (comp_C0 < Y_low_limit) + comp_C0 = Y_low_limit; + + if (comp_C0 > Y_high_limit) + comp_C0 = Y_high_limit; + + if (comp_C1 < C_low_limit) + comp_C1 = C_low_limit; + + if (comp_C1 > C_high_limit) + comp_C1 = C_high_limit; + + if (comp_C2 < C_low_limit) + comp_C2 = C_low_limit; + + if (comp_C2 > C_high_limit) + comp_C2 = C_high_limit; + + output = (comp_C2 << 16) | (comp_C1 << 8) | comp_C0; + return output; +} + +uint32 mdp_conv_matx_yuv2rgb(uint32 input_pixel, + uint16 *matrix_and_bias_vector, + uint32 *clamp_vector, uint32 *look_up_table) +{ + uint8 input_C2, input_C0, input_C1; + uint32 output; + int32 comp_C2, comp_C1, comp_C0, temp; + int32 temp1, temp2, temp3; + int32 matrix[9]; + int32 bias_vector[3]; + int32 Y_low_limit, Y_high_limit, C_low_limit, C_high_limit; + int32 i; + uint32 _is_lookup_table_enabled; + + input_C2 = (input_pixel >> 16) & 0xFF; + input_C1 = (input_pixel >> 8) & 0xFF; + input_C0 = (input_pixel >> 0) & 0xFF; + + comp_C0 = input_C0; + comp_C1 = input_C1; + comp_C2 = input_C2; + + for (i = 0; i < 9; i++) + matrix[i] = + ((int32) (((int32) matrix_and_bias_vector[i]) << 20)) >> 20; + + bias_vector[0] = (int32) (matrix_and_bias_vector[9] & 0xFF); + bias_vector[1] = (int32) (matrix_and_bias_vector[10] & 0xFF); + bias_vector[2] = (int32) (matrix_and_bias_vector[11] & 0xFF); + + Y_low_limit = (int32) clamp_vector[0]; + Y_high_limit = (int32) clamp_vector[1]; + C_low_limit = (int32) clamp_vector[2]; + C_high_limit = (int32) clamp_vector[3]; + + if (look_up_table == 0) /* check for NULL point */ + _is_lookup_table_enabled = 0; + else + _is_lookup_table_enabled = 1; + + /* clamp */ + if (comp_C0 < Y_low_limit) + comp_C0 = Y_low_limit; + + if (comp_C0 > Y_high_limit) + comp_C0 = Y_high_limit; + + if (comp_C1 < C_low_limit) + comp_C1 = C_low_limit; + + if (comp_C1 > C_high_limit) + comp_C1 = C_high_limit; + + if (comp_C2 < C_low_limit) + comp_C2 = C_low_limit; + + if (comp_C2 > C_high_limit) + comp_C2 = C_high_limit; + + /* + * Color Conversion + * pre bias (-) + */ + comp_C0 -= bias_vector[0]; + comp_C1 -= bias_vector[1]; + comp_C2 -= bias_vector[2]; + + /* matrix multiplication */ + temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] + comp_C2 * matrix[2]; + temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] + comp_C2 * matrix[5]; + temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] + comp_C2 * matrix[8]; + + comp_C0 = temp1 + 0x100; + comp_C1 = temp2 + 0x100; + comp_C2 = temp3 + 0x100; + + /* take interger part */ + comp_C0 >>= 9; + comp_C1 >>= 9; + comp_C2 >>= 9; + + /* reorder output colors */ + temp = comp_C0; + comp_C0 = comp_C1; + comp_C1 = comp_C2; + comp_C2 = temp; + + /* limit pixel to 8-bit */ + if (comp_C0 < 0) + comp_C0 = 0; + + if (comp_C0 > 255) + comp_C0 = 255; + + if (comp_C1 < 0) + comp_C1 = 0; + + if (comp_C1 > 255) + comp_C1 = 255; + + if (comp_C2 < 0) + comp_C2 = 0; + + if (comp_C2 > 255) + comp_C2 = 255; + + /* Look-up table */ + if (_is_lookup_table_enabled == 1) { + comp_C2 = (look_up_table[comp_C2] >> 16) & 0xFF; + comp_C1 = (look_up_table[comp_C1] >> 8) & 0xFF; + comp_C0 = (look_up_table[comp_C0] >> 0) & 0xFF; + } + + output = (comp_C2 << 16) | (comp_C1 << 8) | comp_C0; + return output; +} + +static uint32 mdp_calc_tpval(MDPIMG *mdpImg) +{ + uint32 tpVal; + uint8 plane_tp; + + tpVal = 0; + if ((mdpImg->imgType == MDP_RGB_565) + || (mdpImg->imgType == MDP_BGR_565)) { + /* + * transparent color conversion into 24 bpp + * + * C2R_8BIT + * left shift the entire bit and or it with the upper most bits + */ + plane_tp = (uint8) ((mdpImg->tpVal & 0xF800) >> 11); + tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 16; + + /* C1B_8BIT */ + plane_tp = (uint8) (mdpImg->tpVal & 0x1F); + tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 8; + + /* C0G_8BIT */ + plane_tp = (uint8) ((mdpImg->tpVal & 0x7E0) >> 5); + tpVal |= ((plane_tp << 2) | ((plane_tp & 0x30) >> 4)); + } else { + /* 24bit RGB to RBG conversion */ + + tpVal = (mdpImg->tpVal & 0xFF00) >> 8; + tpVal |= (mdpImg->tpVal & 0xFF) << 8; + tpVal |= (mdpImg->tpVal & 0xFF0000); + } + + return tpVal; +} + +static uint8 *mdp_get_chroma_addr(MDPIBUF *iBuf) +{ + uint8 *dest1; + + dest1 = NULL; + switch (iBuf->ibuf_type) { + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + dest1 = (uint8 *) iBuf->buf; + dest1 += iBuf->ibuf_width * iBuf->ibuf_height * iBuf->bpp; + break; + + default: + break; + } + + return dest1; +} + +static void mdp_ppp_setbg(MDPIBUF *iBuf) +{ + uint8 *bg0_addr; + uint8 *bg1_addr; + uint32 bg0_ystride, bg1_ystride; + uint32 ppp_src_cfg_reg, unpack_pattern; + int v_slice, h_slice; + + v_slice = h_slice = 1; + bg0_addr = (uint8 *) iBuf->buf; + bg1_addr = mdp_get_chroma_addr(iBuf); + + bg0_ystride = iBuf->ibuf_width * iBuf->bpp; + bg1_ystride = iBuf->ibuf_width * iBuf->bpp; + + switch (iBuf->ibuf_type) { + case MDP_BGR_565: + case MDP_RGB_565: + /* 888 = 3bytes + * RGB = 3Components + * RGB interleaved + */ + ppp_src_cfg_reg = PPP_SRC_C2R_5BITS | PPP_SRC_C0G_6BITS | + PPP_SRC_C1B_5BITS | PPP_SRC_BPP_INTERLVD_2BYTES | + PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | + PPP_SRC_FETCH_PLANES_INTERLVD; + + if (iBuf->ibuf_type == MDP_RGB_565) + unpack_pattern = + MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8); + else + unpack_pattern = + MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8); + break; + + case MDP_RGB_888: + /* + * 888 = 3bytes + * RGB = 3Components + * RGB interleaved + */ + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | PPP_SRC_BPP_INTERLVD_3BYTES | + PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_INTERLVD; + + unpack_pattern = + MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8); + break; + + case MDP_BGRA_8888: + case MDP_RGBA_8888: + case MDP_ARGB_8888: + case MDP_XRGB_8888: + case MDP_RGBX_8888: + /* + * 8888 = 4bytes + * ARGB = 4Components + * ARGB interleaved + */ + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | PPP_SRC_C3A_8BITS | PPP_SRC_C3_ALPHA_EN | + PPP_SRC_BPP_INTERLVD_4BYTES | PPP_SRC_INTERLVD_4COMPONENTS | + PPP_SRC_UNPACK_TIGHT | PPP_SRC_UNPACK_ALIGN_LSB | + PPP_SRC_FETCH_PLANES_INTERLVD; + + if (iBuf->ibuf_type == MDP_BGRA_8888) + unpack_pattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + else if (iBuf->ibuf_type == MDP_RGBA_8888 || + iBuf->ibuf_type == MDP_RGBX_8888) + unpack_pattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, + 8); + else if (iBuf->ibuf_type == MDP_XRGB_8888) + unpack_pattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + else + unpack_pattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + break; + + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | + PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | + PPP_SRC_C3A_8BITS | + PPP_SRC_BPP_INTERLVD_2BYTES | + PPP_SRC_INTERLVD_2COMPONENTS | + PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_PSEUDOPLNR; + + if (iBuf->ibuf_type == MDP_Y_CBCR_H2V1) + unpack_pattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8); + else + unpack_pattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8); + v_slice = h_slice = 2; + break; + + case MDP_YCRYCB_H2V1: + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | + PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | + PPP_SRC_C3A_8BITS | + PPP_SRC_BPP_INTERLVD_2BYTES | + PPP_SRC_INTERLVD_4COMPONENTS | + PPP_SRC_UNPACK_TIGHT | PPP_SRC_UNPACK_ALIGN_LSB; + + unpack_pattern = + MDP_GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8); + h_slice = 2; + break; + + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | + PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | + PPP_SRC_C3A_8BITS | + PPP_SRC_BPP_INTERLVD_2BYTES | + PPP_SRC_INTERLVD_2COMPONENTS | + PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_PSEUDOPLNR; + + if (iBuf->ibuf_type == MDP_Y_CBCR_H2V1) + unpack_pattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8); + else + unpack_pattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8); + h_slice = 2; + break; + + default: + return; + } + + /* starting input address adjustment */ + mdp_adjust_start_addr(&bg0_addr, &bg1_addr, v_slice, h_slice, + iBuf->roi.lcd_x, iBuf->roi.lcd_y, + iBuf->ibuf_width, iBuf->ibuf_height, iBuf->bpp, + iBuf, 1); + + /* + * 0x01c0: background plane 0 addr + * 0x01c4: background plane 1 addr + * 0x01c8: background plane 2 addr + * 0x01cc: bg y stride for plane 0 and 1 + * 0x01d0: bg y stride for plane 2 + * 0x01d4: bg src PPP config + * 0x01d8: unpack pattern + */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01c0, bg0_addr); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01c4, bg1_addr); + + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01cc, + (bg1_ystride << 16) | bg0_ystride); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01d4, ppp_src_cfg_reg); + + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01d8, unpack_pattern); +} + +#define IS_PSEUDOPLNR(img) ((img == MDP_Y_CRCB_H2V2) | \ + (img == MDP_Y_CBCR_H2V2) | \ + (img == MDP_Y_CBCR_H2V2_ADRENO) | \ + (img == MDP_Y_CRCB_H2V1) | \ + (img == MDP_Y_CBCR_H2V1)) + +#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp) + +#define Y_TO_CRCB_RATIO(format) \ + ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CBCR_H2V2_ADRENO || \ + format == MDP_Y_CRCB_H2V2) ? 2 : (format == MDP_Y_CBCR_H2V1 || \ + format == MDP_Y_CRCB_H2V1) ? 1 : 1) + +#ifdef CONFIG_ANDROID_PMEM +static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp, + uint32_t *len0, uint32_t *len1) +{ + *len0 = IMG_LEN(rect->h, img->width, rect->w, bpp); + if (IS_PSEUDOPLNR(img->format)) + *len1 = *len0/Y_TO_CRCB_RATIO(img->format); + else + *len1 = 0; +} + +static void flush_imgs(struct mdp_blit_req *req, int src_bpp, int dst_bpp, + struct file *p_src_file, struct file *p_dst_file) +{ + uint32_t src0_len, src1_len; + + if (!(req->flags & MDP_BLIT_NON_CACHED)) { + /* flush src images to memory before dma to mdp */ + get_len(&req->src, &req->src_rect, src_bpp, + &src0_len, &src1_len); + + flush_pmem_file(p_src_file, + req->src.offset, src0_len); + + if (IS_PSEUDOPLNR(req->src.format)) + flush_pmem_file(p_src_file, + req->src.offset + src0_len, src1_len); + } + +} +#else +static void flush_imgs(struct mdp_blit_req *req, int src_bpp, int dst_bpp, + struct file *p_src_file, struct file *p_dst_file) { } +#endif + +static void mdp_start_ppp(struct msm_fb_data_type *mfd, MDPIBUF *iBuf, +struct mdp_blit_req *req, struct file *p_src_file, struct file *p_dst_file) +{ + uint8 *src0, *src1; + uint8 *dest0, *dest1; + uint16 inpBpp; + uint32 dest0_ystride; + uint32 src_width; + uint32 src_height; + uint32 src0_ystride; + uint32 src0_y1stride; + uint32 dst_roi_width; + uint32 dst_roi_height; + uint32 ppp_src_cfg_reg, ppp_operation_reg, ppp_dst_cfg_reg; + uint32 alpha, tpVal; + uint32 packPattern; + uint32 dst_packPattern; + boolean inputRGB, outputRGB, pseudoplanr_output; + int sv_slice, sh_slice; + int dv_slice, dh_slice; + boolean perPixelAlpha = FALSE; + boolean ppp_lookUp_enable = FALSE; + + sv_slice = sh_slice = dv_slice = dh_slice = 1; + alpha = tpVal = 0; + src_width = iBuf->mdpImg.width; + src_height = iBuf->roi.y + iBuf->roi.height; + src1 = NULL; + dest1 = NULL; + + inputRGB = outputRGB = TRUE; + pseudoplanr_output = FALSE; + ppp_operation_reg = 0; + ppp_dst_cfg_reg = 0; + ppp_src_cfg_reg = 0; + + /* Wait for the pipe to clear */ + do { } while (mdp_ppp_pipe_wait() <= 0); + + /* + * destination config + */ + switch (iBuf->ibuf_type) { + case MDP_RGB_888: + dst_packPattern = + MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8); + ppp_dst_cfg_reg = + PPP_DST_C0G_8BIT | PPP_DST_C1B_8BIT | PPP_DST_C2R_8BIT | + PPP_DST_PACKET_CNT_INTERLVD_3ELEM | PPP_DST_PACK_TIGHT | + PPP_DST_PACK_ALIGN_LSB | PPP_DST_OUT_SEL_AXI | + PPP_DST_BPP_3BYTES | PPP_DST_PLANE_INTERLVD; + break; + + case MDP_BGRA_8888: + case MDP_XRGB_8888: + case MDP_ARGB_8888: + case MDP_RGBA_8888: + case MDP_RGBX_8888: + if (iBuf->ibuf_type == MDP_BGRA_8888) + dst_packPattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + else if (iBuf->ibuf_type == MDP_RGBA_8888 || + iBuf->ibuf_type == MDP_RGBX_8888) + dst_packPattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, + 8); + else if (iBuf->ibuf_type == MDP_XRGB_8888) + dst_packPattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + else + dst_packPattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + + ppp_dst_cfg_reg = PPP_DST_C0G_8BIT | + PPP_DST_C1B_8BIT | + PPP_DST_C2R_8BIT | + PPP_DST_C3A_8BIT | + PPP_DST_C3ALPHA_EN | + PPP_DST_PACKET_CNT_INTERLVD_4ELEM | + PPP_DST_PACK_TIGHT | + PPP_DST_PACK_ALIGN_LSB | + PPP_DST_OUT_SEL_AXI | + PPP_DST_BPP_4BYTES | PPP_DST_PLANE_INTERLVD; + break; + + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + if (iBuf->ibuf_type == MDP_Y_CBCR_H2V2) + dst_packPattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8); + else + dst_packPattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8); + + ppp_dst_cfg_reg = PPP_DST_C2R_8BIT | + PPP_DST_C0G_8BIT | + PPP_DST_C1B_8BIT | + PPP_DST_C3A_8BIT | + PPP_DST_PACKET_CNT_INTERLVD_2ELEM | + PPP_DST_PACK_TIGHT | + PPP_DST_PACK_ALIGN_LSB | + PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES; + + ppp_operation_reg |= PPP_OP_DST_CHROMA_420; + outputRGB = FALSE; + pseudoplanr_output = TRUE; + /* + * vertically (y direction) and horizontally (x direction) + * sample reduction by 2 + */ + + /* + * H2V2(YUV420) Cosite + * + * Y Y Y Y + * CbCr CbCr + * Y Y Y Y + * Y Y Y Y + * CbCr CbCr + * Y Y Y Y + */ + dv_slice = dh_slice = 2; + + /* (x,y) and (width,height) must be even numbern */ + iBuf->roi.lcd_x = (iBuf->roi.lcd_x / 2) * 2; + iBuf->roi.dst_width = (iBuf->roi.dst_width / 2) * 2; + iBuf->roi.x = (iBuf->roi.x / 2) * 2; + iBuf->roi.width = (iBuf->roi.width / 2) * 2; + + iBuf->roi.lcd_y = (iBuf->roi.lcd_y / 2) * 2; + iBuf->roi.dst_height = (iBuf->roi.dst_height / 2) * 2; + iBuf->roi.y = (iBuf->roi.y / 2) * 2; + iBuf->roi.height = (iBuf->roi.height / 2) * 2; + break; + + case MDP_YCRYCB_H2V1: + dst_packPattern = + MDP_GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8); + ppp_dst_cfg_reg = + PPP_DST_C2R_8BIT | PPP_DST_C0G_8BIT | PPP_DST_C1B_8BIT | + PPP_DST_C3A_8BIT | PPP_DST_PACKET_CNT_INTERLVD_4ELEM | + PPP_DST_PACK_TIGHT | PPP_DST_PACK_ALIGN_LSB | + PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES | + PPP_DST_PLANE_INTERLVD; + + ppp_operation_reg |= PPP_OP_DST_CHROMA_H2V1; + outputRGB = FALSE; + /* + * horizontally (x direction) sample reduction by 2 + * + * H2V1(YUV422) Cosite + * + * YCbCr Y YCbCr Y + * YCbCr Y YCbCr Y + * YCbCr Y YCbCr Y + * YCbCr Y YCbCr Y + */ + dh_slice = 2; + + /* + * if it's TV-Out/MDP_YCRYCB_H2V1, let's go through the + * preloaded gamma setting of 2.2 when the content is + * non-linear ppp_lookUp_enable = TRUE; + */ + + /* x and width must be even number */ + iBuf->roi.lcd_x = (iBuf->roi.lcd_x / 2) * 2; + iBuf->roi.dst_width = (iBuf->roi.dst_width / 2) * 2; + iBuf->roi.x = (iBuf->roi.x / 2) * 2; + iBuf->roi.width = (iBuf->roi.width / 2) * 2; + break; + + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + if (iBuf->ibuf_type == MDP_Y_CBCR_H2V1) + dst_packPattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8); + else + dst_packPattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8); + + ppp_dst_cfg_reg = PPP_DST_C2R_8BIT | + PPP_DST_C0G_8BIT | + PPP_DST_C1B_8BIT | + PPP_DST_C3A_8BIT | + PPP_DST_PACKET_CNT_INTERLVD_2ELEM | + PPP_DST_PACK_TIGHT | + PPP_DST_PACK_ALIGN_LSB | + PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES; + + ppp_operation_reg |= PPP_OP_DST_CHROMA_H2V1; + outputRGB = FALSE; + pseudoplanr_output = TRUE; + /* horizontally (x direction) sample reduction by 2 */ + dh_slice = 2; + + /* x and width must be even number */ + iBuf->roi.lcd_x = (iBuf->roi.lcd_x / 2) * 2; + iBuf->roi.dst_width = (iBuf->roi.dst_width / 2) * 2; + iBuf->roi.x = (iBuf->roi.x / 2) * 2; + iBuf->roi.width = (iBuf->roi.width / 2) * 2; + break; + + case MDP_BGR_565: + case MDP_RGB_565: + default: + if (iBuf->ibuf_type == MDP_RGB_565) + dst_packPattern = + MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8); + else + dst_packPattern = + MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8); + + ppp_dst_cfg_reg = PPP_DST_C0G_6BIT | + PPP_DST_C1B_5BIT | + PPP_DST_C2R_5BIT | + PPP_DST_PACKET_CNT_INTERLVD_3ELEM | + PPP_DST_PACK_TIGHT | + PPP_DST_PACK_ALIGN_LSB | + PPP_DST_OUT_SEL_AXI | + PPP_DST_BPP_2BYTES | PPP_DST_PLANE_INTERLVD; + break; + } + + /* source config */ + switch (iBuf->mdpImg.imgType) { + case MDP_RGB_888: + inpBpp = 3; + /* + * 565 = 2bytes + * RGB = 3Components + * RGB interleaved + */ + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | PPP_SRC_BPP_INTERLVD_3BYTES | + PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | + PPP_SRC_FETCH_PLANES_INTERLVD; + + packPattern = MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8); + + ppp_operation_reg |= PPP_OP_COLOR_SPACE_RGB | + PPP_OP_SRC_CHROMA_RGB | PPP_OP_DST_CHROMA_RGB; + break; + + case MDP_BGRA_8888: + case MDP_RGBA_8888: + case MDP_ARGB_8888: + perPixelAlpha = TRUE; + case MDP_XRGB_8888: + case MDP_RGBX_8888: + inpBpp = 4; + /* + * 8888 = 4bytes + * ARGB = 4Components + * ARGB interleaved + */ + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | PPP_SRC_C3A_8BITS | + PPP_SRC_C3_ALPHA_EN | PPP_SRC_BPP_INTERLVD_4BYTES | + PPP_SRC_INTERLVD_4COMPONENTS | PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | + PPP_SRC_FETCH_PLANES_INTERLVD; + + if (iBuf->mdpImg.imgType == MDP_BGRA_8888) + packPattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + else if (iBuf->mdpImg.imgType == MDP_RGBA_8888 || + iBuf->mdpImg.imgType == MDP_RGBX_8888) + packPattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, + 8); + else if (iBuf->ibuf_type == MDP_XRGB_8888) + packPattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + else + packPattern = + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, + 8); + + ppp_operation_reg |= PPP_OP_COLOR_SPACE_RGB | + PPP_OP_SRC_CHROMA_RGB | PPP_OP_DST_CHROMA_RGB; + break; + + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_ADRENO: + case MDP_Y_CRCB_H2V2: + inpBpp = 1; + src1 = (uint8 *) iBuf->mdpImg.cbcr_addr; + + /* + * CbCr = 2bytes + * CbCr = 2Components + * Y+CbCr + */ + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | PPP_SRC_BPP_INTERLVD_2BYTES | + PPP_SRC_INTERLVD_2COMPONENTS | PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | + PPP_SRC_FETCH_PLANES_PSEUDOPLNR; + + if (iBuf->mdpImg.imgType == MDP_Y_CRCB_H2V2) + packPattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8); + else + packPattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8); + + ppp_operation_reg |= PPP_OP_COLOR_SPACE_YCBCR | + PPP_OP_SRC_CHROMA_420 | + PPP_OP_SRC_CHROMA_COSITE | + PPP_OP_DST_CHROMA_RGB | PPP_OP_DST_CHROMA_COSITE; + + inputRGB = FALSE; + sh_slice = sv_slice = 2; + break; + + case MDP_YCRYCB_H2V1: + inpBpp = 2; + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | + PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | + PPP_SRC_C3A_8BITS | + PPP_SRC_BPP_INTERLVD_2BYTES | + PPP_SRC_INTERLVD_4COMPONENTS | + PPP_SRC_UNPACK_TIGHT | PPP_SRC_UNPACK_ALIGN_LSB; + + packPattern = + MDP_GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8); + + ppp_operation_reg |= PPP_OP_SRC_CHROMA_H2V1 | + PPP_OP_SRC_CHROMA_COSITE | PPP_OP_DST_CHROMA_COSITE; + + /* + * if it's TV-Out/MDP_YCRYCB_H2V1, let's go through the + * preloaded inverse gamma setting of 2.2 since they're + * symetric when the content is non-linear + * ppp_lookUp_enable = TRUE; + */ + + /* x and width must be even number */ + iBuf->roi.lcd_x = (iBuf->roi.lcd_x / 2) * 2; + iBuf->roi.dst_width = (iBuf->roi.dst_width / 2) * 2; + iBuf->roi.x = (iBuf->roi.x / 2) * 2; + iBuf->roi.width = (iBuf->roi.width / 2) * 2; + + inputRGB = FALSE; + sh_slice = 2; + break; + + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + inpBpp = 1; + src1 = (uint8 *) iBuf->mdpImg.cbcr_addr; + + ppp_src_cfg_reg = PPP_SRC_C2R_8BITS | + PPP_SRC_C0G_8BITS | + PPP_SRC_C1B_8BITS | + PPP_SRC_C3A_8BITS | + PPP_SRC_BPP_INTERLVD_2BYTES | + PPP_SRC_INTERLVD_2COMPONENTS | + PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_PSEUDOPLNR; + + if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V1) + packPattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8); + else + packPattern = + MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8); + + ppp_operation_reg |= PPP_OP_SRC_CHROMA_H2V1 | + PPP_OP_SRC_CHROMA_COSITE | PPP_OP_DST_CHROMA_COSITE; + inputRGB = FALSE; + sh_slice = 2; + break; + + case MDP_BGR_565: + case MDP_RGB_565: + default: + inpBpp = 2; + /* + * 565 = 2bytes + * RGB = 3Components + * RGB interleaved + */ + ppp_src_cfg_reg = PPP_SRC_C2R_5BITS | PPP_SRC_C0G_6BITS | + PPP_SRC_C1B_5BITS | PPP_SRC_BPP_INTERLVD_2BYTES | + PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT | + PPP_SRC_UNPACK_ALIGN_LSB | + PPP_SRC_FETCH_PLANES_INTERLVD; + + if (iBuf->mdpImg.imgType == MDP_RGB_565) + packPattern = + MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8); + else + packPattern = + MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8); + + ppp_operation_reg |= PPP_OP_COLOR_SPACE_RGB | + PPP_OP_SRC_CHROMA_RGB | PPP_OP_DST_CHROMA_RGB; + break; + + } + + if (pseudoplanr_output) + ppp_dst_cfg_reg |= PPP_DST_PLANE_PSEUDOPLN; + + /* YCbCr to RGB color conversion flag */ + if ((!inputRGB) && (outputRGB)) { + ppp_operation_reg |= PPP_OP_CONVERT_YCBCR2RGB | + PPP_OP_CONVERT_ON; + + /* + * primary/secondary is sort of misleading term...but + * in mdp2.2/3.0 we only use primary matrix (forward/rev) + * in mdp3.1 we use set1(prim) and set2(secd) + */ +#ifdef CONFIG_FB_MSM_MDP31 + ppp_operation_reg |= PPP_OP_CONVERT_MATRIX_SECONDARY | + PPP_OP_DST_RGB; + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0240, 0); +#endif + + if (ppp_lookUp_enable) { + ppp_operation_reg |= PPP_OP_LUT_C0_ON | + PPP_OP_LUT_C1_ON | PPP_OP_LUT_C2_ON; + } + } + /* RGB to YCbCr color conversion flag */ + if ((inputRGB) && (!outputRGB)) { + ppp_operation_reg |= PPP_OP_CONVERT_RGB2YCBCR | + PPP_OP_CONVERT_ON; + +#ifdef CONFIG_FB_MSM_MDP31 + ppp_operation_reg |= PPP_OP_CONVERT_MATRIX_PRIMARY | + PPP_OP_DST_YCBCR; + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0240, 0x1e); +#endif + + if (ppp_lookUp_enable) { + ppp_operation_reg |= PPP_OP_LUT_C0_ON | + PPP_OP_LUT_C1_ON | PPP_OP_LUT_C2_ON; + } + } + /* YCbCr to YCbCr color conversion flag */ + if ((!inputRGB) && (!outputRGB)) { + if ((ppp_lookUp_enable) && + (iBuf->mdpImg.imgType != iBuf->ibuf_type)) { + ppp_operation_reg |= PPP_OP_LUT_C0_ON; + } + } + + ppp_src_cfg_reg |= (iBuf->roi.x % 2) ? PPP_SRC_BPP_ROI_ODD_X : 0; + ppp_src_cfg_reg |= (iBuf->roi.y % 2) ? PPP_SRC_BPP_ROI_ODD_Y : 0; + + if (req->flags & MDP_DEINTERLACE) + ppp_operation_reg |= PPP_OP_DEINT_EN; + + /* Dither at DMA side only since iBuf format is RGB888 */ + if (iBuf->mdpImg.mdpOp & MDPOP_DITHER) + ppp_operation_reg |= PPP_OP_DITHER_EN; + + if (iBuf->mdpImg.mdpOp & MDPOP_ROTATION) { + ppp_operation_reg |= PPP_OP_ROT_ON; + + if (iBuf->mdpImg.mdpOp & MDPOP_ROT90) { + ppp_operation_reg |= PPP_OP_ROT_90; + } + if (iBuf->mdpImg.mdpOp & MDPOP_LR) { + ppp_operation_reg |= PPP_OP_FLIP_LR; + } + if (iBuf->mdpImg.mdpOp & MDPOP_UD) { + ppp_operation_reg |= PPP_OP_FLIP_UD; + } + } + + if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO) + src0_ystride = ALIGN(src_width, 32) * inpBpp; + else + src0_ystride = src_width * inpBpp; + + if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO) + src0_y1stride = 2 * ALIGN(src_width/2, 32); + else + src0_y1stride = src0_ystride; + + dest0_ystride = iBuf->ibuf_width * iBuf->bpp; + + /* no need to care about rotation since it's the real-XY. */ + dst_roi_width = iBuf->roi.dst_width; + dst_roi_height = iBuf->roi.dst_height; + + src0 = (uint8 *) iBuf->mdpImg.bmy_addr; + dest0 = (uint8 *) iBuf->buf; + + /* Jumping from Y-Plane to Chroma Plane */ + dest1 = mdp_get_chroma_addr(iBuf); + + /* first pixel addr calculation */ + mdp_adjust_start_addr(&src0, &src1, sv_slice, sh_slice, iBuf->roi.x, + iBuf->roi.y, src_width, src_height, inpBpp, iBuf, + 0); + mdp_adjust_start_addr(&dest0, &dest1, dv_slice, dh_slice, + iBuf->roi.lcd_x, iBuf->roi.lcd_y, + iBuf->ibuf_width, iBuf->ibuf_height, iBuf->bpp, + iBuf, 2); + + /* set scale operation */ + mdp_set_scale(iBuf, dst_roi_width, dst_roi_height, + inputRGB, outputRGB, &ppp_operation_reg); + + /* + * setting background source for blending + */ + mdp_set_blend_attr(iBuf, &alpha, &tpVal, perPixelAlpha, + &ppp_operation_reg); + + if (ppp_operation_reg & PPP_OP_BLEND_ON) { + mdp_ppp_setbg(iBuf); + + if (iBuf->ibuf_type == MDP_YCRYCB_H2V1) { + ppp_operation_reg |= PPP_OP_BG_CHROMA_H2V1; + + if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP) { + tpVal = mdp_conv_matx_rgb2yuv(tpVal, + (uint16 *) & + mdp_ccs_rgb2yuv, + &mdp_plv[0], NULL); + } + } + } + + /* + * 0x0004: enable dbg bus + * 0x0100: "don't care" Edge Condit until scaling is on + * 0x0104: xrc tile x&y size u7.6 format = 7bit.6bit + * 0x0108: src pixel size + * 0x010c: component plane 0 starting address + * 0x011c: component plane 0 ystride + * 0x0124: PPP source config register + * 0x0128: unpacked pattern from lsb to msb (eg. RGB->BGR) + */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0108, (iBuf->roi.height << 16 | + iBuf->roi.width)); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x010c, src0); /* comp.plane 0 */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0110, src1); /* comp.plane 1 */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x011c, + (src0_y1stride << 16 | src0_ystride)); + + /* setup for rgb 565 */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0124, ppp_src_cfg_reg); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0128, packPattern); + /* + * 0x0138: PPP destination operation register + * 0x014c: constant_alpha|transparent_color + * 0x0150: PPP destination config register + * 0x0154: PPP packing pattern + */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0138, ppp_operation_reg); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x014c, alpha << 24 | (tpVal & + 0xffffff)); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0150, ppp_dst_cfg_reg); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0154, dst_packPattern); + + /* + * 0x0164: ROI height and width + * 0x0168: Component Plane 0 starting addr + * 0x016c: Component Plane 1 starting addr + * 0x0178: Component Plane 1/0 y stride + */ + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0164, + (dst_roi_height << 16 | dst_roi_width)); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0168, dest0); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x016c, dest1); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0178, + (dest0_ystride << 16 | dest0_ystride)); + + flush_imgs(req, inpBpp, iBuf->bpp, p_src_file, p_dst_file); +#ifdef CONFIG_FB_MSM_MDP31 + MDP_OUTP(MDP_BASE + 0x00100, 0xFF00); +#endif + mdp_pipe_kickoff(MDP_PPP_TERM, mfd); +} + +static int mdp_ppp_verify_req(struct mdp_blit_req *req) +{ + u32 src_width, src_height, dst_width, dst_height; + + if (req == NULL) { + printk(KERN_ERR "\n%s(): Error in Line %u", __func__, + __LINE__); + return -1; + } + + if (MDP_IS_IMGTYPE_BAD(req->src.format) || + MDP_IS_IMGTYPE_BAD(req->dst.format)) { + printk(KERN_ERR "\n%s(): Error in Line %u", __func__, + __LINE__); + return -1; + } + + if ((req->src.width == 0) || (req->src.height == 0) || + (req->src_rect.w == 0) || (req->src_rect.h == 0) || + (req->dst.width == 0) || (req->dst.height == 0) || + (req->dst_rect.w == 0) || (req->dst_rect.h == 0)) { + printk(KERN_ERR "\n%s(): Error in Line %u", __func__, + __LINE__); + + return -1; + } + + if (((req->src_rect.x + req->src_rect.w) > req->src.width) || + ((req->src_rect.y + req->src_rect.h) > req->src.height)) { + printk(KERN_ERR "\n%s(): Error in Line %u", __func__, + __LINE__); + return -1; + } + + if (((req->dst_rect.x + req->dst_rect.w) > req->dst.width) || + ((req->dst_rect.y + req->dst_rect.h) > req->dst.height)) { + printk(KERN_ERR "\n%s(): Error in Line %u", __func__, + __LINE__); + return -1; + } + + /* + * scaling range check + */ + src_width = req->src_rect.w; + src_height = req->src_rect.h; + + if (req->flags & MDP_ROT_90) { + dst_width = req->dst_rect.h; + dst_height = req->dst_rect.w; + } else { + dst_width = req->dst_rect.w; + dst_height = req->dst_rect.h; + } + + switch (req->dst.format) { + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V2: + src_width = (src_width / 2) * 2; + src_height = (src_height / 2) * 2; + dst_width = (src_width / 2) * 2; + dst_height = (src_height / 2) * 2; + break; + + case MDP_Y_CRCB_H2V1: + case MDP_Y_CBCR_H2V1: + case MDP_YCRYCB_H2V1: + src_width = (src_width / 2) * 2; + dst_width = (src_width / 2) * 2; + break; + + default: + break; + } + + if (((MDP_SCALE_Q_FACTOR * dst_width) / src_width > + MDP_MAX_X_SCALE_FACTOR) + || ((MDP_SCALE_Q_FACTOR * dst_width) / src_width < + MDP_MIN_X_SCALE_FACTOR)) { + printk(KERN_ERR "\n%s(): Error in Line %u", __func__, + __LINE__); + return -1; + } + + if (((MDP_SCALE_Q_FACTOR * dst_height) / src_height > + MDP_MAX_Y_SCALE_FACTOR) + || ((MDP_SCALE_Q_FACTOR * dst_height) / src_height < + MDP_MIN_Y_SCALE_FACTOR)) { + printk(KERN_ERR "\n%s(): Error in Line %u", __func__, + __LINE__); + return -1; + } + return 0; +} + +int get_gem_img(struct mdp_img *img, unsigned long *start, unsigned long *len) +{ + /* Set len to zero to appropriately error out if + kgsl_gem_obj_addr fails */ + + *len = 0; + return kgsl_gem_obj_addr(img->memory_id, (int) img->priv, start, len); +} + +int get_img(struct mdp_img *img, struct fb_info *info, unsigned long *start, + unsigned long *len, struct file **pp_file) +{ + int put_needed, ret = 0; + struct file *file; +#ifdef CONFIG_ANDROID_PMEM + unsigned long vstart; +#endif + +#ifdef CONFIG_ANDROID_PMEM + if (!get_pmem_file(img->memory_id, start, &vstart, len, pp_file)) + return 0; +#endif + file = fget_light(img->memory_id, &put_needed); + if (file == NULL) + return -1; + + if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { + *start = info->fix.smem_start; + *len = info->fix.smem_len; + *pp_file = file; + } else { + ret = -1; + fput_light(file, put_needed); + } + return ret; +} + + +void put_img(struct file *p_src_file) +{ +#ifdef CONFIG_ANDROID_PMEM + if (p_src_file) + put_pmem_file(p_src_file); +#endif +} + + +static int mdp_ppp_blit_addr(struct fb_info *info, struct mdp_blit_req *req, + unsigned long srcp0_start, unsigned long srcp0_len, + unsigned long srcp1_start, unsigned long srcp1_len, + unsigned long dst_start, unsigned long dst_len, + struct file *p_src_file, struct file *p_dst_file) +{ + MDPIBUF iBuf; + u32 dst_width, dst_height; + struct msm_fb_data_type *mfd = info->par; + + if (req->dst.format == MDP_FB_FORMAT) + req->dst.format = mfd->fb_imgType; + if (req->src.format == MDP_FB_FORMAT) + req->src.format = mfd->fb_imgType; + + if (mdp_ppp_verify_req(req)) { + printk(KERN_ERR "mdp_ppp: invalid image!\n"); + put_img(p_src_file); + put_img(p_dst_file); + return -1; + } + + iBuf.ibuf_width = req->dst.width; + iBuf.ibuf_height = req->dst.height; + iBuf.bpp = bytes_per_pixel[req->dst.format]; + + iBuf.ibuf_type = req->dst.format; + iBuf.buf = (uint8 *) dst_start; + iBuf.buf += req->dst.offset; + + iBuf.roi.lcd_x = req->dst_rect.x; + iBuf.roi.lcd_y = req->dst_rect.y; + iBuf.roi.dst_width = req->dst_rect.w; + iBuf.roi.dst_height = req->dst_rect.h; + + iBuf.roi.x = req->src_rect.x; + iBuf.roi.width = req->src_rect.w; + iBuf.roi.y = req->src_rect.y; + iBuf.roi.height = req->src_rect.h; + + iBuf.mdpImg.width = req->src.width; + iBuf.mdpImg.imgType = req->src.format; + + + iBuf.mdpImg.bmy_addr = (uint32 *) (srcp0_start + req->src.offset); + if (iBuf.mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO) + iBuf.mdpImg.cbcr_addr = + (uint32 *) ((uint32) iBuf.mdpImg.bmy_addr + + ALIGN((ALIGN(req->src.width, 32) * + ALIGN(req->src.height, 32)), 4096)); + else + iBuf.mdpImg.cbcr_addr = srcp1_start ? (uint32 *)srcp1_start : + (uint32 *) ((uint32) iBuf.mdpImg.bmy_addr + + req->src.width * req->src.height); + + iBuf.mdpImg.mdpOp = MDPOP_NOP; + + /* blending check */ + if (req->transp_mask != MDP_TRANSP_NOP) { + iBuf.mdpImg.mdpOp |= MDPOP_TRANSP; + iBuf.mdpImg.tpVal = req->transp_mask; + iBuf.mdpImg.tpVal = mdp_calc_tpval(&iBuf.mdpImg); + } else { + iBuf.mdpImg.tpVal = 0; + } + + req->alpha &= 0xff; + if (req->alpha < MDP_ALPHA_NOP) { + iBuf.mdpImg.mdpOp |= MDPOP_ALPHAB; + iBuf.mdpImg.alpha = req->alpha; + } else { + iBuf.mdpImg.alpha = 0xff; + } + + /* rotation check */ + if (req->flags & MDP_FLIP_LR) + iBuf.mdpImg.mdpOp |= MDPOP_LR; + if (req->flags & MDP_FLIP_UD) + iBuf.mdpImg.mdpOp |= MDPOP_UD; + if (req->flags & MDP_ROT_90) + iBuf.mdpImg.mdpOp |= MDPOP_ROT90; + if (req->flags & MDP_DITHER) + iBuf.mdpImg.mdpOp |= MDPOP_DITHER; + + if (req->flags & MDP_BLEND_FG_PREMULT) { +#if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP303) + iBuf.mdpImg.mdpOp |= MDPOP_FG_PM_ALPHA; +#else + put_img(p_src_file); + put_img(p_dst_file); + return -EINVAL; +#endif + } + + if (req->flags & MDP_DEINTERLACE) { +#ifdef CONFIG_FB_MSM_MDP31 + if ((req->src.format != MDP_Y_CBCR_H2V2) && + (req->src.format != MDP_Y_CRCB_H2V2)) { +#endif + put_img(p_src_file); + put_img(p_dst_file); + return -EINVAL; +#ifdef CONFIG_FB_MSM_MDP31 + } +#endif + } + + /* scale check */ + if (req->flags & MDP_ROT_90) { + dst_width = req->dst_rect.h; + dst_height = req->dst_rect.w; + } else { + dst_width = req->dst_rect.w; + dst_height = req->dst_rect.h; + } + + if ((iBuf.roi.width != dst_width) || (iBuf.roi.height != dst_height)) + iBuf.mdpImg.mdpOp |= MDPOP_ASCALE; + + if (req->flags & MDP_BLUR) { +#ifdef CONFIG_FB_MSM_MDP31 + if (req->flags & MDP_SHARPENING) + printk(KERN_WARNING + "mdp: MDP_SHARPENING is set with MDP_BLUR!\n"); + req->flags |= MDP_SHARPENING; + req->sharpening_strength = -127; +#else + iBuf.mdpImg.mdpOp |= MDPOP_ASCALE | MDPOP_BLUR; + +#endif + } + + if (req->flags & MDP_SHARPENING) { +#ifdef CONFIG_FB_MSM_MDP31 + if ((req->sharpening_strength > 127) || + (req->sharpening_strength < -127)) { + printk(KERN_ERR + "%s: sharpening strength out of range\n", + __func__); + put_img(p_src_file); + put_img(p_dst_file); + return -EINVAL; + } + + iBuf.mdpImg.mdpOp |= MDPOP_ASCALE | MDPOP_SHARPENING; + iBuf.mdpImg.sp_value = req->sharpening_strength & 0xff; +#else + put_img(p_src_file); + put_img(p_dst_file); + return -EINVAL; +#endif + } + + down(&mdp_ppp_mutex); + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + +#ifndef CONFIG_FB_MSM_MDP22 + mdp_start_ppp(mfd, &iBuf, req, p_src_file, p_dst_file); +#else + /* bg tile fetching HW workaround */ + if (((iBuf.mdpImg.mdpOp & (MDPOP_TRANSP | MDPOP_ALPHAB)) || + (req->src.format == MDP_ARGB_8888) || + (req->src.format == MDP_BGRA_8888) || + (req->src.format == MDP_RGBA_8888)) && + (iBuf.mdpImg.mdpOp & MDPOP_ROT90) && (req->dst_rect.w <= 16)) { + int dst_h, src_w, i; + uint32 mdpOp = iBuf.mdpImg.mdpOp; + + src_w = req->src_rect.w; + dst_h = iBuf.roi.dst_height; + + for (i = 0; i < (req->dst_rect.h / 16); i++) { + /* this tile size */ + iBuf.roi.dst_height = 16; + iBuf.roi.width = + (16 * req->src_rect.w) / req->dst_rect.h; + + /* if it's out of scale range... */ + if (((MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) / + iBuf.roi.width) > MDP_MAX_X_SCALE_FACTOR) + iBuf.roi.width = + (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) / + MDP_MAX_X_SCALE_FACTOR; + else if (((MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) / + iBuf.roi.width) < MDP_MIN_X_SCALE_FACTOR) + iBuf.roi.width = + (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) / + MDP_MIN_X_SCALE_FACTOR; + + mdp_start_ppp(mfd, &iBuf, req, p_src_file, p_dst_file); + + /* next tile location */ + iBuf.roi.lcd_y += 16; + iBuf.roi.x += iBuf.roi.width; + + /* this is for a remainder update */ + dst_h -= 16; + src_w -= iBuf.roi.width; + /* restore mdpOp since MDPOP_ASCALE have been cleared */ + iBuf.mdpImg.mdpOp = mdpOp; + } + + if ((dst_h < 0) || (src_w < 0)) + printk + ("msm_fb: mdp_blt_ex() unexpected result! line:%d\n", + __LINE__); + + /* remainder update */ + if ((dst_h > 0) && (src_w > 0)) { + u32 tmp_v; + + iBuf.roi.dst_height = dst_h; + iBuf.roi.width = src_w; + + if (((MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) / + iBuf.roi.width) > MDP_MAX_X_SCALE_FACTOR) { + tmp_v = + (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) / + MDP_MAX_X_SCALE_FACTOR + + (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) % + MDP_MAX_X_SCALE_FACTOR ? 1 : 0; + + /* move x location as roi width gets bigger */ + iBuf.roi.x -= tmp_v - iBuf.roi.width; + iBuf.roi.width = tmp_v; + } else + if (((MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) / + iBuf.roi.width) < MDP_MIN_X_SCALE_FACTOR) { + tmp_v = + (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) / + MDP_MIN_X_SCALE_FACTOR + + (MDP_SCALE_Q_FACTOR * iBuf.roi.dst_height) % + MDP_MIN_X_SCALE_FACTOR ? 1 : 0; + + /* + * we don't move x location for continuity of + * source image + */ + iBuf.roi.width = tmp_v; + } + + mdp_start_ppp(mfd, &iBuf, req, p_src_file, p_dst_file); + } + } else { + mdp_start_ppp(mfd, &iBuf, req, p_src_file, p_dst_file); + } +#endif + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + up(&mdp_ppp_mutex); + + put_img(p_src_file); + put_img(p_dst_file); + return 0; +} + +int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req) +{ + unsigned long src_start, dst_start; + unsigned long src_len = 0; + unsigned long dst_len = 0; + struct file *p_src_file = 0 , *p_dst_file = 0; + + if (req->flags & MDP_BLIT_SRC_GEM) + get_gem_img(&req->src, &src_start, &src_len); + else + get_img(&req->src, info, &src_start, &src_len, &p_src_file); + if (src_len == 0) { + printk(KERN_ERR "mdp_ppp: could not retrieve image from " + "memory\n"); + return -EINVAL; + } + if (req->flags & MDP_BLIT_DST_GEM) + get_gem_img(&req->dst, &dst_start, &dst_len); + else + get_img(&req->dst, info, &dst_start, &dst_len, &p_dst_file); + if (dst_len == 0) { + put_img(p_src_file); + printk(KERN_ERR "mdp_ppp: could not retrieve image from " + "memory\n"); + return -EINVAL; + } + + return mdp_ppp_blit_addr(info, req, src_start, src_len, 0, 0, dst_start, + dst_len, p_src_file, p_dst_file); +} + +static struct mdp_blit_req overlay_req; +static bool mdp_overlay_req_set; + +int mdp_ppp_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req) +{ + memset(&overlay_req, 0, sizeof(struct mdp_blit_req)); + + overlay_req.src.width = req->src.width; + overlay_req.src.height = req->src.height; + overlay_req.src.format = req->src.format; + + + overlay_req.dst.width = req->dst_rect.w; + overlay_req.dst.height = req->dst_rect.h; + overlay_req.dst.format = MDP_FB_FORMAT; + overlay_req.transp_mask = req->transp_mask; + overlay_req.flags = req->flags; + overlay_req.alpha = req->alpha; + + overlay_req.src_rect.x = req->src_rect.x; + overlay_req.src_rect.y = req->src_rect.y; + overlay_req.src_rect.w = req->src_rect.w; + overlay_req.src_rect.h = req->src_rect.h; + overlay_req.dst_rect.x = req->dst_rect.x; + overlay_req.dst_rect.y = req->dst_rect.y; + overlay_req.dst_rect.w = req->dst_rect.w; + overlay_req.dst_rect.h = req->dst_rect.h; + mdp_overlay_req_set = true; + + pr_debug("%s: Overlay parameters:", __func__); + pr_debug("Src_Image (%u %u)\n", overlay_req.src.width, + overlay_req.src.height); + + if (overlay_req.src.format == MDP_Y_CRCB_H2V2) + pr_debug("Overlay format MDP_Y_CRCB_H2V2\n"); + else if (overlay_req.src.format == MDP_RGB_565) + pr_debug("Overlay format MDP_RGB_565\n"); + else + pr_debug("Overlay format(%u) unknown\n", + overlay_req.src.format); + + pr_debug("Dst_Image (%u %u)\n", overlay_req.dst.width, + overlay_req.dst.height); + pr_debug("Src rect: (%u,%u,%u,%u), Dst rect: (%u,%u,%u,%u)\n", + overlay_req.src_rect.x, overlay_req.src_rect.y, + overlay_req.src_rect.w, overlay_req.src_rect.h, + overlay_req.dst_rect.x, overlay_req.dst_rect.y, + overlay_req.dst_rect.w, overlay_req.dst_rect.h); + return 0; +} + +int mdp_ppp_v4l2_overlay_clear(void) +{ + memset(&overlay_req, 0, sizeof(struct mdp_overlay)); + mdp_overlay_req_set = false; + return 0; +} + +int mdp_ppp_v4l2_overlay_play(struct fb_info *info, + unsigned long srcp0_addr, unsigned long srcp0_size, + unsigned long srcp1_addr, unsigned long srcp1_size) +{ + int ret; + + if (!mdp_overlay_req_set) { + pr_err("mdp_ppp:v4l2:No overlay set, ignore play req\n"); + return -EINVAL; + } + + overlay_req.dst.width = info->var.xres; + overlay_req.dst.height = info->var.yres; + + ret = mdp_ppp_blit_addr(info, &overlay_req, + srcp0_addr, srcp0_size, srcp1_addr, srcp1_size, + info->fix.smem_start, info->fix.smem_len, NULL, NULL); + + if (ret) + pr_err("%s:Blitting overlay failed(%d)\n", __func__, ret); + + return ret; +} diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h new file mode 100644 index 0000000000000000000000000000000000000000..e04564347bdfe16c450c138bf6dfe8be2f118d2d --- /dev/null +++ b/drivers/video/msm/mdp_ppp.h @@ -0,0 +1,82 @@ +/* drivers/video/msm/mdp_ppp.h + * + * Copyright (C) 2009 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _VIDEO_MSM_MDP_PPP_H_ +#define _VIDEO_MSM_MDP_PPP_H_ + +#include + +struct ppp_regs { + uint32_t src0; + uint32_t src1; + uint32_t dst0; + uint32_t dst1; + uint32_t src_cfg; + uint32_t dst_cfg; + uint32_t src_pack; + uint32_t dst_pack; + uint32_t src_rect; + uint32_t dst_rect; + uint32_t src_ystride; + uint32_t dst_ystride; + uint32_t op; + uint32_t src_bpp; + uint32_t dst_bpp; + uint32_t edge; + uint32_t phasex_init; + uint32_t phasey_init; + uint32_t phasex_step; + uint32_t phasey_step; + + uint32_t bg0; + uint32_t bg1; + uint32_t bg_cfg; + uint32_t bg_bpp; + uint32_t bg_pack; + uint32_t bg_ystride; + +#ifdef CONFIG_MSM_MDP31 + uint32_t src_xy; + uint32_t src_img_sz; + uint32_t dst_xy; + uint32_t bg_xy; + uint32_t bg_img_sz; + uint32_t bg_alpha_sel; + + uint32_t scale_cfg; + uint32_t csc_cfg; +#endif +}; + +struct mdp_info; +struct mdp_rect; +struct mdp_blit_req; + +void mdp_ppp_init_scale(const struct mdp_info *mdp); +int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, + struct mdp_rect *src_rect, struct mdp_rect *dst_rect, + uint32_t src_format, uint32_t dst_format); +int mdp_ppp_load_blur(const struct mdp_info *mdp); + +#ifndef CONFIG_MSM_MDP31 +int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs); +#else +static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, + struct ppp_regs *regs) +{ + return 0; +} +#endif + +#endif /* _VIDEO_MSM_MDP_PPP_H_ */ diff --git a/drivers/video/msm/mdp_ppp22.c b/drivers/video/msm/mdp_ppp22.c new file mode 100644 index 0000000000000000000000000000000000000000..799ce524f8363d47cd4c3626fa7e1de6614ee9a9 --- /dev/null +++ b/drivers/video/msm/mdp_ppp22.c @@ -0,0 +1,1091 @@ +/* drivers/video/msm/mdp_ppp22.c + * + * Copyright (C) 2007 The Linux Foundation. All rights reserved. + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "mdp_hw.h" +#include "mdp_ppp.h" + +struct mdp_table_entry { + uint32_t reg; + uint32_t val; +}; + +enum { + MDP_DOWNSCALE_PT2TOPT4, + MDP_DOWNSCALE_PT4TOPT6, + MDP_DOWNSCALE_PT6TOPT8, + MDP_DOWNSCALE_PT8TO1, + MDP_DOWNSCALE_MAX, + + /* not technically in the downscale table list */ + MDP_DOWNSCALE_BLUR, +}; + +static int downscale_x_table; +static int downscale_y_table; + +static struct mdp_table_entry mdp_upscale_table[] = { + { 0x5fffc, 0x0 }, + { 0x50200, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50204, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50208, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5020c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50210, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50214, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50218, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5021c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x50220, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x50224, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x50228, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x5022c, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x50230, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x50234, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x50238, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x5023c, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x50240, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x50244, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x50248, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x5024c, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x50250, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x50254, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x50258, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x5025c, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x50260, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x50264, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x50268, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x5026c, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x50270, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x50274, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x50278, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x5027c, 0x34003fe }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT2TOPT4[] = { + { 0x5fffc, 0x740008c }, + { 0x50280, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50284, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50288, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5028c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50290, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50294, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50298, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5029c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x502a0, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x502a4, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x502a8, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x502ac, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x502b0, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x502b4, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x502b8, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x502bc, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x502c0, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x502c4, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x502c8, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x502cc, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x502d0, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x502d4, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x502d8, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x502dc, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x502e0, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x502e4, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x502e8, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x502ec, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x502f0, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x502f4, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x502f8, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x502fc, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT4TOPT6[] = { + { 0x5fffc, 0x740008c }, + { 0x50280, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50284, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50288, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5028c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50290, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50294, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50298, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5029c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x502a0, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x502a4, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x502a8, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x502ac, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x502b0, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x502b4, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x502b8, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x502bc, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x502c0, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x502c4, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x502c8, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x502cc, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x502d0, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x502d4, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x502d8, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x502dc, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x502e0, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x502e4, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x502e8, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x502ec, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x502f0, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x502f4, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x502f8, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x502fc, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT6TOPT8[] = { + { 0x5fffc, 0xfe000070 }, + { 0x50280, 0x4bc00068 }, + { 0x5fffc, 0xfe000078 }, + { 0x50284, 0x4bc00060 }, + { 0x5fffc, 0xfe000080 }, + { 0x50288, 0x4b800059 }, + { 0x5fffc, 0xfe000089 }, + { 0x5028c, 0x4b000052 }, + { 0x5fffc, 0xfe400091 }, + { 0x50290, 0x4a80004b }, + { 0x5fffc, 0xfe40009a }, + { 0x50294, 0x4a000044 }, + { 0x5fffc, 0xfe8000a3 }, + { 0x50298, 0x4940003d }, + { 0x5fffc, 0xfec000ac }, + { 0x5029c, 0x48400037 }, + { 0x5fffc, 0xff0000b4 }, + { 0x502a0, 0x47800031 }, + { 0x5fffc, 0xff8000bd }, + { 0x502a4, 0x4640002b }, + { 0x5fffc, 0xc5 }, + { 0x502a8, 0x45000026 }, + { 0x5fffc, 0x8000ce }, + { 0x502ac, 0x43800021 }, + { 0x5fffc, 0x10000d6 }, + { 0x502b0, 0x4240001c }, + { 0x5fffc, 0x18000df }, + { 0x502b4, 0x40800018 }, + { 0x5fffc, 0x24000e6 }, + { 0x502b8, 0x3f000014 }, + { 0x5fffc, 0x30000ee }, + { 0x502bc, 0x3d400010 }, + { 0x5fffc, 0x40000f5 }, + { 0x502c0, 0x3b80000c }, + { 0x5fffc, 0x50000fc }, + { 0x502c4, 0x39800009 }, + { 0x5fffc, 0x6000102 }, + { 0x502c8, 0x37c00006 }, + { 0x5fffc, 0x7000109 }, + { 0x502cc, 0x35800004 }, + { 0x5fffc, 0x840010e }, + { 0x502d0, 0x33800002 }, + { 0x5fffc, 0x9800114 }, + { 0x502d4, 0x31400000 }, + { 0x5fffc, 0xac00119 }, + { 0x502d8, 0x2f4003fe }, + { 0x5fffc, 0xc40011e }, + { 0x502dc, 0x2d0003fc }, + { 0x5fffc, 0xdc00121 }, + { 0x502e0, 0x2b0003fb }, + { 0x5fffc, 0xf400125 }, + { 0x502e4, 0x28c003fa }, + { 0x5fffc, 0x11000128 }, + { 0x502e8, 0x268003f9 }, + { 0x5fffc, 0x12c0012a }, + { 0x502ec, 0x244003f9 }, + { 0x5fffc, 0x1480012c }, + { 0x502f0, 0x224003f8 }, + { 0x5fffc, 0x1640012e }, + { 0x502f4, 0x200003f8 }, + { 0x5fffc, 0x1800012f }, + { 0x502f8, 0x1e0003f8 }, + { 0x5fffc, 0x1a00012f }, + { 0x502fc, 0x1c0003f8 }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT8TO1[] = { + { 0x5fffc, 0x0 }, + { 0x50280, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50284, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50288, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5028c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50290, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50294, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50298, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5029c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x502a0, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x502a4, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x502a8, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x502ac, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x502b0, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x502b4, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x502b8, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x502bc, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x502c0, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x502c4, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x502c8, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x502cc, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x502d0, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x502d4, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x502d8, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x502dc, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x502e0, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x502e4, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x502e8, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x502ec, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x502f0, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x502f4, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x502f8, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x502fc, 0x34003fe }, +}; + +struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX] = { + [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_x_table_PT2TOPT4, + [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_x_table_PT4TOPT6, + [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_x_table_PT6TOPT8, + [MDP_DOWNSCALE_PT8TO1] = mdp_downscale_x_table_PT8TO1, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT2TOPT4[] = { + { 0x5fffc, 0x740008c }, + { 0x50300, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50304, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50308, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5030c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50310, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50314, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50318, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5031c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x50320, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x50324, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x50328, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x5032c, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x50330, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x50334, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x50338, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x5033c, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x50340, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x50344, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x50348, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x5034c, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x50350, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x50354, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x50358, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x5035c, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x50360, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x50364, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x50368, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x5036c, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x50370, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x50374, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x50378, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x5037c, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT4TOPT6[] = { + { 0x5fffc, 0x740008c }, + { 0x50300, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50304, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50308, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5030c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50310, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50314, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50318, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5031c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x50320, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x50324, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x50328, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x5032c, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x50330, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x50334, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x50338, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x5033c, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x50340, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x50344, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x50348, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x5034c, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x50350, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x50354, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x50358, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x5035c, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x50360, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x50364, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x50368, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x5036c, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x50370, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x50374, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x50378, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x5037c, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT6TOPT8[] = { + { 0x5fffc, 0xfe000070 }, + { 0x50300, 0x4bc00068 }, + { 0x5fffc, 0xfe000078 }, + { 0x50304, 0x4bc00060 }, + { 0x5fffc, 0xfe000080 }, + { 0x50308, 0x4b800059 }, + { 0x5fffc, 0xfe000089 }, + { 0x5030c, 0x4b000052 }, + { 0x5fffc, 0xfe400091 }, + { 0x50310, 0x4a80004b }, + { 0x5fffc, 0xfe40009a }, + { 0x50314, 0x4a000044 }, + { 0x5fffc, 0xfe8000a3 }, + { 0x50318, 0x4940003d }, + { 0x5fffc, 0xfec000ac }, + { 0x5031c, 0x48400037 }, + { 0x5fffc, 0xff0000b4 }, + { 0x50320, 0x47800031 }, + { 0x5fffc, 0xff8000bd }, + { 0x50324, 0x4640002b }, + { 0x5fffc, 0xc5 }, + { 0x50328, 0x45000026 }, + { 0x5fffc, 0x8000ce }, + { 0x5032c, 0x43800021 }, + { 0x5fffc, 0x10000d6 }, + { 0x50330, 0x4240001c }, + { 0x5fffc, 0x18000df }, + { 0x50334, 0x40800018 }, + { 0x5fffc, 0x24000e6 }, + { 0x50338, 0x3f000014 }, + { 0x5fffc, 0x30000ee }, + { 0x5033c, 0x3d400010 }, + { 0x5fffc, 0x40000f5 }, + { 0x50340, 0x3b80000c }, + { 0x5fffc, 0x50000fc }, + { 0x50344, 0x39800009 }, + { 0x5fffc, 0x6000102 }, + { 0x50348, 0x37c00006 }, + { 0x5fffc, 0x7000109 }, + { 0x5034c, 0x35800004 }, + { 0x5fffc, 0x840010e }, + { 0x50350, 0x33800002 }, + { 0x5fffc, 0x9800114 }, + { 0x50354, 0x31400000 }, + { 0x5fffc, 0xac00119 }, + { 0x50358, 0x2f4003fe }, + { 0x5fffc, 0xc40011e }, + { 0x5035c, 0x2d0003fc }, + { 0x5fffc, 0xdc00121 }, + { 0x50360, 0x2b0003fb }, + { 0x5fffc, 0xf400125 }, + { 0x50364, 0x28c003fa }, + { 0x5fffc, 0x11000128 }, + { 0x50368, 0x268003f9 }, + { 0x5fffc, 0x12c0012a }, + { 0x5036c, 0x244003f9 }, + { 0x5fffc, 0x1480012c }, + { 0x50370, 0x224003f8 }, + { 0x5fffc, 0x1640012e }, + { 0x50374, 0x200003f8 }, + { 0x5fffc, 0x1800012f }, + { 0x50378, 0x1e0003f8 }, + { 0x5fffc, 0x1a00012f }, + { 0x5037c, 0x1c0003f8 }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT8TO1[] = { + { 0x5fffc, 0x0 }, + { 0x50300, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50304, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50308, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5030c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50310, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50314, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50318, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5031c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x50320, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x50324, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x50328, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x5032c, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x50330, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x50334, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x50338, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x5033c, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x50340, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x50344, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x50348, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x5034c, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x50350, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x50354, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x50358, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x5035c, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x50360, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x50364, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x50368, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x5036c, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x50370, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x50374, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x50378, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x5037c, 0x34003fe }, +}; + +struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX] = { + [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_y_table_PT2TOPT4, + [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_y_table_PT4TOPT6, + [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_y_table_PT6TOPT8, + [MDP_DOWNSCALE_PT8TO1] = mdp_downscale_y_table_PT8TO1, +}; + +struct mdp_table_entry mdp_gaussian_blur_table[] = { + /* max variance */ + { 0x5fffc, 0x20000080 }, + { 0x50280, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50284, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50288, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5028c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50290, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50294, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50298, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5029c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ac, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502bc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502cc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502dc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ec, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502fc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50300, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50304, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50308, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5030c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50310, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50314, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50318, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5031c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50320, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50324, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50328, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5032c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50330, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50334, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50338, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5033c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50340, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50344, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50348, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5034c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50350, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50354, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50358, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5035c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50360, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50364, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50368, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5036c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50370, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50374, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50378, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5037c, 0x20000080 }, +}; + +static void load_table(const struct mdp_info *mdp, + struct mdp_table_entry *table, int len) +{ + int i; + for (i = 0; i < len; i++) + mdp_writel(mdp, table[i].val, table[i].reg); +} + +enum { + IMG_LEFT, + IMG_RIGHT, + IMG_TOP, + IMG_BOTTOM, +}; + +static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst, + uint32_t *interp1, uint32_t *interp2, + uint32_t *repeat1, uint32_t *repeat2) { + if (src > 3 * dst) { + *interp1 = 0; + *interp2 = src - 1; + *repeat1 = 0; + *repeat2 = 0; + } else if (src == 3 * dst) { + *interp1 = 0; + *interp2 = src; + *repeat1 = 0; + *repeat2 = 1; + } else if (src > dst && src < 3 * dst) { + *interp1 = -1; + *interp2 = src; + *repeat1 = 1; + *repeat2 = 1; + } else if (src == dst) { + *interp1 = -1; + *interp2 = src + 1; + *repeat1 = 1; + *repeat2 = 2; + } else { + *interp1 = -2; + *interp2 = src + 1; + *repeat1 = 2; + *repeat2 = 2; + } + *interp1 += src_coord; + *interp2 += src_coord; +} + +int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs) +{ + int32_t luma_interp[4]; + int32_t luma_repeat[4]; + int32_t chroma_interp[4]; + int32_t chroma_bound[4]; + int32_t chroma_repeat[4]; + uint32_t dst_w, dst_h; + + memset(&luma_interp, 0, sizeof(int32_t) * 4); + memset(&luma_repeat, 0, sizeof(int32_t) * 4); + memset(&chroma_interp, 0, sizeof(int32_t) * 4); + memset(&chroma_bound, 0, sizeof(int32_t) * 4); + memset(&chroma_repeat, 0, sizeof(int32_t) * 4); + regs->edge = 0; + + if (req->flags & MDP_ROT_90) { + dst_w = req->dst_rect.h; + dst_h = req->dst_rect.w; + } else { + dst_w = req->dst_rect.w; + dst_h = req->dst_rect.h; + } + + if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) { + get_edge_info(req->src_rect.h, req->src_rect.y, dst_h, + &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM], + &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]); + get_edge_info(req->src_rect.w, req->src_rect.x, dst_w, + &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT], + &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]); + } else { + luma_interp[IMG_LEFT] = req->src_rect.x; + luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; + luma_interp[IMG_TOP] = req->src_rect.y; + luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; + luma_repeat[IMG_LEFT] = 0; + luma_repeat[IMG_TOP] = 0; + luma_repeat[IMG_RIGHT] = 0; + luma_repeat[IMG_BOTTOM] = 0; + } + + chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT]; + chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT]; + chroma_interp[IMG_TOP] = luma_interp[IMG_TOP]; + chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM]; + + chroma_bound[IMG_LEFT] = req->src_rect.x; + chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; + chroma_bound[IMG_TOP] = req->src_rect.y; + chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; + + if (IS_YCRCB(req->src.format)) { + chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1; + chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1; + + chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1; + chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1; + } + + if (req->src.format == MDP_Y_CBCR_H2V2 || + req->src.format == MDP_Y_CRCB_H2V2) { + chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1; + chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1) + >> 1; + chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1; + chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1; + } + + chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] - + chroma_interp[IMG_LEFT]; + chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] - + chroma_bound[IMG_RIGHT]; + chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] - + chroma_interp[IMG_TOP]; + chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] - + chroma_bound[IMG_BOTTOM]; + + if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 || + chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 || + chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 || + chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 || + luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 || + luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 || + luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 || + luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3) + return -1; + + regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA; + regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA; + regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA; + regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA; + regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA; + regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA; + regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA; + regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA; + return 0; +} + +#define ONE_HALF (1LL << 32) +#define ONE (1LL << 33) +#define TWO (2LL << 33) +#define THREE (3LL << 33) +#define FRAC_MASK (ONE - 1) +#define INT_MASK (~FRAC_MASK) + +static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin, + uint32_t *phase_init, uint32_t *phase_step) +{ + /* to improve precicsion calculations are done in U31.33 and converted + * to U3.29 at the end */ + int64_t k1, k2, k3, k4, tmp; + uint64_t n, d, os, os_p, od, od_p, oreq; + unsigned rpa = 0; + int64_t ip64, delta; + + if (dim_out % 3 == 0) + rpa = !(dim_in % (dim_out / 3)); + + n = ((uint64_t)dim_out) << 34; + d = dim_in; + if (!d) + return -1; + do_div(n, d); + k3 = (n + 1) >> 1; + if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) + return -1; + + n = ((uint64_t)dim_in) << 34; + d = (uint64_t)dim_out; + if (!d) + return -1; + do_div(n, d); + k1 = (n + 1) >> 1; + k2 = (k1 - ONE) >> 1; + + *phase_init = (int)(k2 >> 4); + k4 = (k3 - ONE) >> 1; + + if (rpa) { + os = ((uint64_t)origin << 33) - ONE_HALF; + tmp = (dim_out * os) + ONE_HALF; + if (!dim_in) + return -1; + do_div(tmp, dim_in); + od = tmp - ONE_HALF; + } else { + os = ((uint64_t)origin << 1) - 1; + od = (((k3 * os) >> 1) + k4); + } + + od_p = od & INT_MASK; + if (od_p != od) + od_p += ONE; + + if (rpa) { + tmp = (dim_in * od_p) + ONE_HALF; + if (!dim_in) + return -1; + do_div(tmp, dim_in); + os_p = tmp - ONE_HALF; + } else { + os_p = ((k1 * (od_p >> 33)) + k2); + } + + oreq = (os_p & INT_MASK) - ONE; + + ip64 = os_p - oreq; + delta = ((int64_t)(origin) << 33) - oreq; + ip64 -= delta; + /* limit to valid range before the left shift */ + delta = (ip64 & (1LL << 63)) ? 4 : -4; + delta <<= 33; + while (abs((int)(ip64 >> 33)) > 4) + ip64 += delta; + *phase_init = (int)(ip64 >> 4); + *phase_step = (uint32_t)(k1 >> 4); + return 0; +} + +int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, + struct mdp_rect *src_rect, struct mdp_rect *dst_rect, + uint32_t src_format, uint32_t dst_format) +{ + int downscale; + uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y; + uint32_t scale_factor_x, scale_factor_y; + + if (scale_params(src_rect->w, dst_rect->w, 1, &phase_init_x, + &phase_step_x) || + scale_params(src_rect->h, dst_rect->h, 1, &phase_init_y, + &phase_step_y)) + return -1; + + regs->phasex_init = phase_init_x; + regs->phasey_init = phase_init_y; + regs->phasex_step = phase_step_x; + regs->phasey_step = phase_step_y; + + scale_factor_x = (dst_rect->w * 10) / src_rect->w; + scale_factor_y = (dst_rect->h * 10) / src_rect->h; + + if (scale_factor_x > 8) + downscale = MDP_DOWNSCALE_PT8TO1; + else if (scale_factor_x > 6) + downscale = MDP_DOWNSCALE_PT6TOPT8; + else if (scale_factor_x > 4) + downscale = MDP_DOWNSCALE_PT4TOPT6; + else + downscale = MDP_DOWNSCALE_PT2TOPT4; + + if (downscale != downscale_x_table) { + load_table(mdp, mdp_downscale_x_table[downscale], 64); + downscale_x_table = downscale; + } + + if (scale_factor_y > 8) + downscale = MDP_DOWNSCALE_PT8TO1; + else if (scale_factor_y > 6) + downscale = MDP_DOWNSCALE_PT6TOPT8; + else if (scale_factor_y > 4) + downscale = MDP_DOWNSCALE_PT4TOPT6; + else + downscale = MDP_DOWNSCALE_PT2TOPT4; + + if (downscale != downscale_y_table) { + load_table(mdp, mdp_downscale_y_table[downscale], 64); + downscale_y_table = downscale; + } + + return 0; +} + + +int mdp_ppp_load_blur(const struct mdp_info *mdp) +{ + if (!(downscale_x_table == MDP_DOWNSCALE_BLUR && + downscale_y_table == MDP_DOWNSCALE_BLUR)) { + load_table(mdp, mdp_gaussian_blur_table, 128); + downscale_x_table = MDP_DOWNSCALE_BLUR; + downscale_y_table = MDP_DOWNSCALE_BLUR; + } + + return 0; +} + +void mdp_ppp_init_scale(const struct mdp_info *mdp) +{ + downscale_x_table = MDP_DOWNSCALE_MAX; + downscale_y_table = MDP_DOWNSCALE_MAX; + + load_table(mdp, mdp_upscale_table, ARRAY_SIZE(mdp_upscale_table)); +} diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c new file mode 100644 index 0000000000000000000000000000000000000000..2d28358fe2df53bac615c5a9fed8cec283f13eb7 --- /dev/null +++ b/drivers/video/msm/mdp_ppp31.c @@ -0,0 +1,332 @@ +/* drivers/video/msm/mdp_ppp31.c + * + * Copyright (C) 2009 The Linux Foundation. All rights reserved. + * Copyright (C) 2009 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "mdp_hw.h" +#include "mdp_ppp.h" + +#define NUM_COEFFS 32 + +struct mdp_scale_coeffs { + uint16_t c[4][NUM_COEFFS]; +}; + +struct mdp_scale_tbl_info { + uint16_t offset; + uint32_t set:2; + int use_pr; + struct mdp_scale_coeffs coeffs; +}; + +enum { + MDP_SCALE_PT2TOPT4, + MDP_SCALE_PT4TOPT6, + MDP_SCALE_PT6TOPT8, + MDP_SCALE_PT8TO8, + MDP_SCALE_MAX, +}; + +static struct mdp_scale_coeffs mdp_scale_pr_coeffs = { + .c = { + [0] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + [1] = { + 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 511, 511, 511, 511, 511, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + [2] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 511, 511, 511, 511, 511, + }, + [3] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, +}; + +static struct mdp_scale_tbl_info mdp_scale_tbl[MDP_SCALE_MAX] = { + [ MDP_SCALE_PT2TOPT4 ] = { + .offset = 0, + .set = MDP_PPP_SCALE_COEFF_D0_SET, + .use_pr = -1, + .coeffs.c = { + [0] = { + 131, 131, 130, 129, 128, 127, 127, 126, + 125, 125, 124, 123, 123, 121, 120, 119, + 119, 118, 117, 117, 116, 115, 115, 114, + 113, 112, 111, 110, 109, 109, 108, 107, + }, + [1] = { + 141, 140, 140, 140, 140, 139, 138, 138, + 138, 137, 137, 137, 136, 137, 137, 137, + 136, 136, 136, 135, 135, 135, 134, 134, + 134, 134, 134, 133, 133, 132, 132, 132, + }, + [2] = { + 132, 132, 132, 133, 133, 134, 134, 134, + 134, 134, 135, 135, 135, 136, 136, 136, + 137, 137, 137, 136, 137, 137, 137, 138, + 138, 138, 139, 140, 140, 140, 140, 141, + }, + [3] = { + 107, 108, 109, 109, 110, 111, 112, 113, + 114, 115, 115, 116, 117, 117, 118, 119, + 119, 120, 121, 123, 123, 124, 125, 125, + 126, 127, 127, 128, 129, 130, 131, 131, + } + }, + }, + [ MDP_SCALE_PT4TOPT6 ] = { + .offset = 32, + .set = MDP_PPP_SCALE_COEFF_D1_SET, + .use_pr = -1, + .coeffs.c = { + [0] = { + 136, 132, 128, 123, 119, 115, 111, 107, + 103, 98, 95, 91, 87, 84, 80, 76, + 73, 69, 66, 62, 59, 57, 54, 50, + 47, 44, 41, 39, 36, 33, 32, 29, + }, + [1] = { + 206, 205, 204, 204, 201, 200, 199, 197, + 196, 194, 191, 191, 189, 185, 184, 182, + 180, 178, 176, 173, 170, 168, 165, 162, + 160, 157, 155, 152, 148, 146, 142, 140, + }, + [2] = { + 140, 142, 146, 148, 152, 155, 157, 160, + 162, 165, 168, 170, 173, 176, 178, 180, + 182, 184, 185, 189, 191, 191, 194, 196, + 197, 199, 200, 201, 204, 204, 205, 206, + }, + [3] = { + 29, 32, 33, 36, 39, 41, 44, 47, + 50, 54, 57, 59, 62, 66, 69, 73, + 76, 80, 84, 87, 91, 95, 98, 103, + 107, 111, 115, 119, 123, 128, 132, 136, + }, + }, + }, + [ MDP_SCALE_PT6TOPT8 ] = { + .offset = 64, + .set = MDP_PPP_SCALE_COEFF_D2_SET, + .use_pr = -1, + .coeffs.c = { + [0] = { + 104, 96, 89, 82, 75, 68, 61, 55, + 49, 43, 38, 33, 28, 24, 20, 16, + 12, 9, 6, 4, 2, 0, -2, -4, + -5, -6, -7, -7, -8, -8, -8, -8, + }, + [1] = { + 303, 303, 302, 300, 298, 296, 293, 289, + 286, 281, 276, 270, 265, 258, 252, 245, + 238, 230, 223, 214, 206, 197, 189, 180, + 172, 163, 154, 145, 137, 128, 120, 112, + }, + [2] = { + 112, 120, 128, 137, 145, 154, 163, 172, + 180, 189, 197, 206, 214, 223, 230, 238, + 245, 252, 258, 265, 270, 276, 281, 286, + 289, 293, 296, 298, 300, 302, 303, 303, + }, + [3] = { + -8, -8, -8, -8, -7, -7, -6, -5, + -4, -2, 0, 2, 4, 6, 9, 12, + 16, 20, 24, 28, 33, 38, 43, 49, + 55, 61, 68, 75, 82, 89, 96, 104, + }, + }, + }, + [ MDP_SCALE_PT8TO8 ] = { + .offset = 96, + .set = MDP_PPP_SCALE_COEFF_U1_SET, + .use_pr = -1, + .coeffs.c = { + [0] = { + 0, -7, -13, -19, -24, -28, -32, -34, + -37, -39, -40, -41, -41, -41, -40, -40, + -38, -37, -35, -33, -31, -29, -26, -24, + -21, -18, -15, -13, -10, -7, -5, -2, + }, + [1] = { + 511, 507, 501, 494, 485, 475, 463, 450, + 436, 422, 405, 388, 370, 352, 333, 314, + 293, 274, 253, 233, 213, 193, 172, 152, + 133, 113, 95, 77, 60, 43, 28, 13, + }, + [2] = { + 0, 13, 28, 43, 60, 77, 95, 113, + 133, 152, 172, 193, 213, 233, 253, 274, + 294, 314, 333, 352, 370, 388, 405, 422, + 436, 450, 463, 475, 485, 494, 501, 507, + }, + [3] = { + 0, -2, -5, -7, -10, -13, -15, -18, + -21, -24, -26, -29, -31, -33, -35, -37, + -38, -40, -40, -41, -41, -41, -40, -39, + -37, -34, -32, -28, -24, -19, -13, -7, + }, + }, + }, +}; + +static void load_table(const struct mdp_info *mdp, int scale, int use_pr) +{ + int i; + uint32_t val; + struct mdp_scale_coeffs *coeffs; + struct mdp_scale_tbl_info *tbl = &mdp_scale_tbl[scale]; + + if (use_pr == tbl->use_pr) + return; + + tbl->use_pr = use_pr; + if (!use_pr) + coeffs = &tbl->coeffs; + else + coeffs = &mdp_scale_pr_coeffs; + + for (i = 0; i < NUM_COEFFS; ++i) { + val = ((coeffs->c[1][i] & 0x3ff) << 16) | + (coeffs->c[0][i] & 0x3ff); + mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_LSBn(tbl->offset + i)); + + val = ((coeffs->c[3][i] & 0x3ff) << 16) | + (coeffs->c[2][i] & 0x3ff); + mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_MSBn(tbl->offset + i)); + } +} + +#define SCALER_PHASE_BITS 29 +static void scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t scaler, + uint32_t *phase_init, uint32_t *phase_step) +{ + uint64_t src = dim_in; + uint64_t dst = dim_out; + uint64_t numer; + uint64_t denom; + + *phase_init = 0; + + if (dst == 1) { + /* if destination is 1 pixel wide, the value of phase_step + * is unimportant. */ + *phase_step = (uint32_t) (src << SCALER_PHASE_BITS); + if (scaler == MDP_PPP_SCALER_FIR) + *phase_init = + (uint32_t) ((src - 1) << SCALER_PHASE_BITS); + return; + } + + if (scaler == MDP_PPP_SCALER_FIR) { + numer = (src - 1) << SCALER_PHASE_BITS; + denom = dst - 1; + /* we want to round up the result*/ + numer += denom - 1; + } else { + numer = src << SCALER_PHASE_BITS; + denom = dst; + } + + do_div(numer, denom); + *phase_step = (uint32_t) numer; +} + +static int scale_idx(int factor) +{ + int idx; + + if (factor > 80) + idx = MDP_SCALE_PT8TO8; + else if (factor > 60) + idx = MDP_SCALE_PT6TOPT8; + else if (factor > 40) + idx = MDP_SCALE_PT4TOPT6; + else + idx = MDP_SCALE_PT2TOPT4; + + return idx; +} + +int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, + struct mdp_rect *src_rect, struct mdp_rect *dst_rect, + uint32_t src_format, uint32_t dst_format) +{ + uint32_t x_fac; + uint32_t y_fac; + uint32_t scaler_x = MDP_PPP_SCALER_FIR; + uint32_t scaler_y = MDP_PPP_SCALER_FIR; + // Don't use pixel repeat mode, it looks bad + int use_pr = 0; + int x_idx; + int y_idx; + + if (unlikely(src_rect->w > 2048 || src_rect->h > 2048)) + return -ENOTSUPP; + + x_fac = (dst_rect->w * 100) / src_rect->w; + y_fac = (dst_rect->h * 100) / src_rect->h; + + /* if down-scaling by a factor smaller than 1/4, use M/N */ + scaler_x = x_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR; + scaler_y = y_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR; + scale_params(src_rect->w, dst_rect->w, scaler_x, ®s->phasex_init, + ®s->phasex_step); + scale_params(src_rect->h, dst_rect->h, scaler_y, ®s->phasey_init, + ®s->phasey_step); + + x_idx = scale_idx(x_fac); + y_idx = scale_idx(y_fac); + load_table(mdp, x_idx, use_pr); + load_table(mdp, y_idx, use_pr); + + regs->scale_cfg = 0; + // Enable SVI when source or destination is YUV + if (!IS_RGB(src_format) && !IS_RGB(dst_format)) + regs->scale_cfg |= (1 << 6); + regs->scale_cfg |= (mdp_scale_tbl[x_idx].set << 2) | + (mdp_scale_tbl[x_idx].set << 4); + regs->scale_cfg |= (scaler_x << 0) | (scaler_y << 1); + + return 0; +} + +int mdp_ppp_load_blur(const struct mdp_info *mdp) +{ + return -ENOTSUPP; +} + +void mdp_ppp_init_scale(const struct mdp_info *mdp) +{ + int scale; + for (scale = 0; scale < MDP_SCALE_MAX; ++scale) + load_table(mdp, scale, 0); +} diff --git a/drivers/video/msm/mdp_ppp_v20.c b/drivers/video/msm/mdp_ppp_v20.c new file mode 100644 index 0000000000000000000000000000000000000000..4062d3abb43429d1d46cd66a8b83a0ac54cecbfb --- /dev/null +++ b/drivers/video/msm/mdp_ppp_v20.c @@ -0,0 +1,2541 @@ +/* Copyright (c) 2008-2009, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" + +#include +#include + +#include +#include +#include +#include + +#include "mdp.h" +#include "msm_fb.h" + +static MDP_SCALE_MODE mdp_curr_up_scale_xy; +static MDP_SCALE_MODE mdp_curr_down_scale_x; +static MDP_SCALE_MODE mdp_curr_down_scale_y; + +static long long mdp_do_div(long long num, long long den) +{ + do_div(num, den); + return num; +} + +struct mdp_table_entry mdp_gaussian_blur_table[] = { + /* max variance */ + { 0x5fffc, 0x20000080 }, + { 0x50280, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50284, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50288, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5028c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50290, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50294, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50298, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5029c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ac, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502bc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502cc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502dc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ec, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502fc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50300, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50304, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50308, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5030c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50310, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50314, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50318, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5031c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50320, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50324, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50328, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5032c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50330, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50334, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50338, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5033c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50340, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50344, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50348, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5034c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50350, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50354, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50358, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5035c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50360, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50364, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50368, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5036c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50370, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50374, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50378, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5037c, 0x20000080 }, +}; + +static void load_scale_table( + struct mdp_table_entry *table, int len) +{ + int i; + for (i = 0; i < len; i++) + MDP_OUTP(MDP_BASE + table[i].reg, table[i].val); +} + +static void mdp_load_pr_upscale_table(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50200, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50204, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50208, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5020c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50210, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50214, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50218, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5021c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50220, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50224, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50228, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5022c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50230, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50234, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50238, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5023c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50240, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50244, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50248, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5024c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50250, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50254, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50258, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5025c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50260, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50264, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50268, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5026c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50270, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50274, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50278, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5027c, 0x0); +} + +static void mdp_load_pr_downscale_table_x_point2TOpoint4(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50284, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50288, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5028c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50290, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50294, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50298, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5029c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a0, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a4, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a8, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502ac, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b0, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b4, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b8, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502bc, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502cc, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502dc, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502ec, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502fc, 0x0); +} + +static void mdp_load_pr_downscale_table_y_point2TOpoint4(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50304, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50308, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5030c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50310, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50314, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50318, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5031c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50320, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50324, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50328, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5032c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50330, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50334, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50338, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5033c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50340, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50344, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50348, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5034c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50350, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50354, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50358, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5035c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50360, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50364, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50368, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5036c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50370, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50374, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50378, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5037c, 0x0); +} + +static void mdp_load_pr_downscale_table_x_point4TOpoint6(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50284, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50288, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5028c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50290, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50294, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50298, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5029c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a0, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a4, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a8, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502ac, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b0, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b4, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b8, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502bc, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502cc, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502dc, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502ec, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502fc, 0x0); +} + +static void mdp_load_pr_downscale_table_y_point4TOpoint6(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50304, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50308, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5030c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50310, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50314, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50318, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5031c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50320, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50324, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50328, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5032c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50330, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50334, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50338, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5033c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50340, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50344, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50348, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5034c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50350, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50354, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50358, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5035c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50360, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50364, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50368, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5036c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50370, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50374, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50378, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5037c, 0x0); +} + +static void mdp_load_pr_downscale_table_x_point6TOpoint8(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50284, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50288, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5028c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50290, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50294, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50298, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5029c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a0, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a4, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a8, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502ac, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b0, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b4, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b8, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502bc, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502cc, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502dc, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502ec, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502fc, 0x0); +} + +static void mdp_load_pr_downscale_table_y_point6TOpoint8(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50304, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50308, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5030c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50310, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50314, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50318, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5031c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50320, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50324, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50328, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5032c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50330, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50334, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50338, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5033c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50340, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50344, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50348, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5034c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50350, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50354, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50358, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5035c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50360, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50364, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50368, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5036c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50370, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50374, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50378, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5037c, 0x0); +} + +static void mdp_load_pr_downscale_table_x_point8TO1(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50284, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50288, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5028c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50290, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50294, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50298, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5029c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a0, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a4, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502a8, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502ac, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b0, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b4, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502b8, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x502bc, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502c8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502cc, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502d8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502dc, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502e8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502ec, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f0, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f4, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502f8, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x502fc, 0x0); +} + +static void mdp_load_pr_downscale_table_y_point8TO1(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50304, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50308, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5030c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50310, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50314, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50318, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5031c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50320, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50324, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50328, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5032c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50330, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50334, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50338, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x5033c, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50340, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50344, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50348, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5034c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50350, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50354, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50358, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5035c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50360, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50364, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50368, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5036c, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50370, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50374, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x50378, 0x0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ff); + MDP_OUTP(MDP_BASE + 0x5037c, 0x0); +} + +static void mdp_load_bc_upscale_table(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50200, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xff80000d); + MDP_OUTP(MDP_BASE + 0x50204, 0x7ec003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec0001c); + MDP_OUTP(MDP_BASE + 0x50208, 0x7d4003f3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40002b); + MDP_OUTP(MDP_BASE + 0x5020c, 0x7b8003ed); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfd80003c); + MDP_OUTP(MDP_BASE + 0x50210, 0x794003e8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc0004d); + MDP_OUTP(MDP_BASE + 0x50214, 0x76c003e4); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfc40005f); + MDP_OUTP(MDP_BASE + 0x50218, 0x73c003e0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb800071); + MDP_OUTP(MDP_BASE + 0x5021c, 0x708003de); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfac00085); + MDP_OUTP(MDP_BASE + 0x50220, 0x6d0003db); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa000098); + MDP_OUTP(MDP_BASE + 0x50224, 0x698003d9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf98000ac); + MDP_OUTP(MDP_BASE + 0x50228, 0x654003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8c000c1); + MDP_OUTP(MDP_BASE + 0x5022c, 0x610003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf84000d5); + MDP_OUTP(MDP_BASE + 0x50230, 0x5c8003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf7c000e9); + MDP_OUTP(MDP_BASE + 0x50234, 0x580003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf74000fd); + MDP_OUTP(MDP_BASE + 0x50238, 0x534003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c00112); + MDP_OUTP(MDP_BASE + 0x5023c, 0x4e8003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6800126); + MDP_OUTP(MDP_BASE + 0x50240, 0x494003da); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600013a); + MDP_OUTP(MDP_BASE + 0x50244, 0x448003db); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600014d); + MDP_OUTP(MDP_BASE + 0x50248, 0x3f4003dd); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00160); + MDP_OUTP(MDP_BASE + 0x5024c, 0x3a4003df); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00172); + MDP_OUTP(MDP_BASE + 0x50250, 0x354003e1); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00184); + MDP_OUTP(MDP_BASE + 0x50254, 0x304003e3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6000195); + MDP_OUTP(MDP_BASE + 0x50258, 0x2b0003e6); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf64001a6); + MDP_OUTP(MDP_BASE + 0x5025c, 0x260003e8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c001b4); + MDP_OUTP(MDP_BASE + 0x50260, 0x214003eb); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf78001c2); + MDP_OUTP(MDP_BASE + 0x50264, 0x1c4003ee); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf80001cf); + MDP_OUTP(MDP_BASE + 0x50268, 0x17c003f1); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf90001db); + MDP_OUTP(MDP_BASE + 0x5026c, 0x134003f3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa0001e5); + MDP_OUTP(MDP_BASE + 0x50270, 0xf0003f6); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb4001ee); + MDP_OUTP(MDP_BASE + 0x50274, 0xac003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc001f5); + MDP_OUTP(MDP_BASE + 0x50278, 0x70003fb); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe4001fb); + MDP_OUTP(MDP_BASE + 0x5027c, 0x34003fe); +} + +static void mdp_load_bc_downscale_table_x_point2TOpoint4(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ac00084); + MDP_OUTP(MDP_BASE + 0x50280, 0x23400083); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b000084); + MDP_OUTP(MDP_BASE + 0x50284, 0x23000083); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b400084); + MDP_OUTP(MDP_BASE + 0x50288, 0x23000082); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b400085); + MDP_OUTP(MDP_BASE + 0x5028c, 0x23000081); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b800085); + MDP_OUTP(MDP_BASE + 0x50290, 0x23000080); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1bc00086); + MDP_OUTP(MDP_BASE + 0x50294, 0x22c0007f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c000086); + MDP_OUTP(MDP_BASE + 0x50298, 0x2280007f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c400086); + MDP_OUTP(MDP_BASE + 0x5029c, 0x2280007e); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c800086); + MDP_OUTP(MDP_BASE + 0x502a0, 0x2280007d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc00086); + MDP_OUTP(MDP_BASE + 0x502a4, 0x2240007d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc00087); + MDP_OUTP(MDP_BASE + 0x502a8, 0x2240007c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d000087); + MDP_OUTP(MDP_BASE + 0x502ac, 0x2240007b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d400087); + MDP_OUTP(MDP_BASE + 0x502b0, 0x2200007b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d400088); + MDP_OUTP(MDP_BASE + 0x502b4, 0x22400079); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d800088); + MDP_OUTP(MDP_BASE + 0x502b8, 0x22400078); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc00088); + MDP_OUTP(MDP_BASE + 0x502bc, 0x22400077); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc00089); + MDP_OUTP(MDP_BASE + 0x502c0, 0x22000077); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1e000089); + MDP_OUTP(MDP_BASE + 0x502c4, 0x22000076); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1e400089); + MDP_OUTP(MDP_BASE + 0x502c8, 0x22000075); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec00088); + MDP_OUTP(MDP_BASE + 0x502cc, 0x21c00075); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec00089); + MDP_OUTP(MDP_BASE + 0x502d0, 0x21c00074); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f000089); + MDP_OUTP(MDP_BASE + 0x502d4, 0x21c00073); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f400089); + MDP_OUTP(MDP_BASE + 0x502d8, 0x21800073); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f40008a); + MDP_OUTP(MDP_BASE + 0x502dc, 0x21800072); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f80008a); + MDP_OUTP(MDP_BASE + 0x502e0, 0x21800071); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1fc0008a); + MDP_OUTP(MDP_BASE + 0x502e4, 0x21800070); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1fc0008b); + MDP_OUTP(MDP_BASE + 0x502e8, 0x2180006f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x2000008c); + MDP_OUTP(MDP_BASE + 0x502ec, 0x2140006e); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x2040008c); + MDP_OUTP(MDP_BASE + 0x502f0, 0x2140006d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x2080008c); + MDP_OUTP(MDP_BASE + 0x502f4, 0x2100006d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x20c0008c); + MDP_OUTP(MDP_BASE + 0x502f8, 0x2100006c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x20c0008d); + MDP_OUTP(MDP_BASE + 0x502fc, 0x2100006b); +} + +static void mdp_load_bc_downscale_table_y_point2TOpoint4(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ac00084); + MDP_OUTP(MDP_BASE + 0x50300, 0x23400083); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b000084); + MDP_OUTP(MDP_BASE + 0x50304, 0x23000083); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b400084); + MDP_OUTP(MDP_BASE + 0x50308, 0x23000082); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b400085); + MDP_OUTP(MDP_BASE + 0x5030c, 0x23000081); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1b800085); + MDP_OUTP(MDP_BASE + 0x50310, 0x23000080); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1bc00086); + MDP_OUTP(MDP_BASE + 0x50314, 0x22c0007f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c000086); + MDP_OUTP(MDP_BASE + 0x50318, 0x2280007f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c400086); + MDP_OUTP(MDP_BASE + 0x5031c, 0x2280007e); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1c800086); + MDP_OUTP(MDP_BASE + 0x50320, 0x2280007d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc00086); + MDP_OUTP(MDP_BASE + 0x50324, 0x2240007d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc00087); + MDP_OUTP(MDP_BASE + 0x50328, 0x2240007c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d000087); + MDP_OUTP(MDP_BASE + 0x5032c, 0x2240007b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d400087); + MDP_OUTP(MDP_BASE + 0x50330, 0x2200007b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d400088); + MDP_OUTP(MDP_BASE + 0x50334, 0x22400079); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1d800088); + MDP_OUTP(MDP_BASE + 0x50338, 0x22400078); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc00088); + MDP_OUTP(MDP_BASE + 0x5033c, 0x22400077); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc00089); + MDP_OUTP(MDP_BASE + 0x50340, 0x22000077); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1e000089); + MDP_OUTP(MDP_BASE + 0x50344, 0x22000076); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1e400089); + MDP_OUTP(MDP_BASE + 0x50348, 0x22000075); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec00088); + MDP_OUTP(MDP_BASE + 0x5034c, 0x21c00075); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec00089); + MDP_OUTP(MDP_BASE + 0x50350, 0x21c00074); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f000089); + MDP_OUTP(MDP_BASE + 0x50354, 0x21c00073); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f400089); + MDP_OUTP(MDP_BASE + 0x50358, 0x21800073); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f40008a); + MDP_OUTP(MDP_BASE + 0x5035c, 0x21800072); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1f80008a); + MDP_OUTP(MDP_BASE + 0x50360, 0x21800071); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1fc0008a); + MDP_OUTP(MDP_BASE + 0x50364, 0x21800070); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1fc0008b); + MDP_OUTP(MDP_BASE + 0x50368, 0x2180006f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x2000008c); + MDP_OUTP(MDP_BASE + 0x5036c, 0x2140006e); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x2040008c); + MDP_OUTP(MDP_BASE + 0x50370, 0x2140006d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x2080008c); + MDP_OUTP(MDP_BASE + 0x50374, 0x2100006d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x20c0008c); + MDP_OUTP(MDP_BASE + 0x50378, 0x2100006c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x20c0008d); + MDP_OUTP(MDP_BASE + 0x5037c, 0x2100006b); +} + +static void mdp_load_bc_downscale_table_x_point4TOpoint6(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x740008c); + MDP_OUTP(MDP_BASE + 0x50280, 0x33800088); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x800008e); + MDP_OUTP(MDP_BASE + 0x50284, 0x33400084); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x8400092); + MDP_OUTP(MDP_BASE + 0x50288, 0x33000080); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x9000094); + MDP_OUTP(MDP_BASE + 0x5028c, 0x3300007b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x9c00098); + MDP_OUTP(MDP_BASE + 0x50290, 0x32400077); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xa40009b); + MDP_OUTP(MDP_BASE + 0x50294, 0x32000073); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xb00009d); + MDP_OUTP(MDP_BASE + 0x50298, 0x31c0006f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xbc000a0); + MDP_OUTP(MDP_BASE + 0x5029c, 0x3140006b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xc8000a2); + MDP_OUTP(MDP_BASE + 0x502a0, 0x31000067); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xd8000a5); + MDP_OUTP(MDP_BASE + 0x502a4, 0x30800062); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xe4000a8); + MDP_OUTP(MDP_BASE + 0x502a8, 0x2fc0005f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xec000aa); + MDP_OUTP(MDP_BASE + 0x502ac, 0x2fc0005b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8000ad); + MDP_OUTP(MDP_BASE + 0x502b0, 0x2f400057); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x108000b0); + MDP_OUTP(MDP_BASE + 0x502b4, 0x2e400054); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x114000b2); + MDP_OUTP(MDP_BASE + 0x502b8, 0x2e000050); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x124000b4); + MDP_OUTP(MDP_BASE + 0x502bc, 0x2d80004c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x130000b6); + MDP_OUTP(MDP_BASE + 0x502c0, 0x2d000049); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x140000b8); + MDP_OUTP(MDP_BASE + 0x502c4, 0x2c800045); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x150000b9); + MDP_OUTP(MDP_BASE + 0x502c8, 0x2c000042); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x15c000bd); + MDP_OUTP(MDP_BASE + 0x502cc, 0x2b40003e); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x16c000bf); + MDP_OUTP(MDP_BASE + 0x502d0, 0x2a80003b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x17c000bf); + MDP_OUTP(MDP_BASE + 0x502d4, 0x2a000039); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x188000c2); + MDP_OUTP(MDP_BASE + 0x502d8, 0x29400036); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x19c000c4); + MDP_OUTP(MDP_BASE + 0x502dc, 0x28800032); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ac000c5); + MDP_OUTP(MDP_BASE + 0x502e0, 0x2800002f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1bc000c7); + MDP_OUTP(MDP_BASE + 0x502e4, 0x2740002c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc000c8); + MDP_OUTP(MDP_BASE + 0x502e8, 0x26c00029); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc000c9); + MDP_OUTP(MDP_BASE + 0x502ec, 0x26000027); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec000cc); + MDP_OUTP(MDP_BASE + 0x502f0, 0x25000024); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x200000cc); + MDP_OUTP(MDP_BASE + 0x502f4, 0x24800021); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x210000cd); + MDP_OUTP(MDP_BASE + 0x502f8, 0x23800020); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x220000ce); + MDP_OUTP(MDP_BASE + 0x502fc, 0x2300001d); +} + +static void mdp_load_bc_downscale_table_y_point4TOpoint6(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x740008c); + MDP_OUTP(MDP_BASE + 0x50300, 0x33800088); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x800008e); + MDP_OUTP(MDP_BASE + 0x50304, 0x33400084); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x8400092); + MDP_OUTP(MDP_BASE + 0x50308, 0x33000080); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x9000094); + MDP_OUTP(MDP_BASE + 0x5030c, 0x3300007b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x9c00098); + MDP_OUTP(MDP_BASE + 0x50310, 0x32400077); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xa40009b); + MDP_OUTP(MDP_BASE + 0x50314, 0x32000073); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xb00009d); + MDP_OUTP(MDP_BASE + 0x50318, 0x31c0006f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xbc000a0); + MDP_OUTP(MDP_BASE + 0x5031c, 0x3140006b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xc8000a2); + MDP_OUTP(MDP_BASE + 0x50320, 0x31000067); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xd8000a5); + MDP_OUTP(MDP_BASE + 0x50324, 0x30800062); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xe4000a8); + MDP_OUTP(MDP_BASE + 0x50328, 0x2fc0005f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xec000aa); + MDP_OUTP(MDP_BASE + 0x5032c, 0x2fc0005b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8000ad); + MDP_OUTP(MDP_BASE + 0x50330, 0x2f400057); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x108000b0); + MDP_OUTP(MDP_BASE + 0x50334, 0x2e400054); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x114000b2); + MDP_OUTP(MDP_BASE + 0x50338, 0x2e000050); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x124000b4); + MDP_OUTP(MDP_BASE + 0x5033c, 0x2d80004c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x130000b6); + MDP_OUTP(MDP_BASE + 0x50340, 0x2d000049); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x140000b8); + MDP_OUTP(MDP_BASE + 0x50344, 0x2c800045); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x150000b9); + MDP_OUTP(MDP_BASE + 0x50348, 0x2c000042); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x15c000bd); + MDP_OUTP(MDP_BASE + 0x5034c, 0x2b40003e); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x16c000bf); + MDP_OUTP(MDP_BASE + 0x50350, 0x2a80003b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x17c000bf); + MDP_OUTP(MDP_BASE + 0x50354, 0x2a000039); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x188000c2); + MDP_OUTP(MDP_BASE + 0x50358, 0x29400036); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x19c000c4); + MDP_OUTP(MDP_BASE + 0x5035c, 0x28800032); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ac000c5); + MDP_OUTP(MDP_BASE + 0x50360, 0x2800002f); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1bc000c7); + MDP_OUTP(MDP_BASE + 0x50364, 0x2740002c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1cc000c8); + MDP_OUTP(MDP_BASE + 0x50368, 0x26c00029); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1dc000c9); + MDP_OUTP(MDP_BASE + 0x5036c, 0x26000027); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1ec000cc); + MDP_OUTP(MDP_BASE + 0x50370, 0x25000024); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x200000cc); + MDP_OUTP(MDP_BASE + 0x50374, 0x24800021); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x210000cd); + MDP_OUTP(MDP_BASE + 0x50378, 0x23800020); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x220000ce); + MDP_OUTP(MDP_BASE + 0x5037c, 0x2300001d); +} + +static void mdp_load_bc_downscale_table_x_point6TOpoint8(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000070); + MDP_OUTP(MDP_BASE + 0x50280, 0x4bc00068); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000078); + MDP_OUTP(MDP_BASE + 0x50284, 0x4bc00060); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000080); + MDP_OUTP(MDP_BASE + 0x50288, 0x4b800059); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000089); + MDP_OUTP(MDP_BASE + 0x5028c, 0x4b000052); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe400091); + MDP_OUTP(MDP_BASE + 0x50290, 0x4a80004b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40009a); + MDP_OUTP(MDP_BASE + 0x50294, 0x4a000044); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe8000a3); + MDP_OUTP(MDP_BASE + 0x50298, 0x4940003d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec000ac); + MDP_OUTP(MDP_BASE + 0x5029c, 0x48400037); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xff0000b4); + MDP_OUTP(MDP_BASE + 0x502a0, 0x47800031); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xff8000bd); + MDP_OUTP(MDP_BASE + 0x502a4, 0x4640002b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xc5); + MDP_OUTP(MDP_BASE + 0x502a8, 0x45000026); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x8000ce); + MDP_OUTP(MDP_BASE + 0x502ac, 0x43800021); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x10000d6); + MDP_OUTP(MDP_BASE + 0x502b0, 0x4240001c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x18000df); + MDP_OUTP(MDP_BASE + 0x502b4, 0x40800018); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x24000e6); + MDP_OUTP(MDP_BASE + 0x502b8, 0x3f000014); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x30000ee); + MDP_OUTP(MDP_BASE + 0x502bc, 0x3d400010); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x40000f5); + MDP_OUTP(MDP_BASE + 0x502c0, 0x3b80000c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x50000fc); + MDP_OUTP(MDP_BASE + 0x502c4, 0x39800009); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x6000102); + MDP_OUTP(MDP_BASE + 0x502c8, 0x37c00006); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x7000109); + MDP_OUTP(MDP_BASE + 0x502cc, 0x35800004); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x840010e); + MDP_OUTP(MDP_BASE + 0x502d0, 0x33800002); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x9800114); + MDP_OUTP(MDP_BASE + 0x502d4, 0x31400000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xac00119); + MDP_OUTP(MDP_BASE + 0x502d8, 0x2f4003fe); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xc40011e); + MDP_OUTP(MDP_BASE + 0x502dc, 0x2d0003fc); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xdc00121); + MDP_OUTP(MDP_BASE + 0x502e0, 0x2b0003fb); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf400125); + MDP_OUTP(MDP_BASE + 0x502e4, 0x28c003fa); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x11000128); + MDP_OUTP(MDP_BASE + 0x502e8, 0x268003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x12c0012a); + MDP_OUTP(MDP_BASE + 0x502ec, 0x244003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1480012c); + MDP_OUTP(MDP_BASE + 0x502f0, 0x224003f8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1640012e); + MDP_OUTP(MDP_BASE + 0x502f4, 0x200003f8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1800012f); + MDP_OUTP(MDP_BASE + 0x502f8, 0x1e0003f8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1a00012f); + MDP_OUTP(MDP_BASE + 0x502fc, 0x1c0003f8); +} + +static void mdp_load_bc_downscale_table_y_point6TOpoint8(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000070); + MDP_OUTP(MDP_BASE + 0x50300, 0x4bc00068); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000078); + MDP_OUTP(MDP_BASE + 0x50304, 0x4bc00060); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000080); + MDP_OUTP(MDP_BASE + 0x50308, 0x4b800059); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe000089); + MDP_OUTP(MDP_BASE + 0x5030c, 0x4b000052); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe400091); + MDP_OUTP(MDP_BASE + 0x50310, 0x4a80004b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40009a); + MDP_OUTP(MDP_BASE + 0x50314, 0x4a000044); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe8000a3); + MDP_OUTP(MDP_BASE + 0x50318, 0x4940003d); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec000ac); + MDP_OUTP(MDP_BASE + 0x5031c, 0x48400037); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xff0000b4); + MDP_OUTP(MDP_BASE + 0x50320, 0x47800031); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xff8000bd); + MDP_OUTP(MDP_BASE + 0x50324, 0x4640002b); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xc5); + MDP_OUTP(MDP_BASE + 0x50328, 0x45000026); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x8000ce); + MDP_OUTP(MDP_BASE + 0x5032c, 0x43800021); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x10000d6); + MDP_OUTP(MDP_BASE + 0x50330, 0x4240001c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x18000df); + MDP_OUTP(MDP_BASE + 0x50334, 0x40800018); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x24000e6); + MDP_OUTP(MDP_BASE + 0x50338, 0x3f000014); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x30000ee); + MDP_OUTP(MDP_BASE + 0x5033c, 0x3d400010); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x40000f5); + MDP_OUTP(MDP_BASE + 0x50340, 0x3b80000c); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x50000fc); + MDP_OUTP(MDP_BASE + 0x50344, 0x39800009); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x6000102); + MDP_OUTP(MDP_BASE + 0x50348, 0x37c00006); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x7000109); + MDP_OUTP(MDP_BASE + 0x5034c, 0x35800004); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x840010e); + MDP_OUTP(MDP_BASE + 0x50350, 0x33800002); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x9800114); + MDP_OUTP(MDP_BASE + 0x50354, 0x31400000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xac00119); + MDP_OUTP(MDP_BASE + 0x50358, 0x2f4003fe); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xc40011e); + MDP_OUTP(MDP_BASE + 0x5035c, 0x2d0003fc); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xdc00121); + MDP_OUTP(MDP_BASE + 0x50360, 0x2b0003fb); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf400125); + MDP_OUTP(MDP_BASE + 0x50364, 0x28c003fa); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x11000128); + MDP_OUTP(MDP_BASE + 0x50368, 0x268003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x12c0012a); + MDP_OUTP(MDP_BASE + 0x5036c, 0x244003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1480012c); + MDP_OUTP(MDP_BASE + 0x50370, 0x224003f8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1640012e); + MDP_OUTP(MDP_BASE + 0x50374, 0x200003f8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1800012f); + MDP_OUTP(MDP_BASE + 0x50378, 0x1e0003f8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0x1a00012f); + MDP_OUTP(MDP_BASE + 0x5037c, 0x1c0003f8); +} + +static void mdp_load_bc_downscale_table_x_point8TO1(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50280, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xff80000d); + MDP_OUTP(MDP_BASE + 0x50284, 0x7ec003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec0001c); + MDP_OUTP(MDP_BASE + 0x50288, 0x7d4003f3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40002b); + MDP_OUTP(MDP_BASE + 0x5028c, 0x7b8003ed); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfd80003c); + MDP_OUTP(MDP_BASE + 0x50290, 0x794003e8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc0004d); + MDP_OUTP(MDP_BASE + 0x50294, 0x76c003e4); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfc40005f); + MDP_OUTP(MDP_BASE + 0x50298, 0x73c003e0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb800071); + MDP_OUTP(MDP_BASE + 0x5029c, 0x708003de); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfac00085); + MDP_OUTP(MDP_BASE + 0x502a0, 0x6d0003db); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa000098); + MDP_OUTP(MDP_BASE + 0x502a4, 0x698003d9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf98000ac); + MDP_OUTP(MDP_BASE + 0x502a8, 0x654003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8c000c1); + MDP_OUTP(MDP_BASE + 0x502ac, 0x610003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf84000d5); + MDP_OUTP(MDP_BASE + 0x502b0, 0x5c8003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf7c000e9); + MDP_OUTP(MDP_BASE + 0x502b4, 0x580003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf74000fd); + MDP_OUTP(MDP_BASE + 0x502b8, 0x534003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c00112); + MDP_OUTP(MDP_BASE + 0x502bc, 0x4e8003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6800126); + MDP_OUTP(MDP_BASE + 0x502c0, 0x494003da); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600013a); + MDP_OUTP(MDP_BASE + 0x502c4, 0x448003db); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600014d); + MDP_OUTP(MDP_BASE + 0x502c8, 0x3f4003dd); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00160); + MDP_OUTP(MDP_BASE + 0x502cc, 0x3a4003df); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00172); + MDP_OUTP(MDP_BASE + 0x502d0, 0x354003e1); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00184); + MDP_OUTP(MDP_BASE + 0x502d4, 0x304003e3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6000195); + MDP_OUTP(MDP_BASE + 0x502d8, 0x2b0003e6); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf64001a6); + MDP_OUTP(MDP_BASE + 0x502dc, 0x260003e8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c001b4); + MDP_OUTP(MDP_BASE + 0x502e0, 0x214003eb); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf78001c2); + MDP_OUTP(MDP_BASE + 0x502e4, 0x1c4003ee); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf80001cf); + MDP_OUTP(MDP_BASE + 0x502e8, 0x17c003f1); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf90001db); + MDP_OUTP(MDP_BASE + 0x502ec, 0x134003f3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa0001e5); + MDP_OUTP(MDP_BASE + 0x502f0, 0xf0003f6); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb4001ee); + MDP_OUTP(MDP_BASE + 0x502f4, 0xac003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc001f5); + MDP_OUTP(MDP_BASE + 0x502f8, 0x70003fb); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe4001fb); + MDP_OUTP(MDP_BASE + 0x502fc, 0x34003fe); +} + +static void mdp_load_bc_downscale_table_y_point8TO1(void) +{ + MDP_OUTP(MDP_BASE + 0x5fffc, 0x0); + MDP_OUTP(MDP_BASE + 0x50300, 0x7fc00000); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xff80000d); + MDP_OUTP(MDP_BASE + 0x50304, 0x7ec003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfec0001c); + MDP_OUTP(MDP_BASE + 0x50308, 0x7d4003f3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe40002b); + MDP_OUTP(MDP_BASE + 0x5030c, 0x7b8003ed); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfd80003c); + MDP_OUTP(MDP_BASE + 0x50310, 0x794003e8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc0004d); + MDP_OUTP(MDP_BASE + 0x50314, 0x76c003e4); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfc40005f); + MDP_OUTP(MDP_BASE + 0x50318, 0x73c003e0); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb800071); + MDP_OUTP(MDP_BASE + 0x5031c, 0x708003de); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfac00085); + MDP_OUTP(MDP_BASE + 0x50320, 0x6d0003db); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa000098); + MDP_OUTP(MDP_BASE + 0x50324, 0x698003d9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf98000ac); + MDP_OUTP(MDP_BASE + 0x50328, 0x654003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf8c000c1); + MDP_OUTP(MDP_BASE + 0x5032c, 0x610003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf84000d5); + MDP_OUTP(MDP_BASE + 0x50330, 0x5c8003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf7c000e9); + MDP_OUTP(MDP_BASE + 0x50334, 0x580003d7); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf74000fd); + MDP_OUTP(MDP_BASE + 0x50338, 0x534003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c00112); + MDP_OUTP(MDP_BASE + 0x5033c, 0x4e8003d8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6800126); + MDP_OUTP(MDP_BASE + 0x50340, 0x494003da); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600013a); + MDP_OUTP(MDP_BASE + 0x50344, 0x448003db); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf600014d); + MDP_OUTP(MDP_BASE + 0x50348, 0x3f4003dd); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00160); + MDP_OUTP(MDP_BASE + 0x5034c, 0x3a4003df); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00172); + MDP_OUTP(MDP_BASE + 0x50350, 0x354003e1); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf5c00184); + MDP_OUTP(MDP_BASE + 0x50354, 0x304003e3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6000195); + MDP_OUTP(MDP_BASE + 0x50358, 0x2b0003e6); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf64001a6); + MDP_OUTP(MDP_BASE + 0x5035c, 0x260003e8); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf6c001b4); + MDP_OUTP(MDP_BASE + 0x50360, 0x214003eb); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf78001c2); + MDP_OUTP(MDP_BASE + 0x50364, 0x1c4003ee); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf80001cf); + MDP_OUTP(MDP_BASE + 0x50368, 0x17c003f1); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xf90001db); + MDP_OUTP(MDP_BASE + 0x5036c, 0x134003f3); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfa0001e5); + MDP_OUTP(MDP_BASE + 0x50370, 0xf0003f6); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfb4001ee); + MDP_OUTP(MDP_BASE + 0x50374, 0xac003f9); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfcc001f5); + MDP_OUTP(MDP_BASE + 0x50378, 0x70003fb); + MDP_OUTP(MDP_BASE + 0x5fffc, 0xfe4001fb); + MDP_OUTP(MDP_BASE + 0x5037c, 0x34003fe); +} + +static int mdp_get_edge_cond(MDPIBUF *iBuf, uint32 *dup, uint32 *dup2) +{ + uint32 reg; + uint32 dst_roi_width; /* Dimensions of DST ROI. */ + uint32 dst_roi_height; /* Used to calculate scaling ratios. */ + + /* + * positions of the luma pixel(relative to the image ) required for + * scaling the ROI + */ + int32 luma_interp_point_left = 0; /* left-most luma pixel needed */ + int32 luma_interp_point_right = 0; /* right-most luma pixel needed */ + int32 luma_interp_point_top = 0; /* top-most luma pixel needed */ + int32 luma_interp_point_bottom = 0; /* bottom-most luma pixel needed */ + + /* + * positions of the chroma pixel(relative to the image ) required for + * interpolating a chroma value at all required luma positions + */ + /* left-most chroma pixel needed */ + int32 chroma_interp_point_left = 0; + /* right-most chroma pixel needed */ + int32 chroma_interp_point_right = 0; + /* top-most chroma pixel needed */ + int32 chroma_interp_point_top = 0; + /* bottom-most chroma pixel needed */ + int32 chroma_interp_point_bottom = 0; + + /* + * a rectangular region within the chroma plane of the "image". + * Chroma pixels falling inside of this rectangle belongs to the ROI + */ + int32 chroma_bound_left = 0; + int32 chroma_bound_right = 0; + int32 chroma_bound_top = 0; + int32 chroma_bound_bottom = 0; + + /* + * number of chroma pixels to replicate on the left, right, + * top and bottom edge of the ROI. + */ + int32 chroma_repeat_left = 0; + int32 chroma_repeat_right = 0; + int32 chroma_repeat_top = 0; + int32 chroma_repeat_bottom = 0; + + /* + * number of luma pixels to replicate on the left, right, + * top and bottom edge of the ROI. + */ + int32 luma_repeat_left = 0; + int32 luma_repeat_right = 0; + int32 luma_repeat_top = 0; + int32 luma_repeat_bottom = 0; + + boolean chroma_edge_enable; + + uint32 _is_scale_enabled = 0; + uint32 _is_yuv_offsite_vertical = 0; + + /* fg edge duplicate */ + reg = 0x0; + + if (iBuf->mdpImg.mdpOp & MDPOP_ASCALE) { /* if scaling enabled */ + + _is_scale_enabled = 1; + + /* + * if rotation mode involves a 90 deg rotation, flip + * dst_roi_width with dst_roi_height. + * Scaling ratios is based on source ROI dimensions, and + * dst ROI dimensions before rotation. + */ + if (iBuf->mdpImg.mdpOp & MDPOP_ROT90) { + dst_roi_width = iBuf->roi.dst_height; + dst_roi_height = iBuf->roi.dst_width; + } else { + dst_roi_width = iBuf->roi.dst_width; + dst_roi_height = iBuf->roi.dst_height; + } + + /* + * Find out the luma pixels needed for scaling in the + * x direction (LEFT and RIGHT). Locations of pixels are + * relative to the ROI. Upper-left corner of ROI corresponds + * to coordinates (0,0). Also set the number of luma pixel + * to repeat. + */ + if (iBuf->roi.width > 3 * dst_roi_width) { + /* scale factor < 1/3 */ + luma_interp_point_left = 0; + luma_interp_point_right = (iBuf->roi.width - 1); + luma_repeat_left = 0; + luma_repeat_right = 0; + } else if (iBuf->roi.width == 3 * dst_roi_width) { + /* scale factor == 1/3 */ + luma_interp_point_left = 0; + luma_interp_point_right = (iBuf->roi.width - 1) + 1; + luma_repeat_left = 0; + luma_repeat_right = 1; + } else if ((iBuf->roi.width > dst_roi_width) && + (iBuf->roi.width < 3 * dst_roi_width)) { + /* 1/3 < scale factor < 1 */ + luma_interp_point_left = -1; + luma_interp_point_right = (iBuf->roi.width - 1) + 1; + luma_repeat_left = 1; + luma_repeat_right = 1; + } + + else if (iBuf->roi.width == dst_roi_width) { + /* scale factor == 1 */ + luma_interp_point_left = -1; + luma_interp_point_right = (iBuf->roi.width - 1) + 2; + luma_repeat_left = 1; + luma_repeat_right = 2; + } else { /* (iBuf->roi.width < dst_roi_width) */ + /* scale factor > 1 */ + luma_interp_point_left = -2; + luma_interp_point_right = (iBuf->roi.width - 1) + 2; + luma_repeat_left = 2; + luma_repeat_right = 2; + } + + /* + * Find out the number of pixels needed for scaling in the + * y direction (TOP and BOTTOM). Locations of pixels are + * relative to the ROI. Upper-left corner of ROI corresponds + * to coordinates (0,0). Also set the number of luma pixel + * to repeat. + */ + if (iBuf->roi.height > 3 * dst_roi_height) { + /* scale factor < 1/3 */ + luma_interp_point_top = 0; + luma_interp_point_bottom = (iBuf->roi.height - 1); + luma_repeat_top = 0; + luma_repeat_bottom = 0; + } else if (iBuf->roi.height == 3 * dst_roi_height) { + /* scale factor == 1/3 */ + luma_interp_point_top = 0; + luma_interp_point_bottom = (iBuf->roi.height - 1) + 1; + luma_repeat_top = 0; + luma_repeat_bottom = 1; + } else if ((iBuf->roi.height > dst_roi_height) && + (iBuf->roi.height < 3 * dst_roi_height)) { + /* 1/3 < scale factor < 1 */ + luma_interp_point_top = -1; + luma_interp_point_bottom = (iBuf->roi.height - 1) + 1; + luma_repeat_top = 1; + luma_repeat_bottom = 1; + } else if (iBuf->roi.height == dst_roi_height) { + /* scale factor == 1 */ + luma_interp_point_top = -1; + luma_interp_point_bottom = (iBuf->roi.height - 1) + 2; + luma_repeat_top = 1; + luma_repeat_bottom = 2; + } else { /* (iBuf->roi.height < dst_roi_height) */ + /* scale factor > 1 */ + luma_interp_point_top = -2; + luma_interp_point_bottom = (iBuf->roi.height - 1) + 2; + luma_repeat_top = 2; + luma_repeat_bottom = 2; + } + } /* if (iBuf->scale.scale_flag) */ + else { /* scaling disabled */ + /* + * Since no scaling needed, Tile Fetch does not require any + * more luma pixel than what the ROI contains. + */ + luma_interp_point_left = (int32) 0; + luma_interp_point_right = (int32) (iBuf->roi.width - 1); + luma_interp_point_top = (int32) 0; + luma_interp_point_bottom = (int32) (iBuf->roi.height - 1); + + luma_repeat_left = 0; + luma_repeat_right = 0; + luma_repeat_top = 0; + luma_repeat_bottom = 0; + } + + /* After adding the ROI offsets, we have locations of + * luma_interp_points relative to the image. + */ + luma_interp_point_left += (int32) (iBuf->roi.x); + luma_interp_point_right += (int32) (iBuf->roi.x); + luma_interp_point_top += (int32) (iBuf->roi.y); + luma_interp_point_bottom += (int32) (iBuf->roi.y); + + /* + * After adding the ROI offsets, we have locations of + * chroma_interp_points relative to the image. + */ + chroma_interp_point_left = luma_interp_point_left; + chroma_interp_point_right = luma_interp_point_right; + chroma_interp_point_top = luma_interp_point_top; + chroma_interp_point_bottom = luma_interp_point_bottom; + + chroma_edge_enable = TRUE; + /* find out which chroma pixels are needed for chroma upsampling. */ + switch (iBuf->mdpImg.imgType) { + /* + * cosite in horizontal axis + * fully sampled in vertical axis + */ + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_YCRYCB_H2V1: + /* floor( luma_interp_point_left / 2 ); */ + chroma_interp_point_left = luma_interp_point_left >> 1; + /* floor( ( luma_interp_point_right + 1 ) / 2 ); */ + chroma_interp_point_right = (luma_interp_point_right + 1) >> 1; + + chroma_interp_point_top = luma_interp_point_top; + chroma_interp_point_bottom = luma_interp_point_bottom; + break; + + /* + * cosite in horizontal axis + * offsite in vertical axis + */ + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_ADRENO: + case MDP_Y_CRCB_H2V2: + /* floor( luma_interp_point_left / 2) */ + chroma_interp_point_left = luma_interp_point_left >> 1; + + /* floor( ( luma_interp_point_right + 1 )/ 2 ) */ + chroma_interp_point_right = (luma_interp_point_right + 1) >> 1; + + /* floor( (luma_interp_point_top - 1 ) / 2 ) */ + chroma_interp_point_top = (luma_interp_point_top - 1) >> 1; + + /* floor( ( luma_interp_point_bottom + 1 ) / 2 ) */ + chroma_interp_point_bottom = + (luma_interp_point_bottom + 1) >> 1; + + _is_yuv_offsite_vertical = 1; + break; + + default: + chroma_edge_enable = FALSE; + chroma_interp_point_left = luma_interp_point_left; + chroma_interp_point_right = luma_interp_point_right; + chroma_interp_point_top = luma_interp_point_top; + chroma_interp_point_bottom = luma_interp_point_bottom; + + break; + } + + /* only if the image type is in YUV domain, we calculate chroma edge */ + if (chroma_edge_enable) { + /* Defines which chroma pixels belongs to the roi */ + switch (iBuf->mdpImg.imgType) { + /* + * Cosite in horizontal direction, and fully sampled + * in vertical direction. + */ + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_YCRYCB_H2V1: + /* + * width of chroma ROI is 1/2 of size of luma ROI + * height of chroma ROI same as size of luma ROI + */ + chroma_bound_left = iBuf->roi.x / 2; + + /* there are half as many chroma pixel as luma pixels */ + chroma_bound_right = + (iBuf->roi.width + iBuf->roi.x - 1) / 2; + chroma_bound_top = iBuf->roi.y; + chroma_bound_bottom = + (iBuf->roi.height + iBuf->roi.y - 1); + break; + + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_ADRENO: + case MDP_Y_CRCB_H2V2: + /* + * cosite in horizontal dir, and offsite in vertical dir + * width of chroma ROI is 1/2 of size of luma ROI + * height of chroma ROI is 1/2 of size of luma ROI + */ + + chroma_bound_left = iBuf->roi.x / 2; + chroma_bound_right = + (iBuf->roi.width + iBuf->roi.x - 1) / 2; + chroma_bound_top = iBuf->roi.y / 2; + chroma_bound_bottom = + (iBuf->roi.height + iBuf->roi.y - 1) / 2; + break; + + default: + /* + * If no valid chroma sub-sampling format specified, + * assume 4:4:4 ( i.e. fully sampled). Set ROI + * boundaries for chroma same as ROI boundaries for + * luma. + */ + chroma_bound_left = iBuf->roi.x; + chroma_bound_right = iBuf->roi.width + iBuf->roi.x - 1; + chroma_bound_top = iBuf->roi.y; + chroma_bound_bottom = + (iBuf->roi.height + iBuf->roi.y - 1); + break; + } + + /* + * Knowing which chroma pixels are needed, and which chroma + * pixels belong to the ROI (i.e. available for fetching ), + * calculate how many chroma pixels Tile Fetch needs to + * duplicate. If any required chroma pixels falls outside + * of the ROI, Tile Fetch must obtain them by replicating + * pixels. + */ + if (chroma_bound_left > chroma_interp_point_left) + chroma_repeat_left = + chroma_bound_left - chroma_interp_point_left; + else + chroma_repeat_left = 0; + + if (chroma_interp_point_right > chroma_bound_right) + chroma_repeat_right = + chroma_interp_point_right - chroma_bound_right; + else + chroma_repeat_right = 0; + + if (chroma_bound_top > chroma_interp_point_top) + chroma_repeat_top = + chroma_bound_top - chroma_interp_point_top; + else + chroma_repeat_top = 0; + + if (chroma_interp_point_bottom > chroma_bound_bottom) + chroma_repeat_bottom = + chroma_interp_point_bottom - chroma_bound_bottom; + else + chroma_repeat_bottom = 0; + + if (_is_scale_enabled && (iBuf->roi.height == 1) + && _is_yuv_offsite_vertical) { + chroma_repeat_bottom = 3; + chroma_repeat_top = 0; + } + } + /* make sure chroma repeats are non-negative */ + if ((chroma_repeat_left < 0) || (chroma_repeat_right < 0) || + (chroma_repeat_top < 0) || (chroma_repeat_bottom < 0)) + return -1; + + /* make sure chroma repeats are no larger than 3 pixels */ + if ((chroma_repeat_left > 3) || (chroma_repeat_right > 3) || + (chroma_repeat_top > 3) || (chroma_repeat_bottom > 3)) + return -1; + + /* make sure luma repeats are non-negative */ + if ((luma_repeat_left < 0) || (luma_repeat_right < 0) || + (luma_repeat_top < 0) || (luma_repeat_bottom < 0)) + return -1; + + /* make sure luma repeats are no larger than 3 pixels */ + if ((luma_repeat_left > 3) || (luma_repeat_right > 3) || + (luma_repeat_top > 3) || (luma_repeat_bottom > 3)) + return -1; + + /* write chroma_repeat_left to register */ + reg |= (chroma_repeat_left & 3) << MDP_LEFT_CHROMA; + + /* write chroma_repeat_right to register */ + reg |= (chroma_repeat_right & 3) << MDP_RIGHT_CHROMA; + + /* write chroma_repeat_top to register */ + reg |= (chroma_repeat_top & 3) << MDP_TOP_CHROMA; + + /* write chroma_repeat_bottom to register */ + reg |= (chroma_repeat_bottom & 3) << MDP_BOTTOM_CHROMA; + + /* write luma_repeat_left to register */ + reg |= (luma_repeat_left & 3) << MDP_LEFT_LUMA; + + /* write luma_repeat_right to register */ + reg |= (luma_repeat_right & 3) << MDP_RIGHT_LUMA; + + /* write luma_repeat_top to register */ + reg |= (luma_repeat_top & 3) << MDP_TOP_LUMA; + + /* write luma_repeat_bottom to register */ + reg |= (luma_repeat_bottom & 3) << MDP_BOTTOM_LUMA; + + /* done with reg */ + *dup = reg; + + /* bg edge duplicate */ + reg = 0x0; + + switch (iBuf->ibuf_type) { + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + /* + * Edge condition for MDP_Y_CRCB/CBCR_H2V2 cosite only. + * For 420 cosite, 1 chroma replicated on all sides except + * left, so reg 101b8 should be 0x0209. For 420 offsite, + * 1 chroma replicated all sides. + */ + if (iBuf->roi.lcd_y == 0) { + reg |= BIT(MDP_TOP_CHROMA); + } + + if ((iBuf->roi.lcd_y + iBuf->roi.dst_height) == + iBuf->ibuf_height) { + reg |= BIT(MDP_BOTTOM_CHROMA); + } + + if (((iBuf->roi.lcd_x + iBuf->roi.dst_width) == + iBuf->ibuf_width) && ((iBuf->roi.dst_width % 2) == 0)) { + reg |= BIT(MDP_RIGHT_CHROMA); + } + + break; + + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_YCRYCB_H2V1: + if (((iBuf->roi.lcd_x + iBuf->roi.dst_width) == + iBuf->ibuf_width) && ((iBuf->roi.dst_width % 2) == 0)) { + reg |= BIT(MDP_RIGHT_CHROMA); + } + break; + default: + break; + } + + *dup2 = reg; + + return 0; +} + +#define ADJUST_IP /* for 1/3 scale factor fix */ + +static int mdp_calc_scale_params( +/* ROI origin coordinate for the dimension */ + uint32 org, +/* src ROI dimension */ + uint32 dim_in, +/* scaled ROI dimension*/ + uint32 dim_out, +/* is this ROI width dimension? */ + boolean is_W, +/* initial phase location address */ + int32 *phase_init_ptr, +/* phase increment location address */ + uint32 *phase_step_ptr, +/* ROI start over-fetch location address */ + uint32 *num_repl_beg_ptr, +/* ROI end over-fetch location address */ + uint32 *num_repl_end_ptr) +{ + boolean rpa_on = FALSE; + int init_phase = 0; + uint32 beg_of = 0; + uint32 end_of = 0; + uint64 numer = 0; + uint64 denom = 0; + /*uint64 inverter = 1; */ + int64 point5 = 1; + int64 one = 1; + int64 k1, k2, k3, k4; /* linear equation coefficients */ + uint64 int_mask; + uint64 fract_mask; + uint64 Os; + int64 Osprime; + int64 Od; + int64 Odprime; + int64 Oreq; + uint64 Es; + uint64 Ed; + uint64 Ereq; +#ifdef ADJUST_IP + int64 IP64; + int64 delta; +#endif + uint32 mult; + + /* + * The phase accumulator should really be rational for all cases in a + * general purpose polyphase scaler for a tiled architecture with + * non-zero * origin capability because there is no way to represent + * certain scale factors in fixed point regardless of precision. + * The error incurred in attempting to use fixed point is most + * eggregious for SF where 1/SF is an integral multiple of 1/3. + * + * However, since the MDP2 has already been committed to HW, we + * only use the rational phase accumulator (RPA) when 1/SF is an + * integral multiple of 1/3. This will help minimize regressions in + * matching the HW to the C-Sim. + */ + /* + * Set the RPA flag for this dimension. + * + * In order for 1/SF (dim_in/dim_out) to be an integral multiple of + * 1/3, dim_out must be an integral multiple of 3. + */ + if (!(dim_out % 3)) { + mult = dim_out / 3; + rpa_on = (!(dim_in % mult)); + } + + numer = dim_out; + denom = dim_in; + + /* + * convert to U30.34 before division + * + * The K vectors carry 4 extra bits of precision + * and are rounded. + * + * We initially go 5 bits over then round by adding + * 1 and right shifting by 1 + * so final result is U31.33 + */ + numer <<= PQF_PLUS_5; + + /* now calculate the scale factor (aka k3) */ + k3 = ((mdp_do_div(numer, denom) + 1) >> 1); + + /* check scale factor for legal range [0.25 - 4.0] */ + if (((k3 >> 4) < (1LL << PQF_MINUS_2)) || + ((k3 >> 4) > (1LL << PQF_PLUS_2))) { + return -1; + } + + /* calculate inverse scale factor (aka k1) for phase init */ + numer = dim_in; + denom = dim_out; + numer <<= PQF_PLUS_5; + k1 = ((mdp_do_div(numer, denom) + 1) >> 1); + + /* + * calculate initial phase and ROI overfetch + */ + /* convert point5 & one to S39.24 (will always be positive) */ + point5 <<= (PQF_PLUS_4 - 1); + one <<= PQF_PLUS_4; + k2 = ((k1 - one) >> 1); + init_phase = (int)(k2 >> 4); + k4 = ((k3 - one) >> 1); + if (k3 == one) { + /* the simple case; SF = 1.0 */ + beg_of = 1; + end_of = 2; + } else { + /* calculate the masks */ + fract_mask = one - 1; + int_mask = ~fract_mask; + + if (!rpa_on) { + /* + * FIXED POINT IMPLEMENTATION + */ + if (!org) { + /* A fairly simple case; ROI origin = 0 */ + if (k1 < one) { + /* upscaling */ + beg_of = end_of = 2; + } + /* 0.33 <= SF < 1.0 */ + else if (k1 < (3LL << PQF_PLUS_4)) + beg_of = end_of = 1; + /* 0.33 == SF */ + else if (k1 == (3LL << PQF_PLUS_4)) { + beg_of = 0; + end_of = 1; + } + /* 0.25 <= SF < 0.33 */ + else + beg_of = end_of = 0; + } else { + /* + * The complicated case; ROI origin != 0 + * init_phase needs to be adjusted + * OF is also position dependent + */ + + /* map (org - .5) into destination space */ + Os = ((uint64) org << 1) - 1; + Od = ((k3 * Os) >> 1) + k4; + + /* take the ceiling */ + Odprime = (Od & int_mask); + if (Odprime != Od) + Odprime += one; + + /* now map that back to source space */ + Osprime = (k1 * (Odprime >> PQF_PLUS_4)) + k2; + + /* then floor & decrement to calculate the required + starting coordinate */ + Oreq = (Osprime & int_mask) - one; + + /* calculate end coord in destination space then map to + source space */ + Ed = Odprime + + ((uint64) dim_out << PQF_PLUS_4) - one; + Es = (k1 * (Ed >> PQF_PLUS_4)) + k2; + + /* now floor & increment by 2 to calculate the required + ending coordinate */ + Ereq = (Es & int_mask) + (one << 1); + + /* calculate initial phase */ +#ifdef ADJUST_IP + + IP64 = Osprime - Oreq; + delta = ((int64) (org) << PQF_PLUS_4) - Oreq; + IP64 -= delta; + + /* limit to valid range before the left shift */ + delta = (IP64 & (1LL << 63)) ? 4 : -4; + delta <<= PQF_PLUS_4; + while (abs((int)(IP64 >> PQF_PLUS_4)) > 4) + IP64 += delta; + + /* right shift to account for extra bits of precision */ + init_phase = (int)(IP64 >> 4); + +#else /* ADJUST_IP */ + + /* just calculate the real initial phase */ + init_phase = (int)((Osprime - Oreq) >> 4); + +#endif /* ADJUST_IP */ + + /* calculate the overfetch */ + beg_of = org - (uint32) (Oreq >> PQF_PLUS_4); + end_of = + (uint32) (Ereq >> PQF_PLUS_4) - (org + + dim_in - + 1); + } + } else { + /* + * RPA IMPLEMENTATION + * + * init_phase needs to be calculated in all RPA_on cases + * because it's a numerator, not a fixed point value. + */ + + /* map (org - .5) into destination space */ + Os = ((uint64) org << PQF_PLUS_4) - point5; + Od = mdp_do_div((dim_out * (Os + point5)), + dim_in) - point5; + + /* take the ceiling */ + Odprime = (Od & int_mask); + if (Odprime != Od) + Odprime += one; + + /* now map that back to source space */ + Osprime = + mdp_do_div((dim_in * (Odprime + point5)), + dim_out) - point5; + + /* then floor & decrement to calculate the required + starting coordinate */ + Oreq = (Osprime & int_mask) - one; + + /* calculate end coord in destination space then map to + source space */ + Ed = Odprime + ((uint64) dim_out << PQF_PLUS_4) - one; + Es = mdp_do_div((dim_in * (Ed + point5)), + dim_out) - point5; + + /* now floor & increment by 2 to calculate the required + ending coordinate */ + Ereq = (Es & int_mask) + (one << 1); + + /* calculate initial phase */ + +#ifdef ADJUST_IP + + IP64 = Osprime - Oreq; + delta = ((int64) (org) << PQF_PLUS_4) - Oreq; + IP64 -= delta; + + /* limit to valid range before the left shift */ + delta = (IP64 & (1LL << 63)) ? 4 : -4; + delta <<= PQF_PLUS_4; + while (abs((int)(IP64 >> PQF_PLUS_4)) > 4) + IP64 += delta; + + /* right shift to account for extra bits of precision */ + init_phase = (int)(IP64 >> 4); + +#else /* ADJUST_IP */ + + /* just calculate the real initial phase */ + init_phase = (int)((Osprime - Oreq) >> 4); + +#endif /* ADJUST_IP */ + + /* calculate the overfetch */ + beg_of = org - (uint32) (Oreq >> PQF_PLUS_4); + end_of = + (uint32) (Ereq >> PQF_PLUS_4) - (org + dim_in - 1); + } + } + + /* return the scale parameters */ + *phase_init_ptr = init_phase; + *phase_step_ptr = (uint32) (k1 >> 4); + *num_repl_beg_ptr = beg_of; + *num_repl_end_ptr = end_of; + + return 0; +} + +static uint8 *mdp_adjust_rot_addr(MDPIBUF *iBuf, uint8 *addr, uint32 uv) +{ + uint32 dest_ystride = iBuf->ibuf_width * iBuf->bpp; + uint32 h_slice = 1; + + if (uv && ((iBuf->ibuf_type == MDP_Y_CBCR_H2V2) || + (iBuf->ibuf_type == MDP_Y_CRCB_H2V2))) + h_slice = 2; + + if (MDP_CHKBIT(iBuf->mdpImg.mdpOp, MDPOP_ROT90) ^ + MDP_CHKBIT(iBuf->mdpImg.mdpOp, MDPOP_LR)) { + addr = + addr + (iBuf->roi.dst_width - + MIN(16, iBuf->roi.dst_width)) * iBuf->bpp; + } + if (MDP_CHKBIT(iBuf->mdpImg.mdpOp, MDPOP_UD)) { + addr = + addr + ((iBuf->roi.dst_height - + MIN(16, iBuf->roi.dst_height))/h_slice) * dest_ystride; + } + + return addr; +} + +void mdp_set_scale(MDPIBUF *iBuf, + uint32 dst_roi_width, + uint32 dst_roi_height, + boolean inputRGB, boolean outputRGB, uint32 *pppop_reg_ptr) +{ + uint32 dst_roi_width_scale; + uint32 dst_roi_height_scale; + boolean use_pr; + uint32 phasex_step = 0; + uint32 phasey_step = 0; + int32 phasex_init = 0; + int32 phasey_init = 0; + uint32 lines_dup = 0; + uint32 lines_dup_bg = 0; + uint32 dummy; + uint32 mdp_blur = 0; + + if (iBuf->mdpImg.mdpOp & MDPOP_ASCALE) { + if (iBuf->mdpImg.mdpOp & MDPOP_ROT90) { + dst_roi_width_scale = dst_roi_height; + dst_roi_height_scale = dst_roi_width; + } else { + dst_roi_width_scale = dst_roi_width; + dst_roi_height_scale = dst_roi_height; + } + + mdp_blur = iBuf->mdpImg.mdpOp & MDPOP_BLUR; + + if ((dst_roi_width_scale != iBuf->roi.width) || + (dst_roi_height_scale != iBuf->roi.height) || + mdp_blur) { + *pppop_reg_ptr |= + (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); + + /* let's use SHIM logic to calculate the + partial ROI scaling */ + mdp_calc_scale_params(iBuf->roi.x, iBuf->roi.width, + dst_roi_width_scale, 1, + &phasex_init, &phasex_step, + &dummy, &dummy); + mdp_calc_scale_params(iBuf->roi.y, iBuf->roi.height, + dst_roi_height_scale, 0, + &phasey_init, &phasey_step, + &dummy, &dummy); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x013c, + phasex_init); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0140, + phasey_init); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0144, + phasex_step); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0148, + phasey_step); + + /* disable the pixel repeat option for scaling */ + use_pr = false; + + if ((dst_roi_width_scale > iBuf->roi.width) || + (dst_roi_height_scale > iBuf->roi.height)) { + if ((use_pr) + && (mdp_curr_up_scale_xy != + MDP_PR_SCALE_UP)) { + mdp_load_pr_upscale_table(); + mdp_curr_up_scale_xy = MDP_PR_SCALE_UP; + } else if ((!use_pr) + && (mdp_curr_up_scale_xy != + MDP_BC_SCALE_UP)) { + mdp_load_bc_upscale_table(); + mdp_curr_up_scale_xy = MDP_BC_SCALE_UP; + } + } + + if (mdp_blur) { + load_scale_table(mdp_gaussian_blur_table, + ARRAY_SIZE(mdp_gaussian_blur_table)); + mdp_curr_down_scale_x = MDP_SCALE_BLUR; + mdp_curr_down_scale_y = MDP_SCALE_BLUR; + } + + /* 0.2 < x <= 1 scaling factor */ + if ((dst_roi_width_scale <= iBuf->roi.width) && + !mdp_blur) { + if (((dst_roi_width_scale * 10) / + iBuf->roi.width) > 8) { + if ((use_pr) + && (mdp_curr_down_scale_x != + MDP_PR_SCALE_POINT8_1)) { + mdp_load_pr_downscale_table_x_point8TO1 + (); + mdp_curr_down_scale_x = + MDP_PR_SCALE_POINT8_1; + } else if ((!use_pr) + && (mdp_curr_down_scale_x != + MDP_BC_SCALE_POINT8_1)) { + mdp_load_bc_downscale_table_x_point8TO1 + (); + mdp_curr_down_scale_x = + MDP_BC_SCALE_POINT8_1; + } + } else + if (((dst_roi_width_scale * 10) / + iBuf->roi.width) > 6) { + if ((use_pr) + && (mdp_curr_down_scale_x != + MDP_PR_SCALE_POINT6_POINT8)) { + mdp_load_pr_downscale_table_x_point6TOpoint8 + (); + mdp_curr_down_scale_x = + MDP_PR_SCALE_POINT6_POINT8; + } else if ((!use_pr) + && (mdp_curr_down_scale_x != + MDP_BC_SCALE_POINT6_POINT8)) + { + mdp_load_bc_downscale_table_x_point6TOpoint8 + (); + mdp_curr_down_scale_x = + MDP_BC_SCALE_POINT6_POINT8; + } + } else + if (((dst_roi_width_scale * 10) / + iBuf->roi.width) > 4) { + if ((use_pr) + && (mdp_curr_down_scale_x != + MDP_PR_SCALE_POINT4_POINT6)) { + mdp_load_pr_downscale_table_x_point4TOpoint6 + (); + mdp_curr_down_scale_x = + MDP_PR_SCALE_POINT4_POINT6; + } else if ((!use_pr) + && (mdp_curr_down_scale_x != + MDP_BC_SCALE_POINT4_POINT6)) + { + mdp_load_bc_downscale_table_x_point4TOpoint6 + (); + mdp_curr_down_scale_x = + MDP_BC_SCALE_POINT4_POINT6; + } + } else { + if ((use_pr) + && (mdp_curr_down_scale_x != + MDP_PR_SCALE_POINT2_POINT4)) { + mdp_load_pr_downscale_table_x_point2TOpoint4 + (); + mdp_curr_down_scale_x = + MDP_PR_SCALE_POINT2_POINT4; + } else if ((!use_pr) + && (mdp_curr_down_scale_x != + MDP_BC_SCALE_POINT2_POINT4)) + { + mdp_load_bc_downscale_table_x_point2TOpoint4 + (); + mdp_curr_down_scale_x = + MDP_BC_SCALE_POINT2_POINT4; + } + } + } + /* 0.2 < y <= 1 scaling factor */ + if ((dst_roi_height_scale <= iBuf->roi.height) && + !mdp_blur) { + if (((dst_roi_height_scale * 10) / + iBuf->roi.height) > 8) { + if ((use_pr) + && (mdp_curr_down_scale_y != + MDP_PR_SCALE_POINT8_1)) { + mdp_load_pr_downscale_table_y_point8TO1 + (); + mdp_curr_down_scale_y = + MDP_PR_SCALE_POINT8_1; + } else if ((!use_pr) + && (mdp_curr_down_scale_y != + MDP_BC_SCALE_POINT8_1)) { + mdp_load_bc_downscale_table_y_point8TO1 + (); + mdp_curr_down_scale_y = + MDP_BC_SCALE_POINT8_1; + } + } else + if (((dst_roi_height_scale * 10) / + iBuf->roi.height) > 6) { + if ((use_pr) + && (mdp_curr_down_scale_y != + MDP_PR_SCALE_POINT6_POINT8)) { + mdp_load_pr_downscale_table_y_point6TOpoint8 + (); + mdp_curr_down_scale_y = + MDP_PR_SCALE_POINT6_POINT8; + } else if ((!use_pr) + && (mdp_curr_down_scale_y != + MDP_BC_SCALE_POINT6_POINT8)) + { + mdp_load_bc_downscale_table_y_point6TOpoint8 + (); + mdp_curr_down_scale_y = + MDP_BC_SCALE_POINT6_POINT8; + } + } else + if (((dst_roi_height_scale * 10) / + iBuf->roi.height) > 4) { + if ((use_pr) + && (mdp_curr_down_scale_y != + MDP_PR_SCALE_POINT4_POINT6)) { + mdp_load_pr_downscale_table_y_point4TOpoint6 + (); + mdp_curr_down_scale_y = + MDP_PR_SCALE_POINT4_POINT6; + } else if ((!use_pr) + && (mdp_curr_down_scale_y != + MDP_BC_SCALE_POINT4_POINT6)) + { + mdp_load_bc_downscale_table_y_point4TOpoint6 + (); + mdp_curr_down_scale_y = + MDP_BC_SCALE_POINT4_POINT6; + } + } else { + if ((use_pr) + && (mdp_curr_down_scale_y != + MDP_PR_SCALE_POINT2_POINT4)) { + mdp_load_pr_downscale_table_y_point2TOpoint4 + (); + mdp_curr_down_scale_y = + MDP_PR_SCALE_POINT2_POINT4; + } else if ((!use_pr) + && (mdp_curr_down_scale_y != + MDP_BC_SCALE_POINT2_POINT4)) + { + mdp_load_bc_downscale_table_y_point2TOpoint4 + (); + mdp_curr_down_scale_y = + MDP_BC_SCALE_POINT2_POINT4; + } + } + } + } else { + iBuf->mdpImg.mdpOp &= ~(MDPOP_ASCALE); + } + } + /* setting edge condition here after scaling check */ + if (mdp_get_edge_cond(iBuf, &lines_dup, &lines_dup_bg)) + printk(KERN_ERR "msm_fb: mdp_get_edge_cond() error!\n"); + + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01b8, lines_dup); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01bc, lines_dup_bg); +} + +void mdp_init_scale_table(void) +{ + mdp_curr_up_scale_xy = MDP_INIT_SCALE; + mdp_curr_down_scale_x = MDP_INIT_SCALE; + mdp_curr_down_scale_y = MDP_INIT_SCALE; +} + +void mdp_adjust_start_addr(uint8 **src0, + uint8 **src1, + int v_slice, + int h_slice, + int x, + int y, + uint32 width, + uint32 height, int bpp, MDPIBUF *iBuf, int layer) +{ + if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO && layer == 0) + *src0 += (x + y * ALIGN(width, 32)) * bpp; + else + *src0 += (x + y * width) * bpp; + + /* if it's dest/bg buffer, we need to adjust it for rotation */ + if (layer != 0) + *src0 = mdp_adjust_rot_addr(iBuf, *src0, 0); + + if (*src1) { + /* + * MDP_Y_CBCR_H2V2/MDP_Y_CRCB_H2V2 cosite for now + * we need to shift x direction same as y dir for offsite + */ + if (iBuf->mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO + && layer == 0) + *src1 += ((x / h_slice) * h_slice + ((y == 0) ? 0 : + (((y + 1) / v_slice - 1) * (ALIGN(width/2, 32) * 2)))) + * bpp; + else + *src1 += ((x / h_slice) * h_slice + + ((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp; + + /* if it's dest/bg buffer, we need to adjust it for rotation */ + if (layer != 0) + *src1 = mdp_adjust_rot_addr(iBuf, *src1, 1); + } +} + +void mdp_set_blend_attr(MDPIBUF *iBuf, + uint32 *alpha, + uint32 *tpVal, + uint32 perPixelAlpha, uint32 *pppop_reg_ptr) +{ + if (mdp_rev == MDP_REV_303) { + int bg_alpha; + + *alpha = iBuf->mdpImg.alpha; + *tpVal = iBuf->mdpImg.tpVal; + + if (iBuf->mdpImg.mdpOp & MDPOP_FG_PM_ALPHA) { + if (perPixelAlpha) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA; + } else { + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + && (iBuf->mdpImg.alpha == 0xff)) { + iBuf->mdpImg.mdpOp &= ~(MDPOP_ALPHAB); + } + + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + || (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) { + + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL; + } + } + + bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL | + PPP_BLEND_BG_ALPHA_REVERSE; + + if (perPixelAlpha) { + bg_alpha |= PPP_BLEND_BG_SRCPIXEL_ALPHA; + } else { + bg_alpha |= PPP_BLEND_BG_CONSTANT_ALPHA; + bg_alpha |= iBuf->mdpImg.alpha << 24; + } + outpdw(MDP_BASE + 0x70010, bg_alpha); + + if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP) + *pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP; + } else if (perPixelAlpha) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_SRCPIXEL_ALPHA; + outpdw(MDP_BASE + 0x70010, 0); + } else { + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + && (iBuf->mdpImg.alpha == 0xff)) { + iBuf->mdpImg.mdpOp &= + ~(MDPOP_ALPHAB); + } + + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + || (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL; + } + + if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP) + *pppop_reg_ptr |= + PPP_BLEND_CALPHA_TRNASP; + outpdw(MDP_BASE + 0x70010, 0); + } + } else { + if (perPixelAlpha) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | PPP_OP_BLEND_SRCPIXEL_ALPHA; + } else { + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + && (iBuf->mdpImg.alpha == 0xff)) { + iBuf->mdpImg.mdpOp &= ~(MDPOP_ALPHAB); + } + + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + && (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) { + *pppop_reg_ptr |= + PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL | + PPP_BLEND_CALPHA_TRNASP; + + *alpha = iBuf->mdpImg.alpha; + *tpVal = iBuf->mdpImg.tpVal; + } else { + if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_SRCPIXEL_TRANSP; + *tpVal = iBuf->mdpImg.tpVal; + } else if (iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL | + PPP_OP_BLEND_CONSTANT_ALPHA; + *alpha = iBuf->mdpImg.alpha; + } + } + } + } +} diff --git a/drivers/video/msm/mdp_ppp_v31.c b/drivers/video/msm/mdp_ppp_v31.c new file mode 100644 index 0000000000000000000000000000000000000000..3c149b19782d77e3fd04cc3fb1533a031132fe89 --- /dev/null +++ b/drivers/video/msm/mdp_ppp_v31.c @@ -0,0 +1,844 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" + +#include +#include + +#include +#include +#include +#include + +#include "mdp.h" +#include "msm_fb.h" + +#define MDP_SCALE_COEFF_NUM 32 +#define MDP_SCALE_0P2_TO_0P4_INDEX 0 +#define MDP_SCALE_0P4_TO_0P6_INDEX 32 +#define MDP_SCALE_0P6_TO_0P8_INDEX 64 +#define MDP_SCALE_0P8_TO_8P0_INDEX 96 +#define MDP_SCALE_COEFF_MASK 0x3ff + +#define MDP_SCALE_PR 0 +#define MDP_SCALE_FIR 1 + +static uint32 mdp_scale_0p8_to_8p0_mode; +static uint32 mdp_scale_0p6_to_0p8_mode; +static uint32 mdp_scale_0p4_to_0p6_mode; +static uint32 mdp_scale_0p2_to_0p4_mode; + +/* -------- All scaling range, "pixel repeat" -------- */ +static int16 mdp_scale_pixel_repeat_C0[MDP_SCALE_COEFF_NUM] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static int16 mdp_scale_pixel_repeat_C1[MDP_SCALE_COEFF_NUM] = { + 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 511, 511, 511, 511, 511 +}; + +static int16 mdp_scale_pixel_repeat_C2[MDP_SCALE_COEFF_NUM] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static int16 mdp_scale_pixel_repeat_C3[MDP_SCALE_COEFF_NUM] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* --------------------------- FIR ------------------------------------- */ +/* -------- Downscale, ranging from 0.8x to 8.0x of original size -------- */ + +static int16 mdp_scale_0p8_to_8p0_C0[MDP_SCALE_COEFF_NUM] = { + 0, -7, -13, -19, -24, -28, -32, -34, -37, -39, + -40, -41, -41, -41, -40, -40, -38, -37, -35, -33, + -31, -29, -26, -24, -21, -18, -15, -13, -10, -7, + -5, -2 +}; + +static int16 mdp_scale_0p8_to_8p0_C1[MDP_SCALE_COEFF_NUM] = { + 511, 507, 501, 494, 485, 475, 463, 450, 436, 422, + 405, 388, 370, 352, 333, 314, 293, 274, 253, 233, + 213, 193, 172, 152, 133, 113, 95, 77, 60, 43, + 28, 13 +}; + +static int16 mdp_scale_0p8_to_8p0_C2[MDP_SCALE_COEFF_NUM] = { + 0, 13, 28, 43, 60, 77, 95, 113, 133, 152, + 172, 193, 213, 233, 253, 274, 294, 314, 333, 352, + 370, 388, 405, 422, 436, 450, 463, 475, 485, 494, + 501, 507, +}; + +static int16 mdp_scale_0p8_to_8p0_C3[MDP_SCALE_COEFF_NUM] = { + 0, -2, -5, -7, -10, -13, -15, -18, -21, -24, + -26, -29, -31, -33, -35, -37, -38, -40, -40, -41, + -41, -41, -40, -39, -37, -34, -32, -28, -24, -19, + -13, -7 +}; + +/* -------- Downscale, ranging from 0.6x to 0.8x of original size -------- */ + +static int16 mdp_scale_0p6_to_0p8_C0[MDP_SCALE_COEFF_NUM] = { + 104, 96, 89, 82, 75, 68, 61, 55, 49, 43, + 38, 33, 28, 24, 20, 16, 12, 9, 6, 4, + 2, 0, -2, -4, -5, -6, -7, -7, -8, -8, + -8, -8 +}; + +static int16 mdp_scale_0p6_to_0p8_C1[MDP_SCALE_COEFF_NUM] = { + 303, 303, 302, 300, 298, 296, 293, 289, 286, 281, + 276, 270, 265, 258, 252, 245, 238, 230, 223, 214, + 206, 197, 189, 180, 172, 163, 154, 145, 137, 128, + 120, 112 +}; + +static int16 mdp_scale_0p6_to_0p8_C2[MDP_SCALE_COEFF_NUM] = { + 112, 120, 128, 137, 145, 154, 163, 172, 180, 189, + 197, 206, 214, 223, 230, 238, 245, 252, 258, 265, + 270, 276, 281, 286, 289, 293, 296, 298, 300, 302, + 303, 303 +}; + +static int16 mdp_scale_0p6_to_0p8_C3[MDP_SCALE_COEFF_NUM] = { + -8, -8, -8, -8, -7, -7, -6, -5, -4, -2, + 0, 2, 4, 6, 9, 12, 16, 20, 24, 28, + 33, 38, 43, 49, 55, 61, 68, 75, 82, 89, + 96, 104 +}; + +/* -------- Downscale, ranging from 0.4x to 0.6x of original size -------- */ + +static int16 mdp_scale_0p4_to_0p6_C0[MDP_SCALE_COEFF_NUM] = { + 136, 132, 128, 123, 119, 115, 111, 107, 103, 98, + 95, 91, 87, 84, 80, 76, 73, 69, 66, 62, + 59, 57, 54, 50, 47, 44, 41, 39, 36, 33, + 32, 29 +}; + +static int16 mdp_scale_0p4_to_0p6_C1[MDP_SCALE_COEFF_NUM] = { + 206, 205, 204, 204, 201, 200, 199, 197, 196, 194, + 191, 191, 189, 185, 184, 182, 180, 178, 176, 173, + 170, 168, 165, 162, 160, 157, 155, 152, 148, 146, + 142, 140 +}; + +static int16 mdp_scale_0p4_to_0p6_C2[MDP_SCALE_COEFF_NUM] = { + 140, 142, 146, 148, 152, 155, 157, 160, 162, 165, + 168, 170, 173, 176, 178, 180, 182, 184, 185, 189, + 191, 191, 194, 196, 197, 199, 200, 201, 204, 204, + 205, 206 +}; + +static int16 mdp_scale_0p4_to_0p6_C3[MDP_SCALE_COEFF_NUM] = { + 29, 32, 33, 36, 39, 41, 44, 47, 50, 54, + 57, 59, 62, 66, 69, 73, 76, 80, 84, 87, + 91, 95, 98, 103, 107, 111, 115, 119, 123, 128, + 132, 136 +}; + +/* -------- Downscale, ranging from 0.2x to 0.4x of original size -------- */ + +static int16 mdp_scale_0p2_to_0p4_C0[MDP_SCALE_COEFF_NUM] = { + 131, 131, 130, 129, 128, 127, 127, 126, 125, 125, + 124, 123, 123, 121, 120, 119, 119, 118, 117, 117, + 116, 115, 115, 114, 113, 112, 111, 110, 109, 109, + 108, 107 +}; + +static int16 mdp_scale_0p2_to_0p4_C1[MDP_SCALE_COEFF_NUM] = { + 141, 140, 140, 140, 140, 139, 138, 138, 138, 137, + 137, 137, 136, 137, 137, 137, 136, 136, 136, 135, + 135, 135, 134, 134, 134, 134, 134, 133, 133, 132, + 132, 132 +}; + +static int16 mdp_scale_0p2_to_0p4_C2[MDP_SCALE_COEFF_NUM] = { + 132, 132, 132, 133, 133, 134, 134, 134, 134, 134, + 135, 135, 135, 136, 136, 136, 137, 137, 137, 136, + 137, 137, 137, 138, 138, 138, 139, 140, 140, 140, + 140, 141 +}; + +static int16 mdp_scale_0p2_to_0p4_C3[MDP_SCALE_COEFF_NUM] = { + 107, 108, 109, 109, 110, 111, 112, 113, 114, 115, + 115, 116, 117, 117, 118, 119, 119, 120, 121, 123, + 123, 124, 125, 125, 126, 127, 127, 128, 129, 130, + 131, 131 +}; + +static void mdp_update_scale_table(int index, int16 *c0, int16 *c1, + int16 *c2, int16 *c3) +{ + int i, val; + + for (i = 0; i < MDP_SCALE_COEFF_NUM; i++) { + val = + ((MDP_SCALE_COEFF_MASK & c1[i]) << 16) | + (MDP_SCALE_COEFF_MASK & c0[i]); + writel(val, MDP_PPP_SCALE_COEFF_LSBn(index)); + val = + ((MDP_SCALE_COEFF_MASK & c3[i]) << 16) | + (MDP_SCALE_COEFF_MASK & c2[i]); + writel(val, MDP_PPP_SCALE_COEFF_MSBn(index)); + index++; + } +} + +void mdp_init_scale_table(void) +{ + mdp_scale_0p2_to_0p4_mode = MDP_SCALE_FIR; + mdp_update_scale_table(MDP_SCALE_0P2_TO_0P4_INDEX, + mdp_scale_0p2_to_0p4_C0, + mdp_scale_0p2_to_0p4_C1, + mdp_scale_0p2_to_0p4_C2, + mdp_scale_0p2_to_0p4_C3); + + mdp_scale_0p4_to_0p6_mode = MDP_SCALE_FIR; + mdp_update_scale_table(MDP_SCALE_0P4_TO_0P6_INDEX, + mdp_scale_0p4_to_0p6_C0, + mdp_scale_0p4_to_0p6_C1, + mdp_scale_0p4_to_0p6_C2, + mdp_scale_0p4_to_0p6_C3); + + mdp_scale_0p6_to_0p8_mode = MDP_SCALE_FIR; + mdp_update_scale_table(MDP_SCALE_0P6_TO_0P8_INDEX, + mdp_scale_0p6_to_0p8_C0, + mdp_scale_0p6_to_0p8_C1, + mdp_scale_0p6_to_0p8_C2, + mdp_scale_0p6_to_0p8_C3); + + mdp_scale_0p8_to_8p0_mode = MDP_SCALE_FIR; + mdp_update_scale_table(MDP_SCALE_0P8_TO_8P0_INDEX, + mdp_scale_0p8_to_8p0_C0, + mdp_scale_0p8_to_8p0_C1, + mdp_scale_0p8_to_8p0_C2, + mdp_scale_0p8_to_8p0_C3); +} + +static long long mdp_do_div(long long num, long long den) +{ + do_div(num, den); + return num; +} + +#define SCALER_PHASE_BITS 29 +#define HAL_MDP_PHASE_STEP_2P50 0x50000000 +#define HAL_MDP_PHASE_STEP_1P66 0x35555555 +#define HAL_MDP_PHASE_STEP_1P25 0x28000000 + +struct phase_val { + int phase_init_x; + int phase_init_y; + int phase_step_x; + int phase_step_y; +}; + +static void mdp_calc_scaleInitPhase_3p1(uint32 in_w, + uint32 in_h, + uint32 out_w, + uint32 out_h, + boolean is_rotate, + boolean is_pp_x, + boolean is_pp_y, struct phase_val *pval) +{ + uint64 dst_ROI_width; + uint64 dst_ROI_height; + uint64 src_ROI_width; + uint64 src_ROI_height; + + /* + * phase_step_x, phase_step_y, phase_init_x and phase_init_y + * are represented in fixed-point, unsigned 3.29 format + */ + uint32 phase_step_x = 0; + uint32 phase_step_y = 0; + uint32 phase_init_x = 0; + uint32 phase_init_y = 0; + uint32 yscale_filter_sel, xscale_filter_sel; + uint32 scale_unit_sel_x, scale_unit_sel_y; + + uint64 numerator, denominator; + uint64 temp_dim; + + src_ROI_width = in_w; + src_ROI_height = in_h; + dst_ROI_width = out_w; + dst_ROI_height = out_h; + + /* if there is a 90 degree rotation */ + if (is_rotate) { + /* decide whether to use FIR or M/N for scaling */ + + /* if down-scaling by a factor smaller than 1/4 */ + if ((dst_ROI_height == 1 && src_ROI_width < 4) || + (src_ROI_width < 4 * dst_ROI_height - 3)) + scale_unit_sel_x = 0;/* use FIR scalar */ + else + scale_unit_sel_x = 1;/* use M/N scalar */ + + /* if down-scaling by a factor smaller than 1/4 */ + if ((dst_ROI_width == 1 && src_ROI_height < 4) || + (src_ROI_height < 4 * dst_ROI_width - 3)) + scale_unit_sel_y = 0;/* use FIR scalar */ + else + scale_unit_sel_y = 1;/* use M/N scalar */ + } else { + /* decide whether to use FIR or M/N for scaling */ + if ((dst_ROI_width == 1 && src_ROI_width < 4) || + (src_ROI_width < 4 * dst_ROI_width - 3)) + scale_unit_sel_x = 0;/* use FIR scalar */ + else + scale_unit_sel_x = 1;/* use M/N scalar */ + + if ((dst_ROI_height == 1 && src_ROI_height < 4) || + (src_ROI_height < 4 * dst_ROI_height - 3)) + scale_unit_sel_y = 0;/* use FIR scalar */ + else + scale_unit_sel_y = 1;/* use M/N scalar */ + } + + /* if there is a 90 degree rotation */ + if (is_rotate) { + /* swap the width and height of dst ROI */ + temp_dim = dst_ROI_width; + dst_ROI_width = dst_ROI_height; + dst_ROI_height = temp_dim; + } + + /* calculate phase step for the x direction */ + + /* if destination is only 1 pixel wide, the value of phase_step_x + is unimportant. Assigning phase_step_x to src ROI width + as an arbitrary value. */ + if (dst_ROI_width == 1) + phase_step_x = (uint32) ((src_ROI_width) << SCALER_PHASE_BITS); + + /* if using FIR scalar */ + else if (scale_unit_sel_x == 0) { + + /* Calculate the quotient ( src_ROI_width - 1 ) / ( dst_ROI_width - 1) + with u3.29 precision. Quotient is rounded up to the larger + 29th decimal point. */ + numerator = (src_ROI_width - 1) << SCALER_PHASE_BITS; + denominator = (dst_ROI_width - 1); /* never equals to 0 because of the "( dst_ROI_width == 1 ) case" */ + phase_step_x = (uint32) mdp_do_div((numerator + denominator - 1), denominator); /* divide and round up to the larger 29th decimal point. */ + + } + + /* if M/N scalar */ + else if (scale_unit_sel_x == 1) { + /* Calculate the quotient ( src_ROI_width ) / ( dst_ROI_width) + with u3.29 precision. Quotient is rounded down to the + smaller 29th decimal point. */ + numerator = (src_ROI_width) << SCALER_PHASE_BITS; + denominator = (dst_ROI_width); + phase_step_x = (uint32) mdp_do_div(numerator, denominator); + } + /* calculate phase step for the y direction */ + + /* if destination is only 1 pixel wide, the value of + phase_step_x is unimportant. Assigning phase_step_x + to src ROI width as an arbitrary value. */ + if (dst_ROI_height == 1) + phase_step_y = (uint32) ((src_ROI_height) << SCALER_PHASE_BITS); + + /* if FIR scalar */ + else if (scale_unit_sel_y == 0) { + /* Calculate the quotient ( src_ROI_height - 1 ) / ( dst_ROI_height - 1) + with u3.29 precision. Quotient is rounded up to the larger + 29th decimal point. */ + numerator = (src_ROI_height - 1) << SCALER_PHASE_BITS; + denominator = (dst_ROI_height - 1); /* never equals to 0 because of the "( dst_ROI_height == 1 )" case */ + phase_step_y = (uint32) mdp_do_div((numerator + denominator - 1), denominator); /* Quotient is rounded up to the larger 29th decimal point. */ + + } + + /* if M/N scalar */ + else if (scale_unit_sel_y == 1) { + /* Calculate the quotient ( src_ROI_height ) / ( dst_ROI_height) + with u3.29 precision. Quotient is rounded down to the smaller + 29th decimal point. */ + numerator = (src_ROI_height) << SCALER_PHASE_BITS; + denominator = (dst_ROI_height); + phase_step_y = (uint32) mdp_do_div(numerator, denominator); + } + + /* decide which set of FIR coefficients to use */ + if (phase_step_x > HAL_MDP_PHASE_STEP_2P50) + xscale_filter_sel = 0; + else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66) + xscale_filter_sel = 1; + else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25) + xscale_filter_sel = 2; + else + xscale_filter_sel = 3; + + if (phase_step_y > HAL_MDP_PHASE_STEP_2P50) + yscale_filter_sel = 0; + else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66) + yscale_filter_sel = 1; + else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25) + yscale_filter_sel = 2; + else + yscale_filter_sel = 3; + + /* calculate phase init for the x direction */ + + /* if using FIR scalar */ + if (scale_unit_sel_x == 0) { + if (dst_ROI_width == 1) + phase_init_x = + (uint32) ((src_ROI_width - 1) << SCALER_PHASE_BITS); + else + phase_init_x = 0; + + } + /* M over N scalar */ + else if (scale_unit_sel_x == 1) + phase_init_x = 0; + + /* calculate phase init for the y direction + if using FIR scalar */ + if (scale_unit_sel_y == 0) { + if (dst_ROI_height == 1) + phase_init_y = + (uint32) ((src_ROI_height - + 1) << SCALER_PHASE_BITS); + else + phase_init_y = 0; + + } + /* M over N scalar */ + else if (scale_unit_sel_y == 1) + phase_init_y = 0; + + /* write registers */ + pval->phase_step_x = (uint32) phase_step_x; + pval->phase_step_y = (uint32) phase_step_y; + pval->phase_init_x = (uint32) phase_init_x; + pval->phase_init_y = (uint32) phase_init_y; + + return; +} + +void mdp_set_scale(MDPIBUF *iBuf, + uint32 dst_roi_width, + uint32 dst_roi_height, + boolean inputRGB, boolean outputRGB, uint32 *pppop_reg_ptr) +{ + uint32 dst_roi_width_scale; + uint32 dst_roi_height_scale; + struct phase_val pval; + boolean use_pr; + uint32 ppp_scale_config = 0; + + if (!inputRGB) + ppp_scale_config |= BIT(6); + + if (iBuf->mdpImg.mdpOp & MDPOP_ASCALE) { + if (iBuf->mdpImg.mdpOp & MDPOP_ROT90) { + dst_roi_width_scale = dst_roi_height; + dst_roi_height_scale = dst_roi_width; + } else { + dst_roi_width_scale = dst_roi_width; + dst_roi_height_scale = dst_roi_height; + } + + if ((dst_roi_width_scale != iBuf->roi.width) || + (dst_roi_height_scale != iBuf->roi.height) || + (iBuf->mdpImg.mdpOp & MDPOP_SHARPENING)) { + *pppop_reg_ptr |= + (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); + + mdp_calc_scaleInitPhase_3p1(iBuf->roi.width, + iBuf->roi.height, + dst_roi_width, + dst_roi_height, + iBuf->mdpImg. + mdpOp & MDPOP_ROT90, 1, 1, + &pval); + + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x013c, + pval.phase_init_x); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0140, + pval.phase_init_y); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0144, + pval.phase_step_x); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0148, + pval.phase_step_y); + + /* disable the pixel repeat option for scaling */ + use_pr = false; + + /* x-direction */ + if ((dst_roi_width_scale == iBuf->roi.width) && + !(iBuf->mdpImg.mdpOp & MDPOP_SHARPENING)) { + *pppop_reg_ptr &= ~PPP_OP_SCALE_X_ON; + } else + if (((dst_roi_width_scale * 10) / iBuf->roi.width) > + 8) { + if ((use_pr) + && (mdp_scale_0p8_to_8p0_mode != + MDP_SCALE_PR)) { + mdp_scale_0p8_to_8p0_mode = + MDP_SCALE_PR; + mdp_update_scale_table + (MDP_SCALE_0P8_TO_8P0_INDEX, + mdp_scale_pixel_repeat_C0, + mdp_scale_pixel_repeat_C1, + mdp_scale_pixel_repeat_C2, + mdp_scale_pixel_repeat_C3); + } else if ((!use_pr) + && (mdp_scale_0p8_to_8p0_mode != + MDP_SCALE_FIR)) { + mdp_scale_0p8_to_8p0_mode = + MDP_SCALE_FIR; + mdp_update_scale_table + (MDP_SCALE_0P8_TO_8P0_INDEX, + mdp_scale_0p8_to_8p0_C0, + mdp_scale_0p8_to_8p0_C1, + mdp_scale_0p8_to_8p0_C2, + mdp_scale_0p8_to_8p0_C3); + } + ppp_scale_config |= (SCALE_U1_SET << 2); + } else + if (((dst_roi_width_scale * 10) / iBuf->roi.width) > + 6) { + if ((use_pr) + && (mdp_scale_0p6_to_0p8_mode != + MDP_SCALE_PR)) { + mdp_scale_0p6_to_0p8_mode = + MDP_SCALE_PR; + mdp_update_scale_table + (MDP_SCALE_0P6_TO_0P8_INDEX, + mdp_scale_pixel_repeat_C0, + mdp_scale_pixel_repeat_C1, + mdp_scale_pixel_repeat_C2, + mdp_scale_pixel_repeat_C3); + } else if ((!use_pr) + && (mdp_scale_0p6_to_0p8_mode != + MDP_SCALE_FIR)) { + mdp_scale_0p6_to_0p8_mode = + MDP_SCALE_FIR; + mdp_update_scale_table + (MDP_SCALE_0P6_TO_0P8_INDEX, + mdp_scale_0p6_to_0p8_C0, + mdp_scale_0p6_to_0p8_C1, + mdp_scale_0p6_to_0p8_C2, + mdp_scale_0p6_to_0p8_C3); + } + ppp_scale_config |= (SCALE_D2_SET << 2); + } else + if (((dst_roi_width_scale * 10) / iBuf->roi.width) > + 4) { + if ((use_pr) + && (mdp_scale_0p4_to_0p6_mode != + MDP_SCALE_PR)) { + mdp_scale_0p4_to_0p6_mode = + MDP_SCALE_PR; + mdp_update_scale_table + (MDP_SCALE_0P4_TO_0P6_INDEX, + mdp_scale_pixel_repeat_C0, + mdp_scale_pixel_repeat_C1, + mdp_scale_pixel_repeat_C2, + mdp_scale_pixel_repeat_C3); + } else if ((!use_pr) + && (mdp_scale_0p4_to_0p6_mode != + MDP_SCALE_FIR)) { + mdp_scale_0p4_to_0p6_mode = + MDP_SCALE_FIR; + mdp_update_scale_table + (MDP_SCALE_0P4_TO_0P6_INDEX, + mdp_scale_0p4_to_0p6_C0, + mdp_scale_0p4_to_0p6_C1, + mdp_scale_0p4_to_0p6_C2, + mdp_scale_0p4_to_0p6_C3); + } + ppp_scale_config |= (SCALE_D1_SET << 2); + } else + if ((dst_roi_width_scale == 1 && iBuf->roi.width < 4) || + (iBuf->roi.width < 4 * dst_roi_width_scale - 3)) { + if ((use_pr) + && (mdp_scale_0p2_to_0p4_mode != + MDP_SCALE_PR)) { + mdp_scale_0p2_to_0p4_mode = + MDP_SCALE_PR; + mdp_update_scale_table + (MDP_SCALE_0P2_TO_0P4_INDEX, + mdp_scale_pixel_repeat_C0, + mdp_scale_pixel_repeat_C1, + mdp_scale_pixel_repeat_C2, + mdp_scale_pixel_repeat_C3); + } else if ((!use_pr) + && (mdp_scale_0p2_to_0p4_mode != + MDP_SCALE_FIR)) { + mdp_scale_0p2_to_0p4_mode = + MDP_SCALE_FIR; + mdp_update_scale_table + (MDP_SCALE_0P2_TO_0P4_INDEX, + mdp_scale_0p2_to_0p4_C0, + mdp_scale_0p2_to_0p4_C1, + mdp_scale_0p2_to_0p4_C2, + mdp_scale_0p2_to_0p4_C3); + } + ppp_scale_config |= (SCALE_D0_SET << 2); + } else + ppp_scale_config |= BIT(0); + + /* y-direction */ + if ((dst_roi_height_scale == iBuf->roi.height) && + !(iBuf->mdpImg.mdpOp & MDPOP_SHARPENING)) { + *pppop_reg_ptr &= ~PPP_OP_SCALE_Y_ON; + } else if (((dst_roi_height_scale * 10) / + iBuf->roi.height) > 8) { + if ((use_pr) + && (mdp_scale_0p8_to_8p0_mode != + MDP_SCALE_PR)) { + mdp_scale_0p8_to_8p0_mode = + MDP_SCALE_PR; + mdp_update_scale_table + (MDP_SCALE_0P8_TO_8P0_INDEX, + mdp_scale_pixel_repeat_C0, + mdp_scale_pixel_repeat_C1, + mdp_scale_pixel_repeat_C2, + mdp_scale_pixel_repeat_C3); + } else if ((!use_pr) + && (mdp_scale_0p8_to_8p0_mode != + MDP_SCALE_FIR)) { + mdp_scale_0p8_to_8p0_mode = + MDP_SCALE_FIR; + mdp_update_scale_table + (MDP_SCALE_0P8_TO_8P0_INDEX, + mdp_scale_0p8_to_8p0_C0, + mdp_scale_0p8_to_8p0_C1, + mdp_scale_0p8_to_8p0_C2, + mdp_scale_0p8_to_8p0_C3); + } + ppp_scale_config |= (SCALE_U1_SET << 4); + } else + if (((dst_roi_height_scale * 10) / + iBuf->roi.height) > 6) { + if ((use_pr) + && (mdp_scale_0p6_to_0p8_mode != + MDP_SCALE_PR)) { + mdp_scale_0p6_to_0p8_mode = + MDP_SCALE_PR; + mdp_update_scale_table + (MDP_SCALE_0P6_TO_0P8_INDEX, + mdp_scale_pixel_repeat_C0, + mdp_scale_pixel_repeat_C1, + mdp_scale_pixel_repeat_C2, + mdp_scale_pixel_repeat_C3); + } else if ((!use_pr) + && (mdp_scale_0p6_to_0p8_mode != + MDP_SCALE_FIR)) { + mdp_scale_0p6_to_0p8_mode = + MDP_SCALE_FIR; + mdp_update_scale_table + (MDP_SCALE_0P6_TO_0P8_INDEX, + mdp_scale_0p6_to_0p8_C0, + mdp_scale_0p6_to_0p8_C1, + mdp_scale_0p6_to_0p8_C2, + mdp_scale_0p6_to_0p8_C3); + } + ppp_scale_config |= (SCALE_D2_SET << 4); + } else + if (((dst_roi_height_scale * 10) / + iBuf->roi.height) > 4) { + if ((use_pr) + && (mdp_scale_0p4_to_0p6_mode != + MDP_SCALE_PR)) { + mdp_scale_0p4_to_0p6_mode = + MDP_SCALE_PR; + mdp_update_scale_table + (MDP_SCALE_0P4_TO_0P6_INDEX, + mdp_scale_pixel_repeat_C0, + mdp_scale_pixel_repeat_C1, + mdp_scale_pixel_repeat_C2, + mdp_scale_pixel_repeat_C3); + } else if ((!use_pr) + && (mdp_scale_0p4_to_0p6_mode != + MDP_SCALE_FIR)) { + mdp_scale_0p4_to_0p6_mode = + MDP_SCALE_FIR; + mdp_update_scale_table + (MDP_SCALE_0P4_TO_0P6_INDEX, + mdp_scale_0p4_to_0p6_C0, + mdp_scale_0p4_to_0p6_C1, + mdp_scale_0p4_to_0p6_C2, + mdp_scale_0p4_to_0p6_C3); + } + ppp_scale_config |= (SCALE_D1_SET << 4); + } else if ((dst_roi_height_scale == 1 && + iBuf->roi.height < 4) || + (iBuf->roi.height < 4 * dst_roi_height_scale - 3)) { + if ((use_pr) + && (mdp_scale_0p2_to_0p4_mode != + MDP_SCALE_PR)) { + mdp_scale_0p2_to_0p4_mode = + MDP_SCALE_PR; + mdp_update_scale_table + (MDP_SCALE_0P2_TO_0P4_INDEX, + mdp_scale_pixel_repeat_C0, + mdp_scale_pixel_repeat_C1, + mdp_scale_pixel_repeat_C2, + mdp_scale_pixel_repeat_C3); + } else if ((!use_pr) + && (mdp_scale_0p2_to_0p4_mode != + MDP_SCALE_FIR)) { + mdp_scale_0p2_to_0p4_mode = + MDP_SCALE_FIR; + mdp_update_scale_table + (MDP_SCALE_0P2_TO_0P4_INDEX, + mdp_scale_0p2_to_0p4_C0, + mdp_scale_0p2_to_0p4_C1, + mdp_scale_0p2_to_0p4_C2, + mdp_scale_0p2_to_0p4_C3); + } + ppp_scale_config |= (SCALE_D0_SET << 4); + } else + ppp_scale_config |= BIT(1); + + if (iBuf->mdpImg.mdpOp & MDPOP_SHARPENING) { + ppp_scale_config |= BIT(7); + MDP_OUTP(MDP_BASE + 0x50020, + iBuf->mdpImg.sp_value); + } + + MDP_OUTP(MDP_BASE + 0x10230, ppp_scale_config); + } else { + iBuf->mdpImg.mdpOp &= ~(MDPOP_ASCALE); + } + } +} + +void mdp_adjust_start_addr(uint8 **src0, + uint8 **src1, + int v_slice, + int h_slice, + int x, + int y, + uint32 width, + uint32 height, int bpp, MDPIBUF *iBuf, int layer) +{ + switch (layer) { + case 0: + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0200, (y << 16) | (x)); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0208, + (height << 16) | (width)); + break; + + case 1: + /* MDP 3.1 HW bug workaround */ + if (iBuf->ibuf_type == MDP_YCRYCB_H2V1) { + *src0 += (x + y * width) * bpp; + x = y = 0; + width = iBuf->roi.dst_width; + height = iBuf->roi.dst_height; + } + + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0204, (y << 16) | (x)); + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x020c, + (height << 16) | (width)); + break; + + case 2: + MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x019c, (y << 16) | (x)); + break; + } +} + +void mdp_set_blend_attr(MDPIBUF *iBuf, + uint32 *alpha, + uint32 *tpVal, + uint32 perPixelAlpha, uint32 *pppop_reg_ptr) +{ + int bg_alpha; + + *alpha = iBuf->mdpImg.alpha; + *tpVal = iBuf->mdpImg.tpVal; + + if (iBuf->mdpImg.mdpOp & MDPOP_FG_PM_ALPHA) { + if (perPixelAlpha) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | PPP_OP_BLEND_CONSTANT_ALPHA; + } + else { + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + && (iBuf->mdpImg.alpha == 0xff)) { + iBuf->mdpImg.mdpOp &= ~(MDPOP_ALPHAB); + } + + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + || (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) { + *pppop_reg_ptr |= + PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL; + } + } + + bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL | + PPP_BLEND_BG_ALPHA_REVERSE; + + if (perPixelAlpha) + bg_alpha |= PPP_BLEND_BG_SRCPIXEL_ALPHA; + else { + bg_alpha |= PPP_BLEND_BG_CONSTANT_ALPHA; + bg_alpha |= iBuf->mdpImg.alpha << 24; + } + outpdw(MDP_BASE + 0x70010, bg_alpha); + + if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP) + *pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP; + } else if (perPixelAlpha) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | PPP_OP_BLEND_SRCPIXEL_ALPHA; + } else { + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + && (iBuf->mdpImg.alpha == 0xff)) { + iBuf->mdpImg.mdpOp &= ~(MDPOP_ALPHAB); + } + + if ((iBuf->mdpImg.mdpOp & MDPOP_ALPHAB) + || (iBuf->mdpImg.mdpOp & MDPOP_TRANSP)) { + *pppop_reg_ptr |= + PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL; + } + + if (iBuf->mdpImg.mdpOp & MDPOP_TRANSP) + *pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP; + } +} diff --git a/drivers/video/msm/mdp_vsync.c b/drivers/video/msm/mdp_vsync.c new file mode 100644 index 0000000000000000000000000000000000000000..c80a7cc8e078f5a2450da083dfc5a98724e69c87 --- /dev/null +++ b/drivers/video/msm/mdp_vsync.c @@ -0,0 +1,491 @@ +/* Copyright (c) 2008-2009, 2012 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "mdp.h" +#include "msm_fb.h" +#include "mddihost.h" + +#ifdef CONFIG_FB_MSM_MDP40 +#include "mdp4.h" + +#define MDP_SYNC_CFG_0 0x100 +#define MDP_SYNC_STATUS_0 0x10c +#define MDP_SYNC_CFG_1 0x104 +#define MDP_SYNC_STATUS_1 0x110 +#define MDP_PRIM_VSYNC_OUT_CTRL 0x118 +#define MDP_SEC_VSYNC_OUT_CTRL 0x11C +#define MDP_VSYNC_SEL 0x124 +#define MDP_PRIM_VSYNC_INIT_VAL 0x128 +#define MDP_SEC_VSYNC_INIT_VAL 0x12C +#else +#define MDP_SYNC_CFG_0 0x300 +#define MDP_SYNC_STATUS_0 0x30c +#define MDP_PRIM_VSYNC_OUT_CTRL 0x318 +#define MDP_PRIM_VSYNC_INIT_VAL 0x328 +#endif + +extern mddi_lcd_type mddi_lcd_idx; +extern spinlock_t mdp_spin_lock; +extern struct workqueue_struct *mdp_vsync_wq; +extern int lcdc_mode; +extern int vsync_mode; + +#ifdef MDP_HW_VSYNC +int vsync_above_th = 4; +int vsync_start_th = 1; +int vsync_load_cnt; +int vsync_clk_status; +DEFINE_MUTEX(vsync_clk_lock); +static DEFINE_SPINLOCK(vsync_timer_lock); + +static struct clk *mdp_vsync_clk; +static struct msm_fb_data_type *vsync_mfd; +static unsigned char timer_shutdown_flag; +static uint32 vsync_cnt_cfg; + +void mdp_hw_vsync_clk_enable(struct msm_fb_data_type *mfd) +{ + if (vsync_clk_status == 1) + return; + mutex_lock(&vsync_clk_lock); + if (mfd->use_mdp_vsync) { + clk_prepare_enable(mdp_vsync_clk); + vsync_clk_status = 1; + } + mutex_unlock(&vsync_clk_lock); +} + +void mdp_hw_vsync_clk_disable(struct msm_fb_data_type *mfd) +{ + if (vsync_clk_status == 0) + return; + mutex_lock(&vsync_clk_lock); + if (mfd->use_mdp_vsync) { + clk_disable_unprepare(mdp_vsync_clk); + vsync_clk_status = 0; + } + mutex_unlock(&vsync_clk_lock); +} + +static void mdp_set_vsync(unsigned long data); +void mdp_vsync_clk_enable(void) +{ + if (vsync_mfd) { + mdp_hw_vsync_clk_enable(vsync_mfd); + if (!vsync_mfd->vsync_resync_timer.function) + mdp_set_vsync((unsigned long) vsync_mfd); + } +} + +void mdp_vsync_clk_disable(void) +{ + if (vsync_mfd) { + if (vsync_mfd->vsync_resync_timer.function) { + spin_lock(&vsync_timer_lock); + timer_shutdown_flag = 1; + spin_unlock(&vsync_timer_lock); + del_timer_sync(&vsync_mfd->vsync_resync_timer); + spin_lock(&vsync_timer_lock); + timer_shutdown_flag = 0; + spin_unlock(&vsync_timer_lock); + vsync_mfd->vsync_resync_timer.function = NULL; + } + + mdp_hw_vsync_clk_disable(vsync_mfd); + } +} +#endif + +static void mdp_set_vsync(unsigned long data) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; + struct msm_fb_panel_data *pdata = NULL; + + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + vsync_mfd = mfd; + init_timer(&mfd->vsync_resync_timer); + + if ((pdata) && (pdata->set_vsync_notifier == NULL)) + return; + + if ((mfd->panel_info.lcd.vsync_enable) && (mfd->panel_power_on) + && (!mfd->vsync_handler_pending)) { + mfd->vsync_handler_pending = TRUE; + if (!queue_work(mdp_vsync_wq, &mfd->vsync_resync_worker)) { + MSM_FB_INFO + ("mdp_set_vsync: can't queue_work! -> needs to increase vsync_resync_timer_duration\n"); + } + } else { + MSM_FB_DEBUG + ("mdp_set_vsync failed! EN:%d PWR:%d PENDING:%d\n", + mfd->panel_info.lcd.vsync_enable, mfd->panel_power_on, + mfd->vsync_handler_pending); + } + + spin_lock(&vsync_timer_lock); + if (!timer_shutdown_flag) { + mfd->vsync_resync_timer.function = mdp_set_vsync; + mfd->vsync_resync_timer.data = data; + mfd->vsync_resync_timer.expires = + jiffies + mfd->panel_info.lcd.vsync_notifier_period; + add_timer(&mfd->vsync_resync_timer); + } + spin_unlock(&vsync_timer_lock); +} + +static void mdp_vsync_handler(void *data) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; + + if (vsync_clk_status == 0) { + pr_debug("Warning: vsync clk is disabled\n"); + mfd->vsync_handler_pending = FALSE; + return; + } + + if (mfd->use_mdp_vsync) { +#ifdef MDP_HW_VSYNC + if (mfd->panel_power_on) { + MDP_OUTP(MDP_BASE + MDP_SYNC_STATUS_0, vsync_load_cnt); + +#ifdef CONFIG_FB_MSM_MDP40 + if (mdp_hw_revision < MDP4_REVISION_V2_1) + MDP_OUTP(MDP_BASE + MDP_SYNC_STATUS_1, + vsync_load_cnt); +#endif + } + +#endif + } else { + mfd->last_vsync_timetick = ktime_get_real(); + } + + mfd->vsync_handler_pending = FALSE; +} + +irqreturn_t mdp_hw_vsync_handler_proxy(int irq, void *data) +{ + /* + * ToDo: tried enabling/disabling GPIO MDP HW VSYNC interrupt + * but getting inaccurate timing in mdp_vsync_handler() + * disable_irq(MDP_HW_VSYNC_IRQ); + */ + mdp_vsync_handler(data); + + return IRQ_HANDLED; +} + +#ifdef MDP_HW_VSYNC +static void mdp_set_sync_cfg_0(struct msm_fb_data_type *mfd, int vsync_cnt) +{ + unsigned long cfg; + + cfg = mfd->total_lcd_lines - 1; + cfg <<= MDP_SYNCFG_HGT_LOC; + if (mfd->panel_info.lcd.hw_vsync_mode) + cfg |= MDP_SYNCFG_VSYNC_EXT_EN; + cfg |= (MDP_SYNCFG_VSYNC_INT_EN | vsync_cnt); + + MDP_OUTP(MDP_BASE + MDP_SYNC_CFG_0, cfg); +} + +#ifdef CONFIG_FB_MSM_MDP40 +static void mdp_set_sync_cfg_1(struct msm_fb_data_type *mfd, int vsync_cnt) +{ + unsigned long cfg; + + cfg = mfd->total_lcd_lines - 1; + cfg <<= MDP_SYNCFG_HGT_LOC; + if (mfd->panel_info.lcd.hw_vsync_mode) + cfg |= MDP_SYNCFG_VSYNC_EXT_EN; + cfg |= (MDP_SYNCFG_VSYNC_INT_EN | vsync_cnt); + + MDP_OUTP(MDP_BASE + MDP_SYNC_CFG_1, cfg); +} +#endif + +void mdp_vsync_cfg_regs(struct msm_fb_data_type *mfd, + boolean first_time) +{ + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, + FALSE); + if (first_time) + mdp_hw_vsync_clk_enable(mfd); + + mdp_set_sync_cfg_0(mfd, vsync_cnt_cfg); + +#ifdef CONFIG_FB_MSM_MDP40 + if (mdp_hw_revision < MDP4_REVISION_V2_1) + mdp_set_sync_cfg_1(mfd, vsync_cnt_cfg); +#endif + + /* + * load the last line + 1 to be in the + * safety zone + */ + vsync_load_cnt = mfd->panel_info.yres; + + /* line counter init value at the next pulse */ + MDP_OUTP(MDP_BASE + MDP_PRIM_VSYNC_INIT_VAL, + vsync_load_cnt); +#ifdef CONFIG_FB_MSM_MDP40 + if (mdp_hw_revision < MDP4_REVISION_V2_1) { + MDP_OUTP(MDP_BASE + MDP_SEC_VSYNC_INIT_VAL, + vsync_load_cnt); + } +#endif + + /* + * external vsync source pulse width and + * polarity flip + */ + MDP_OUTP(MDP_BASE + MDP_PRIM_VSYNC_OUT_CTRL, BIT(0)); +#ifdef CONFIG_FB_MSM_MDP40 + if (mdp_hw_revision < MDP4_REVISION_V2_1) { + MDP_OUTP(MDP_BASE + MDP_SEC_VSYNC_OUT_CTRL, BIT(0)); + MDP_OUTP(MDP_BASE + MDP_VSYNC_SEL, 0x20); + } +#endif + + /* threshold */ + MDP_OUTP(MDP_BASE + 0x200, (vsync_above_th << 16) | + (vsync_start_th)); + + if (first_time) + mdp_hw_vsync_clk_disable(mfd); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} +#endif + +void mdp_config_vsync(struct platform_device *pdev, + struct msm_fb_data_type *mfd) +{ + /* vsync on primary lcd only for now */ + if ((mfd->dest != DISPLAY_LCD) || (mfd->panel_info.pdest != DISPLAY_1) + || (!vsync_mode)) { + goto err_handle; + } + + vsync_clk_status = 0; + if (mfd->panel_info.lcd.vsync_enable) { + mfd->total_porch_lines = mfd->panel_info.lcd.v_back_porch + + mfd->panel_info.lcd.v_front_porch + + mfd->panel_info.lcd.v_pulse_width; + mfd->total_lcd_lines = + mfd->panel_info.yres + mfd->total_porch_lines; + mfd->lcd_ref_usec_time = + 100000000 / mfd->panel_info.lcd.refx100; + mfd->vsync_handler_pending = FALSE; + + mfd->last_vsync_timetick.tv64 = 0; + +#ifdef MDP_HW_VSYNC + if (mdp_vsync_clk == NULL) + mdp_vsync_clk = clk_get(&pdev->dev, "vsync_clk"); + + if (IS_ERR(mdp_vsync_clk)) { + printk(KERN_ERR "error: can't get mdp_vsync_clk!\n"); + mfd->use_mdp_vsync = 0; + } else + mfd->use_mdp_vsync = 1; + + if (mfd->use_mdp_vsync) { + uint32 vsync_cnt_cfg_dem; + uint32 mdp_vsync_clk_speed_hz; + + mdp_vsync_clk_speed_hz = clk_get_rate(mdp_vsync_clk); + + if (mdp_vsync_clk_speed_hz == 0) { + mfd->use_mdp_vsync = 0; + } else { + /* + * Do this calculation in 2 steps for + * rounding uint32 properly. + */ + vsync_cnt_cfg_dem = + (mfd->panel_info.lcd.refx100 * + mfd->total_lcd_lines) / 100; + vsync_cnt_cfg = + (mdp_vsync_clk_speed_hz) / + vsync_cnt_cfg_dem; + mdp_vsync_cfg_regs(mfd, TRUE); + } + } +#else + mfd->use_mdp_vsync = 0; + hrtimer_init(&mfd->dma_hrtimer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + mfd->dma_hrtimer.function = mdp_dma2_vsync_hrtimer_handler; + mfd->vsync_width_boundary = vmalloc(mfd->panel_info.xres * 4); +#endif + +#ifdef CONFIG_FB_MSM_MDDI + mfd->channel_irq = 0; + if (mfd->panel_info.lcd.hw_vsync_mode) { + u32 vsync_gpio = mfd->vsync_gpio; + u32 ret; + + if (vsync_gpio == -1) { + MSM_FB_INFO("vsync_gpio not defined!\n"); + goto err_handle; + } + + ret = gpio_tlmm_config(GPIO_CFG + (vsync_gpio, + (mfd->use_mdp_vsync) ? 1 : 0, + GPIO_CFG_INPUT, + GPIO_CFG_PULL_DOWN, + GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + if (ret) + goto err_handle; + + /* + * if use_mdp_vsync, then no interrupt need since + * mdp_vsync is feed directly to mdp to reset the + * write pointer counter. therefore no irq_handler + * need to reset write pointer counter. + */ + if (!mfd->use_mdp_vsync) { + mfd->channel_irq = MSM_GPIO_TO_INT(vsync_gpio); + if (request_irq + (mfd->channel_irq, + &mdp_hw_vsync_handler_proxy, + IRQF_TRIGGER_FALLING, "VSYNC_GPIO", + (void *)mfd)) { + MSM_FB_INFO + ("irq=%d failed! vsync_gpio=%d\n", + mfd->channel_irq, + vsync_gpio); + goto err_handle; + } + } + } +#endif + mdp_hw_vsync_clk_enable(mfd); + mdp_set_vsync((unsigned long)mfd); + } + + return; + +err_handle: + if (mfd->vsync_width_boundary) + vfree(mfd->vsync_width_boundary); + mfd->panel_info.lcd.vsync_enable = FALSE; + printk(KERN_ERR "%s: failed!\n", __func__); +} + +void mdp_vsync_resync_workqueue_handler(struct work_struct *work) +{ + struct msm_fb_data_type *mfd = NULL; + int vsync_fnc_enabled = FALSE; + struct msm_fb_panel_data *pdata = NULL; + + mfd = container_of(work, struct msm_fb_data_type, vsync_resync_worker); + + if (mfd) { + if (mfd->panel_power_on) { + pdata = + (struct msm_fb_panel_data *)mfd->pdev->dev. + platform_data; + + if (pdata->set_vsync_notifier != NULL) { + if (pdata->clk_func && !pdata->clk_func(2)) { + mfd->vsync_handler_pending = FALSE; + return; + } + + pdata->set_vsync_notifier( + mdp_vsync_handler, + (void *)mfd); + vsync_fnc_enabled = TRUE; + } + } + } + + if ((mfd) && (!vsync_fnc_enabled)) + mfd->vsync_handler_pending = FALSE; +} + +boolean mdp_hw_vsync_set_handler(msm_fb_vsync_handler_type handler, void *data) +{ + /* + * ToDo: tried enabling/disabling GPIO MDP HW VSYNC interrupt + * but getting inaccurate timing in mdp_vsync_handler() + * enable_irq(MDP_HW_VSYNC_IRQ); + */ + + return TRUE; +} + +uint32 mdp_get_lcd_line_counter(struct msm_fb_data_type *mfd) +{ + uint32 elapsed_usec_time; + uint32 lcd_line; + ktime_t last_vsync_timetick_local; + ktime_t curr_time; + unsigned long flag; + + if ((!mfd->panel_info.lcd.vsync_enable) || (!vsync_mode)) + return 0; + + spin_lock_irqsave(&mdp_spin_lock, flag); + last_vsync_timetick_local = mfd->last_vsync_timetick; + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + curr_time = ktime_get_real(); + elapsed_usec_time = ktime_to_us(ktime_sub(curr_time, + last_vsync_timetick_local)); + + elapsed_usec_time = elapsed_usec_time % mfd->lcd_ref_usec_time; + + /* lcd line calculation referencing to line counter = 0 */ + lcd_line = + (elapsed_usec_time * mfd->total_lcd_lines) / mfd->lcd_ref_usec_time; + + /* lcd line adjusment referencing to the actual line counter at vsync */ + lcd_line = + (mfd->total_lcd_lines - mfd->panel_info.lcd.v_back_porch + + lcd_line) % (mfd->total_lcd_lines + 1); + + if (lcd_line > mfd->total_lcd_lines) { + MSM_FB_INFO + ("mdp_get_lcd_line_counter: mdp_lcd_rd_cnt >= mfd->total_lcd_lines error!\n"); + } + + return lcd_line; +} diff --git a/drivers/video/msm/mhl/mhl_8334.c b/drivers/video/msm/mhl/mhl_8334.c new file mode 100644 index 0000000000000000000000000000000000000000..46eb03bbafd67a674320daa016bdf0df7267cb7e --- /dev/null +++ b/drivers/video/msm/mhl/mhl_8334.c @@ -0,0 +1,849 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" +#include "external_common.h" +#include "mhl_8334.h" +#include "mhl_i2c_utils.h" + +#define DEBUG + + +static struct i2c_device_id mhl_sii_i2c_id[] = { + { MHL_DRIVER_NAME, 0 }, + { } +}; + +struct mhl_msm_state_t *mhl_msm_state; +spinlock_t mhl_state_lock; + +static int mhl_i2c_probe(struct i2c_client *client,\ + const struct i2c_device_id *id); +static int mhl_i2c_remove(struct i2c_client *client); +static void force_usb_switch_open(void); +static void release_usb_switch_open(void); +static void switch_mode(enum mhl_st_type to_mode); +static irqreturn_t mhl_tx_isr(int irq, void *dev_id); + +static struct i2c_driver mhl_sii_i2c_driver = { + .driver = { + .name = MHL_DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mhl_i2c_probe, + /*.remove = __exit_p(mhl_i2c_remove),*/ + .remove = mhl_i2c_remove, + .id_table = mhl_sii_i2c_id, +}; + +bool mhl_is_connected(void) +{ + return true; +} + +static void cbus_reset(void) +{ + uint8_t i; + + /* + * REG_SRST + */ + mhl_i2c_reg_modify(TX_PAGE_3, 0x0000, BIT3, BIT3); + msleep(20); + mhl_i2c_reg_modify(TX_PAGE_3, 0x0000, BIT3, 0x00); + /* + * REG_INTR1 and REG_INTR4 + */ + mhl_i2c_reg_write(TX_PAGE_L0, 0x0075, BIT6 | BIT5); + mhl_i2c_reg_write(TX_PAGE_3, 0x0022, + BIT0 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6); + /* REG5 */ + if (mhl_msm_state->chip_rev_id < 1) + mhl_i2c_reg_write(TX_PAGE_3, 0x0024, BIT3 | BIT4); + else + /*REG5 Mask disabled due to auto FIFO reset ??*/ + mhl_i2c_reg_write(TX_PAGE_3, 0x0024, 0x00); + + /* Unmask CBUS1 Intrs */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0009, + BIT2 | BIT3 | BIT4 | BIT5 | BIT6); + + /* Unmask CBUS2 Intrs */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x001F, BIT2 | BIT3); + + for (i = 0; i < 4; i++) { + /* + * Enable WRITE_STAT interrupt for writes to + * all 4 MSC Status registers. + */ + mhl_i2c_reg_write(TX_PAGE_CBUS, (0xE0 + i), 0xFF); + + /* + * Enable SET_INT interrupt for writes to + * all 4 MSC Interrupt registers. + */ + mhl_i2c_reg_write(TX_PAGE_CBUS, (0xF0 + i), 0xFF); + } +} + +static void init_cbus_regs(void) +{ + uint8_t regval; + + /* Increase DDC translation layer timer*/ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0007, 0xF2); + /* Drive High Time */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0036, 0x03); + /* Use programmed timing */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0039, 0x30); + /* CBUS Drive Strength */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0040, 0x03); + /* + * Write initial default settings + * to devcap regs: default settings + */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEV_STATE, + DEVCAP_VAL_DEV_STATE); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_MHL_VERSION, + DEVCAP_VAL_MHL_VERSION); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEV_CAT, + DEVCAP_VAL_DEV_CAT); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_ADOPTER_ID_H, + DEVCAP_VAL_ADOPTER_ID_H); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_ADOPTER_ID_L, + DEVCAP_VAL_ADOPTER_ID_L); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_VID_LINK_MODE, + DEVCAP_VAL_VID_LINK_MODE); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_AUD_LINK_MODE, + DEVCAP_VAL_AUD_LINK_MODE); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_VIDEO_TYPE, + DEVCAP_VAL_VIDEO_TYPE); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_LOG_DEV_MAP, + DEVCAP_VAL_LOG_DEV_MAP); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_BANDWIDTH, + DEVCAP_VAL_BANDWIDTH); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_FEATURE_FLAG, + DEVCAP_VAL_FEATURE_FLAG); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEVICE_ID_H, + DEVCAP_VAL_DEVICE_ID_H); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEVICE_ID_L, + DEVCAP_VAL_DEVICE_ID_L); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_SCRATCHPAD_SIZE, + DEVCAP_VAL_SCRATCHPAD_SIZE); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_INT_STAT_SIZE, + DEVCAP_VAL_INT_STAT_SIZE); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_RESERVED, + DEVCAP_VAL_RESERVED); + + /* Make bits 2,3 (initiator timeout) to 1,1 + * for register CBUS_LINK_CONTROL_2 + * REG_CBUS_LINK_CONTROL_2 + */ + regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0031); + regval = (regval | 0x0C); + /* REG_CBUS_LINK_CONTROL_2 */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0031, regval); + /* REG_MSC_TIMEOUT_LIMIT */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0022, 0x0F); + /* REG_CBUS_LINK_CONTROL_1 */ + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0030, 0x01); + /* disallow vendor specific commands */ + mhl_i2c_reg_modify(TX_PAGE_CBUS, 0x002E, BIT4, BIT4); +} + +/* + * Configure the initial reg settings + */ +static void mhl_init_reg_settings(void) +{ + + /* + * ============================================ + * POWER UP + * ============================================ + */ + + /* Power up 1.2V core */ + mhl_i2c_reg_write(TX_PAGE_L1, 0x003D, 0x3F); + /* Enable Tx PLL Clock */ + mhl_i2c_reg_write(TX_PAGE_2, 0x0011, 0x01); + /* Enable Tx Clock Path and Equalizer */ + mhl_i2c_reg_write(TX_PAGE_2, 0x0012, 0x11); + /* Tx Source Termination ON */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0030, 0x10); + /* Enable 1X MHL Clock output */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0035, 0xAC); + /* Tx Differential Driver Config */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0031, 0x3C); + mhl_i2c_reg_write(TX_PAGE_3, 0x0033, 0xD9); + /* PLL Bandwidth Control */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0037, 0x02); + /* + * ============================================ + * Analog PLL Control + * ============================================ + */ + /* Enable Rx PLL clock */ + mhl_i2c_reg_write(TX_PAGE_L0, 0x0080, 0x00); + mhl_i2c_reg_write(TX_PAGE_L0, 0x00F8, 0x0C); + mhl_i2c_reg_write(TX_PAGE_L0, 0x0085, 0x02); + mhl_i2c_reg_write(TX_PAGE_2, 0x0000, 0x00); + mhl_i2c_reg_write(TX_PAGE_2, 0x0013, 0x60); + /* PLL Cal ref sel */ + mhl_i2c_reg_write(TX_PAGE_2, 0x0017, 0x03); + /* VCO Cal */ + mhl_i2c_reg_write(TX_PAGE_2, 0x001A, 0x20); + /* Auto EQ */ + mhl_i2c_reg_write(TX_PAGE_2, 0x0022, 0xE0); + mhl_i2c_reg_write(TX_PAGE_2, 0x0023, 0xC0); + mhl_i2c_reg_write(TX_PAGE_2, 0x0024, 0xA0); + mhl_i2c_reg_write(TX_PAGE_2, 0x0025, 0x80); + mhl_i2c_reg_write(TX_PAGE_2, 0x0026, 0x60); + mhl_i2c_reg_write(TX_PAGE_2, 0x0027, 0x40); + mhl_i2c_reg_write(TX_PAGE_2, 0x0028, 0x20); + mhl_i2c_reg_write(TX_PAGE_2, 0x0029, 0x00); + /* Rx PLL Bandwidth 4MHz */ + mhl_i2c_reg_write(TX_PAGE_2, 0x0031, 0x0A); + /* Rx PLL Bandwidth value from I2C */ + mhl_i2c_reg_write(TX_PAGE_2, 0x0045, 0x06); + mhl_i2c_reg_write(TX_PAGE_2, 0x004B, 0x06); + /* Manual zone control */ + mhl_i2c_reg_write(TX_PAGE_2, 0x004C, 0xE0); + /* PLL Mode value */ + mhl_i2c_reg_write(TX_PAGE_2, 0x004D, 0x00); + mhl_i2c_reg_write(TX_PAGE_L0, 0x0008, 0x35); + /* + * Discovery Control and Status regs + * Setting De-glitch time to 50 ms (default) + * Switch Control Disabled + */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0011, 0xAD); + /* 1.8V CBUS VTH */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0014, 0x55); + /* RGND and single Discovery attempt */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0015, 0x11); + /* Ignore VBUS */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0017, 0x82); + mhl_i2c_reg_write(TX_PAGE_3, 0x0018, 0x24); + /* Pull-up resistance off for IDLE state */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0013, 0x84); + /* Enable CBUS Discovery */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x27); + mhl_i2c_reg_write(TX_PAGE_3, 0x0016, 0x20); + /* MHL CBUS Discovery - immediate comm. */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0012, 0x86); + /* Do not force HPD to 0 during wake-up from D3 */ + if (mhl_msm_state->cur_state != POWER_STATE_D3) { + mhl_i2c_reg_modify(TX_PAGE_3, 0x0020, + BIT5 | BIT4, BIT4); + } + /* Enable Auto Soft RESET */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0000, 0x084); + /* HDMI Transcode mode enable */ + mhl_i2c_reg_write(TX_PAGE_L0, 0x000D, 0x1C); + + cbus_reset(); + init_cbus_regs(); +} + +static int mhl_chip_init(void) +{ + /* Read the chip rev ID */ + mhl_msm_state->chip_rev_id = mhl_i2c_reg_read(TX_PAGE_L0, 0x04); + pr_debug("MHL: chip rev ID read=[%x]\n", mhl_msm_state->chip_rev_id); + + /* Reset the TX chip */ + mhl_msm_state->mhl_data->reset_pin(0); + msleep(20); + mhl_msm_state->mhl_data->reset_pin(1); + /* MHL spec requires a 100 ms wait here. */ + msleep(100); + + mhl_init_reg_settings(); + + /* + * Power down the chip to the + * D3 - a low power standby mode + * cable impedance measurement logic is operational + */ + switch_mode(POWER_STATE_D3); + return 0; +} + +/* + * I2C probe + */ +static int mhl_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + mhl_msm_state->mhl_data = kzalloc(sizeof(struct msm_mhl_platform_data), + GFP_KERNEL); + if (!(mhl_msm_state->mhl_data)) { + ret = -ENOMEM; + goto probe_exit; + } + pr_debug("Inside probe\n"); + mhl_msm_state->i2c_client = client; + + spin_lock_init(&mhl_state_lock); + + i2c_set_clientdata(client, mhl_msm_state); + mhl_msm_state->mhl_data = client->dev.platform_data; + + /* Init GPIO stuff here */ + ret = mhl_msm_state->mhl_data->gpio_setup(1); + if (ret == -1) { + pr_err("MHL: mhl_gpio_init has failed\n"); + ret = -ENODEV; + goto probe_exit; + } + return 0; + +probe_exit: + if (mhl_msm_state->mhl_data) { + /* free the gpios */ + mhl_msm_state->mhl_data->gpio_setup(0); + kfree(mhl_msm_state->mhl_data); + mhl_msm_state->mhl_data = NULL; + } + return ret; +} + +static int mhl_i2c_remove(struct i2c_client *client) +{ + pr_debug("inside i2c remove\n"); + mhl_msm_state->mhl_data->gpio_setup(0); + kfree(mhl_msm_state->mhl_data); + return 0; +} + +static int __init mhl_msm_init(void) +{ + int32_t ret; + + mhl_msm_state = kzalloc(sizeof(struct mhl_msm_state_t), GFP_KERNEL); + if (!mhl_msm_state) { + pr_err("mhl_msm_init FAILED: out of memory\n"); + ret = -ENOMEM; + goto init_exit; + } + + mhl_msm_state->i2c_client = NULL; + ret = i2c_add_driver(&mhl_sii_i2c_driver); + if (ret) { + pr_err("MHL: I2C driver add failed: %d\n", ret); + ret = -ENODEV; + goto init_exit; + } else { + if (mhl_msm_state->i2c_client == NULL) { + pr_err("JSR: I2C driver add failed\n"); + ret = -ENODEV; + goto init_exit; + } + pr_debug("MHL: I2C driver added\n"); + } + + /* Request IRQ stuff here */ + pr_debug("MHL: mhl_msm_state->mhl_data->irq=[%d]\n", + mhl_msm_state->mhl_data->irq); + ret = request_threaded_irq(mhl_msm_state->mhl_data->irq, NULL, + &mhl_tx_isr, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "mhl_tx_isr", mhl_msm_state); + if (ret != 0) { + pr_err("request_threaded_irq failed, status: %d\n", + ret); + ret = -EACCES; /* Error code???? */ + goto init_exit; + } + + mhl_msm_state->cur_state = POWER_STATE_D0_MHL; + + /* MHL SII 8334 chip specific init */ + mhl_chip_init(); + return 0; + +init_exit: + pr_err("Exiting from the init with err\n"); + i2c_del_driver(&mhl_sii_i2c_driver); + if (!mhl_msm_state) { + kfree(mhl_msm_state); + mhl_msm_state = NULL; + } + return ret; +} + +static void switch_mode(enum mhl_st_type to_mode) +{ + unsigned long flags; + + switch (to_mode) { + case POWER_STATE_D0_NO_MHL: + break; + case POWER_STATE_D0_MHL: + mhl_init_reg_settings(); + + /* REG_DISC_CTRL1 */ + mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT1, 0); + + /* + * TPI_DEVICE_POWER_STATE_CTRL_REG + * TX_POWER_STATE_MASK = BIT1 | BIT0 + */ + mhl_i2c_reg_modify(TX_PAGE_TPI, 0x001E, BIT1 | BIT0, 0x00); + break; + case POWER_STATE_D3: + if (mhl_msm_state->cur_state != POWER_STATE_D3) { + /* Force HPD to 0 when not in MHL mode. */ + mhl_i2c_reg_modify(TX_PAGE_3, 0x0020, + BIT5 | BIT4, BIT4); + + /* + * Change TMDS termination to high impedance + * on disconnection. + */ + mhl_i2c_reg_write(TX_PAGE_3, 0x0030, 0xD0); + mhl_i2c_reg_modify(TX_PAGE_L1, 0x003D, + BIT1 | BIT0, BIT0); + spin_lock_irqsave(&mhl_state_lock, flags); + mhl_msm_state->cur_state = POWER_STATE_D3; + spin_unlock_irqrestore(&mhl_state_lock, flags); + } + break; + default: + break; + } +} + +static void mhl_drive_hpd(uint8_t to_state) +{ + pr_debug("%s: To state=[0x%x]\n", __func__, to_state); + if (to_state == HPD_UP) { + /* + * Drive HPD to UP state + * + * The below two reg configs combined + * enable TMDS output. + */ + + /* Enable TMDS on TMDS_CCTRL */ + mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, BIT4); + + /* + * Set HPD_OUT_OVR_EN = HPD State + * EDID read and Un-force HPD (from low) + * propogate to src let HPD float by clearing + * HPD OUT OVRRD EN + */ + mhl_i2c_reg_modify(TX_PAGE_3, 0x0020, BIT4, 0x00); + } else { + /* + * Drive HPD to DOWN state + * Disable TMDS Output on REG_TMDS_CCTRL + * Enable/Disable TMDS output (MHL TMDS output only) + */ + mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, 0x00); + } + return; +} + +static void mhl_msm_connection(void) +{ + uint8_t val; + unsigned long flags; + + pr_err("%s: cur state = [0x%x]\n", __func__, mhl_msm_state->cur_state); + + if (mhl_msm_state->cur_state == POWER_STATE_D0_MHL) { + /* Already in D0 - MHL power state */ + return; + } + spin_lock_irqsave(&mhl_state_lock, flags); + mhl_msm_state->cur_state = POWER_STATE_D0_MHL; + spin_unlock_irqrestore(&mhl_state_lock, flags); + + mhl_i2c_reg_write(TX_PAGE_3, 0x30, 0x10); + + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x07, 0xF2); + + /* + * Keep the discovery enabled. Need RGND interrupt + * Possibly chip disables discovery after MHL_EST?? + * Need to re-enable here + */ + val = mhl_i2c_reg_read(TX_PAGE_3, 0x10); + mhl_i2c_reg_write(TX_PAGE_3, 0x10, val | BIT(0)); + + return; +} + +static void mhl_msm_disconnection(void) +{ + uint8_t reg; + + /* Clear interrupts - REG INTR4 */ + reg = mhl_i2c_reg_read(TX_PAGE_3, 0x0021); + mhl_i2c_reg_write(TX_PAGE_3, 0x0021, reg); + /* + * MHL TX CTL1 + * Disabling Tx termination + */ + mhl_i2c_reg_write(TX_PAGE_3, 0x30, 0xD0); + /* + * MSC REQUESTOR ABORT REASON + * Clear CBUS_HPD status + */ + mhl_i2c_reg_modify(TX_PAGE_CBUS, 0x000D, BIT6, 0x00); + /* Change HPD line to drive it low */ + mhl_drive_hpd(HPD_DOWN); + /* switch power state to D3 */ + switch_mode(POWER_STATE_D3); + return; +} + +/* + * If hardware detected a change in impedence and raised an INTR + * We check the range of this impedence to infer if the connected + * device is MHL or USB and take appropriate actions. + */ +static void mhl_msm_read_rgnd_int(void) +{ + uint8_t rgnd_imp; + + /* + * DISC STATUS REG 2 + * 1:0 RGND + * 00 - open (USB) + * 01 - 2 kOHM (USB) + * 10 - 1 kOHM ***(MHL)**** It's range 800 - 1200 OHM from MHL spec + * 11 - short (USB) + */ + rgnd_imp = mhl_i2c_reg_read(TX_PAGE_3, 0x001C); + pr_debug("Imp Range read = %02X\n", (int)rgnd_imp); + + + if (0x02 == rgnd_imp) { + pr_debug("MHL: MHL DEVICE!!!\n"); + /* + * Handling the MHL event in driver + */ + mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT0, BIT0); + } else { + pr_debug("MHL: NON-MHL DEVICE!!!\n"); + mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT3, BIT3); + } +} + +static void force_usb_switch_open(void) +{ + /*DISABLE_DISCOVERY*/ + mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT0, 0); + /* Force USB ID switch to open*/ + mhl_i2c_reg_modify(TX_PAGE_3, 0x0015, BIT6, BIT6); + mhl_i2c_reg_write(TX_PAGE_3, 0x0012, 0x86); + /* Force HPD to 0 when not in Mobile HD mode. */ + mhl_i2c_reg_modify(TX_PAGE_3, 0x0020, BIT5 | BIT4, BIT4); +} + +static void release_usb_switch_open(void) +{ + msleep(50); + mhl_i2c_reg_modify(TX_PAGE_3, 0x0015, BIT6, 0x00); + mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT0, BIT0); +} + +static void int_4_isr(void) +{ + uint8_t status; + + /* INTR_STATUS4 */ + status = mhl_i2c_reg_read(TX_PAGE_3, 0x0021); + + /* + * When I2C is inoperational (D3) and + * a previous interrupt brought us here, + * do nothing. + */ + pr_debug("MHL: MRR Interrupt status is = %02X\n", (int) status); + if (0xFF != status) { + if ((status & BIT0) && (mhl_msm_state->chip_rev_id < 1)) { + uint8_t tmds_cstat; + uint8_t mhl_fifo_status; + + /* TMDS CSTAT */ + tmds_cstat = mhl_i2c_reg_read(TX_PAGE_3, 0x0040); + + pr_debug("TMDS CSTAT: 0x%02x\n", tmds_cstat); + + if (tmds_cstat & 0x02) { + mhl_fifo_status = mhl_i2c_reg_read(TX_PAGE_3, + 0x0023); + pr_debug("MHL FIFO status: 0x%02x\n", + mhl_fifo_status); + if (mhl_fifo_status & 0x0C) { + mhl_i2c_reg_write(TX_PAGE_3, 0x0023, + 0x0C); + + pr_debug("Apply MHL FIFO Reset\n"); + mhl_i2c_reg_write(TX_PAGE_3, 0x0000, + 0x94); + mhl_i2c_reg_write(TX_PAGE_3, 0x0000, + 0x84); + } + } + } + + if (status & BIT1) + pr_err("MHL: INT4 BIT1 is set\n"); + + /* MHL_EST interrupt */ + if (status & BIT2) { + pr_err("MHL: Calling mhl_msm_connection() from ISR\n"); + mhl_msm_connection(); + pr_err("MHL Connect Drv: INT4 Status = %02X\n", + (int) status); + } else if (status & BIT3) { + pr_err("MHL: uUSB-A type device detected.\n"); + mhl_i2c_reg_write(TX_PAGE_3, 0x001C, 0x80); + switch_mode(POWER_STATE_D3); + } + + if (status & BIT5) { + mhl_msm_disconnection(); + pr_err("MHL Disconnect Drv: INT4 Status = %02X\n", + (int)status); + } + + if ((mhl_msm_state->cur_state != POWER_STATE_D0_MHL) &&\ + (status & BIT6)) { + /* RGND READY Intr */ + switch_mode(POWER_STATE_D0_MHL); + mhl_msm_read_rgnd_int(); + } + + /* Can't succeed at these in D3 */ + if (mhl_msm_state->cur_state != POWER_STATE_D3) { + /* CBUS Lockout interrupt? */ + /* + * Hardware detection mechanism figures that + * CBUS line is latched and raises this intr + * where we force usb switch open and release + */ + if (status & BIT4) { + force_usb_switch_open(); + release_usb_switch_open(); + } + } + } + pr_debug("MHL END Drv: INT4 Status = %02X\n", (int) status); + mhl_i2c_reg_write(TX_PAGE_3, 0x0021, status); + + return; +} + +static void int_5_isr(void) +{ + uint8_t intr_5_stat; + + /* + * Clear INT 5 ?? + * Probably need to revisit this later + * INTR5 is related to FIFO underflow/overflow reset + * which is handled in 8334 by auto FIFO reset + */ + intr_5_stat = mhl_i2c_reg_read(TX_PAGE_3, 0x0023); + mhl_i2c_reg_write(TX_PAGE_3, 0x0023, intr_5_stat); +} + + +static void int_1_isr(void) +{ + /* This ISR mainly handles the HPD status changes */ + uint8_t intr_1_stat; + uint8_t cbus_stat; + + /* INTR STATUS 1 */ + intr_1_stat = mhl_i2c_reg_read(TX_PAGE_L0, 0x0071); + + if (intr_1_stat) { + /* Clear interrupts */ + mhl_i2c_reg_write(TX_PAGE_L0, 0x0071, intr_1_stat); + if (BIT6 & intr_1_stat) { + /* + * HPD status change event is pending + * Read CBUS HPD status for this info + */ + + /* MSC REQ ABRT REASON */ + cbus_stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0D); + if (BIT6 & cbus_stat) + mhl_drive_hpd(HPD_UP); + } + } + return; +} + +/* + * RCP, RAP messages - mandatory for compliance + * + */ +static void mhl_cbus_isr(void) +{ + uint8_t regval; + int req_done = FALSE; + uint8_t sub_cmd; + uint8_t cmd_data; + int msc_msg_recved = FALSE; + int rc = -1; + + regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x08); + if (regval == 0xff) + return; + + /* clear all interrupts that were raised even if we did not process */ + if (regval) + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x08, regval); + + pr_err("%s: CBUS_INT = %02x\n", __func__, regval); + + /* MSC_MSG (RCP/RAP) */ + if (regval & BIT(3)) { + sub_cmd = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x18); + cmd_data = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x19); + msc_msg_recved = TRUE; + } + + /* MSC_REQ_DONE */ + if (regval & BIT(4)) + req_done = TRUE; + + /* Now look for interrupts on CBUS_MSC_INT2 */ + regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x1E); + + /* clear all interrupts that were raised */ + /* even if we did not process */ + if (regval) + mhl_i2c_reg_write(TX_PAGE_CBUS, 0x1E, regval); + + pr_err("%s: CBUS_MSC_INT2 = %02x\n", __func__, regval); + + /* received SET_INT */ + if (regval & BIT(2)) { + uint8_t intr; + intr = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xA0); + pr_debug("%s: MHL_INT_0 = %02x\n", __func__, intr); + intr = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xA1); + pr_debug("%s: MHL_INT_1 = %02x\n", __func__, intr); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA0, 0xFF); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA1, 0xFF); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA2, 0xFF); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA3, 0xFF); + } + + /* received WRITE_STAT */ + if (regval & BIT(3)) { + uint8_t stat; + stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xB0); + pr_err("%s: MHL_STATUS_0 = %02x\n", __func__, stat); + stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xB1); + pr_err("%s: MHL_STATUS_1 = %02x\n", __func__, stat); + + mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB0, 0xFF); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB1, 0xFF); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB2, 0xFF); + mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB3, 0xFF); + } + + /* received MSC_MSG */ + if (msc_msg_recved) { + /*mhl msc recv msc msg*/ + if (rc) + pr_err("MHL: mhl msc recv msc msg failed(%d)!\n", rc); + } + + return; +} + +static irqreturn_t mhl_tx_isr(int irq, void *dev_id) +{ + /* + * Check discovery interrupt + * if not yet connected + */ + pr_debug("MHL: Current POWER state is [0x%x]\n", + mhl_msm_state->cur_state); + /* + * Check RGND, MHL_EST, CBUS_LOCKOUT, SCDT + * interrupts. In D3, we get only RGND + */ + int_4_isr(); + + pr_debug("MHL: Current POWER state is [0x%x]\n", + mhl_msm_state->cur_state); + if (mhl_msm_state->cur_state == POWER_STATE_D0_MHL) { + /* + * If int_4_isr() didn't move the tx to D3 + * on disconnect, continue to check other + * interrupt sources. + */ + int_5_isr(); + + /* + * Check for any peer messages for DCAP_CHG etc + * Dispatch to have the CBUS module working only + * once connected. + */ + mhl_cbus_isr(); + int_1_isr(); + } + return IRQ_HANDLED; +} + +static void __exit mhl_msm_exit(void) +{ + pr_warn("MHL: Exiting, Bye\n"); + /* + * Delete driver if i2c client structure is NULL + */ + i2c_del_driver(&mhl_sii_i2c_driver); + if (!mhl_msm_state) { + kfree(mhl_msm_state); + mhl_msm_state = NULL; + } +} + +module_init(mhl_msm_init); +module_exit(mhl_msm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MHL SII 8334 TX driver"); diff --git a/drivers/video/msm/mhl/mhl_8334.h b/drivers/video/msm/mhl/mhl_8334.h new file mode 100644 index 0000000000000000000000000000000000000000..239d4d3936bcf25d36ec3fc6828d642c54e37417 --- /dev/null +++ b/drivers/video/msm/mhl/mhl_8334.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __MHL_MSM_H__ +#define __MHL_MSM_H__ + +#include +#include +#include + +#include "mhl_devcap.h" +#include "mhl_defs.h" + +#define GPIO_MHL_RESET 15 +#define GPIO_MHL_INT 4 + +#define MHL_DEVICE_NAME "sii8334" +#define MHL_DRIVER_NAME "sii8334" + +#define HPD_UP 1 +#define HPD_DOWN 0 + +struct mhl_msm_state_t { + struct i2c_client *i2c_client; + struct i2c_driver *i2c_driver; + uint8_t cur_state; + uint8_t chip_rev_id; + struct msm_mhl_platform_data *mhl_data; +}; + +enum { + TX_PAGE_TPI = 0x00, + TX_PAGE_L0 = 0x01, + TX_PAGE_L1 = 0x02, + TX_PAGE_2 = 0x03, + TX_PAGE_3 = 0x04, + TX_PAGE_CBUS = 0x05, + TX_PAGE_DDC_EDID = 0x06, + TX_PAGE_DDC_SEGM = 0x07, +}; + +enum mhl_st_type { + POWER_STATE_D0_NO_MHL = 0, + POWER_STATE_D0_MHL = 2, + POWER_STATE_D3 = 3, +}; + +enum { + DEV_PAGE_TPI_0 = (0x72), + DEV_PAGE_TX_L0_0 = (0x72), + DEV_PAGE_TPI_1 = (0x76), + DEV_PAGE_TX_L0_1 = (0x76), + DEV_PAGE_TX_L1_0 = (0x7A), + DEV_PAGE_TX_L1_1 = (0x7E), + DEV_PAGE_TX_2_0 = (0x92), + DEV_PAGE_TX_2_1 = (0x96), + DEV_PAGE_TX_3_0 = (0x9A), + DEV_PAGE_TX_3_1 = (0x9E), + DEV_PAGE_CBUS = (0xC8), + DEV_PAGE_DDC_EDID = (0xA0), + DEV_PAGE_DDC_SEGM = (0x60), +}; + +#endif /* __MHL_MSM_H__ */ diff --git a/drivers/video/msm/mhl/mhl_defs.h b/drivers/video/msm/mhl/mhl_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..9e85f07aec47cad6158959aa922482dbcbfcd826 --- /dev/null +++ b/drivers/video/msm/mhl/mhl_defs.h @@ -0,0 +1,222 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MHL_SPEC_DEFS_H__ +#define __MHL_SPEC_DEFS_H__ + +enum DevCapOffset_e { + DEVCAP_OFFSET_DEV_STATE = 0x00, + DEVCAP_OFFSET_MHL_VERSION = 0x01, + DEVCAP_OFFSET_DEV_CAT = 0x02, + DEVCAP_OFFSET_ADOPTER_ID_H = 0x03, + DEVCAP_OFFSET_ADOPTER_ID_L = 0x04, + DEVCAP_OFFSET_VID_LINK_MODE = 0x05, + DEVCAP_OFFSET_AUD_LINK_MODE = 0x06, + DEVCAP_OFFSET_VIDEO_TYPE = 0x07, + DEVCAP_OFFSET_LOG_DEV_MAP = 0x08, + DEVCAP_OFFSET_BANDWIDTH = 0x09, + DEVCAP_OFFSET_FEATURE_FLAG = 0x0A, + DEVCAP_OFFSET_DEVICE_ID_H = 0x0B, + DEVCAP_OFFSET_DEVICE_ID_L = 0x0C, + DEVCAP_OFFSET_SCRATCHPAD_SIZE = 0x0D, + DEVCAP_OFFSET_INT_STAT_SIZE = 0x0E, + DEVCAP_OFFSET_RESERVED = 0x0F, + /* this one must be last */ + DEVCAP_SIZE +}; + +#ifndef __MHL_MSM_8334_REGS_H__ +#define __MHL_MSM_8334_REGS_H__ + +#define BIT0 0x01 +#define BIT1 0x02 +#define BIT2 0x04 +#define BIT3 0x08 +#define BIT4 0x10 +#define BIT5 0x20 +#define BIT6 0x40 +#define BIT7 0x80 + +#define LOW 0 +#define HIGH 1 + +#define MAX_PAGES 8 +#endif + + +/* Version that this chip supports*/ +/* bits 4..7 */ +#define MHL_VER_MAJOR (0x01 << 4) +/* bits 0..3 */ +#define MHL_VER_MINOR 0x01 +#define MHL_VERSION (MHL_VER_MAJOR | MHL_VER_MINOR) + +/*Device Category*/ +#define MHL_DEV_CATEGORY_OFFSET DEVCAP_OFFSET_DEV_CAT +#define MHL_DEV_CATEGORY_POW_BIT (BIT4) + +#define MHL_DEV_CAT_SOURCE 0x02 + +/*Video Link Mode*/ +#define MHL_DEV_VID_LINK_SUPPRGB444 0x01 +#define MHL_DEV_VID_LINK_SUPPYCBCR444 0x02 +#define MHL_DEV_VID_LINK_SUPPYCBCR422 0x04 +#define MHL_DEV_VID_LINK_SUPP_PPIXEL 0x08 +#define MHL_DEV_VID_LINK_SUPP_ISLANDS 0x10 + +/*Audio Link Mode Support*/ +#define MHL_DEV_AUD_LINK_2CH 0x01 +#define MHL_DEV_AUD_LINK_8CH 0x02 + + +/*Feature Flag in the devcap*/ +#define MHL_DEV_FEATURE_FLAG_OFFSET DEVCAP_OFFSET_FEATURE_FLAG +/* Dongles have freedom to not support RCP */ +#define MHL_FEATURE_RCP_SUPPORT BIT0 +/* Dongles have freedom to not support RAP */ +#define MHL_FEATURE_RAP_SUPPORT BIT1 +/* Dongles have freedom to not support SCRATCHPAD */ +#define MHL_FEATURE_SP_SUPPORT BIT2 + +/*Logical Dev Map*/ +#define MHL_DEV_LD_DISPLAY (0x01 << 0) +#define MHL_DEV_LD_VIDEO (0x01 << 1) +#define MHL_DEV_LD_AUDIO (0x01 << 2) +#define MHL_DEV_LD_MEDIA (0x01 << 3) +#define MHL_DEV_LD_TUNER (0x01 << 4) +#define MHL_DEV_LD_RECORD (0x01 << 5) +#define MHL_DEV_LD_SPEAKER (0x01 << 6) +#define MHL_DEV_LD_GUI (0x01 << 7) + +/*Bandwidth*/ +/* 225 MHz */ +#define MHL_BANDWIDTH_LIMIT 22 + + +#define MHL_STATUS_REG_CONNECTED_RDY 0x30 +#define MHL_STATUS_REG_LINK_MODE 0x31 + +#define MHL_STATUS_DCAP_RDY BIT0 + +#define MHL_STATUS_CLK_MODE_MASK 0x07 +#define MHL_STATUS_CLK_MODE_PACKED_PIXEL 0x02 +#define MHL_STATUS_CLK_MODE_NORMAL 0x03 +#define MHL_STATUS_PATH_EN_MASK 0x08 +#define MHL_STATUS_PATH_ENABLED 0x08 +#define MHL_STATUS_PATH_DISABLED 0x00 +#define MHL_STATUS_MUTED_MASK 0x10 + +#define MHL_RCHANGE_INT 0x20 +#define MHL_DCHANGE_INT 0x21 + +#define MHL_INT_DCAP_CHG BIT0 +#define MHL_INT_DSCR_CHG BIT1 +#define MHL_INT_REQ_WRT BIT2 +#define MHL_INT_GRT_WRT BIT3 + +/* On INTR_1 the EDID_CHG is located at BIT 0*/ +#define MHL_INT_EDID_CHG BIT1 + +/* This contains one nibble each - max offset */ +#define MHL_INT_AND_STATUS_SIZE 0x33 +#define MHL_SCRATCHPAD_SIZE 16 +/* manually define highest number */ +#define MHL_MAX_BUFFER_SIZE MHL_SCRATCHPAD_SIZE + + + +enum { + /* RCP sub-command */ + MHL_MSC_MSG_RCP = 0x10, + /* RCP Acknowledge sub-command */ + MHL_MSC_MSG_RCPK = 0x11, + /* RCP Error sub-command */ + MHL_MSC_MSG_RCPE = 0x12, + /* Mode Change Warning sub-command */ + MHL_MSC_MSG_RAP = 0x20, + /* MCW Acknowledge sub-command */ + MHL_MSC_MSG_RAPK = 0x21, +}; + +#define RCPE_NO_ERROR 0x00 +#define RCPE_INEEFECTIVE_KEY_CODE 0x01 +#define RCPE_BUSY 0x02 +/* MHL spec related defines*/ +enum { + /* Command or Data byte acknowledge */ + MHL_ACK = 0x33, + /* Command or Data byte not acknowledge */ + MHL_NACK = 0x34, + /* Transaction abort */ + MHL_ABORT = 0x35, + /* 0xE0 - Write one status register strip top bit */ + MHL_WRITE_STAT = 0x60 | 0x80, + /* Write one interrupt register */ + MHL_SET_INT = 0x60, + /* Read one register */ + MHL_READ_DEVCAP = 0x61, + /* Read CBUS revision level from follower */ + MHL_GET_STATE = 0x62, + /* Read vendor ID value from follower. */ + MHL_GET_VENDOR_ID = 0x63, + /* Set Hot Plug Detect in follower */ + MHL_SET_HPD = 0x64, + /* Clear Hot Plug Detect in follower */ + MHL_CLR_HPD = 0x65, + /* Set Capture ID for downstream device. */ + MHL_SET_CAP_ID = 0x66, + /* Get Capture ID from downstream device. */ + MHL_GET_CAP_ID = 0x67, + /* VS command to send RCP sub-commands */ + MHL_MSC_MSG = 0x68, + /* Get Vendor-Specific command error code. */ + MHL_GET_SC1_ERRORCODE = 0x69, + /* Get DDC channel command error code. */ + MHL_GET_DDC_ERRORCODE = 0x6A, + /* Get MSC command error code. */ + MHL_GET_MSC_ERRORCODE = 0x6B, + /* Write 1-16 bytes to responder's scratchpad. */ + MHL_WRITE_BURST = 0x6C, + /* Get channel 3 command error code. */ + MHL_GET_SC3_ERRORCODE = 0x6D, +}; + +/* Turn content streaming ON. */ +#define MHL_RAP_CONTENT_ON 0x10 +/* Turn content streaming OFF. */ +#define MHL_RAP_CONTENT_OFF 0x11 + +/* + * + * MHL Timings applicable to this driver. + * + */ +/* 100 - 1000 milliseconds. Per MHL 1.0 Specs */ +#define T_SRC_VBUS_CBUS_TO_STABLE (200) +/* 20 milliseconds. Per MHL 1.0 Specs */ +#define T_SRC_WAKE_PULSE_WIDTH_1 (20) +/* 60 milliseconds. Per MHL 1.0 Specs */ +#define T_SRC_WAKE_PULSE_WIDTH_2 (60) + +/* 100 - 1000 milliseconds. Per MHL 1.0 Specs */ +#define T_SRC_WAKE_TO_DISCOVER (500) + +#define T_SRC_VBUS_CBUS_T0_STABLE (500) + +/* Allow RSEN to stay low this much before reacting.*/ +#define T_SRC_RSEN_DEGLITCH (100) + +/* Wait this much after connection before reacting to RSEN (300-500ms)*/ +/* Per specs between 300 to 500 ms*/ +#define T_SRC_RXSENSE_CHK (400) + +#endif /* __MHL_SPEC_DEFS_H__ */ diff --git a/drivers/video/msm/mhl/mhl_devcap.h b/drivers/video/msm/mhl/mhl_devcap.h new file mode 100644 index 0000000000000000000000000000000000000000..40a87fe737034b633326f5bcc0d83877d2d30d73 --- /dev/null +++ b/drivers/video/msm/mhl/mhl_devcap.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MHL_DEVCAP_H__ +#define __MHL_DEVCAP_H__ + +#define SILICON_IMAGE_ADOPTER_ID 322 +#define TRANSCODER_DEVICE_ID 0x8334 + +#define MHL_DEV_LD_AUDIO (0x01 << 2) +#define MHL_DEV_LD_VIDEO (0x01 << 1) +#define MHL_DEV_LD_MEDIA (0x01 << 3) +#define MHL_DEV_LD_GUI (0x01 << 7) +#define MHL_LOGICAL_DEVICE_MAP (MHL_DEV_LD_AUDIO |\ + MHL_DEV_LD_VIDEO | MHL_DEV_LD_MEDIA | MHL_DEV_LD_GUI) + +#define DEVCAP_VAL_DEV_STATE 0 +#define DEVCAP_VAL_MHL_VERSION MHL_VERSION +#define DEVCAP_VAL_DEV_CAT (MHL_DEV_CAT_SOURCE |\ + MHL_DEV_CATEGORY_POW_BIT) +#define DEVCAP_VAL_ADOPTER_ID_H (uint8_t)(SILICON_IMAGE_ADOPTER_ID >> 8) +#define DEVCAP_VAL_ADOPTER_ID_L (uint8_t)(SILICON_IMAGE_ADOPTER_ID & 0xFF) +#define DEVCAP_VAL_VID_LINK_MODE MHL_DEV_VID_LINK_SUPPRGB444 +#define DEVCAP_VAL_AUD_LINK_MODE MHL_DEV_AUD_LINK_2CH +#define DEVCAP_VAL_VIDEO_TYPE 0 +#define DEVCAP_VAL_LOG_DEV_MAP MHL_LOGICAL_DEVICE_MAP +#define DEVCAP_VAL_BANDWIDTH 0 +#define DEVCAP_VAL_FEATURE_FLAG (MHL_FEATURE_RCP_SUPPORT |\ + MHL_FEATURE_RAP_SUPPORT | MHL_FEATURE_SP_SUPPORT) +#define DEVCAP_VAL_DEVICE_ID_H (uint8_t)(TRANSCODER_DEVICE_ID >> 8) +#define DEVCAP_VAL_DEVICE_ID_L (uint8_t)(TRANSCODER_DEVICE_ID & 0xFF) +#define DEVCAP_VAL_SCRATCHPAD_SIZE MHL_SCRATCHPAD_SIZE +#define DEVCAP_VAL_INT_STAT_SIZE MHL_INT_AND_STATUS_SIZE +#define DEVCAP_VAL_RESERVED 0 + +#endif /* __MHL_DEVCAP_H__ */ diff --git a/drivers/video/msm/mhl/mhl_i2c_utils.c b/drivers/video/msm/mhl/mhl_i2c_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..c4728b4ddce9a573bfdb12353ae719bb9927122c --- /dev/null +++ b/drivers/video/msm/mhl/mhl_i2c_utils.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include + +#include "mhl_i2c_utils.h" +#include "mhl_8334.h" + +#define DEBUG + +uint8_t slave_addrs[MAX_PAGES] = { + DEV_PAGE_TPI_0 , + DEV_PAGE_TX_L0_0 , + DEV_PAGE_TX_L1_0 , + DEV_PAGE_TX_2_0 , + DEV_PAGE_TX_3_0 , + DEV_PAGE_CBUS , + DEV_PAGE_DDC_EDID , + DEV_PAGE_DDC_SEGM , +}; + +int mhl_i2c_reg_read(uint8_t slave_addr_index, uint8_t reg_offset) +{ + struct i2c_msg msgs[2]; + uint8_t buffer = 0; + int ret = -1; + + pr_debug("MRR: Reading from slave_addr_index=[%x] and offset=[%x]\n", + slave_addr_index, reg_offset); + pr_debug("MRR: Addr slave_addr_index=[%x]\n", + slave_addrs[slave_addr_index]); + + /* Slave addr */ + msgs[0].addr = slave_addrs[slave_addr_index] >> 1; + msgs[1].addr = slave_addrs[slave_addr_index] >> 1; + + /* Write Command */ + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + + /* Register offset for the next transaction */ + msgs[0].buf = ®_offset; + msgs[1].buf = &buffer; + + /* Offset is 1 Byte long */ + msgs[0].len = 1; + msgs[1].len = 1; + + ret = i2c_transfer(mhl_msm_state->i2c_client->adapter, msgs, 2); + if (ret < 1) { + pr_err("I2C READ FAILED=[%d]\n", ret); + return -EACCES; + } + pr_err("Buffer is [%x]\n", buffer); + return buffer; +} + + +int mhl_i2c_reg_write(uint8_t slave_addr_index, uint8_t reg_offset, + uint8_t value) +{ + return mhl_i2c_reg_write_cmds(slave_addr_index, reg_offset, &value, 1); +} + +int mhl_i2c_reg_write_cmds(uint8_t slave_addr_index, uint8_t reg_offset, + uint8_t *value, uint16_t count) +{ + struct i2c_msg msgs[1]; + uint8_t data[2]; + int status = -EACCES; + + msgs[0].addr = slave_addrs[slave_addr_index] >> 1; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = data; + data[0] = reg_offset; + data[1] = *value; + + status = i2c_transfer(mhl_msm_state->i2c_client->adapter, msgs, 1); + if (status < 1) { + pr_err("I2C WRITE FAILED=[%d]\n", status); + return -EACCES; + } + + return status; +} + +void mhl_i2c_reg_modify(uint8_t slave_addr_index, uint8_t reg_offset, + uint8_t mask, uint8_t val) +{ + uint8_t temp; + + temp = mhl_i2c_reg_read(slave_addr_index, reg_offset); + temp &= (~mask); + temp |= (mask & val); + mhl_i2c_reg_write(slave_addr_index, reg_offset, temp); +} + diff --git a/drivers/video/msm/mhl/mhl_i2c_utils.h b/drivers/video/msm/mhl/mhl_i2c_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..94daad7bec7648f1b263175c203f7b01119e9687 --- /dev/null +++ b/drivers/video/msm/mhl/mhl_i2c_utils.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __MHL_I2C_UTILS_H__ +#define __MHL_I2C_UTILS_H__ + +#include +#include + +#include "mhl_defs.h" + +/* + * I2C command to the adapter to append + * the buffer from next msg to this one. + */ +#define I2C_M_APPND_NXT_WR 0x0002 + +extern uint8_t slave_addrs[MAX_PAGES]; +extern struct mhl_msm_state_t *mhl_msm_state; + +int mhl_i2c_reg_read(uint8_t slave_addr_index, uint8_t reg_offset); +int mhl_i2c_reg_write(uint8_t slave_addr_index, uint8_t reg_offset, + uint8_t value); +int mhl_i2c_reg_write_cmds(uint8_t slave_addr_index, uint8_t reg_offset, + uint8_t *value, uint16_t count); +void mhl_i2c_reg_modify(uint8_t slave_addr_index, uint8_t reg_offset, + uint8_t mask, uint8_t val); + +#endif /* __MHL_I2C_UTILS_H__ */ diff --git a/drivers/video/msm/mhl_api.h b/drivers/video/msm/mhl_api.h new file mode 100644 index 0000000000000000000000000000000000000000..26037ce21dc4dbbb238acfc1acda5550362d5a96 --- /dev/null +++ b/drivers/video/msm/mhl_api.h @@ -0,0 +1,26 @@ + +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MHL_API_H__ +#define __MHL_API_H__ + +#ifdef CONFIG_FB_MSM_HDMI_MHL_8334 +bool mhl_is_connected(void); +#else +static bool mhl_is_connected(void) +{ + return false; +} +#endif + +#endif /* __MHL_API_H__ */ diff --git a/drivers/video/msm/mipi_NT35510.c b/drivers/video/msm/mipi_NT35510.c new file mode 100644 index 0000000000000000000000000000000000000000..be3502ac599ac3edf9a6d4e6e221d4c65300a93b --- /dev/null +++ b/drivers/video/msm/mipi_NT35510.c @@ -0,0 +1,609 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_NT35510.h" + +static struct msm_panel_common_pdata *mipi_nt35510_pdata; +static struct dsi_buf nt35510_tx_buf; +static struct dsi_buf nt35510_rx_buf; + +#define NT35510_SLEEP_OFF_DELAY 150 +#define NT35510_DISPLAY_ON_DELAY 150 + +/* common setting */ +static char exit_sleep[2] = {0x11, 0x00}; +static char display_on[2] = {0x29, 0x00}; +static char display_off[2] = {0x28, 0x00}; +static char enter_sleep[2] = {0x10, 0x00}; +static char write_ram[2] = {0x2c, 0x00}; /* write ram */ + +static struct dsi_cmd_desc nt35510_display_off_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(display_off), display_off}, + {DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(enter_sleep), enter_sleep} +}; + +static char cmd0[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x01, +}; +static char cmd1[4] = { + 0xBC, 0x00, 0xA0, 0x00, +}; +static char cmd2[4] = { + 0xBD, 0x00, 0xA0, 0x00, +}; +static char cmd3[3] = { + 0xBE, 0x00, 0x79, +}; +static char cmd4[53] = { + 0xD1, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char cmd5[53] = { + 0xD2, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char cmd6[53] = { + 0xD3, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char cmd7[53] = { + 0xD4, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char cmd8[53] = { + 0xD5, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char cmd9[53] = { + 0xD6, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char cmd10[4] = { + 0xB0, 0x0A, 0x0A, 0x0A, +}; +static char cmd11[4] = { + 0xB1, 0x0A, 0x0A, 0x0A, +}; +static char cmd12[4] = { + 0xBA, 0x24, 0x24, 0x24, +}; +static char cmd13[4] = { + 0xB9, 0x24, 0x24, 0x24, +}; +static char cmd14[4] = { + 0xB8, 0x24, 0x24, 0x24, +}; +static char cmd15[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x00, +}; +static char cmd16[2] = { + 0xB3, 0x00, +}; +static char cmd17[2] = { + 0xB4, 0x10, +}; +static char cmd18[2] = { + 0xB6, 0x02, +}; +static char cmd19[3] = { + 0xB1, 0xEC, 0x00, +}; +static char cmd20[4] = { + 0xBC, 0x05, 0x05, 0x05, +}; +static char cmd21[3] = { + 0xB7, 0x20, 0x20, +}; +static char cmd22[5] = { + 0xB8, 0x01, 0x03, 0x03, + 0x03, +}; +static char cmd23[19] = { + 0xC8, 0x01, 0x00, 0x78, + 0x50, 0x78, 0x50, 0x78, + 0x50, 0x78, 0x50, 0xC8, + 0x3C, 0x3C, 0xC8, 0xC8, + 0x3C, 0x3C, 0xC8, +}; +static char cmd24[6] = { + 0xBD, 0x01, 0x84, 0x07, + 0x31, 0x00, +}; +static char cmd25[6] = { + 0xBE, 0x01, 0x84, 0x07, + 0x31, 0x00, +}; +static char cmd26[6] = { + 0xBF, 0x01, 0x84, 0x07, + 0x31, 0x00, +}; +static char cmd27[2] = { + 0x35, 0x00, +}; +static char config_MADCTL[2] = {0x36, 0x00}; +static struct dsi_cmd_desc nt35510_cmd_display_on_cmds[] = { + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd0), cmd0}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd1), cmd1}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd2), cmd2}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd3), cmd3}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd4), cmd4}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd5), cmd5}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd6), cmd6}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd7), cmd7}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd8), cmd8}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd9), cmd9}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd10), cmd10}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd11), cmd11}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd12), cmd12}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd13), cmd13}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd14), cmd14}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd15), cmd15}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd16), cmd16}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd17), cmd17}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd18), cmd18}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd19), cmd19}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd20), cmd20}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd21), cmd21}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd22), cmd22}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd23), cmd23}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd24), cmd24}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd25), cmd25}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd26), cmd26}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(cmd27), cmd27}, + + {DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 10, sizeof(display_on), display_on}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, 150, + sizeof(config_MADCTL), config_MADCTL}, + + {DTYPE_DCS_WRITE, 1, 0, 0, 10, sizeof(write_ram), write_ram}, +}; + +static char video0[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x01, +}; +static char video1[4] = { + 0xBC, 0x00, 0xA0, 0x00, +}; +static char video2[4] = { + 0xBD, 0x00, 0xA0, 0x00, +}; +static char video3[3] = { + 0xBE, 0x00, 0x79, +}; +static char video4[53] = { + 0xD1, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char video5[53] = { + 0xD2, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char video6[53] = { + 0xD3, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char video7[53] = { + 0xD4, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char video8[53] = { + 0xD5, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char video9[53] = { + 0xD6, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x32, 0x00, + 0x4F, 0x00, 0x65, 0x00, + 0x8B, 0x00, 0xA8, 0x00, + 0xD5, 0x00, 0xF7, 0x01, + 0x2B, 0x01, 0x54, 0x01, + 0x8E, 0x01, 0xBB, 0x01, + 0xBC, 0x01, 0xE3, 0x02, + 0x08, 0x02, 0x1C, 0x02, + 0x39, 0x02, 0x4F, 0x02, + 0x76, 0x02, 0xA3, 0x02, + 0xE3, 0x03, 0x12, 0x03, + 0x4C, 0x03, 0x66, 0x03, + 0x9A, +}; +static char video10[4] = { + 0xB0, 0x0A, 0x0A, 0x0A, +}; +static char video11[4] = { + 0xB1, 0x0A, 0x0A, 0x0A, +}; +static char video12[4] = { + 0xBA, 0x24, 0x24, 0x24, +}; +static char video13[4] = { + 0xB9, 0x24, 0x24, 0x24, +}; +static char video14[4] = { + 0xB8, 0x24, 0x24, 0x24, +}; +static char video15[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x00, +}; +static char video16[2] = { + 0xB3, 0x00, +}; +static char video17[2] = { + 0xB4, 0x10, +}; +static char video18[2] = { + 0xB6, 0x02, +}; +static char video19[3] = { + 0xB1, 0xFC, 0x00, +}; +static char video20[4] = { + 0xBC, 0x05, 0x05, 0x05, +}; +static char video21[3] = { + 0xB7, 0x20, 0x20, +}; +static char video22[5] = { + 0xB8, 0x01, 0x03, 0x03, + 0x03, +}; +static char video23[19] = { + 0xC8, 0x01, 0x00, 0x78, + 0x50, 0x78, 0x50, 0x78, + 0x50, 0x78, 0x50, 0xC8, + 0x3C, 0x3C, 0xC8, 0xC8, + 0x3C, 0x3C, 0xC8, +}; +static char video24[6] = { + 0xBD, 0x01, 0x84, 0x07, + 0x31, 0x00, +}; +static char video25[6] = { + 0xBE, 0x01, 0x84, 0x07, + 0x31, 0x00, +}; +static char video26[6] = { + 0xBF, 0x01, 0x84, 0x07, + 0x31, 0x00, +}; +static char video27[2] = { + 0x35, 0x00, +}; +static char config_video_MADCTL[2] = {0x36, 0xC0}; +static struct dsi_cmd_desc nt35510_video_display_on_cmds[] = { + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video0), video0}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video1), video1}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video2), video2}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video3), video3}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video4), video4}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video5), video5}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video6), video6}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video7), video7}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video8), video8}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video9), video9}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video10), video10}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video11), video11}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video12), video12}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video13), video13}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video14), video14}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video15), video15}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video16), video16}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video17), video17}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video18), video18}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video19), video19}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video20), video20}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video21), video21}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video22), video22}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video23), video23}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video24), video24}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video25), video25}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video26), video26}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 50, sizeof(video27), video27}, + {DTYPE_DCS_WRITE, 1, 0, 0, NT35510_SLEEP_OFF_DELAY, sizeof(exit_sleep), + exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, NT35510_DISPLAY_ON_DELAY, sizeof(display_on), + display_on}, +}; + +static struct dsi_cmd_desc nt35510_video_display_on_cmds_rotate[] = { + {DTYPE_DCS_WRITE1, 1, 0, 0, 150, + sizeof(config_video_MADCTL), config_video_MADCTL}, +}; +static int mipi_nt35510_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + static int rotate; + mfd = platform_get_drvdata(pdev); + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi = &mfd->panel_info.mipi; + + if (mipi_nt35510_pdata && mipi_nt35510_pdata->rotate_panel) + rotate = mipi_nt35510_pdata->rotate_panel(); + + if (mipi->mode == DSI_VIDEO_MODE) { + mipi_dsi_cmds_tx(mfd, &nt35510_tx_buf, + nt35510_video_display_on_cmds, + ARRAY_SIZE(nt35510_video_display_on_cmds)); + + if (rotate) { + mipi_dsi_cmds_tx(mfd, &nt35510_tx_buf, + nt35510_video_display_on_cmds_rotate, + ARRAY_SIZE(nt35510_video_display_on_cmds_rotate)); + } + } else if (mipi->mode == DSI_CMD_MODE) { + mipi_dsi_cmds_tx(mfd, &nt35510_tx_buf, + nt35510_cmd_display_on_cmds, + ARRAY_SIZE(nt35510_cmd_display_on_cmds)); + } + + return 0; +} + +static int mipi_nt35510_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + pr_debug("mipi_nt35510_lcd_off E\n"); + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi_dsi_cmds_tx(mfd, &nt35510_tx_buf, nt35510_display_off_cmds, + ARRAY_SIZE(nt35510_display_off_cmds)); + + pr_debug("mipi_nt35510_lcd_off X\n"); + return 0; +} + +static int mipi_nt35510_lcd_probe(struct platform_device *pdev) +{ + pr_debug("%s\n", __func__); + + if (pdev->id == 0) { + mipi_nt35510_pdata = pdev->dev.platform_data; + if (mipi_nt35510_pdata->bl_lock) + spin_lock_init(&mipi_nt35510_pdata->bl_spinlock); + return 0; + } + + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = mipi_nt35510_lcd_probe, + .driver = { + .name = "mipi_NT35510", + }, +}; + +static void mipi_nt35510_set_backlight(struct msm_fb_data_type *mfd) +{ + int bl_level; + unsigned long flags; + bl_level = mfd->bl_level; + + if (mipi_nt35510_pdata->bl_lock) { + spin_lock_irqsave(&mipi_nt35510_pdata->bl_spinlock, flags); + mipi_nt35510_pdata->pmic_backlight(bl_level); + spin_unlock_irqrestore(&mipi_nt35510_pdata->bl_spinlock, flags); + } else + mipi_nt35510_pdata->pmic_backlight(bl_level); +} + +static struct msm_fb_panel_data nt35510_panel_data = { + .on = mipi_nt35510_lcd_on, + .off = mipi_nt35510_lcd_off, + .set_backlight = mipi_nt35510_set_backlight, +}; + +static int ch_used[3]; + +static int mipi_nt35510_lcd_init(void) +{ + mipi_dsi_buf_alloc(&nt35510_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&nt35510_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} +int mipi_nt35510_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + ch_used[channel] = TRUE; + + ret = mipi_nt35510_lcd_init(); + if (ret) { + pr_err("mipi_nt35510_lcd_init() failed with ret %u\n", ret); + return ret; + } + + pdev = platform_device_alloc("mipi_NT35510", (panel << 8)|channel); + if (!pdev) + return -ENOMEM; + + nt35510_panel_data.panel_info = *pinfo; + ret = platform_device_add_data(pdev, &nt35510_panel_data, + sizeof(nt35510_panel_data)); + if (ret) { + pr_debug("%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_debug("%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} diff --git a/drivers/video/msm/mipi_NT35510.h b/drivers/video/msm/mipi_NT35510.h new file mode 100644 index 0000000000000000000000000000000000000000..58ac05a5435591909781dc6a3d2241c51510d14b --- /dev/null +++ b/drivers/video/msm/mipi_NT35510.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MIPI_NT35510_H +#define MIPI_NT35510_H + +int mipi_nt35510_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#endif /* MIPI_NT35510_H */ diff --git a/drivers/video/msm/mipi_NT35510_cmd_wvga_pt.c b/drivers/video/msm/mipi_NT35510_cmd_wvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..d44b1c911a9ffc5e17e44beedc43c3e97451c1bb --- /dev/null +++ b/drivers/video/msm/mipi_NT35510_cmd_wvga_pt.c @@ -0,0 +1,98 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_NT35510.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = { + /* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */ + /* regulator */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + {0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90, + 0x18, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x01, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +}; + +static int mipi_cmd_nt35510_wvga_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_cmd_nt35510_wvga")) + return 0; + + pinfo.xres = 480; + pinfo.yres = 800; + pinfo.type = MIPI_CMD_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 100; + pinfo.lcdc.h_front_porch = 100; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 20; + pinfo.lcdc.v_front_porch = 20; + pinfo.lcdc.v_pulse_width = 1; + + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 31; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.clk_rate = 499000000; + + pinfo.lcd.vsync_enable = TRUE; + pinfo.lcd.hw_vsync_mode = TRUE; + pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */ + + pinfo.mipi.mode = DSI_CMD_MODE; + pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2F; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.te_sel = 1; /* TE from vsync gpio */ + pinfo.mipi.interleave_max = 1; + pinfo.mipi.insert_dcs_cmd = TRUE; + pinfo.mipi.wr_mem_continue = 0x3c; + pinfo.mipi.wr_mem_start = 0x2c; + pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db; + pinfo.mipi.tx_eot_append = 0x01; + pinfo.mipi.rx_eot_ignore = 0x0; + pinfo.mipi.dlane_swap = 0x01; + + ret = mipi_nt35510_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WVGA_PT); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_cmd_nt35510_wvga_pt_init); diff --git a/drivers/video/msm/mipi_NT35510_video_wvga_pt.c b/drivers/video/msm/mipi_NT35510_video_wvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..5b7a3cfbf0db7adc7e245c7ae2f055d982338539 --- /dev/null +++ b/drivers/video/msm/mipi_NT35510_video_wvga_pt.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_NT35510.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */ + /* regulator */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + {0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90, + 0x18, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x00, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +}; + +static int mipi_video_nt35510_wvga_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_nt35510_wvga")) + return 0; + + pinfo.xres = 480; + pinfo.yres = 800; + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 100; + pinfo.lcdc.h_front_porch = 100; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 20; + pinfo.lcdc.v_front_porch = 20; + pinfo.lcdc.v_pulse_width = 1; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + /* number of dot_clk cycles HSYNC active edge is + delayed from VSYNC active edge */ + pinfo.lcdc.hsync_skew = 0; + pinfo.clk_rate = 499000000; + pinfo.bl_max = 31; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + /* send HSA and HE following VS/VE packet */ + pinfo.mipi.pulse_mode_hsa_he = TRUE; + pinfo.mipi.hfp_power_stop = TRUE; /* LP-11 during the HFP period */ + pinfo.mipi.hbp_power_stop = TRUE; /* LP-11 during the HBP period */ + pinfo.mipi.hsa_power_stop = TRUE; /* LP-11 during the HSA period */ + /* LP-11 or let Command Mode Engine send packets in + HS or LP mode for the BLLP of the last line of a frame */ + pinfo.mipi.eof_bllp_power_stop = TRUE; + /* LP-11 or let Command Mode Engine send packets in + HS or LP mode for packets sent during BLLP period */ + pinfo.mipi.bllp_power_stop = TRUE; + + pinfo.mipi.traffic_mode = DSI_BURST_MODE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; /* RGB */ + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2f; + + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; /* FIXME */ + + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + pinfo.mipi.dlane_swap = 0x01; + /* append EOT at the end of data burst */ + pinfo.mipi.tx_eot_append = 0x01; + + ret = mipi_nt35510_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WVGA_PT); + + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_nt35510_wvga_pt_init); diff --git a/drivers/video/msm/mipi_chimei_wuxga.c b/drivers/video/msm/mipi_chimei_wuxga.c new file mode 100644 index 0000000000000000000000000000000000000000..3645d61d08673ae4bae4444acb4d2fb77efb60d2 --- /dev/null +++ b/drivers/video/msm/mipi_chimei_wuxga.c @@ -0,0 +1,197 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Chimei WUXGA LVDS Panel driver. + * The panel model is N101JSF-L21. + * + * The panel interface includes: + * 1. LVDS input for video (clock & data). + * 2. few configuration pins to control 3D module: Enable, Mode (2D/3D). + * 3. Backlight LED control (PWM 200 HZ). + * + * This panel is controled via the Toshiba DSI-to-LVDS bridge. + * + */ + +/* #define DEBUG 1 */ + +#include "msm_fb.h" +#include "msm_fb_panel.h" +#include "mipi_dsi.h" +#include "mipi_tc358764_dsi2lvds.h" + +#define MHZ (1000*1000) + +/** + * Panel info parameters. + * The panel info is passed to the mipi framebuffer driver. + */ +static struct msm_panel_info chimei_wuxga_pinfo; + +/** + * The mipi_dsi_phy_ctrl is calculated according to the + * "dsi_timing_program.xlsm" excel sheet. + * Output is based on: 1200x1920, RGB565, 4 lanes , 58 frames + * per second. + */ +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* DSIPHY_REGULATOR_CTRL */ + .regulator = {0x03, 0x0a, 0x04, 0x00, 0x20}, /* common 8960 */ + /* DSIPHY_CTRL */ + .ctrl = {0x5f, 0x00, 0x00, 0x10}, /* common 8960 */ + /* DSIPHY_STRENGTH_CTRL */ + .strength = {0xff, 0x00, 0x06, 0x00}, /* common 8960 */ + /* DSIPHY_TIMING_CTRL */ + .timing = { 0xC9, 0x92, 0x29, /* panel specific */ + 0, /* DSIPHY_TIMING_CTRL_3 = 0 */ + 0x2D, 0x9B, 0x2B, 0x94, 0x2D, 0x03, 0x04}, /* panel specific */ + + /* DSIPHY_PLL_CTRL */ + .pll = { 0x00, /* common 8960 */ + /* VCO */ + 0x30, (0x01 | 0x30) , (0x19 | 0xC0), /* panel specific */ + 0x00, 0x50, 0x48, 0x63, + 0x77, 0x88, 0x99, /* Auto update by dsi-mipi driver */ + 0x00, 0x14, 0x03, 0x00, 0x02, /* common 8960 */ + 0x00, 0x20, 0x00, 0x01 }, /* common 8960 */ +}; + +/** + * Module init. + * + * Register the panel-info. + * + * Some parameters are from the panel datasheet + * and other are *calculated* by the "dsi_timing_program.xlsm" + * excel file + * + * @return int + */ +static int __init mipi_chimei_wuxga_init(void) +{ + int ret; + struct msm_panel_info *pinfo = &chimei_wuxga_pinfo; + + if (msm_fb_detect_client("mipi_video_chimei_wuxga")) + return 0; + + pr_info("mipi-dsi chimei wuxga (1200x1920) driver ver 1.0.\n"); + + /* Portrait */ + pinfo->xres = 1200; + pinfo->yres = 1920; + pinfo->type = MIPI_VIDEO_PANEL; + pinfo->pdest = DISPLAY_1; /* Primary Display */ + pinfo->wait_cycle = 0; + pinfo->bpp = 24; /* RGB565 requires 24 bits-per-pixel :-O */ + pinfo->fb_num = 2; /* using two frame buffers */ + + /* + * The CMI panel requires 80 MHZ LVDS-CLK. + * The D2L bridge drives the LVDS-CLK from the DSI-CLK. + * The DSI-CLK = bitclk/2, 640 MHZ/2= 320 MHZ. + * LVDS-CLK = DSI-CLK/4 , 320 MHZ/4= 80 MHZ. + */ + + pinfo->clk_rate = 635 * MHZ ; /* bitclk Calculated */ + + /* + * this panel is operated by DE, + * vsycn and hsync are ignored + */ + + pinfo->lcdc.h_front_porch = 160-48-32; /* thfp */ + pinfo->lcdc.h_back_porch = 48; /* thb */ + pinfo->lcdc.h_pulse_width = 32; /* thpw */ + + pinfo->lcdc.v_front_porch = 26-3-6; /* tvfp */ + pinfo->lcdc.v_back_porch = 3; /* tvb */ + pinfo->lcdc.v_pulse_width = 6; /* tvpw */ + + pinfo->lcdc.border_clr = 0; /* black */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + + pinfo->lcdc.hsync_skew = 0; + + /* Backlight levels - controled via PMIC pwm gpio */ + pinfo->bl_max = PWM_LEVEL; + pinfo->bl_min = 1; + + /* mipi - general */ + pinfo->mipi.vc = 0; /* virtual channel */ + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo->mipi.tx_eot_append = true; + pinfo->mipi.t_clk_post = 34; /* Calculated */ + pinfo->mipi.t_clk_pre = 69; /* Calculated */ + + pinfo->mipi.dsi_phy_db = &dsi_video_mode_phy_db; + + /* Four lanes are recomended for 1920x1200 at 60 frames per second */ + pinfo->mipi.frame_rate = 60; + pinfo->mipi.data_lane0 = true; + pinfo->mipi.data_lane1 = true; + pinfo->mipi.data_lane2 = true; + pinfo->mipi.data_lane3 = true; + pinfo->mipi.esc_byte_ratio = 6; + + pinfo->mipi.mode = DSI_VIDEO_MODE; + /* + * Note: The CMI panel input is RGB888, + * thus the DSI-to-LVDS bridge output is RGB888. + * This parameter selects the DSI-Core output to the bridge. + */ + pinfo->mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB565; + + /* mipi - video mode */ + pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT; + pinfo->mipi.pulse_mode_hsa_he = false; /* sync mode */ + + pinfo->mipi.hfp_power_stop = false; + pinfo->mipi.hbp_power_stop = false; + pinfo->mipi.hsa_power_stop = false; + pinfo->mipi.eof_bllp_power_stop = false; + pinfo->mipi.bllp_power_stop = false; + + /* mipi - command mode */ + pinfo->mipi.te_sel = 1; /* TE from vsycn gpio */ + pinfo->mipi.interleave_max = 1; + /* The bridge supports only Generic Read/Write commands */ + pinfo->mipi.insert_dcs_cmd = false; + pinfo->mipi.wr_mem_continue = 0; + pinfo->mipi.wr_mem_start = 0; + pinfo->mipi.stream = false; /* dma_p */ + pinfo->mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE; + pinfo->mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + /* + * toshiba d2l chip does not need max_pkt_size dcs cmd + * client reply len is directly configure through + * RDPKTLN register (0x0404) + */ + pinfo->mipi.no_max_pkt_size = 1; + pinfo->mipi.force_clk_lane_hs = 1; + + pinfo->is_3d_panel = FB_TYPE_3D_PANEL; + + ret = mipi_tc358764_dsi2lvds_register(pinfo, MIPI_DSI_PRIM, 1); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_chimei_wuxga_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Chimei WUXGA LVDS Panel driver"); +MODULE_AUTHOR("Amir Samuelov "); diff --git a/drivers/video/msm/mipi_chimei_wxga_pt.c b/drivers/video/msm/mipi_chimei_wxga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..8b1b828ce204202f97ac0de9a9db9e19a866b55e --- /dev/null +++ b/drivers/video/msm/mipi_chimei_wxga_pt.c @@ -0,0 +1,192 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Chimei WXGA LVDS Panel driver. + * The panel model is N101BCG-L21. + * + * The panel interface includes: + * 1. LVDS input for video (clock & data). + * 2. few configuration pins: Up/Down scan, Left/Right scan etc. + * 3. Backlight LED control. + * 4. I2C interface for EEPROM access. + * + * The Panel is *internally* controlled by Novatek NT51009 controller. + * However, the "3-wire" SPI interface is not exposed on the panel interface. + * + * This panel is controled via the Toshiba DSI-to-LVDS bridge. + * + */ + +/* #define DEBUG 1 */ + +#include "msm_fb.h" +#include "msm_fb_panel.h" +#include "mipi_dsi.h" +#include "mipi_tc358764_dsi2lvds.h" + +#define MHZ (1000*1000) + +/** + * Panel info parameters. + * The panel info is passed to the mipi framebuffer driver. + */ +static struct msm_panel_info chimei_wxga_pinfo; + +/** + * The mipi_dsi_phy_ctrl is calculated according to the + * "DSI_panel_bring_up_guide_ver3.docm" using the excel sheet. + * Output is based on: 1366x768, RGB888, 4 lanes , 60 frames per second. + */ +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* DSIPHY_REGULATOR_CTRL */ + .regulator = {0x03, 0x0a, 0x04, 0x00, 0x20}, /* common 8960 */ + /* DSIPHY_CTRL */ + .ctrl = {0x5f, 0x00, 0x00, 0x10}, /* common 8960 */ + /* DSIPHY_STRENGTH_CTRL */ + .strength = {0xff, 0x00, 0x06, 0x00}, /* common 8960 */ + /* DSIPHY_TIMING_CTRL */ + .timing = { 0xB6, 0x8D, 0x1E, /* panel specific */ + 0, /* DSIPHY_TIMING_CTRL_3 = 0 */ + 0x21, 0x95, 0x21, 0x8F, 0x21, 0x03, 0x04}, /* panel specific */ + + /* DSIPHY_PLL_CTRL */ + .pll = { 0x00, /* common 8960 */ + /* VCO */ + 0xC6, 0x01, 0x19, /* panel specific */ + 0x00, 0x50, 0x48, 0x63, + 0x77, 0x88, 0x99, /* Auto update by dsi-mipi driver */ + 0x00, 0x14, 0x03, 0x00, 0x02, /* common 8960 */ + 0x00, 0x20, 0x00, 0x01 }, /* common 8960 */ +}; + +/** + * Module init. + * + * Register the panel-info. + * + * Some parameters are from the panel datasheet + * and other are *calculated* according to the + * "DSI_panel_bring_up_guide_ver3.docm". + * + * @return int + */ +static int __init mipi_chimei_wxga_init(void) +{ + int ret; + struct msm_panel_info *pinfo = &chimei_wxga_pinfo; + + if (msm_fb_detect_client("mipi_video_chimei_wxga")) + return 0; + + pr_debug("mipi-dsi chimei wxga (1366x768) driver ver 1.0.\n"); + /* Landscape */ + pinfo->xres = 1366; + pinfo->yres = 768; + pinfo->type = MIPI_VIDEO_PANEL; + pinfo->pdest = DISPLAY_1; /* Primary Display */ + pinfo->wait_cycle = 0; + pinfo->bpp = 24; /* RGB888 = 24 bits-per-pixel */ + pinfo->fb_num = 2; /* using two frame buffers */ + + /* bitclk */ + pinfo->clk_rate = 473400000; /* 473.4 MHZ Calculated */ + + /* + * this panel is operated by DE, + * vsycn and hsync are ignored + */ + + pinfo->lcdc.h_front_porch = 96+2;/* thfp */ + pinfo->lcdc.h_back_porch = 88; /* thb */ + pinfo->lcdc.h_pulse_width = 40; /* thpw */ + + pinfo->lcdc.v_front_porch = 15; /* tvfp */ + pinfo->lcdc.v_back_porch = 23; /* tvb */ + pinfo->lcdc.v_pulse_width = 20; /* tvpw */ + + pinfo->lcdc.border_clr = 0; /* black */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + + pinfo->lcdc.hsync_skew = 0; + + /* Backlight levels - controled via PMIC pwm gpio */ + pinfo->bl_max = PWM_LEVEL; + pinfo->bl_min = 1; + + /* mipi - general */ + pinfo->mipi.vc = 0; /* virtual channel */ + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo->mipi.tx_eot_append = true; + pinfo->mipi.t_clk_post = 34; /* Calculated */ + pinfo->mipi.t_clk_pre = 64; /* Calculated */ + pinfo->mipi.esc_byte_ratio = 4; + + pinfo->mipi.dsi_phy_db = &dsi_video_mode_phy_db; + + /* Four lanes are recomended for 1366x768 at 60 frames per second */ + pinfo->mipi.frame_rate = 60; /* 60 frames per second */ + pinfo->mipi.data_lane0 = true; + pinfo->mipi.data_lane1 = true; + pinfo->mipi.data_lane2 = true; + pinfo->mipi.data_lane3 = true; + + pinfo->mipi.mode = DSI_VIDEO_MODE; + /* + * Note: The CMI panel input is RGB888, + * thus the DSI-to-LVDS bridge output is RGB888. + * This parameter selects the DSI-Core output to the bridge. + */ + pinfo->mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + + /* mipi - video mode */ + pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT; + pinfo->mipi.pulse_mode_hsa_he = false; /* sync mode */ + + pinfo->mipi.hfp_power_stop = false; + pinfo->mipi.hbp_power_stop = false; + pinfo->mipi.hsa_power_stop = false; + pinfo->mipi.eof_bllp_power_stop = false; + pinfo->mipi.bllp_power_stop = false; + + /* mipi - command mode */ + pinfo->mipi.te_sel = 1; /* TE from vsycn gpio */ + pinfo->mipi.interleave_max = 1; + /* The bridge supports only Generic Read/Write commands */ + pinfo->mipi.insert_dcs_cmd = false; + pinfo->mipi.wr_mem_continue = 0; + pinfo->mipi.wr_mem_start = 0; + pinfo->mipi.stream = false; /* dma_p */ + pinfo->mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE; + pinfo->mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + /* + * toshiba d2l chip does not need max_pkt_szie dcs cmd + * client reply len is directly configure through + * RDPKTLN register (0x0404) + */ + pinfo->mipi.no_max_pkt_size = 1; + pinfo->mipi.force_clk_lane_hs = 1; + + ret = mipi_tc358764_dsi2lvds_register(pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WXGA); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_chimei_wxga_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Chimei WXGA LVDS Panel driver"); +MODULE_AUTHOR("Amir Samuelov "); diff --git a/drivers/video/msm/mipi_dsi.c b/drivers/video/msm/mipi_dsi.c new file mode 100644 index 0000000000000000000000000000000000000000..623f3311382c2edb7c3bd131c45bf163ce21f1e6 --- /dev/null +++ b/drivers/video/msm/mipi_dsi.c @@ -0,0 +1,639 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mdp.h" +#include "mdp4.h" + +u32 dsi_irq; +u32 esc_byte_ratio; + +static boolean tlmm_settings = FALSE; + +static int mipi_dsi_probe(struct platform_device *pdev); +static int mipi_dsi_remove(struct platform_device *pdev); + +static int mipi_dsi_off(struct platform_device *pdev); +static int mipi_dsi_on(struct platform_device *pdev); + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; +static struct mipi_dsi_platform_data *mipi_dsi_pdata; + +static int vsync_gpio = -1; + +static struct platform_driver mipi_dsi_driver = { + .probe = mipi_dsi_probe, + .remove = mipi_dsi_remove, + .shutdown = NULL, + .driver = { + .name = "mipi_dsi", + }, +}; + +struct device dsi_dev; + +static int mipi_dsi_off(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_data_type *mfd; + struct msm_panel_info *pinfo; + + mfd = platform_get_drvdata(pdev); + pinfo = &mfd->panel_info; + + if (mdp_rev >= MDP_REV_41) + mutex_lock(&mfd->dma->ov_mutex); + else + down(&mfd->dma->mutex); + + mdp4_overlay_dsi_state_set(ST_DSI_SUSPEND); + + /* + * Description: dsi clock is need to perform shutdown. + * mdp4_dsi_cmd_dma_busy_wait() will enable dsi clock if disabled. + * also, wait until dma (overlay and dmap) finish. + */ + if (mfd->panel_info.type == MIPI_CMD_PANEL) { + if (mdp_rev >= MDP_REV_41) { + mdp4_dsi_cmd_dma_busy_wait(mfd); + mdp4_dsi_blt_dmap_busy_wait(mfd); + mipi_dsi_mdp_busy_wait(mfd); + } else { + mdp3_dsi_cmd_dma_busy_wait(mfd); + } + } else { + /* video mode, wait until fifo cleaned */ + mipi_dsi_controller_cfg(0); + } + + /* + * Desctiption: change to DSI_CMD_MODE since it needed to + * tx DCS dsiplay off comamnd to panel + */ + mipi_dsi_op_mode_config(DSI_CMD_MODE); + + if (mfd->panel_info.type == MIPI_CMD_PANEL) { + if (pinfo->lcd.vsync_enable) { + if (pinfo->lcd.hw_vsync_mode && vsync_gpio >= 0) { + if (MDP_REV_303 != mdp_rev) + gpio_free(vsync_gpio); + } + mipi_dsi_set_tear_off(mfd); + } + } + + ret = panel_next_off(pdev); + +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(0); +#endif + + local_bh_disable(); + mipi_dsi_clk_disable(); + local_bh_enable(); + + /* disbale dsi engine */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0000, 0); + + mipi_dsi_phy_ctrl(0); + + + local_bh_disable(); + mipi_dsi_ahb_ctrl(0); + local_bh_enable(); + + mipi_dsi_unprepare_clocks(); + if (mipi_dsi_pdata && mipi_dsi_pdata->dsi_power_save) + mipi_dsi_pdata->dsi_power_save(0); + + if (mdp_rev >= MDP_REV_41) + mutex_unlock(&mfd->dma->ov_mutex); + else + up(&mfd->dma->mutex); + + pr_debug("%s-:\n", __func__); + + return ret; +} + +static int mipi_dsi_on(struct platform_device *pdev) +{ + int ret = 0; + u32 clk_rate; + struct msm_fb_data_type *mfd; + struct fb_info *fbi; + struct fb_var_screeninfo *var; + struct msm_panel_info *pinfo; + struct mipi_panel_info *mipi; + u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height; + u32 ystride, bpp, data; + u32 dummy_xres, dummy_yres; + int target_type = 0; + + mfd = platform_get_drvdata(pdev); + fbi = mfd->fbi; + var = &fbi->var; + pinfo = &mfd->panel_info; + esc_byte_ratio = pinfo->mipi.esc_byte_ratio; + + if (mipi_dsi_pdata && mipi_dsi_pdata->dsi_power_save) + mipi_dsi_pdata->dsi_power_save(1); + + cont_splash_clk_ctrl(0); + mipi_dsi_prepare_clocks(); + + local_bh_disable(); + mipi_dsi_ahb_ctrl(1); + local_bh_enable(); + + clk_rate = mfd->fbi->var.pixclock; + clk_rate = min(clk_rate, mfd->panel_info.clk_max); + + mipi_dsi_phy_ctrl(1); + + if (mdp_rev == MDP_REV_42 && mipi_dsi_pdata) + target_type = mipi_dsi_pdata->target_type; + + mipi_dsi_phy_init(0, &(mfd->panel_info), target_type); + + local_bh_disable(); + mipi_dsi_clk_enable(); + local_bh_enable(); + + MIPI_OUTP(MIPI_DSI_BASE + 0x114, 1); + MIPI_OUTP(MIPI_DSI_BASE + 0x114, 0); + + hbp = var->left_margin; + hfp = var->right_margin; + vbp = var->upper_margin; + vfp = var->lower_margin; + hspw = var->hsync_len; + vspw = var->vsync_len; + width = mfd->panel_info.xres; + height = mfd->panel_info.yres; + + mipi = &mfd->panel_info.mipi; + if (mfd->panel_info.type == MIPI_VIDEO_PANEL) { + dummy_xres = mfd->panel_info.lcdc.xres_pad; + dummy_yres = mfd->panel_info.lcdc.yres_pad; + + if (mdp_rev >= MDP_REV_41) { + MIPI_OUTP(MIPI_DSI_BASE + 0x20, + ((hspw + hbp + width + dummy_xres) << 16 | + (hspw + hbp))); + MIPI_OUTP(MIPI_DSI_BASE + 0x24, + ((vspw + vbp + height + dummy_yres) << 16 | + (vspw + vbp))); + MIPI_OUTP(MIPI_DSI_BASE + 0x28, + (vspw + vbp + height + dummy_yres + + vfp - 1) << 16 | (hspw + hbp + + width + dummy_xres + hfp - 1)); + } else { + /* DSI_LAN_SWAP_CTRL */ + MIPI_OUTP(MIPI_DSI_BASE + 0x00ac, mipi->dlane_swap); + + MIPI_OUTP(MIPI_DSI_BASE + 0x20, + ((hbp + width + dummy_xres) << 16 | (hbp))); + MIPI_OUTP(MIPI_DSI_BASE + 0x24, + ((vbp + height + dummy_yres) << 16 | (vbp))); + MIPI_OUTP(MIPI_DSI_BASE + 0x28, + (vbp + height + dummy_yres + vfp) << 16 | + (hbp + width + dummy_xres + hfp)); + } + + MIPI_OUTP(MIPI_DSI_BASE + 0x2c, (hspw << 16)); + MIPI_OUTP(MIPI_DSI_BASE + 0x30, 0); + MIPI_OUTP(MIPI_DSI_BASE + 0x34, (vspw << 16)); + + } else { /* command mode */ + if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888) + bpp = 3; + else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB666) + bpp = 3; + else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565) + bpp = 2; + else + bpp = 3; /* Default format set to RGB888 */ + + ystride = width * bpp + 1; + + /* DSI_COMMAND_MODE_MDP_STREAM_CTRL */ + data = (ystride << 16) | (mipi->vc << 8) | DTYPE_DCS_LWRITE; + MIPI_OUTP(MIPI_DSI_BASE + 0x5c, data); + MIPI_OUTP(MIPI_DSI_BASE + 0x54, data); + + /* DSI_COMMAND_MODE_MDP_STREAM_TOTAL */ + data = height << 16 | width; + MIPI_OUTP(MIPI_DSI_BASE + 0x60, data); + MIPI_OUTP(MIPI_DSI_BASE + 0x58, data); + } + + mipi_dsi_host_init(mipi); + + if (mipi->force_clk_lane_hs) { + u32 tmp; + + tmp = MIPI_INP(MIPI_DSI_BASE + 0xA8); + tmp |= (1<<28); + MIPI_OUTP(MIPI_DSI_BASE + 0xA8, tmp); + wmb(); + } + + if (mdp_rev >= MDP_REV_41) + mutex_lock(&mfd->dma->ov_mutex); + else + down(&mfd->dma->mutex); + + ret = panel_next_on(pdev); + + mipi_dsi_op_mode_config(mipi->mode); + + if (mfd->panel_info.type == MIPI_CMD_PANEL) { + if (pinfo->lcd.vsync_enable) { + if (pinfo->lcd.hw_vsync_mode && vsync_gpio >= 0) { + if (mdp_rev >= MDP_REV_41) { + if (gpio_request(vsync_gpio, + "MDP_VSYNC") == 0) + gpio_direction_input( + vsync_gpio); + else + pr_err("%s: unable to \ + request gpio=%d\n", + __func__, vsync_gpio); + } else if (mdp_rev == MDP_REV_303) { + if (!tlmm_settings && gpio_request( + vsync_gpio, "MDP_VSYNC") == 0) { + ret = gpio_tlmm_config( + GPIO_CFG( + vsync_gpio, 1, + GPIO_CFG_INPUT, + GPIO_CFG_PULL_DOWN, + GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + + if (ret) { + pr_err( + "%s: unable to config \ + tlmm = %d\n", + __func__, vsync_gpio); + } + tlmm_settings = TRUE; + + gpio_direction_input( + vsync_gpio); + } else { + if (!tlmm_settings) { + pr_err( + "%s: unable to request \ + gpio=%d\n", + __func__, vsync_gpio); + } + } + } + } + mipi_dsi_set_tear_on(mfd); + } + } + +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(2); +#endif + + mdp4_overlay_dsi_state_set(ST_DSI_RESUME); + + if (mdp_rev >= MDP_REV_41) + mutex_unlock(&mfd->dma->ov_mutex); + else + up(&mfd->dma->mutex); + + pr_debug("%s-:\n", __func__); + + return ret; +} + + +static int mipi_dsi_resource_initialized; + +static int mipi_dsi_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct fb_info *fbi; + struct msm_panel_info *pinfo; + struct mipi_panel_info *mipi; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc; + uint8 lanes = 0, bpp; + uint32 h_period, v_period, dsi_pclk_rate; + + resource_size_t size ; + + if ((pdev->id == 1) && (pdev->num_resources >= 0)) { + mipi_dsi_pdata = pdev->dev.platform_data; + + size = resource_size(&pdev->resource[0]); + mipi_dsi_base = ioremap(pdev->resource[0].start, size); + + MSM_FB_INFO("mipi_dsi base phy_addr = 0x%x virt = 0x%x\n", + pdev->resource[0].start, (int) mipi_dsi_base); + + if (!mipi_dsi_base) + return -ENOMEM; + + if (mdp_rev >= MDP_REV_41) { + mmss_sfpb_base = ioremap(MMSS_SFPB_BASE_PHY, 0x100); + MSM_FB_INFO("mmss_sfpb base phy_addr = 0x%x," + "virt = 0x%x\n", MMSS_SFPB_BASE_PHY, + (int) mmss_sfpb_base); + + if (!mmss_sfpb_base) + return -ENOMEM; + } + + dsi_irq = platform_get_irq(pdev, 0); + if (dsi_irq < 0) { + pr_err("mipi_dsi: can not get mdp irq\n"); + return -ENOMEM; + } + + rc = request_irq(dsi_irq, mipi_dsi_isr, IRQF_DISABLED, + "MIPI_DSI", 0); + if (rc) { + pr_err("mipi_dsi_host request_irq() failed!\n"); + return rc; + } + + disable_irq(dsi_irq); + + if (mdp_rev == MDP_REV_42 && mipi_dsi_pdata && + mipi_dsi_pdata->target_type == 1) { + /* Target type is 1 for device with (De)serializer + * 0x4f00000 is the base for TV Encoder. + * Unused Offset 0x1000 is used for + * (de)serializer on emulation platform + */ + periph_base = ioremap(MMSS_SERDES_BASE_PHY, 0x100); + + if (periph_base) { + pr_debug("periph_base %p\n", periph_base); + writel(0x4, periph_base + 0x28); + writel(0xc, periph_base + 0x28); + } else { + pr_err("periph_base is NULL\n"); + free_irq(dsi_irq, 0); + return -ENOMEM; + } + } + + if (mipi_dsi_pdata) { + vsync_gpio = mipi_dsi_pdata->vsync_gpio; + pr_debug("%s: vsync_gpio=%d\n", __func__, vsync_gpio); + + if (mdp_rev == MDP_REV_303 && + mipi_dsi_pdata->dsi_client_reset) { + if (mipi_dsi_pdata->dsi_client_reset()) + pr_err("%s: DSI Client Reset failed!\n", + __func__); + else + pr_debug("%s: DSI Client Reset success\n", + __func__); + } + } + + if (mipi_dsi_clk_init(pdev)) + return -EPERM; + + if (mipi_dsi_pdata->splash_is_enabled && + !mipi_dsi_pdata->splash_is_enabled()) { + mipi_dsi_ahb_ctrl(1); + MIPI_OUTP(MIPI_DSI_BASE + 0x118, 0); + MIPI_OUTP(MIPI_DSI_BASE + 0x0, 0); + MIPI_OUTP(MIPI_DSI_BASE + 0x200, 0); + mipi_dsi_ahb_ctrl(0); + } + mipi_dsi_resource_initialized = 1; + + return 0; + } + + if (!mipi_dsi_resource_initialized) + return -EPERM; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + if (!mfd->cont_splash_done) + cont_splash_clk_ctrl(1); + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + + /* + * link to the latest pdev + */ + mfd->pdev = mdp_dev; + + /* + * alloc panel device data + */ + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + pr_err("mipi_dsi_probe: platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + /* + * data chain + */ + pdata = mdp_dev->dev.platform_data; + pdata->on = mipi_dsi_on; + pdata->off = mipi_dsi_off; + pdata->next = pdev; + + /* + * get/set panel specific fb info + */ + mfd->panel_info = pdata->panel_info; + pinfo = &mfd->panel_info; + + if (mfd->panel_info.type == MIPI_VIDEO_PANEL) + mfd->dest = DISPLAY_LCDC; + else + mfd->dest = DISPLAY_LCD; + + if (mdp_rev == MDP_REV_303 && + mipi_dsi_pdata->get_lane_config) { + if (mipi_dsi_pdata->get_lane_config() != 2) { + pr_info("Changing to DSI Single Mode Configuration\n"); +#ifdef CONFIG_FB_MSM_MDP303 + update_lane_config(pinfo); +#endif + } + } + + if (mfd->index == 0) + mfd->fb_imgType = MSMFB_DEFAULT_TYPE; + else + mfd->fb_imgType = MDP_RGB_565; + + fbi = mfd->fbi; + fbi->var.pixclock = mfd->panel_info.clk_rate; + fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch; + fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch; + fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch; + fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch; + fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width; + fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width; + + h_period = ((mfd->panel_info.lcdc.h_pulse_width) + + (mfd->panel_info.lcdc.h_back_porch) + + (mfd->panel_info.xres) + + (mfd->panel_info.lcdc.h_front_porch)); + + v_period = ((mfd->panel_info.lcdc.v_pulse_width) + + (mfd->panel_info.lcdc.v_back_porch) + + (mfd->panel_info.yres) + + (mfd->panel_info.lcdc.v_front_porch)); + + mipi = &mfd->panel_info.mipi; + + if (mipi->data_lane3) + lanes += 1; + if (mipi->data_lane2) + lanes += 1; + if (mipi->data_lane1) + lanes += 1; + if (mipi->data_lane0) + lanes += 1; + + if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB888) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB666_LOOSE)) + bpp = 3; + else if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB565)) + bpp = 2; + else + bpp = 3; /* Default format set to RGB888 */ + + if (mfd->panel_info.type == MIPI_VIDEO_PANEL && + !mfd->panel_info.clk_rate) { + h_period += mfd->panel_info.lcdc.xres_pad; + v_period += mfd->panel_info.lcdc.yres_pad; + + if (lanes > 0) { + mfd->panel_info.clk_rate = + ((h_period * v_period * (mipi->frame_rate) * bpp * 8) + / lanes); + } else { + pr_err("%s: forcing mipi_dsi lanes to 1\n", __func__); + mfd->panel_info.clk_rate = + (h_period * v_period + * (mipi->frame_rate) * bpp * 8); + } + } + pll_divider_config.clk_rate = mfd->panel_info.clk_rate; + + rc = mipi_dsi_clk_div_config(bpp, lanes, &dsi_pclk_rate); + if (rc) + goto mipi_dsi_probe_err; + + if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 103300000)) + dsi_pclk_rate = 35000000; + mipi->dsi_pclk_rate = dsi_pclk_rate; + + /* + * set driver data + */ + platform_set_drvdata(mdp_dev, mfd); + + /* + * register in mdp driver + */ + rc = platform_device_add(mdp_dev); + if (rc) + goto mipi_dsi_probe_err; + + pdev_list[pdev_list_cnt++] = pdev; + +return 0; + +mipi_dsi_probe_err: + platform_device_put(mdp_dev); + return rc; +} + +static int mipi_dsi_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + iounmap(mipi_dsi_base); + return 0; +} + +static int mipi_dsi_register_driver(void) +{ + return platform_driver_register(&mipi_dsi_driver); +} + +static int __init mipi_dsi_driver_init(void) +{ + int ret; + + mipi_dsi_init(); + + ret = mipi_dsi_register_driver(); + + device_initialize(&dsi_dev); + + if (ret) { + pr_err("mipi_dsi_register_driver() failed!\n"); + return ret; + } + + return ret; +} + +module_init(mipi_dsi_driver_init); diff --git a/drivers/video/msm/mipi_dsi.h b/drivers/video/msm/mipi_dsi.h new file mode 100644 index 0000000000000000000000000000000000000000..3433d4391f60cd0195898a6235ad11a51e653bfc --- /dev/null +++ b/drivers/video/msm/mipi_dsi.h @@ -0,0 +1,318 @@ +/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MIPI_DSI_H +#define MIPI_DSI_H + +#include +#include + +#ifdef BIT +#undef BIT +#endif + +#define BIT(x) (1<<(x)) + +#define MMSS_CC_BASE_PHY 0x04000000 /* mmss clcok control */ +#define MMSS_SFPB_BASE_PHY 0x05700000 /* mmss SFPB CFG */ +#define MMSS_SERDES_BASE_PHY 0x04f01000 /* mmss (De)Serializer CFG */ + +#define MIPI_DSI_BASE mipi_dsi_base + +#define MIPI_OUTP(addr, data) writel((data), (addr)) +#define MIPI_INP(addr) readl(addr) + +#ifdef CONFIG_MSM_SECURE_IO +#define MIPI_OUTP_SECURE(addr, data) secure_writel((data), (addr)) +#define MIPI_INP_SECURE(addr) secure_readl(addr) +#else +#define MIPI_OUTP_SECURE(addr, data) writel((data), (addr)) +#define MIPI_INP_SECURE(addr) readl(addr) +#endif + +#define MIPI_DSI_PRIM 1 +#define MIPI_DSI_SECD 2 + +#define MIPI_DSI_PANEL_VGA 0 +#define MIPI_DSI_PANEL_WVGA 1 +#define MIPI_DSI_PANEL_WVGA_PT 2 +#define MIPI_DSI_PANEL_FWVGA_PT 3 +#define MIPI_DSI_PANEL_WSVGA_PT 4 +#define MIPI_DSI_PANEL_QHD_PT 5 +#define MIPI_DSI_PANEL_WXGA 6 +#define MIPI_DSI_PANEL_WUXGA 7 +#define MIPI_DSI_PANEL_720P_PT 8 +#define DSI_PANEL_MAX 8 + +enum { /* mipi dsi panel */ + DSI_VIDEO_MODE, + DSI_CMD_MODE, +}; + +enum { + ST_DSI_CLK_OFF, + ST_DSI_SUSPEND, + ST_DSI_RESUME, + ST_DSI_PLAYING, + ST_DSI_NUM +}; + +enum { + EV_DSI_UPDATE, + EV_DSI_DONE, + EV_DSI_TOUT, + EV_DSI_NUM +}; + +enum { + LANDSCAPE = 1, + PORTRAIT = 2, +}; + +enum dsi_trigger_type { + DSI_CMD_MODE_DMA, + DSI_CMD_MODE_MDP, +}; + +#define DSI_NON_BURST_SYNCH_PULSE 0 +#define DSI_NON_BURST_SYNCH_EVENT 1 +#define DSI_BURST_MODE 2 + + +#define DSI_RGB_SWAP_RGB 0 +#define DSI_RGB_SWAP_RBG 1 +#define DSI_RGB_SWAP_BGR 2 +#define DSI_RGB_SWAP_BRG 3 +#define DSI_RGB_SWAP_GRB 4 +#define DSI_RGB_SWAP_GBR 5 + +#define DSI_VIDEO_DST_FORMAT_RGB565 0 +#define DSI_VIDEO_DST_FORMAT_RGB666 1 +#define DSI_VIDEO_DST_FORMAT_RGB666_LOOSE 2 +#define DSI_VIDEO_DST_FORMAT_RGB888 3 + +#define DSI_CMD_DST_FORMAT_RGB111 0 +#define DSI_CMD_DST_FORMAT_RGB332 3 +#define DSI_CMD_DST_FORMAT_RGB444 4 +#define DSI_CMD_DST_FORMAT_RGB565 6 +#define DSI_CMD_DST_FORMAT_RGB666 7 +#define DSI_CMD_DST_FORMAT_RGB888 8 + +#define DSI_INTR_ERROR_MASK BIT(25) +#define DSI_INTR_ERROR BIT(24) +#define DSI_INTR_VIDEO_DONE_MASK BIT(17) +#define DSI_INTR_VIDEO_DONE BIT(16) +#define DSI_INTR_CMD_MDP_DONE_MASK BIT(9) +#define DSI_INTR_CMD_MDP_DONE BIT(8) +#define DSI_INTR_CMD_DMA_DONE_MASK BIT(1) +#define DSI_INTR_CMD_DMA_DONE BIT(0) + +#define DSI_CMD_TRIGGER_NONE 0x0 /* mdp trigger */ +#define DSI_CMD_TRIGGER_TE 0x02 +#define DSI_CMD_TRIGGER_SW 0x04 +#define DSI_CMD_TRIGGER_SW_SEOF 0x05 /* cmd dma only */ +#define DSI_CMD_TRIGGER_SW_TE 0x06 + +extern struct device dsi_dev; +extern int mipi_dsi_clk_on; +extern u32 dsi_irq; +extern u32 esc_byte_ratio; + +extern void __iomem *periph_base; +extern char *mmss_cc_base; /* mutimedia sub system clock control */ +extern char *mmss_sfpb_base; /* mutimedia sub system sfpb */ + +struct dsiphy_pll_divider_config { + u32 clk_rate; + u32 fb_divider; + u32 ref_divider_ratio; + u32 bit_clk_divider; /* oCLK1 */ + u32 byte_clk_divider; /* oCLK2 */ + u32 dsi_clk_divider; /* oCLK3 */ +}; + +extern struct dsiphy_pll_divider_config pll_divider_config; + +struct dsi_clk_mnd_table { + uint8 lanes; + uint8 bpp; + uint8 dsiclk_div; + uint8 dsiclk_m; + uint8 dsiclk_n; + uint8 dsiclk_d; + uint8 pclk_m; + uint8 pclk_n; + uint8 pclk_d; +}; + +static const struct dsi_clk_mnd_table mnd_table[] = { + { 1, 2, 8, 1, 1, 0, 1, 2, 1}, + { 1, 3, 8, 1, 1, 0, 1, 3, 2}, + { 2, 2, 4, 1, 1, 0, 1, 2, 1}, + { 2, 3, 4, 1, 1, 0, 1, 3, 2}, + { 3, 2, 1, 3, 8, 4, 3, 16, 8}, + { 3, 3, 1, 3, 8, 4, 1, 8, 4}, + { 4, 2, 2, 1, 1, 0, 1, 2, 1}, + { 4, 3, 2, 1, 1, 0, 1, 3, 2}, +}; + +struct dsi_clk_desc { + uint32 src; + uint32 m; + uint32 n; + uint32 d; + uint32 mnd_mode; + uint32 pre_div_func; +}; + +#define DSI_HOST_HDR_SIZE 4 +#define DSI_HDR_LAST BIT(31) +#define DSI_HDR_LONG_PKT BIT(30) +#define DSI_HDR_BTA BIT(29) +#define DSI_HDR_VC(vc) (((vc) & 0x03) << 22) +#define DSI_HDR_DTYPE(dtype) (((dtype) & 0x03f) << 16) +#define DSI_HDR_DATA2(data) (((data) & 0x0ff) << 8) +#define DSI_HDR_DATA1(data) ((data) & 0x0ff) +#define DSI_HDR_WC(wc) ((wc) & 0x0ffff) + +#define DSI_BUF_SIZE 1024 +#define MIPI_DSI_MRPS 0x04 /* Maximum Return Packet Size */ + +#define MIPI_DSI_LEN 8 /* 4 x 4 - 6 - 2, bytes dcs header+crc-align */ + +struct dsi_buf { + uint32 *hdr; /* dsi host header */ + char *start; /* buffer start addr */ + char *end; /* buffer end addr */ + int size; /* size of buffer */ + char *data; /* buffer */ + int len; /* data length */ + dma_addr_t dmap; /* mapped dma addr */ +}; + +/* dcs read/write */ +#define DTYPE_DCS_WRITE 0x05 /* short write, 0 parameter */ +#define DTYPE_DCS_WRITE1 0x15 /* short write, 1 parameter */ +#define DTYPE_DCS_READ 0x06 /* read */ +#define DTYPE_DCS_LWRITE 0x39 /* long write */ + +/* generic read/write */ +#define DTYPE_GEN_WRITE 0x03 /* short write, 0 parameter */ +#define DTYPE_GEN_WRITE1 0x13 /* short write, 1 parameter */ +#define DTYPE_GEN_WRITE2 0x23 /* short write, 2 parameter */ +#define DTYPE_GEN_LWRITE 0x29 /* long write */ +#define DTYPE_GEN_READ 0x04 /* long read, 0 parameter */ +#define DTYPE_GEN_READ1 0x14 /* long read, 1 parameter */ +#define DTYPE_GEN_READ2 0x24 /* long read, 2 parameter */ + +#define DTYPE_TEAR_ON 0x35 /* set tear on */ +#define DTYPE_MAX_PKTSIZE 0x37 /* set max packet size */ +#define DTYPE_NULL_PKT 0x09 /* null packet, no data */ +#define DTYPE_BLANK_PKT 0x19 /* blankiing packet, no data */ + +#define DTYPE_CM_ON 0x02 /* color mode off */ +#define DTYPE_CM_OFF 0x12 /* color mode on */ +#define DTYPE_PERIPHERAL_OFF 0x22 +#define DTYPE_PERIPHERAL_ON 0x32 + +/* + * dcs response + */ +#define DTYPE_ACK_ERR_RESP 0x02 +#define DTYPE_EOT_RESP 0x08 /* end of tx */ +#define DTYPE_GEN_READ1_RESP 0x11 /* 1 parameter, short */ +#define DTYPE_GEN_READ2_RESP 0x12 /* 2 parameter, short */ +#define DTYPE_GEN_LREAD_RESP 0x1a +#define DTYPE_DCS_LREAD_RESP 0x1c +#define DTYPE_DCS_READ1_RESP 0x21 /* 1 parameter, short */ +#define DTYPE_DCS_READ2_RESP 0x22 /* 2 parameter, short */ + +struct dsi_cmd_desc { + int dtype; + int last; + int vc; + int ack; /* ask ACK from peripheral */ + int wait; + int dlen; + char *payload; +}; + + +typedef void (*kickoff_act)(void *); + +struct dsi_kickoff_action { + struct list_head act_entry; + kickoff_act action; + void *data; +}; + + +char *mipi_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen); +char *mipi_dsi_buf_init(struct dsi_buf *dp); +void mipi_dsi_init(void); +void mipi_dsi_lane_cfg(void); +void mipi_dsi_bist_ctrl(void); +int mipi_dsi_buf_alloc(struct dsi_buf *, int size); +int mipi_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm); +int mipi_dsi_cmds_tx(struct msm_fb_data_type *mfd, + struct dsi_buf *dp, struct dsi_cmd_desc *cmds, int cnt); + +int mipi_dsi_cmd_dma_tx(struct dsi_buf *dp); +int mipi_dsi_cmd_reg_tx(uint32 data); +int mipi_dsi_cmds_rx(struct msm_fb_data_type *mfd, + struct dsi_buf *tp, struct dsi_buf *rp, + struct dsi_cmd_desc *cmds, int len); +int mipi_dsi_cmd_dma_rx(struct dsi_buf *tp, int rlen); +void mipi_dsi_host_init(struct mipi_panel_info *pinfo); +void mipi_dsi_op_mode_config(int mode); +void mipi_dsi_cmd_mode_ctrl(int enable); +void mdp4_dsi_cmd_trigger(void); +void mipi_dsi_cmd_mdp_start(void); +void mipi_dsi_cmd_bta_sw_trigger(void); +void mipi_dsi_ack_err_status(void); +void mipi_dsi_set_tear_on(struct msm_fb_data_type *mfd); +void mipi_dsi_set_tear_off(struct msm_fb_data_type *mfd); +void mipi_dsi_clk_enable(void); +void mipi_dsi_clk_disable(void); +void mipi_dsi_pre_kickoff_action(void); +void mipi_dsi_post_kickoff_action(void); +void mipi_dsi_pre_kickoff_add(struct dsi_kickoff_action *act); +void mipi_dsi_post_kickoff_add(struct dsi_kickoff_action *act); +void mipi_dsi_pre_kickoff_del(struct dsi_kickoff_action *act); +void mipi_dsi_post_kickoff_del(struct dsi_kickoff_action *act); +void mipi_dsi_controller_cfg(int enable); +void mipi_dsi_sw_reset(void); +void mipi_dsi_mdp_busy_wait(struct msm_fb_data_type *mfd); + +irqreturn_t mipi_dsi_isr(int irq, void *ptr); + +void mipi_set_tx_power_mode(int mode); +void mipi_dsi_phy_ctrl(int on); +void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info, + int target_type); +int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes, + uint32 *expected_dsi_pclk); +int mipi_dsi_clk_init(struct platform_device *pdev); +void mipi_dsi_clk_deinit(struct device *dev); +void mipi_dsi_prepare_clocks(void); +void mipi_dsi_unprepare_clocks(void); +void mipi_dsi_ahb_ctrl(u32 enable); +void cont_splash_clk_ctrl(int enable); +void mipi_dsi_turn_on_clks(void); +void mipi_dsi_turn_off_clks(void); + +#ifdef CONFIG_FB_MSM_MDP303 +void update_lane_config(struct msm_panel_info *pinfo); +#endif + +#endif /* MIPI_DSI_H */ diff --git a/drivers/video/msm/mipi_dsi_host.c b/drivers/video/msm/mipi_dsi_host.c new file mode 100644 index 0000000000000000000000000000000000000000..c03c40c0b4346c38b5528eb8f95c476b04b822be --- /dev/null +++ b/drivers/video/msm/mipi_dsi_host.c @@ -0,0 +1,1493 @@ + +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mdp.h" +#include "mdp4.h" + +static struct completion dsi_dma_comp; +static struct completion dsi_mdp_comp; +static struct dsi_buf dsi_tx_buf; +static int dsi_irq_enabled; +static spinlock_t dsi_irq_lock; +static spinlock_t dsi_mdp_lock; +static int dsi_mdp_busy; + +static struct list_head pre_kickoff_list; +static struct list_head post_kickoff_list; + +enum { + STAT_DSI_START, + STAT_DSI_ERROR, + STAT_DSI_CMD, + STAT_DSI_MDP +}; + +#ifdef CONFIG_FB_MSM_MDP40 +void mipi_dsi_mdp_stat_inc(int which) +{ + switch (which) { + case STAT_DSI_START: + mdp4_stat.dsi_mdp_start++; + break; + case STAT_DSI_ERROR: + mdp4_stat.intr_dsi_err++; + break; + case STAT_DSI_CMD: + mdp4_stat.intr_dsi_cmd++; + break; + case STAT_DSI_MDP: + mdp4_stat.intr_dsi_mdp++; + break; + default: + break; + } +} +#else +void mipi_dsi_mdp_stat_inc(int which) +{ +} +#endif + +void mipi_dsi_init(void) +{ + init_completion(&dsi_dma_comp); + init_completion(&dsi_mdp_comp); + mipi_dsi_buf_alloc(&dsi_tx_buf, DSI_BUF_SIZE); + spin_lock_init(&dsi_irq_lock); + spin_lock_init(&dsi_mdp_lock); + + INIT_LIST_HEAD(&pre_kickoff_list); + INIT_LIST_HEAD(&post_kickoff_list); +} + +void mipi_dsi_enable_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&dsi_irq_lock, flags); + if (dsi_irq_enabled) { + pr_debug("%s: IRQ aleady enabled\n", __func__); + spin_unlock_irqrestore(&dsi_irq_lock, flags); + return; + } + dsi_irq_enabled = 1; + enable_irq(dsi_irq); + spin_unlock_irqrestore(&dsi_irq_lock, flags); +} + +void mipi_dsi_disable_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&dsi_irq_lock, flags); + if (dsi_irq_enabled == 0) { + pr_debug("%s: IRQ already disabled\n", __func__); + spin_unlock_irqrestore(&dsi_irq_lock, flags); + return; + } + + dsi_irq_enabled = 0; + disable_irq(dsi_irq); + spin_unlock_irqrestore(&dsi_irq_lock, flags); +} + +/* + * mipi_dsi_disale_irq_nosync() should be called + * from interrupt context + */ + void mipi_dsi_disable_irq_nosync(void) +{ + spin_lock(&dsi_irq_lock); + if (dsi_irq_enabled == 0) { + pr_debug("%s: IRQ cannot be disabled\n", __func__); + spin_unlock(&dsi_irq_lock); + return; + } + + dsi_irq_enabled = 0; + disable_irq_nosync(dsi_irq); + spin_unlock(&dsi_irq_lock); +} + +void mipi_dsi_turn_on_clks(void) +{ + local_bh_disable(); + mipi_dsi_ahb_ctrl(1); + mipi_dsi_clk_enable(); + local_bh_enable(); +} + +void mipi_dsi_turn_off_clks(void) +{ + local_bh_disable(); + mipi_dsi_clk_disable(); + mipi_dsi_ahb_ctrl(0); + local_bh_enable(); +} + +static void mipi_dsi_action(struct list_head *act_list) +{ + struct list_head *lp; + struct dsi_kickoff_action *act; + + list_for_each(lp, act_list) { + act = list_entry(lp, struct dsi_kickoff_action, act_entry); + if (act && act->action) + act->action(act->data); + } +} + +void mipi_dsi_pre_kickoff_action(void) +{ + mipi_dsi_action(&pre_kickoff_list); +} + +void mipi_dsi_post_kickoff_action(void) +{ + mipi_dsi_action(&post_kickoff_list); +} + +/* + * mipi_dsi_pre_kickoff_add: + * ov_mutex need to be acquired before call this function. + */ +void mipi_dsi_pre_kickoff_add(struct dsi_kickoff_action *act) +{ + if (act) + list_add_tail(&act->act_entry, &pre_kickoff_list); +} + +/* + * mipi_dsi_pre_kickoff_add: + * ov_mutex need to be acquired before call this function. + */ +void mipi_dsi_post_kickoff_add(struct dsi_kickoff_action *act) +{ + if (act) + list_add_tail(&act->act_entry, &post_kickoff_list); +} + +/* + * mipi_dsi_pre_kickoff_add: + * ov_mutex need to be acquired before call this function. + */ +void mipi_dsi_pre_kickoff_del(struct dsi_kickoff_action *act) +{ + if (!list_empty(&pre_kickoff_list) && act) + list_del(&act->act_entry); +} + +/* + * mipi_dsi_pre_kickoff_add: + * ov_mutex need to be acquired before call this function. + */ +void mipi_dsi_post_kickoff_del(struct dsi_kickoff_action *act) +{ + if (!list_empty(&post_kickoff_list) && act) + list_del(&act->act_entry); +} + +/* + * mipi dsi buf mechanism + */ +char *mipi_dsi_buf_reserve(struct dsi_buf *dp, int len) +{ + dp->data += len; + return dp->data; +} + +char *mipi_dsi_buf_unreserve(struct dsi_buf *dp, int len) +{ + dp->data -= len; + return dp->data; +} + +char *mipi_dsi_buf_push(struct dsi_buf *dp, int len) +{ + dp->data -= len; + dp->len += len; + return dp->data; +} + +char *mipi_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen) +{ + dp->hdr = (uint32 *)dp->data; + return mipi_dsi_buf_reserve(dp, hlen); +} + +char *mipi_dsi_buf_init(struct dsi_buf *dp) +{ + int off; + + dp->data = dp->start; + off = (int)dp->data; + /* 8 byte align */ + off &= 0x07; + if (off) + off = 8 - off; + dp->data += off; + dp->len = 0; + return dp->data; +} + +int mipi_dsi_buf_alloc(struct dsi_buf *dp, int size) +{ + + dp->start = kmalloc(size, GFP_KERNEL); + if (dp->start == NULL) { + pr_err("%s:%u\n", __func__, __LINE__); + return -ENOMEM; + } + + dp->end = dp->start + size; + dp->size = size; + + if ((int)dp->start & 0x07) + pr_err("%s: buf NOT 8 bytes aligned\n", __func__); + + dp->data = dp->start; + dp->len = 0; + return size; +} + +/* + * mipi dsi gerneric long write + */ +static int mipi_dsi_generic_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + char *bp; + uint32 *hp; + int i, len; + + bp = mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + + /* fill up payload */ + if (cm->payload) { + len = cm->dlen; + len += 3; + len &= ~0x03; /* multipled by 4 */ + for (i = 0; i < cm->dlen; i++) + *bp++ = cm->payload[i]; + + /* append 0xff to the end */ + for (; i < len; i++) + *bp++ = 0xff; + + dp->len += len; + } + + /* fill up header */ + hp = dp->hdr; + *hp = 0; + *hp = DSI_HDR_WC(cm->dlen); + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_LONG_PKT; + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_LWRITE); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; +} + +/* + * mipi dsi gerneric short write with 0, 1 2 parameters + */ +static int mipi_dsi_generic_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + int len; + + if (cm->dlen && cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return 0; + } + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + if (cm->last) + *hp |= DSI_HDR_LAST; + + + len = (cm->dlen > 2) ? 2 : cm->dlen; + + if (len == 1) { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE1); + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(0); + } else if (len == 2) { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE2); + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(cm->payload[1]); + } else { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE); + *hp |= DSI_HDR_DATA1(0); + *hp |= DSI_HDR_DATA2(0); + } + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +/* + * mipi dsi gerneric read with 0, 1 2 parameters + */ +static int mipi_dsi_generic_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + int len; + + if (cm->dlen && cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return 0; + } + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_BTA; + if (cm->last) + *hp |= DSI_HDR_LAST; + + len = (cm->dlen > 2) ? 2 : cm->dlen; + + if (len == 1) { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ1); + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(0); + } else if (len == 2) { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ2); + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(cm->payload[1]); + } else { + *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ); + *hp |= DSI_HDR_DATA1(0); + *hp |= DSI_HDR_DATA2(0); + } + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + return dp->len; /* 4 bytes */ +} + +/* + * mipi dsi dcs long write + */ +static int mipi_dsi_dcs_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + char *bp; + uint32 *hp; + int i, len; + + bp = mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + + /* + * fill up payload + * dcs command byte (first byte) followed by payload + */ + if (cm->payload) { + len = cm->dlen; + len += 3; + len &= ~0x03; /* multipled by 4 */ + for (i = 0; i < cm->dlen; i++) + *bp++ = cm->payload[i]; + + /* append 0xff to the end */ + for (; i < len; i++) + *bp++ = 0xff; + + dp->len += len; + } + + /* fill up header */ + hp = dp->hdr; + *hp = 0; + *hp = DSI_HDR_WC(cm->dlen); + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_LONG_PKT; + *hp |= DSI_HDR_DTYPE(DTYPE_DCS_LWRITE); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; +} + +/* + * mipi dsi dcs short write with 0 parameters + */ +static int mipi_dsi_dcs_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + int len; + + if (cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return -EINVAL; + } + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + if (cm->ack) /* ask ACK trigger msg from peripeheral */ + *hp |= DSI_HDR_BTA; + if (cm->last) + *hp |= DSI_HDR_LAST; + + len = (cm->dlen > 1) ? 1 : cm->dlen; + + *hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE); + *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */ + *hp |= DSI_HDR_DATA2(0); + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + return dp->len; +} + +/* + * mipi dsi dcs short write with 1 parameters + */ +static int mipi_dsi_dcs_swrite1(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + if (cm->dlen < 2 || cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return -EINVAL; + } + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + if (cm->ack) /* ask ACK trigger msg from peripeheral */ + *hp |= DSI_HDR_BTA; + if (cm->last) + *hp |= DSI_HDR_LAST; + + *hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE1); + *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs comamnd byte */ + *hp |= DSI_HDR_DATA2(cm->payload[1]); /* parameter */ + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; +} +/* + * mipi dsi dcs read with 0 parameters + */ + +static int mipi_dsi_dcs_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + if (cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return -EINVAL; + } + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_BTA; + *hp |= DSI_HDR_DTYPE(DTYPE_DCS_READ); + if (cm->last) + *hp |= DSI_HDR_LAST; + + *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */ + *hp |= DSI_HDR_DATA2(0); + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mipi_dsi_cm_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_CM_ON); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mipi_dsi_cm_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_CM_OFF); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mipi_dsi_peripheral_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_ON); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mipi_dsi_peripheral_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_OFF); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mipi_dsi_set_max_pktsize(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + if (cm->payload == 0) { + pr_err("%s: NO payload error\n", __func__); + return 0; + } + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_MAX_PKTSIZE); + if (cm->last) + *hp |= DSI_HDR_LAST; + + *hp |= DSI_HDR_DATA1(cm->payload[0]); + *hp |= DSI_HDR_DATA2(cm->payload[1]); + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mipi_dsi_null_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp = DSI_HDR_WC(cm->dlen); + *hp |= DSI_HDR_LONG_PKT; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_NULL_PKT); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +static int mipi_dsi_blank_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + uint32 *hp; + + mipi_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); + hp = dp->hdr; + *hp = 0; + *hp = DSI_HDR_WC(cm->dlen); + *hp |= DSI_HDR_LONG_PKT; + *hp |= DSI_HDR_VC(cm->vc); + *hp |= DSI_HDR_DTYPE(DTYPE_BLANK_PKT); + if (cm->last) + *hp |= DSI_HDR_LAST; + + mipi_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); + + return dp->len; /* 4 bytes */ +} + +/* + * prepare cmd buffer to be txed + */ +int mipi_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm) +{ + int len = 0; + + switch (cm->dtype) { + case DTYPE_GEN_WRITE: + case DTYPE_GEN_WRITE1: + case DTYPE_GEN_WRITE2: + len = mipi_dsi_generic_swrite(dp, cm); + break; + case DTYPE_GEN_LWRITE: + len = mipi_dsi_generic_lwrite(dp, cm); + break; + case DTYPE_GEN_READ: + case DTYPE_GEN_READ1: + case DTYPE_GEN_READ2: + len = mipi_dsi_generic_read(dp, cm); + break; + case DTYPE_DCS_LWRITE: + len = mipi_dsi_dcs_lwrite(dp, cm); + break; + case DTYPE_DCS_WRITE: + len = mipi_dsi_dcs_swrite(dp, cm); + break; + case DTYPE_DCS_WRITE1: + len = mipi_dsi_dcs_swrite1(dp, cm); + break; + case DTYPE_DCS_READ: + len = mipi_dsi_dcs_read(dp, cm); + break; + case DTYPE_MAX_PKTSIZE: + len = mipi_dsi_set_max_pktsize(dp, cm); + break; + case DTYPE_NULL_PKT: + len = mipi_dsi_null_pkt(dp, cm); + break; + case DTYPE_BLANK_PKT: + len = mipi_dsi_blank_pkt(dp, cm); + break; + case DTYPE_CM_ON: + len = mipi_dsi_cm_on(dp, cm); + break; + case DTYPE_CM_OFF: + len = mipi_dsi_cm_off(dp, cm); + break; + case DTYPE_PERIPHERAL_ON: + len = mipi_dsi_peripheral_on(dp, cm); + break; + case DTYPE_PERIPHERAL_OFF: + len = mipi_dsi_peripheral_off(dp, cm); + break; + default: + pr_debug("%s: dtype=%x NOT supported\n", + __func__, cm->dtype); + break; + + } + + return len; +} + +/* + * mipi_dsi_short_read1_resp: 1 parameter + */ +static int mipi_dsi_short_read1_resp(struct dsi_buf *rp) +{ + /* strip out dcs type */ + rp->data++; + rp->len = 1; + return rp->len; +} + +/* + * mipi_dsi_short_read2_resp: 2 parameter + */ +static int mipi_dsi_short_read2_resp(struct dsi_buf *rp) +{ + /* strip out dcs type */ + rp->data++; + rp->len = 2; + return rp->len; +} + +static int mipi_dsi_long_read_resp(struct dsi_buf *rp) +{ + short len; + + len = rp->data[2]; + len <<= 8; + len |= rp->data[1]; + /* strip out dcs header */ + rp->data += 4; + rp->len -= 4; + /* strip out 2 bytes of checksum */ + rp->len -= 2; + return len; +} + +void mipi_dsi_host_init(struct mipi_panel_info *pinfo) +{ + uint32 dsi_ctrl, intr_ctrl; + uint32 data; + + if (mdp_rev > MDP_REV_41 || mdp_rev == MDP_REV_303) + pinfo->rgb_swap = DSI_RGB_SWAP_RGB; + else + pinfo->rgb_swap = DSI_RGB_SWAP_BGR; + + if (pinfo->mode == DSI_VIDEO_MODE) { + data = 0; + if (pinfo->pulse_mode_hsa_he) + data |= BIT(28); + if (pinfo->hfp_power_stop) + data |= BIT(24); + if (pinfo->hbp_power_stop) + data |= BIT(20); + if (pinfo->hsa_power_stop) + data |= BIT(16); + if (pinfo->eof_bllp_power_stop) + data |= BIT(15); + if (pinfo->bllp_power_stop) + data |= BIT(12); + data |= ((pinfo->traffic_mode & 0x03) << 8); + data |= ((pinfo->dst_format & 0x03) << 4); /* 2 bits */ + data |= (pinfo->vc & 0x03); + MIPI_OUTP(MIPI_DSI_BASE + 0x000c, data); + + data = 0; + data |= ((pinfo->rgb_swap & 0x07) << 12); + if (pinfo->b_sel) + data |= BIT(8); + if (pinfo->g_sel) + data |= BIT(4); + if (pinfo->r_sel) + data |= BIT(0); + MIPI_OUTP(MIPI_DSI_BASE + 0x001c, data); + } else if (pinfo->mode == DSI_CMD_MODE) { + data = 0; + data |= ((pinfo->interleave_max & 0x0f) << 20); + data |= ((pinfo->rgb_swap & 0x07) << 16); + if (pinfo->b_sel) + data |= BIT(12); + if (pinfo->g_sel) + data |= BIT(8); + if (pinfo->r_sel) + data |= BIT(4); + data |= (pinfo->dst_format & 0x0f); /* 4 bits */ + MIPI_OUTP(MIPI_DSI_BASE + 0x003c, data); + + /* DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL */ + data = pinfo->wr_mem_continue & 0x0ff; + data <<= 8; + data |= (pinfo->wr_mem_start & 0x0ff); + if (pinfo->insert_dcs_cmd) + data |= BIT(16); + MIPI_OUTP(MIPI_DSI_BASE + 0x0040, data); + } else + pr_err("%s: Unknown DSI mode=%d\n", __func__, pinfo->mode); + + dsi_ctrl = BIT(8) | BIT(2); /* clock enable & cmd mode */ + intr_ctrl = 0; + intr_ctrl = (DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_CMD_MDP_DONE_MASK); + + if (pinfo->crc_check) + dsi_ctrl |= BIT(24); + if (pinfo->ecc_check) + dsi_ctrl |= BIT(20); + if (pinfo->data_lane3) + dsi_ctrl |= BIT(7); + if (pinfo->data_lane2) + dsi_ctrl |= BIT(6); + if (pinfo->data_lane1) + dsi_ctrl |= BIT(5); + if (pinfo->data_lane0) + dsi_ctrl |= BIT(4); + + /* from frame buffer, low power mode */ + /* DSI_COMMAND_MODE_DMA_CTRL */ + MIPI_OUTP(MIPI_DSI_BASE + 0x38, 0x14000000); + + data = 0; + if (pinfo->te_sel) + data |= BIT(31); + data |= pinfo->mdp_trigger << 4;/* cmd mdp trigger */ + data |= pinfo->dma_trigger; /* cmd dma trigger */ + data |= (pinfo->stream & 0x01) << 8; + MIPI_OUTP(MIPI_DSI_BASE + 0x0080, data); /* DSI_TRIG_CTRL */ + + /* DSI_LAN_SWAP_CTRL */ + MIPI_OUTP(MIPI_DSI_BASE + 0x00ac, pinfo->dlane_swap); + + /* clock out ctrl */ + data = pinfo->t_clk_post & 0x3f; /* 6 bits */ + data <<= 8; + data |= pinfo->t_clk_pre & 0x3f; /* 6 bits */ + MIPI_OUTP(MIPI_DSI_BASE + 0xc0, data); /* DSI_CLKOUT_TIMING_CTRL */ + + data = 0; + if (pinfo->rx_eot_ignore) + data |= BIT(4); + if (pinfo->tx_eot_append) + data |= BIT(0); + MIPI_OUTP(MIPI_DSI_BASE + 0x00c8, data); /* DSI_EOT_PACKET_CTRL */ + + + /* allow only ack-err-status to generate interrupt */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0108, 0x13ff3fe0); /* DSI_ERR_INT_MASK0 */ + + intr_ctrl |= DSI_INTR_ERROR_MASK; + MIPI_OUTP(MIPI_DSI_BASE + 0x010c, intr_ctrl); /* DSI_INTL_CTRL */ + + /* turn esc, byte, dsi, pclk, sclk, hclk on */ + if (mdp_rev >= MDP_REV_41) + MIPI_OUTP(MIPI_DSI_BASE + 0x118, 0x23f); /* DSI_CLK_CTRL */ + else + MIPI_OUTP(MIPI_DSI_BASE + 0x118, 0x33f); /* DSI_CLK_CTRL */ + + dsi_ctrl |= BIT(0); /* enable dsi */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl); + + wmb(); +} + +void mipi_set_tx_power_mode(int mode) +{ + uint32 data = MIPI_INP(MIPI_DSI_BASE + 0x38); + + if (mode == 0) + data &= ~BIT(26); + else + data |= BIT(26); + + MIPI_OUTP(MIPI_DSI_BASE + 0x38, data); +} + +void mipi_dsi_sw_reset(void) +{ + MIPI_OUTP(MIPI_DSI_BASE + 0x114, 0x01); + wmb(); + MIPI_OUTP(MIPI_DSI_BASE + 0x114, 0x00); + wmb(); +} + +void mipi_dsi_controller_cfg(int enable) +{ + + uint32 dsi_ctrl; + uint32 status; + int cnt; + + cnt = 16; + while (cnt--) { + status = MIPI_INP(MIPI_DSI_BASE + 0x0004); + status &= 0x02; /* CMD_MODE_DMA_BUSY */ + if (status == 0) + break; + usleep(1000); + } + if (cnt == 0) + pr_info("%s: DSI status=%x failed\n", __func__, status); + + cnt = 16; + while (cnt--) { + status = MIPI_INP(MIPI_DSI_BASE + 0x0008); + status &= 0x11111000; /* x_HS_FIFO_EMPTY */ + if (status == 0x11111000) /* all empty */ + break; + usleep(1000); + } + + if (cnt == 0) + pr_info("%s: FIFO status=%x failed\n", __func__, status); + + dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000); + if (enable) + dsi_ctrl |= 0x01; + else + dsi_ctrl &= ~0x01; + + MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl); + wmb(); +} + +void mipi_dsi_op_mode_config(int mode) +{ + + uint32 dsi_ctrl, intr_ctrl; + + dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000); + dsi_ctrl &= ~0x07; + if (mode == DSI_VIDEO_MODE) { + dsi_ctrl |= 0x03; + intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK; + } else { /* command mode */ + dsi_ctrl |= 0x05; + intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_ERROR_MASK | + DSI_INTR_CMD_MDP_DONE_MASK; + } + + pr_debug("%s: dsi_ctrl=%x intr=%x\n", __func__, dsi_ctrl, intr_ctrl); + + MIPI_OUTP(MIPI_DSI_BASE + 0x010c, intr_ctrl); /* DSI_INTL_CTRL */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl); + wmb(); +} + +void mipi_dsi_mdp_busy_wait(struct msm_fb_data_type *mfd) +{ + unsigned long flag; + int need_wait = 0; + + pr_debug("%s: start pid=%d\n", + __func__, current->pid); + spin_lock_irqsave(&dsi_mdp_lock, flag); + if (dsi_mdp_busy == TRUE) { + INIT_COMPLETION(dsi_mdp_comp); + need_wait++; + } + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + if (need_wait) { + /* wait until DMA finishes the current job */ + pr_debug("%s: pending pid=%d\n", + __func__, current->pid); + wait_for_completion(&dsi_mdp_comp); + } + pr_debug("%s: done pid=%d\n", + __func__, current->pid); +} + + +void mipi_dsi_cmd_mdp_start(void) +{ + unsigned long flag; + + + if (!in_interrupt()) + mipi_dsi_pre_kickoff_action(); + + mipi_dsi_mdp_stat_inc(STAT_DSI_START); + + spin_lock_irqsave(&dsi_mdp_lock, flag); + mipi_dsi_enable_irq(); + dsi_mdp_busy = TRUE; + spin_unlock_irqrestore(&dsi_mdp_lock, flag); +} + + +void mipi_dsi_cmd_bta_sw_trigger(void) +{ + uint32 data; + int cnt = 0; + + MIPI_OUTP(MIPI_DSI_BASE + 0x094, 0x01); /* trigger */ + wmb(); + + while (cnt < 10000) { + data = MIPI_INP(MIPI_DSI_BASE + 0x0004);/* DSI_STATUS */ + if ((data & 0x0010) == 0) + break; + cnt++; + } + + mipi_dsi_ack_err_status(); + + pr_debug("%s: BTA done, cnt=%d\n", __func__, cnt); +} + +static char set_tear_on[2] = {0x35, 0x00}; +static struct dsi_cmd_desc dsi_tear_on_cmd = { + DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_tear_on), set_tear_on}; + +static char set_tear_off[2] = {0x34, 0x00}; +static struct dsi_cmd_desc dsi_tear_off_cmd = { + DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(set_tear_off), set_tear_off}; + +void mipi_dsi_set_tear_on(struct msm_fb_data_type *mfd) +{ + mipi_dsi_buf_init(&dsi_tx_buf); + mipi_dsi_cmds_tx(mfd, &dsi_tx_buf, &dsi_tear_on_cmd, 1); +} + +void mipi_dsi_set_tear_off(struct msm_fb_data_type *mfd) +{ + mipi_dsi_buf_init(&dsi_tx_buf); + mipi_dsi_cmds_tx(mfd, &dsi_tx_buf, &dsi_tear_off_cmd, 1); +} + +int mipi_dsi_cmd_reg_tx(uint32 data) +{ +#ifdef DSI_HOST_DEBUG + int i; + char *bp; + + bp = (char *)&data; + pr_debug("%s: ", __func__); + for (i = 0; i < 4; i++) + pr_debug("%x ", *bp++); + + pr_debug("\n"); +#endif + + MIPI_OUTP(MIPI_DSI_BASE + 0x0080, 0x04);/* sw trigger */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0, 0x135); + + wmb(); + + MIPI_OUTP(MIPI_DSI_BASE + 0x038, data); + wmb(); + MIPI_OUTP(MIPI_DSI_BASE + 0x08c, 0x01); /* trigger */ + wmb(); + + udelay(300); + + return 4; +} + +/* + * mipi_dsi_cmds_tx: + * ov_mutex need to be acquired before call this function. + */ +int mipi_dsi_cmds_tx(struct msm_fb_data_type *mfd, + struct dsi_buf *tp, struct dsi_cmd_desc *cmds, int cnt) +{ + struct dsi_cmd_desc *cm; + uint32 dsi_ctrl, ctrl; + int i, video_mode; + unsigned long flag; + + /* turn on cmd mode + * for video mode, do not send cmds more than + * one pixel line, since it only transmit it + * during BLLP. + */ + dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000); + video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */ + if (video_mode) { + ctrl = dsi_ctrl | 0x04; /* CMD_MODE_EN */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0000, ctrl); + } else { /* cmd mode */ + /* + * during boot up, cmd mode is configured + * even it is video mode panel. + */ + /* make sure mdp dma is not txing pixel data */ + if (mfd->panel_info.type == MIPI_CMD_PANEL) { +#ifndef CONFIG_FB_MSM_MDP303 + mdp4_dsi_cmd_dma_busy_wait(mfd); +#else + mdp3_dsi_cmd_dma_busy_wait(mfd); +#endif + } + } + + spin_lock_irqsave(&dsi_mdp_lock, flag); + mipi_dsi_enable_irq(); + dsi_mdp_busy = TRUE; + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + cm = cmds; + mipi_dsi_buf_init(tp); + for (i = 0; i < cnt; i++) { + mipi_dsi_buf_init(tp); + mipi_dsi_cmd_dma_add(tp, cm); + mipi_dsi_cmd_dma_tx(tp); + if (cm->wait) + msleep(cm->wait); + cm++; + } + + spin_lock_irqsave(&dsi_mdp_lock, flag); + dsi_mdp_busy = FALSE; + mipi_dsi_disable_irq(); + complete(&dsi_mdp_comp); + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + if (video_mode) + MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl); /* restore */ + + return cnt; +} + +/* MIPI_DSI_MRPS, Maximum Return Packet Size */ +static char max_pktsize[2] = {0x00, 0x00}; /* LSB tx first, 10 bytes */ + +static struct dsi_cmd_desc pkt_size_cmd[] = { + {DTYPE_MAX_PKTSIZE, 1, 0, 0, 0, + sizeof(max_pktsize), max_pktsize} +}; + +/* + * DSI panel reply with MAX_RETURN_PACKET_SIZE bytes of data + * plus DCS header, ECC and CRC for DCS long read response + * mipi_dsi_controller only have 4x32 bits register ( 16 bytes) to + * hold data per transaction. + * MIPI_DSI_LEN equal to 8 + * len should be either 4 or 8 + * any return data more than MIPI_DSI_LEN need to be break down + * to multiple transactions. + * + * ov_mutex need to be acquired before call this function. + */ +int mipi_dsi_cmds_rx(struct msm_fb_data_type *mfd, + struct dsi_buf *tp, struct dsi_buf *rp, + struct dsi_cmd_desc *cmds, int rlen) +{ + int cnt, len, diff, pkt_size; + unsigned long flag; + char cmd; + + if (mfd->panel_info.mipi.no_max_pkt_size) { + /* Only support rlen = 4*n */ + rlen += 3; + rlen &= ~0x03; + } + + len = rlen; + diff = 0; + + if (len <= 2) + cnt = 4; /* short read */ + else { + if (len > MIPI_DSI_LEN) + len = MIPI_DSI_LEN; /* 8 bytes at most */ + + len = (len + 3) & ~0x03; /* len 4 bytes align */ + diff = len - rlen; + /* + * add extra 2 bytes to len to have overall + * packet size is multipe by 4. This also make + * sure 4 bytes dcs headerlocates within a + * 32 bits register after shift in. + * after all, len should be either 6 or 10. + */ + len += 2; + cnt = len + 6; /* 4 bytes header + 2 bytes crc */ + } + + if (mfd->panel_info.type == MIPI_CMD_PANEL) { + /* make sure mdp dma is not txing pixel data */ +#ifndef CONFIG_FB_MSM_MDP303 + mdp4_dsi_cmd_dma_busy_wait(mfd); +#else + mdp3_dsi_cmd_dma_busy_wait(mfd); +#endif + } + + spin_lock_irqsave(&dsi_mdp_lock, flag); + mipi_dsi_enable_irq(); + dsi_mdp_busy = TRUE; + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + if (!mfd->panel_info.mipi.no_max_pkt_size) { + /* packet size need to be set at every read */ + pkt_size = len; + max_pktsize[0] = pkt_size; + mipi_dsi_buf_init(tp); + mipi_dsi_cmd_dma_add(tp, pkt_size_cmd); + mipi_dsi_cmd_dma_tx(tp); + } + + mipi_dsi_buf_init(tp); + mipi_dsi_cmd_dma_add(tp, cmds); + + /* transmit read comamnd to client */ + mipi_dsi_cmd_dma_tx(tp); + /* + * once cmd_dma_done interrupt received, + * return data from client is ready and stored + * at RDBK_DATA register already + */ + mipi_dsi_buf_init(rp); + if (mfd->panel_info.mipi.no_max_pkt_size) { + /* + * expect rlen = n * 4 + * short alignement for start addr + */ + rp->data += 2; + } + + mipi_dsi_cmd_dma_rx(rp, cnt); + + spin_lock_irqsave(&dsi_mdp_lock, flag); + dsi_mdp_busy = FALSE; + mipi_dsi_disable_irq(); + complete(&dsi_mdp_comp); + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + if (mfd->panel_info.mipi.no_max_pkt_size) { + /* + * remove extra 2 bytes from previous + * rx transaction at shift register + * which was inserted during copy + * shift registers to rx buffer + * rx payload start from long alignment addr + */ + rp->data += 2; + } + + cmd = rp->data[0]; + switch (cmd) { + case DTYPE_ACK_ERR_RESP: + pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__); + break; + case DTYPE_GEN_READ1_RESP: + case DTYPE_DCS_READ1_RESP: + mipi_dsi_short_read1_resp(rp); + break; + case DTYPE_GEN_READ2_RESP: + case DTYPE_DCS_READ2_RESP: + mipi_dsi_short_read2_resp(rp); + break; + case DTYPE_GEN_LREAD_RESP: + case DTYPE_DCS_LREAD_RESP: + mipi_dsi_long_read_resp(rp); + rp->len -= 2; /* extra 2 bytes added */ + rp->len -= diff; /* align bytes */ + break; + default: + break; + } + + return rp->len; +} + +int mipi_dsi_cmd_dma_tx(struct dsi_buf *tp) +{ + int len; + +#ifdef DSI_HOST_DEBUG + int i; + char *bp; + + bp = tp->data; + + pr_debug("%s: ", __func__); + for (i = 0; i < tp->len; i++) + pr_debug("%x ", *bp++); + + pr_debug("\n"); +#endif + + len = tp->len; + len += 3; + len &= ~0x03; /* multipled by 4 */ + + tp->dmap = dma_map_single(&dsi_dev, tp->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(&dsi_dev, tp->dmap)) + pr_err("%s: dmap mapp failed\n", __func__); + + INIT_COMPLETION(dsi_dma_comp); + + MIPI_OUTP(MIPI_DSI_BASE + 0x044, tp->dmap); + MIPI_OUTP(MIPI_DSI_BASE + 0x048, len); + wmb(); + MIPI_OUTP(MIPI_DSI_BASE + 0x08c, 0x01); /* trigger */ + wmb(); + + wait_for_completion(&dsi_dma_comp); + + dma_unmap_single(&dsi_dev, tp->dmap, len, DMA_TO_DEVICE); + tp->dmap = 0; + return tp->len; +} + +int mipi_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen) +{ + uint32 *lp, data; + int i, off, cnt; + + lp = (uint32 *)rp->data; + cnt = rlen; + cnt += 3; + cnt >>= 2; + + if (cnt > 4) + cnt = 4; /* 4 x 32 bits registers only */ + + off = 0x068; /* DSI_RDBK_DATA0 */ + off += ((cnt - 1) * 4); + + + for (i = 0; i < cnt; i++) { + data = (uint32)MIPI_INP(MIPI_DSI_BASE + off); + *lp++ = ntohl(data); /* to network byte order */ + off -= 4; + rp->len += sizeof(*lp); + } + + return rlen; +} + +void mipi_dsi_irq_set(uint32 mask, uint32 irq) +{ + uint32 data; + + data = MIPI_INP(MIPI_DSI_BASE + 0x010c);/* DSI_INTR_CTRL */ + data &= ~mask; + data |= irq; + MIPI_OUTP(MIPI_DSI_BASE + 0x010c, data); +} + + +void mipi_dsi_ack_err_status(void) +{ + uint32 status; + + status = MIPI_INP(MIPI_DSI_BASE + 0x0064);/* DSI_ACK_ERR_STATUS */ + + if (status) { + MIPI_OUTP(MIPI_DSI_BASE + 0x0064, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mipi_dsi_timeout_status(void) +{ + uint32 status; + + status = MIPI_INP(MIPI_DSI_BASE + 0x00bc);/* DSI_TIMEOUT_STATUS */ + if (status & 0x0111) { + MIPI_OUTP(MIPI_DSI_BASE + 0x00bc, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mipi_dsi_dln0_phy_err(void) +{ + uint32 status; + + status = MIPI_INP(MIPI_DSI_BASE + 0x00b0);/* DSI_DLN0_PHY_ERR */ + + if (status & 0x011111) { + MIPI_OUTP(MIPI_DSI_BASE + 0x00b0, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mipi_dsi_fifo_status(void) +{ + uint32 status; + + status = MIPI_INP(MIPI_DSI_BASE + 0x0008);/* DSI_FIFO_STATUS */ + + if (status & 0x44444489) { + MIPI_OUTP(MIPI_DSI_BASE + 0x0008, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mipi_dsi_status(void) +{ + uint32 status; + + status = MIPI_INP(MIPI_DSI_BASE + 0x0004);/* DSI_STATUS */ + + if (status & 0x80000000) { + MIPI_OUTP(MIPI_DSI_BASE + 0x0004, status); + pr_debug("%s: status=%x\n", __func__, status); + } +} + +void mipi_dsi_error(void) +{ + /* DSI_ERR_INT_MASK0 */ + mipi_dsi_ack_err_status(); /* mask0, 0x01f */ + mipi_dsi_timeout_status(); /* mask0, 0x0e0 */ + mipi_dsi_fifo_status(); /* mask0, 0x133d00 */ + mipi_dsi_status(); /* mask0, 0xc0100 */ + mipi_dsi_dln0_phy_err(); /* mask0, 0x3e00000 */ +} + + +irqreturn_t mipi_dsi_isr(int irq, void *ptr) +{ + uint32 isr; + + isr = MIPI_INP(MIPI_DSI_BASE + 0x010c);/* DSI_INTR_CTRL */ + MIPI_OUTP(MIPI_DSI_BASE + 0x010c, isr); + +#ifdef CONFIG_FB_MSM_MDP40 + mdp4_stat.intr_dsi++; +#endif + + if (isr & DSI_INTR_ERROR) { + mipi_dsi_mdp_stat_inc(STAT_DSI_ERROR); + mipi_dsi_error(); + } + + if (isr & DSI_INTR_VIDEO_DONE) { + /* + * do something here + */ + } + + if (isr & DSI_INTR_CMD_DMA_DONE) { + mipi_dsi_mdp_stat_inc(STAT_DSI_CMD); + complete(&dsi_dma_comp); + } + + if (isr & DSI_INTR_CMD_MDP_DONE) { + mipi_dsi_mdp_stat_inc(STAT_DSI_MDP); + spin_lock(&dsi_mdp_lock); + dsi_mdp_busy = FALSE; + mipi_dsi_disable_irq_nosync(); + spin_unlock(&dsi_mdp_lock); + complete(&dsi_mdp_comp); + mipi_dsi_post_kickoff_action(); + } + + + return IRQ_HANDLED; +} diff --git a/drivers/video/msm/mipi_novatek.c b/drivers/video/msm/mipi_novatek.c new file mode 100644 index 0000000000000000000000000000000000000000..d6e1e57bca5310b015d44fdbdc2cac412641d2c9 --- /dev/null +++ b/drivers/video/msm/mipi_novatek.c @@ -0,0 +1,658 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifdef CONFIG_SPI_QUP +#include +#endif +#include +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_novatek.h" +#include "mdp4.h" + + +static struct mipi_dsi_panel_platform_data *mipi_novatek_pdata; + +static struct dsi_buf novatek_tx_buf; +static struct dsi_buf novatek_rx_buf; +static int mipi_novatek_lcd_init(void); + +static int wled_trigger_initialized; + +#define MIPI_DSI_NOVATEK_SPI_DEVICE_NAME "dsi_novatek_3d_panel_spi" +#define HPCI_FPGA_READ_CMD 0x84 +#define HPCI_FPGA_WRITE_CMD 0x04 + +#ifdef CONFIG_SPI_QUP +static struct spi_device *panel_3d_spi_client; + +static void novatek_fpga_write(uint8 addr, uint16 value) +{ + char tx_buf[32]; + int rc; + struct spi_message m; + struct spi_transfer t; + u8 data[4] = {0x0, 0x0, 0x0, 0x0}; + + if (!panel_3d_spi_client) { + pr_err("%s panel_3d_spi_client is NULL\n", __func__); + return; + } + data[0] = HPCI_FPGA_WRITE_CMD; + data[1] = addr; + data[2] = ((value >> 8) & 0xFF); + data[3] = (value & 0xFF); + + memset(&t, 0, sizeof t); + memset(tx_buf, 0, sizeof tx_buf); + t.tx_buf = data; + t.len = 4; + spi_setup(panel_3d_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + rc = spi_sync(panel_3d_spi_client, &m); + if (rc) + pr_err("%s: SPI transfer failed\n", __func__); + + return; +} + +static void novatek_fpga_read(uint8 addr) +{ + char tx_buf[32]; + int rc; + struct spi_message m; + struct spi_transfer t; + struct spi_transfer rx; + char rx_value[2]; + u8 data[4] = {0x0, 0x0}; + + if (!panel_3d_spi_client) { + pr_err("%s panel_3d_spi_client is NULL\n", __func__); + return; + } + + data[0] = HPCI_FPGA_READ_CMD; + data[1] = addr; + + memset(&t, 0, sizeof t); + memset(tx_buf, 0, sizeof tx_buf); + memset(&rx, 0, sizeof rx); + memset(rx_value, 0, sizeof rx_value); + t.tx_buf = data; + t.len = 2; + rx.rx_buf = rx_value; + rx.len = 2; + spi_setup(panel_3d_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + spi_message_add_tail(&rx, &m); + + rc = spi_sync(panel_3d_spi_client, &m); + if (rc) + pr_err("%s: SPI transfer failed\n", __func__); + else + pr_info("%s: rx_value = 0x%x, 0x%x\n", __func__, + rx_value[0], rx_value[1]); + + return; +} + +static int panel_3d_spi_probe(struct spi_device *spi) +{ + panel_3d_spi_client = spi; + return 0; +} +static int panel_3d_spi_remove(struct spi_device *spi) +{ + panel_3d_spi_client = NULL; + return 0; +} +static struct spi_driver panel_3d_spi_driver = { + .probe = panel_3d_spi_probe, + .remove = panel_3d_spi_remove, + .driver = { + .name = "dsi_novatek_3d_panel_spi", + .owner = THIS_MODULE, + } +}; + +#else + +static void novatek_fpga_write(uint8 addr, uint16 value) +{ + return; +} + +static void novatek_fpga_read(uint8 addr) +{ + return; +} + +#endif + + +/* novatek blue panel */ + +#ifdef NOVETAK_COMMANDS_UNUSED +static char display_config_cmd_mode1[] = { + /* TYPE_DCS_LWRITE */ + 0x2A, 0x00, 0x00, 0x01, + 0x3F, 0xFF, 0xFF, 0xFF +}; + +static char display_config_cmd_mode2[] = { + /* DTYPE_DCS_LWRITE */ + 0x2B, 0x00, 0x00, 0x01, + 0xDF, 0xFF, 0xFF, 0xFF +}; + +static char display_config_cmd_mode3_666[] = { + /* DTYPE_DCS_WRITE1 */ + 0x3A, 0x66, 0x15, 0x80 /* 666 Packed (18-bits) */ +}; + +static char display_config_cmd_mode3_565[] = { + /* DTYPE_DCS_WRITE1 */ + 0x3A, 0x55, 0x15, 0x80 /* 565 mode */ +}; + +static char display_config_321[] = { + /* DTYPE_DCS_WRITE1 */ + 0x66, 0x2e, 0x15, 0x00 /* Reg 0x66 : 2E */ +}; + +static char display_config_323[] = { + /* DTYPE_DCS_WRITE */ + 0x13, 0x00, 0x05, 0x00 /* Reg 0x13 < Set for Normal Mode> */ +}; + +static char display_config_2lan[] = { + /* DTYPE_DCS_WRITE */ + 0x61, 0x01, 0x02, 0xff /* Reg 0x61 : 01,02 < Set for 2 Data Lane > */ +}; + +static char display_config_exit_sleep[] = { + /* DTYPE_DCS_WRITE */ + 0x11, 0x00, 0x05, 0x80 /* Reg 0x11 < exit sleep mode> */ +}; + +static char display_config_TE_ON[] = { + /* DTYPE_DCS_WRITE1 */ + 0x35, 0x00, 0x15, 0x80 +}; + +static char display_config_39H[] = { + /* DTYPE_DCS_WRITE */ + 0x39, 0x00, 0x05, 0x80 +}; + +static char display_config_set_tear_scanline[] = { + /* DTYPE_DCS_LWRITE */ + 0x44, 0x00, 0x00, 0xff +}; + +static char display_config_set_twolane[] = { + /* DTYPE_DCS_WRITE1 */ + 0xae, 0x03, 0x15, 0x80 +}; + +static char display_config_set_threelane[] = { + /* DTYPE_DCS_WRITE1 */ + 0xae, 0x05, 0x15, 0x80 +}; + +#else + +static char sw_reset[2] = {0x01, 0x00}; /* DTYPE_DCS_WRITE */ +static char enter_sleep[2] = {0x10, 0x00}; /* DTYPE_DCS_WRITE */ +static char exit_sleep[2] = {0x11, 0x00}; /* DTYPE_DCS_WRITE */ +static char display_off[2] = {0x28, 0x00}; /* DTYPE_DCS_WRITE */ +static char display_on[2] = {0x29, 0x00}; /* DTYPE_DCS_WRITE */ + + + +static char rgb_888[2] = {0x3A, 0x77}; /* DTYPE_DCS_WRITE1 */ + +#if defined(NOVATEK_TWO_LANE) +static char set_num_of_lanes[2] = {0xae, 0x03}; /* DTYPE_DCS_WRITE1 */ +#else /* 1 lane */ +static char set_num_of_lanes[2] = {0xae, 0x01}; /* DTYPE_DCS_WRITE1 */ +#endif +/* commands by Novatke */ +static char novatek_f4[2] = {0xf4, 0x55}; /* DTYPE_DCS_WRITE1 */ +static char novatek_8c[16] = { /* DTYPE_DCS_LWRITE */ + 0x8C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x30, 0xC0, 0xB7, 0x37}; +static char novatek_ff[2] = {0xff, 0x55 }; /* DTYPE_DCS_WRITE1 */ + +static char set_width[5] = { /* DTYPE_DCS_LWRITE */ + 0x2A, 0x00, 0x00, 0x02, 0x1B}; /* 540 - 1 */ +static char set_height[5] = { /* DTYPE_DCS_LWRITE */ + 0x2B, 0x00, 0x00, 0x03, 0xBF}; /* 960 - 1 */ +#endif + +static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */ +static char led_pwm2[2] = {0x53, 0x24}; /* DTYPE_DCS_WRITE1 */ +static char led_pwm3[2] = {0x55, 0x00}; /* DTYPE_DCS_WRITE1 */ + +static struct dsi_cmd_desc novatek_cmd_backlight_cmds[] = { + {DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(led_pwm1), led_pwm1}, +}; + +static struct dsi_cmd_desc novatek_video_on_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 50, + sizeof(sw_reset), sw_reset}, + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(display_on), display_on}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 10, + sizeof(set_num_of_lanes), set_num_of_lanes}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 10, + sizeof(rgb_888), rgb_888}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 10, + sizeof(led_pwm2), led_pwm2}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 10, + sizeof(led_pwm3), led_pwm3}, +}; + +static struct dsi_cmd_desc novatek_cmd_on_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 50, + sizeof(sw_reset), sw_reset}, + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(display_on), display_on}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 50, + sizeof(novatek_f4), novatek_f4}, + {DTYPE_DCS_LWRITE, 1, 0, 0, 50, + sizeof(novatek_8c), novatek_8c}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 50, + sizeof(novatek_ff), novatek_ff}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 10, + sizeof(set_num_of_lanes), set_num_of_lanes}, + {DTYPE_DCS_LWRITE, 1, 0, 0, 50, + sizeof(set_width), set_width}, + {DTYPE_DCS_LWRITE, 1, 0, 0, 50, + sizeof(set_height), set_height}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 10, + sizeof(rgb_888), rgb_888}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 1, + sizeof(led_pwm2), led_pwm2}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 1, + sizeof(led_pwm3), led_pwm3}, +}; + +static struct dsi_cmd_desc novatek_display_off_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(display_off), display_off}, + {DTYPE_DCS_WRITE, 1, 0, 0, 120, + sizeof(enter_sleep), enter_sleep} +}; + +static char manufacture_id[2] = {0x04, 0x00}; /* DTYPE_DCS_READ */ + +static struct dsi_cmd_desc novatek_manufacture_id_cmd = { + DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(manufacture_id), manufacture_id}; + +static uint32 mipi_novatek_manufacture_id(struct msm_fb_data_type *mfd) +{ + struct dsi_buf *rp, *tp; + struct dsi_cmd_desc *cmd; + uint32 *lp; + + tp = &novatek_tx_buf; + rp = &novatek_rx_buf; + cmd = &novatek_manufacture_id_cmd; + mipi_dsi_cmds_rx(mfd, tp, rp, cmd, 3); + lp = (uint32 *)rp->data; + pr_info("%s: manufacture_id=%x", __func__, *lp); + return *lp; +} + +static int fpga_addr; +static int fpga_access_mode; +static bool support_3d; + +static void mipi_novatek_3d_init(int addr, int mode) +{ + fpga_addr = addr; + fpga_access_mode = mode; +} + +static void mipi_dsi_enable_3d_barrier(int mode) +{ + void __iomem *fpga_ptr; + uint32_t ptr_value = 0; + + if (!fpga_addr && support_3d) { + pr_err("%s: fpga_addr not set. Failed to enable 3D barrier\n", + __func__); + return; + } + + if (fpga_access_mode == FPGA_SPI_INTF) { + if (mode == LANDSCAPE) + novatek_fpga_write(fpga_addr, 1); + else if (mode == PORTRAIT) + novatek_fpga_write(fpga_addr, 3); + else + novatek_fpga_write(fpga_addr, 0); + + mb(); + novatek_fpga_read(fpga_addr); + } else if (fpga_access_mode == FPGA_EBI2_INTF) { + fpga_ptr = ioremap_nocache(fpga_addr, sizeof(uint32_t)); + if (!fpga_ptr) { + pr_err("%s: FPGA ioremap failed." + "Failed to enable 3D barrier\n", + __func__); + return; + } + + ptr_value = readl_relaxed(fpga_ptr); + if (mode == LANDSCAPE) + writel_relaxed(((0xFFFF0000 & ptr_value) | 1), + fpga_ptr); + else if (mode == PORTRAIT) + writel_relaxed(((0xFFFF0000 & ptr_value) | 3), + fpga_ptr); + else + writel_relaxed((0xFFFF0000 & ptr_value), + fpga_ptr); + + mb(); + iounmap(fpga_ptr); + } else + pr_err("%s: 3D barrier not configured correctly\n", + __func__); +} + +static int mipi_novatek_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + struct msm_panel_info *pinfo; + + mfd = platform_get_drvdata(pdev); + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + pinfo = &mfd->panel_info; + if (pinfo->is_3d_panel) + support_3d = TRUE; + + mipi = &mfd->panel_info.mipi; + + if (mipi->mode == DSI_VIDEO_MODE) { + mipi_dsi_cmds_tx(mfd, &novatek_tx_buf, novatek_video_on_cmds, + ARRAY_SIZE(novatek_video_on_cmds)); + } else { + mipi_dsi_cmds_tx(mfd, &novatek_tx_buf, novatek_cmd_on_cmds, + ARRAY_SIZE(novatek_cmd_on_cmds)); + + mipi_dsi_cmd_bta_sw_trigger(); /* clean up ack_err_status */ + + mipi_novatek_manufacture_id(mfd); + } + + return 0; +} + +static int mipi_novatek_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi_dsi_cmds_tx(mfd, &novatek_tx_buf, novatek_display_off_cmds, + ARRAY_SIZE(novatek_display_off_cmds)); + + return 0; +} + +DEFINE_LED_TRIGGER(bkl_led_trigger); + +static void mipi_novatek_set_backlight(struct msm_fb_data_type *mfd) +{ + struct mipi_panel_info *mipi; + + if ((mipi_novatek_pdata->enable_wled_bl_ctrl) + && (wled_trigger_initialized)) { + led_trigger_event(bkl_led_trigger, mfd->bl_level); + return; + } + mipi = &mfd->panel_info.mipi; + + mutex_lock(&mfd->dma->ov_mutex); + if (mdp4_overlay_dsi_state_get() <= ST_DSI_SUSPEND) { + mutex_unlock(&mfd->dma->ov_mutex); + return; + } + /* mdp4_dsi_cmd_busy_wait: will turn on dsi clock also */ + mdp4_dsi_cmd_dma_busy_wait(mfd); + mdp4_dsi_blt_dmap_busy_wait(mfd); + mipi_dsi_mdp_busy_wait(mfd); + + led_pwm1[1] = (unsigned char)(mfd->bl_level); + mipi_dsi_cmds_tx(mfd, &novatek_tx_buf, novatek_cmd_backlight_cmds, + ARRAY_SIZE(novatek_cmd_backlight_cmds)); + mutex_unlock(&mfd->dma->ov_mutex); + return; +} + +static int mipi_dsi_3d_barrier_sysfs_register(struct device *dev); +static int barrier_mode; + +static int mipi_novatek_lcd_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + struct platform_device *current_pdev; + static struct mipi_dsi_phy_ctrl *phy_settings; + static char dlane_swap; + + if (pdev->id == 0) { + mipi_novatek_pdata = pdev->dev.platform_data; + + if (mipi_novatek_pdata + && mipi_novatek_pdata->phy_ctrl_settings) { + phy_settings = (mipi_novatek_pdata->phy_ctrl_settings); + } + + if (mipi_novatek_pdata + && mipi_novatek_pdata->dlane_swap) { + dlane_swap = (mipi_novatek_pdata->dlane_swap); + } + + if (mipi_novatek_pdata + && mipi_novatek_pdata->fpga_3d_config_addr) + mipi_novatek_3d_init(mipi_novatek_pdata + ->fpga_3d_config_addr, mipi_novatek_pdata->fpga_ctrl_mode); + + /* create sysfs to control 3D barrier for the Sharp panel */ + if (mipi_dsi_3d_barrier_sysfs_register(&pdev->dev)) { + pr_err("%s: Failed to register 3d Barrier sysfs\n", + __func__); + return -ENODEV; + } + barrier_mode = 0; + + return 0; + } + + current_pdev = msm_fb_add_device(pdev); + + if (current_pdev) { + mfd = platform_get_drvdata(current_pdev); + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi = &mfd->panel_info.mipi; + + if (phy_settings != NULL) + mipi->dsi_phy_db = phy_settings; + + if (dlane_swap) + mipi->dlane_swap = dlane_swap; + } + return 0; +} + +static struct platform_driver this_driver = { + .probe = mipi_novatek_lcd_probe, + .driver = { + .name = "mipi_novatek", + }, +}; + +static struct msm_fb_panel_data novatek_panel_data = { + .on = mipi_novatek_lcd_on, + .off = mipi_novatek_lcd_off, + .set_backlight = mipi_novatek_set_backlight, +}; + +static ssize_t mipi_dsi_3d_barrier_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf((char *)buf, sizeof(buf), "%u\n", barrier_mode); +} + +static ssize_t mipi_dsi_3d_barrier_write(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret = -1; + u32 data = 0; + + if (sscanf((char *)buf, "%u", &data) != 1) { + dev_err(dev, "%s\n", __func__); + ret = -EINVAL; + } else { + barrier_mode = data; + if (data == 1) + mipi_dsi_enable_3d_barrier(LANDSCAPE); + else if (data == 2) + mipi_dsi_enable_3d_barrier(PORTRAIT); + else + mipi_dsi_enable_3d_barrier(0); + } + + return count; +} + +static struct device_attribute mipi_dsi_3d_barrier_attributes[] = { + __ATTR(enable_3d_barrier, 0664, mipi_dsi_3d_barrier_read, + mipi_dsi_3d_barrier_write), +}; + +static int mipi_dsi_3d_barrier_sysfs_register(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mipi_dsi_3d_barrier_attributes); i++) + if (device_create_file(dev, mipi_dsi_3d_barrier_attributes + i)) + goto error; + + return 0; + +error: + for (; i >= 0 ; i--) + device_remove_file(dev, mipi_dsi_3d_barrier_attributes + i); + pr_err("%s: Unable to create interface\n", __func__); + + return -ENODEV; +} + +static int ch_used[3]; + +int mipi_novatek_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + ch_used[channel] = TRUE; + + ret = mipi_novatek_lcd_init(); + if (ret) { + pr_err("mipi_novatek_lcd_init() failed with ret %u\n", ret); + return ret; + } + + pdev = platform_device_alloc("mipi_novatek", (panel << 8)|channel); + if (!pdev) + return -ENOMEM; + + novatek_panel_data.panel_info = *pinfo; + + ret = platform_device_add_data(pdev, &novatek_panel_data, + sizeof(novatek_panel_data)); + if (ret) { + printk(KERN_ERR + "%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + printk(KERN_ERR + "%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static int mipi_novatek_lcd_init(void) +{ +#ifdef CONFIG_SPI_QUP + int ret; + ret = spi_register_driver(&panel_3d_spi_driver); + + if (ret) { + pr_err("%s: spi register failed: rc=%d\n", __func__, ret); + platform_driver_unregister(&this_driver); + } else + pr_info("%s: SUCCESS (SPI)\n", __func__); +#endif + + led_trigger_register_simple("bkl_trigger", &bkl_led_trigger); + pr_info("%s: SUCCESS (WLED TRIGGER)\n", __func__); + wled_trigger_initialized = 1; + + mipi_dsi_buf_alloc(&novatek_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&novatek_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} diff --git a/drivers/video/msm/mipi_novatek.h b/drivers/video/msm/mipi_novatek.h new file mode 100644 index 0000000000000000000000000000000000000000..7abe86399bdcbababd2126ee83baedf8f3b6c5db --- /dev/null +++ b/drivers/video/msm/mipi_novatek.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MIPI_NOVATEK_BLUE_H +#define MIPI_NOVATEK_BLUE_H + +#define NOVATEK_TWO_LANE + +int mipi_novatek_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#endif /* MIPI_NOVATEK_BLUE_H */ diff --git a/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..616622ec3829a97c4e5c140e21b0fd08fd616b0e --- /dev/null +++ b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c @@ -0,0 +1,100 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_novatek.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = { +/* DSI_BIT_CLK at 500MHz, 2 lane, RGB888 */ + {0x03, 0x01, 0x01, 0x00}, /* regulator */ + /* timing */ + {0xB4, 0x8D, 0x1D, 0x00, 0x20, 0x94, 0x20, + 0x8F, 0x20, 0x03, 0x04}, + {0x7f, 0x00, 0x00, 0x00}, /* phy ctrl */ + {0xee, 0x02, 0x86, 0x00}, /* strength */ + /* pll control */ + {0x40, 0xf9, 0xb0, 0xda, 0x00, 0x50, 0x48, 0x63, +#if defined(NOVATEK_TWO_LANE) + 0x30, 0x07, 0x03, +#else /* default set to 1 lane */ + 0x30, 0x07, 0x07, +#endif + 0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0}, +}; + +static int __init mipi_cmd_novatek_blue_qhd_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_cmd_novatek_qhd")) + return 0; + + pinfo.xres = 540; + pinfo.yres = 960; + pinfo.type = MIPI_CMD_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 50; + pinfo.lcdc.h_front_porch = 50; + pinfo.lcdc.h_pulse_width = 20; + pinfo.lcdc.v_back_porch = 11; + pinfo.lcdc.v_front_porch = 10; + pinfo.lcdc.v_pulse_width = 5; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 255; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + pinfo.clk_rate = 454000000; + pinfo.is_3d_panel = FB_TYPE_3D_PANEL; + pinfo.lcd.vsync_enable = TRUE; + pinfo.lcd.hw_vsync_mode = TRUE; + pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */ + pinfo.lcd.v_back_porch = 11; + pinfo.lcd.v_front_porch = 10; + pinfo.lcd.v_pulse_width = 5; + + pinfo.mipi.mode = DSI_CMD_MODE; + pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.esc_byte_ratio = 4; +#if defined(NOVATEK_TWO_LANE) + pinfo.mipi.data_lane1 = TRUE; +#endif + pinfo.mipi.t_clk_post = 0x22; + pinfo.mipi.t_clk_pre = 0x3f; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.te_sel = 1; /* TE from vsycn gpio */ + pinfo.mipi.interleave_max = 1; + pinfo.mipi.insert_dcs_cmd = TRUE; + pinfo.mipi.wr_mem_continue = 0x3c; + pinfo.mipi.wr_mem_start = 0x2c; + pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db; + + ret = mipi_novatek_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_QHD_PT); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_cmd_novatek_blue_qhd_pt_init); diff --git a/drivers/video/msm/mipi_novatek_video_qhd_pt.c b/drivers/video/msm/mipi_novatek_video_qhd_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..8881b60b20e94524338cd93b4e6a501fa5d33632 --- /dev/null +++ b/drivers/video/msm/mipi_novatek_video_qhd_pt.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_novatek.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { +/* DSI_BIT_CLK at 500MHz, 2 lane, RGB888 */ + {0x03, 0x01, 0x01, 0x00}, /* regulator */ + /* timing */ + {0x82, 0x31, 0x13, 0x0, 0x42, 0x4D, 0x18, + 0x35, 0x21, 0x03, 0x04}, + {0x7f, 0x00, 0x00, 0x00}, /* phy ctrl */ + {0xee, 0x02, 0x86, 0x00}, /* strength */ + /* pll control */ + {0x40, 0xf9, 0xb0, 0xda, 0x00, 0x50, 0x48, 0x63, +#if defined(NOVATEK_TWO_LANE) + 0x30, 0x07, 0x03, +#else /* default set to 1 lane */ + 0x30, 0x07, 0x07, +#endif + 0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0}, +}; + +static int __init mipi_video_novatek_qhd_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_novatek_qhd")) + return 0; + + pinfo.xres = 540; + pinfo.yres = 960; + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 80; + pinfo.lcdc.h_front_porch = 24; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 16; + pinfo.lcdc.v_front_porch = 8; + pinfo.lcdc.v_pulse_width = 1; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 15; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + pinfo.mipi.pulse_mode_hsa_he = TRUE; + pinfo.mipi.hfp_power_stop = FALSE; + pinfo.mipi.hbp_power_stop = FALSE; + pinfo.mipi.hsa_power_stop = FALSE; + pinfo.mipi.eof_bllp_power_stop = TRUE; + pinfo.mipi.bllp_power_stop = TRUE; + pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.esc_byte_ratio = 4; +#if defined(NOVATEK_TWO_LANE) + pinfo.mipi.data_lane1 = TRUE; +#endif + pinfo.mipi.tx_eot_append = TRUE; + pinfo.mipi.t_clk_post = 0x04; + pinfo.mipi.t_clk_pre = 0x1c; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + + ret = mipi_novatek_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_QHD_PT); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_novatek_qhd_pt_init); diff --git a/drivers/video/msm/mipi_orise.c b/drivers/video/msm/mipi_orise.c new file mode 100644 index 0000000000000000000000000000000000000000..2e2c373b659fe6779641e848f12fe1c9e38342cd --- /dev/null +++ b/drivers/video/msm/mipi_orise.c @@ -0,0 +1,194 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_orise.h" +#include "mdp4.h" + + +static struct mipi_dsi_panel_platform_data *mipi_orise_pdata; + +static struct dsi_buf orise_tx_buf; +static struct dsi_buf orise_rx_buf; + +static char enter_sleep[2] = {0x10, 0x00}; /* DTYPE_DCS_WRITE */ +static char exit_sleep[2] = {0x11, 0x00}; /* DTYPE_DCS_WRITE */ +static char display_off[2] = {0x28, 0x00}; /* DTYPE_DCS_WRITE */ +static char display_on[2] = {0x29, 0x00}; /* DTYPE_DCS_WRITE */ + +static struct dsi_cmd_desc orise_video_on_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(display_on), display_on}, +}; + +static struct dsi_cmd_desc orise_cmd_on_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(display_on), display_on}, +}; + +static struct dsi_cmd_desc orise_display_off_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 10, + sizeof(display_off), display_off}, + {DTYPE_DCS_WRITE, 1, 0, 0, 120, + sizeof(enter_sleep), enter_sleep} +}; + +static int mipi_orise_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + struct msm_panel_info *pinfo; + + mfd = platform_get_drvdata(pdev); + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + pinfo = &mfd->panel_info; + mipi = &mfd->panel_info.mipi; + + if (mipi->mode == DSI_VIDEO_MODE) { + mipi_dsi_cmds_tx(mfd, &orise_tx_buf, orise_video_on_cmds, + ARRAY_SIZE(orise_video_on_cmds)); + } else { + mipi_dsi_cmds_tx(mfd, &orise_tx_buf, orise_cmd_on_cmds, + ARRAY_SIZE(orise_cmd_on_cmds)); + + mipi_dsi_cmd_bta_sw_trigger(); /* clean up ack_err_status */ + } + + return 0; +} + +static int mipi_orise_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi_dsi_cmds_tx(mfd, &orise_tx_buf, orise_display_off_cmds, + ARRAY_SIZE(orise_display_off_cmds)); + + return 0; +} + + + +static int mipi_orise_lcd_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + struct platform_device *current_pdev; + static struct mipi_dsi_phy_ctrl *phy_settings; + + if (pdev->id == 0) { + mipi_orise_pdata = pdev->dev.platform_data; + + if (mipi_orise_pdata + && mipi_orise_pdata->phy_ctrl_settings) { + phy_settings = (mipi_orise_pdata->phy_ctrl_settings); + } + + return 0; + } + + current_pdev = msm_fb_add_device(pdev); + + if (current_pdev) { + mfd = platform_get_drvdata(current_pdev); + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi = &mfd->panel_info.mipi; + + if (phy_settings != NULL) + mipi->dsi_phy_db = phy_settings; + } + return 0; +} + +static struct platform_driver this_driver = { + .probe = mipi_orise_lcd_probe, + .driver = { + .name = "mipi_orise", + }, +}; + +static struct msm_fb_panel_data orise_panel_data = { + .on = mipi_orise_lcd_on, + .off = mipi_orise_lcd_off, +}; + +static int ch_used[3]; + +int mipi_orise_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + ch_used[channel] = TRUE; + + pdev = platform_device_alloc("mipi_orise", (panel << 8)|channel); + if (!pdev) + return -ENOMEM; + + orise_panel_data.panel_info = *pinfo; + + ret = platform_device_add_data(pdev, &orise_panel_data, + sizeof(orise_panel_data)); + if (ret) { + printk(KERN_ERR + "%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + printk(KERN_ERR + "%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static int __init mipi_orise_lcd_init(void) +{ + mipi_dsi_buf_alloc(&orise_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&orise_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} + +module_init(mipi_orise_lcd_init); diff --git a/drivers/video/msm/mipi_orise.h b/drivers/video/msm/mipi_orise.h new file mode 100644 index 0000000000000000000000000000000000000000..da4b59f2a5f2a735ac3482200cc50f598b2c426e --- /dev/null +++ b/drivers/video/msm/mipi_orise.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#ifndef MIPI_ORISE_H +#define MIPI_ORISE_H + +int mipi_orise_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#endif /* MIPI_ORISE_H */ diff --git a/drivers/video/msm/mipi_orise_cmd_720p_pt.c b/drivers/video/msm/mipi_orise_cmd_720p_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..9b7020ad498a2e8db15edfbe7d95388e85145514 --- /dev/null +++ b/drivers/video/msm/mipi_orise_cmd_720p_pt.c @@ -0,0 +1,96 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_orise.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = { +/* DSI_BIT_CLK at 507MHz, 4 lane, RGB888 */ + {0x03, 0x0a, 0x04, 0x00, 0x20}, + /* timing */ + {0x8c, 0x34, 0x15, 0x00, 0x46, 0x50, 0x1a, 0x38, + 0x24, 0x03, 0x04, 0xa0}, + /* phy ctrl */ + {0x5f, 0x00, 0x00, 0x10}, + /* strength */ + {0xff, 0x00, 0x06, 0x00}, + /* pll control */ + {0x0, 0xf9, 0x30, 0xda, 0x00, 0x40, 0x03, 0x62, + 0x40, 0x07, 0x03, + 0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01 }, +}; + +static int __init mipi_cmd_orise_720p_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_cmd_orise_720p")) + return 0; + + pinfo.xres = 720; + pinfo.yres = 1280; + pinfo.type = MIPI_CMD_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 160; + pinfo.lcdc.h_front_porch = 160; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 32; + pinfo.lcdc.v_front_porch = 32; + pinfo.lcdc.v_pulse_width = 1; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 200; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + pinfo.clk_rate = 507000000; + pinfo.lcd.vsync_enable = TRUE; + pinfo.lcd.hw_vsync_mode = TRUE; + pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */ + pinfo.lcd.v_back_porch = 32; + pinfo.lcd.v_front_porch = 32; + pinfo.lcd.v_pulse_width = 1; + + pinfo.mipi.mode = DSI_CMD_MODE; + pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.data_lane2 = TRUE; + pinfo.mipi.data_lane3 = TRUE; + pinfo.mipi.t_clk_post = 0x04; + pinfo.mipi.t_clk_pre = 0x1e; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.te_sel = 1; /* TE from vsycn gpio */ + pinfo.mipi.interleave_max = 1; + pinfo.mipi.insert_dcs_cmd = TRUE; + pinfo.mipi.wr_mem_continue = 0x3c; + pinfo.mipi.wr_mem_start = 0x2c; + pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db; + + ret = mipi_orise_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_720P_PT); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_cmd_orise_720p_pt_init); diff --git a/drivers/video/msm/mipi_orise_video_720p_pt.c b/drivers/video/msm/mipi_orise_video_720p_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..1484576c99b2982820c77e456221d8676711a37d --- /dev/null +++ b/drivers/video/msm/mipi_orise_video_720p_pt.c @@ -0,0 +1,98 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_orise.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* regulator */ + {0x03, 0x0a, 0x04, 0x00, 0x20}, + /* timing */ + {0x83, 0x31, 0x13, 0x00, 0x42, 0x4d, 0x18, 0x35, + 0x21, 0x03, 0x04, 0xa0}, + /* phy ctrl */ + {0x5f, 0x00, 0x00, 0x10}, + /* strength */ + {0xff, 0x00, 0x06, 0x00}, + /* pll control */ + {0x0, 0x0e, 0x30, 0xc0, 0x00, 0x40, 0x03, 0x62, + 0x40, 0x07, 0x07, + 0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01 }, +}; + +static int __init mipi_video_orise_720p_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_orise_720p")) + return 0; + + pinfo.xres = 720; + pinfo.yres = 1280; + pinfo.lcdc.xres_pad = 0; + pinfo.lcdc.yres_pad = 0; + + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 160; + pinfo.lcdc.h_front_porch = 160; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 32; + pinfo.lcdc.v_front_porch = 32; + pinfo.lcdc.v_pulse_width = 1; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 200; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + pinfo.mipi.pulse_mode_hsa_he = TRUE; + pinfo.mipi.hfp_power_stop = TRUE; + pinfo.mipi.hbp_power_stop = TRUE; + pinfo.mipi.hsa_power_stop = FALSE; + pinfo.mipi.eof_bllp_power_stop = TRUE; + pinfo.mipi.bllp_power_stop = TRUE; + pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.data_lane2 = TRUE; + pinfo.mipi.data_lane3 = TRUE; + pinfo.mipi.t_clk_post = 0x04; + pinfo.mipi.t_clk_pre = 0x1c; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = 0; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 55; + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + pinfo.mipi.tx_eot_append = TRUE; + pinfo.mipi.esc_byte_ratio = 4; + + ret = mipi_orise_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_720P_PT); + if (ret) + printk(KERN_ERR "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_orise_720p_pt_init); diff --git a/drivers/video/msm/mipi_renesas.c b/drivers/video/msm/mipi_renesas.c new file mode 100644 index 0000000000000000000000000000000000000000..ff3bdae129aff761a6524e93aba828ba6d022412 --- /dev/null +++ b/drivers/video/msm/mipi_renesas.c @@ -0,0 +1,1262 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_renesas.h" +#include + +#define RENESAS_CMD_DELAY 0 /* 50 */ +#define RENESAS_SLEEP_OFF_DELAY 50 +static struct msm_panel_common_pdata *mipi_renesas_pdata; + +static struct dsi_buf renesas_tx_buf; +static struct dsi_buf renesas_rx_buf; + +static int mipi_renesas_lcd_init(void); + +static char config_sleep_out[2] = {0x11, 0x00}; +static char config_CMD_MODE[2] = {0x40, 0x01}; +static char config_WRTXHT[7] = {0x92, 0x16, 0x08, 0x08, 0x00, 0x01, 0xe0}; +static char config_WRTXVT[7] = {0x8b, 0x02, 0x02, 0x02, 0x00, 0x03, 0x60}; +static char config_PLL2NR[2] = {0xa0, 0x24}; +static char config_PLL2NF1[2] = {0xa2, 0xd0}; +static char config_PLL2NF2[2] = {0xa4, 0x00}; +static char config_PLL2BWADJ1[2] = {0xa6, 0xd0}; +static char config_PLL2BWADJ2[2] = {0xa8, 0x00}; +static char config_PLL2CTL[2] = {0xaa, 0x00}; +static char config_DBICBR[2] = {0x48, 0x03}; +static char config_DBICTYPE[2] = {0x49, 0x00}; +static char config_DBICSET1[2] = {0x4a, 0x1c}; +static char config_DBICADD[2] = {0x4b, 0x00}; +static char config_DBICCTL[2] = {0x4e, 0x01}; +/* static char config_COLMOD_565[2] = {0x3a, 0x05}; */ +/* static char config_COLMOD_666PACK[2] = {0x3a, 0x06}; */ +static char config_COLMOD_888[2] = {0x3a, 0x07}; +static char config_MADCTL[2] = {0x36, 0x00}; +static char config_DBIOC[2] = {0x82, 0x40}; +static char config_CASET[7] = {0x2a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xdf }; +static char config_PASET[7] = {0x2b, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5f }; +static char config_TXON[2] = {0x81, 0x00}; +static char config_BLSET_TM[2] = {0xff, 0x6c}; +static char config_DSIRXCTL[2] = {0x41, 0x01}; +static char config_TEON[2] = {0x35, 0x00}; +static char config_TEOFF[1] = {0x34}; + +static char config_AGCPSCTL_TM[2] = {0x56, 0x08}; + +static char config_DBICADD70[2] = {0x4b, 0x70}; +static char config_DBICSET_15[2] = {0x4a, 0x15}; +static char config_DBICADD72[2] = {0x4b, 0x72}; + +static char config_Power_Ctrl_2a_cmd[3] = {0x4c, 0x40, 0x10}; +static char config_Auto_Sequencer_Setting_a_cmd[3] = {0x4c, 0x00, 0x00}; +static char Driver_Output_Ctrl_indx[3] = {0x4c, 0x00, 0x01}; +static char Driver_Output_Ctrl_cmd[3] = {0x4c, 0x03, 0x10}; +static char config_LCD_drive_AC_Ctrl_indx[3] = {0x4c, 0x00, 0x02}; +static char config_LCD_drive_AC_Ctrl_cmd[3] = {0x4c, 0x01, 0x00}; +static char config_Entry_Mode_indx[3] = {0x4c, 0x00, 0x03}; +static char config_Entry_Mode_cmd[3] = {0x4c, 0x00, 0x00}; +static char config_Display_Ctrl_1_indx[3] = {0x4c, 0x00, 0x07}; +static char config_Display_Ctrl_1_cmd[3] = {0x4c, 0x00, 0x00}; +static char config_Display_Ctrl_2_indx[3] = {0x4c, 0x00, 0x08}; +static char config_Display_Ctrl_2_cmd[3] = {0x4c, 0x00, 0x04}; +static char config_Display_Ctrl_3_indx[3] = {0x4c, 0x00, 0x09}; +static char config_Display_Ctrl_3_cmd[3] = {0x4c, 0x00, 0x0c}; +static char config_Display_IF_Ctrl_1_indx[3] = {0x4c, 0x00, 0x0c}; +static char config_Display_IF_Ctrl_1_cmd[3] = {0x4c, 0x40, 0x10}; +static char config_Display_IF_Ctrl_2_indx[3] = {0x4c, 0x00, 0x0e}; +static char config_Display_IF_Ctrl_2_cmd[3] = {0x4c, 0x00, 0x00}; + +static char config_Panel_IF_Ctrl_1_indx[3] = {0x4c, 0x00, 0x20}; +static char config_Panel_IF_Ctrl_1_cmd[3] = {0x4c, 0x01, 0x3f}; +static char config_Panel_IF_Ctrl_3_indx[3] = {0x4c, 0x00, 0x22}; +static char config_Panel_IF_Ctrl_3_cmd[3] = {0x4c, 0x76, 0x00}; +static char config_Panel_IF_Ctrl_4_indx[3] = {0x4c, 0x00, 0x23}; +static char config_Panel_IF_Ctrl_4_cmd[3] = {0x4c, 0x1c, 0x0a}; +static char config_Panel_IF_Ctrl_5_indx[3] = {0x4c, 0x00, 0x24}; +static char config_Panel_IF_Ctrl_5_cmd[3] = {0x4c, 0x1c, 0x2c}; +static char config_Panel_IF_Ctrl_6_indx[3] = {0x4c, 0x00, 0x25}; +static char config_Panel_IF_Ctrl_6_cmd[3] = {0x4c, 0x1c, 0x4e}; +static char config_Panel_IF_Ctrl_8_indx[3] = {0x4c, 0x00, 0x27}; +static char config_Panel_IF_Ctrl_8_cmd[3] = {0x4c, 0x00, 0x00}; +static char config_Panel_IF_Ctrl_9_indx[3] = {0x4c, 0x00, 0x28}; +static char config_Panel_IF_Ctrl_9_cmd[3] = {0x4c, 0x76, 0x0c}; + + +static char config_gam_adjust_00_indx[3] = {0x4c, 0x03, 0x00}; +static char config_gam_adjust_00_cmd[3] = {0x4c, 0x00, 0x00}; +static char config_gam_adjust_01_indx[3] = {0x4c, 0x03, 0x01}; +static char config_gam_adjust_01_cmd[3] = {0x4c, 0x05, 0x02}; +static char config_gam_adjust_02_indx[3] = {0x4c, 0x03, 0x02}; +static char config_gam_adjust_02_cmd[3] = {0x4c, 0x07, 0x05}; +static char config_gam_adjust_03_indx[3] = {0x4c, 0x03, 0x03}; +static char config_gam_adjust_03_cmd[3] = {0x4c, 0x00, 0x00}; +static char config_gam_adjust_04_indx[3] = {0x4c, 0x03, 0x04}; +static char config_gam_adjust_04_cmd[3] = {0x4c, 0x02, 0x00}; +static char config_gam_adjust_05_indx[3] = {0x4c, 0x03, 0x05}; +static char config_gam_adjust_05_cmd[3] = {0x4c, 0x07, 0x07}; +static char config_gam_adjust_06_indx[3] = {0x4c, 0x03, 0x06}; +static char config_gam_adjust_06_cmd[3] = {0x4c, 0x10, 0x10}; +static char config_gam_adjust_07_indx[3] = {0x4c, 0x03, 0x07}; +static char config_gam_adjust_07_cmd[3] = {0x4c, 0x02, 0x02}; +static char config_gam_adjust_08_indx[3] = {0x4c, 0x03, 0x08}; +static char config_gam_adjust_08_cmd[3] = {0x4c, 0x07, 0x04}; +static char config_gam_adjust_09_indx[3] = {0x4c, 0x03, 0x09}; +static char config_gam_adjust_09_cmd[3] = {0x4c, 0x07, 0x07}; +static char config_gam_adjust_0A_indx[3] = {0x4c, 0x03, 0x0a}; +static char config_gam_adjust_0A_cmd[3] = {0x4c, 0x00, 0x00}; +static char config_gam_adjust_0B_indx[3] = {0x4c, 0x03, 0x0b}; +static char config_gam_adjust_0B_cmd[3] = {0x4c, 0x00, 0x00}; +static char config_gam_adjust_0C_indx[3] = {0x4c, 0x03, 0x0c}; +static char config_gam_adjust_0C_cmd[3] = {0x4c, 0x07, 0x07}; +static char config_gam_adjust_0D_indx[3] = {0x4c, 0x03, 0x0d}; +static char config_gam_adjust_0D_cmd[3] = {0x4c, 0x10, 0x10}; +static char config_gam_adjust_10_indx[3] = {0x4c, 0x03, 0x10}; +static char config_gam_adjust_10_cmd[3] = {0x4c, 0x01, 0x04}; +static char config_gam_adjust_11_indx[3] = {0x4c, 0x03, 0x11}; +static char config_gam_adjust_11_cmd[3] = {0x4c, 0x05, 0x03}; +static char config_gam_adjust_12_indx[3] = {0x4c, 0x03, 0x12}; +static char config_gam_adjust_12_cmd[3] = {0x4c, 0x03, 0x04}; +static char config_gam_adjust_15_indx[3] = {0x4c, 0x03, 0x15}; +static char config_gam_adjust_15_cmd[3] = {0x4c, 0x03, 0x04}; +static char config_gam_adjust_16_indx[3] = {0x4c, 0x03, 0x16}; +static char config_gam_adjust_16_cmd[3] = {0x4c, 0x03, 0x1c}; +static char config_gam_adjust_17_indx[3] = {0x4c, 0x03, 0x17}; +static char config_gam_adjust_17_cmd[3] = {0x4c, 0x02, 0x04}; +static char config_gam_adjust_18_indx[3] = {0x4c, 0x03, 0x18}; +static char config_gam_adjust_18_cmd[3] = {0x4c, 0x04, 0x02}; +static char config_gam_adjust_19_indx[3] = {0x4c, 0x03, 0x19}; +static char config_gam_adjust_19_cmd[3] = {0x4c, 0x03, 0x05}; +static char config_gam_adjust_1C_indx[3] = {0x4c, 0x03, 0x1c}; +static char config_gam_adjust_1C_cmd[3] = {0x4c, 0x07, 0x07}; +static char config_gam_adjust_1D_indx[3] = {0x4c, 0x03, 0x1D}; +static char config_gam_adjust_1D_cmd[3] = {0x4c, 0x02, 0x1f}; +static char config_gam_adjust_20_indx[3] = {0x4c, 0x03, 0x20}; +static char config_gam_adjust_20_cmd[3] = {0x4c, 0x05, 0x07}; +static char config_gam_adjust_21_indx[3] = {0x4c, 0x03, 0x21}; +static char config_gam_adjust_21_cmd[3] = {0x4c, 0x06, 0x04}; +static char config_gam_adjust_22_indx[3] = {0x4c, 0x03, 0x22}; +static char config_gam_adjust_22_cmd[3] = {0x4c, 0x04, 0x05}; +static char config_gam_adjust_27_indx[3] = {0x4c, 0x03, 0x27}; +static char config_gam_adjust_27_cmd[3] = {0x4c, 0x02, 0x03}; +static char config_gam_adjust_28_indx[3] = {0x4c, 0x03, 0x28}; +static char config_gam_adjust_28_cmd[3] = {0x4c, 0x03, 0x00}; +static char config_gam_adjust_29_indx[3] = {0x4c, 0x03, 0x29}; +static char config_gam_adjust_29_cmd[3] = {0x4c, 0x00, 0x02}; + +static char config_Power_Ctrl_1_indx[3] = {0x4c, 0x01, 0x00}; +static char config_Power_Ctrl_1b_cmd[3] = {0x4c, 0x36, 0x3c}; +static char config_Power_Ctrl_2_indx[3] = {0x4c, 0x01, 0x01}; +static char config_Power_Ctrl_2b_cmd[3] = {0x4c, 0x40, 0x03}; +static char config_Power_Ctrl_3_indx[3] = {0x4c, 0x01, 0x02}; +static char config_Power_Ctrl_3a_cmd[3] = {0x4c, 0x00, 0x01}; +static char config_Power_Ctrl_4_indx[3] = {0x4c, 0x01, 0x03}; +static char config_Power_Ctrl_4a_cmd[3] = {0x4c, 0x3c, 0x58}; +static char config_Power_Ctrl_6_indx[3] = {0x4c, 0x01, 0x0c}; +static char config_Power_Ctrl_6a_cmd[3] = {0x4c, 0x01, 0x35}; + +static char config_Auto_Sequencer_Setting_b_cmd[3] = {0x4c, 0x00, 0x02}; + +static char config_Panel_IF_Ctrl_10_indx[3] = {0x4c, 0x00, 0x29}; +static char config_Panel_IF_Ctrl_10a_cmd[3] = {0x4c, 0x03, 0xbf}; +static char config_Auto_Sequencer_Setting_indx[3] = {0x4c, 0x01, 0x06}; +static char config_Auto_Sequencer_Setting_c_cmd[3] = {0x4c, 0x00, 0x03}; +static char config_Power_Ctrl_2c_cmd[3] = {0x4c, 0x40, 0x10}; + +static char config_VIDEO[2] = {0x40, 0x00}; + +static char config_Panel_IF_Ctrl_10_indx_off[3] = {0x4C, 0x00, 0x29}; + +static char config_Panel_IF_Ctrl_10b_cmd_off[3] = {0x4C, 0x00, 0x02}; + +static char config_Power_Ctrl_1a_cmd[3] = {0x4C, 0x30, 0x00}; + +static struct dsi_cmd_desc renesas_sleep_off_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, RENESAS_SLEEP_OFF_DELAY, + sizeof(config_sleep_out), config_sleep_out } +}; + +static struct dsi_cmd_desc renesas_display_off_cmds[] = { + /* Choosing Command Mode */ + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_CMD_MODE), config_CMD_MODE }, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_indx), + config_Auto_Sequencer_Setting_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_b_cmd), + config_Auto_Sequencer_Setting_b_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY * 2, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + /* After waiting >= 5 frames, turn OFF RGB signals + This is done by on DSI/MDP (depends on Vid/Cmd Mode. */ + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_indx), + config_Auto_Sequencer_Setting_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_a_cmd), + config_Auto_Sequencer_Setting_a_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_10_indx_off), + config_Panel_IF_Ctrl_10_indx_off}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_10b_cmd_off), + config_Panel_IF_Ctrl_10b_cmd_off}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_1_indx), + config_Power_Ctrl_1_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_1a_cmd), + config_Power_Ctrl_1a_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_TEOFF), config_TEOFF}, +}; + +static struct dsi_cmd_desc renesas_display_on_cmds[] = { + /* Choosing Command Mode */ + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_CMD_MODE), config_CMD_MODE }, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_WRTXHT), config_WRTXHT }, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_WRTXVT), config_WRTXVT }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_PLL2NR), config_PLL2NR }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_PLL2NF1), config_PLL2NF1 }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_PLL2NF2), config_PLL2NF2 }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_PLL2BWADJ1), config_PLL2BWADJ1}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_PLL2BWADJ2), config_PLL2BWADJ2}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_PLL2CTL), config_PLL2CTL}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICBR), config_DBICBR}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICTYPE), config_DBICTYPE}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET1), config_DBICSET1}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD), config_DBICADD}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICCTL), config_DBICCTL}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_COLMOD_888), config_COLMOD_888}, + /* Choose config_COLMOD_565 or config_COLMOD_666PACK for other modes */ + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_MADCTL), config_MADCTL}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBIOC), config_DBIOC}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_CASET), config_CASET}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_PASET), config_PASET}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DSIRXCTL), config_DSIRXCTL}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_TEON), config_TEON}, + {DTYPE_DCS_WRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_TXON), config_TXON}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_BLSET_TM), config_BLSET_TM}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_AGCPSCTL_TM), config_AGCPSCTL_TM}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_1_indx), config_Power_Ctrl_1_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_1a_cmd), config_Power_Ctrl_1a_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_2_indx), config_Power_Ctrl_2_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_2a_cmd), config_Power_Ctrl_2a_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_indx), + config_Auto_Sequencer_Setting_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_a_cmd), + config_Auto_Sequencer_Setting_a_cmd }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(Driver_Output_Ctrl_indx), Driver_Output_Ctrl_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(Driver_Output_Ctrl_cmd), + Driver_Output_Ctrl_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_LCD_drive_AC_Ctrl_indx), + config_LCD_drive_AC_Ctrl_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_LCD_drive_AC_Ctrl_cmd), + config_LCD_drive_AC_Ctrl_cmd }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Entry_Mode_indx), + config_Entry_Mode_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Entry_Mode_cmd), + config_Entry_Mode_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_Ctrl_1_indx), + config_Display_Ctrl_1_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_Ctrl_1_cmd), + config_Display_Ctrl_1_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_Ctrl_2_indx), + config_Display_Ctrl_2_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_Ctrl_2_cmd), + config_Display_Ctrl_2_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_Ctrl_3_indx), + config_Display_Ctrl_3_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_Ctrl_3_cmd), + config_Display_Ctrl_3_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_IF_Ctrl_1_indx), + config_Display_IF_Ctrl_1_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_IF_Ctrl_1_cmd), + config_Display_IF_Ctrl_1_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_IF_Ctrl_2_indx), + config_Display_IF_Ctrl_2_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Display_IF_Ctrl_2_cmd), + config_Display_IF_Ctrl_2_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_1_indx), + config_Panel_IF_Ctrl_1_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_1_cmd), + config_Panel_IF_Ctrl_1_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_3_indx), + config_Panel_IF_Ctrl_3_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_3_cmd), + config_Panel_IF_Ctrl_3_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_4_indx), + config_Panel_IF_Ctrl_4_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_4_cmd), + config_Panel_IF_Ctrl_4_cmd }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_5_indx), + config_Panel_IF_Ctrl_5_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_5_cmd), + config_Panel_IF_Ctrl_5_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_6_indx), + config_Panel_IF_Ctrl_6_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_6_cmd), + config_Panel_IF_Ctrl_6_cmd }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_8_indx), + config_Panel_IF_Ctrl_8_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_8_cmd), + config_Panel_IF_Ctrl_8_cmd }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_9_indx), + config_Panel_IF_Ctrl_9_indx }, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_9_cmd), + config_Panel_IF_Ctrl_9_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_00_indx), + config_gam_adjust_00_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_00_cmd), + config_gam_adjust_00_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_01_indx), + config_gam_adjust_01_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_01_cmd), + config_gam_adjust_01_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_02_indx), + config_gam_adjust_02_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_02_cmd), + config_gam_adjust_02_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_03_indx), + config_gam_adjust_03_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_03_cmd), + config_gam_adjust_03_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_04_indx), config_gam_adjust_04_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_04_cmd), config_gam_adjust_04_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_05_indx), config_gam_adjust_05_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_05_cmd), config_gam_adjust_05_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_06_indx), config_gam_adjust_06_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_06_cmd), config_gam_adjust_06_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_07_indx), config_gam_adjust_07_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_07_cmd), config_gam_adjust_07_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_08_indx), config_gam_adjust_08_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_08_cmd), config_gam_adjust_08_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_09_indx), config_gam_adjust_09_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_09_cmd), config_gam_adjust_09_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_0A_indx), config_gam_adjust_0A_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_0A_cmd), config_gam_adjust_0A_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_0B_indx), config_gam_adjust_0B_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_0B_cmd), config_gam_adjust_0B_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_0C_indx), config_gam_adjust_0C_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_0C_cmd), config_gam_adjust_0C_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_0D_indx), config_gam_adjust_0D_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_0D_cmd), config_gam_adjust_0D_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_10_indx), config_gam_adjust_10_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_10_cmd), config_gam_adjust_10_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_11_indx), config_gam_adjust_11_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_11_cmd), config_gam_adjust_11_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_12_indx), config_gam_adjust_12_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_12_cmd), config_gam_adjust_12_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_15_indx), config_gam_adjust_15_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_15_cmd), config_gam_adjust_15_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_16_indx), config_gam_adjust_16_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_16_cmd), config_gam_adjust_16_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_17_indx), config_gam_adjust_17_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_17_cmd), config_gam_adjust_17_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_18_indx), config_gam_adjust_18_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_18_cmd), config_gam_adjust_18_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_19_indx), config_gam_adjust_19_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_19_cmd), config_gam_adjust_19_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_1C_indx), config_gam_adjust_1C_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_1C_cmd), config_gam_adjust_1C_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_1D_indx), config_gam_adjust_1D_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_1D_cmd), config_gam_adjust_1D_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_20_indx), config_gam_adjust_20_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_20_cmd), config_gam_adjust_20_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_21_indx), config_gam_adjust_21_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_21_cmd), config_gam_adjust_21_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_22_indx), config_gam_adjust_22_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_22_cmd), config_gam_adjust_22_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_27_indx), config_gam_adjust_27_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_27_cmd), config_gam_adjust_27_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_28_indx), config_gam_adjust_28_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_28_cmd), config_gam_adjust_28_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_29_indx), config_gam_adjust_29_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_gam_adjust_29_cmd), config_gam_adjust_29_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_1_indx), config_Power_Ctrl_1_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_1b_cmd), config_Power_Ctrl_1b_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_2_indx), config_Power_Ctrl_2_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_2b_cmd), config_Power_Ctrl_2b_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_3_indx), config_Power_Ctrl_3_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_3a_cmd), config_Power_Ctrl_3a_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_4_indx), config_Power_Ctrl_4_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_4a_cmd), config_Power_Ctrl_4a_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_6_indx), config_Power_Ctrl_6_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_6a_cmd), config_Power_Ctrl_6a_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_indx), + config_Auto_Sequencer_Setting_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_b_cmd), + config_Auto_Sequencer_Setting_b_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_10_indx), + config_Panel_IF_Ctrl_10_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Panel_IF_Ctrl_10a_cmd), + config_Panel_IF_Ctrl_10a_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_indx), + config_Auto_Sequencer_Setting_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Auto_Sequencer_Setting_c_cmd), + config_Auto_Sequencer_Setting_c_cmd}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD70), config_DBICADD70}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_2_indx), + config_Power_Ctrl_2_indx}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICSET_15), config_DBICSET_15}, + {DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_DBICADD72), config_DBICADD72}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_Power_Ctrl_2c_cmd), + config_Power_Ctrl_2c_cmd}, + + {DTYPE_DCS_WRITE1, 1, 0, 0, 0/* RENESAS_CMD_DELAY */, + sizeof(config_DBICSET_15), config_DBICSET_15}, + +}; + +static char config_WRTXHT2[7] = {0x92, 0x15, 0x05, 0x0F, 0x00, 0x01, 0xe0}; +static char config_WRTXVT2[7] = {0x8b, 0x14, 0x01, 0x14, 0x00, 0x03, 0x60}; + +static struct dsi_cmd_desc renesas_hvga_on_cmds[] = { + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_WRTXHT2), config_WRTXHT2}, + {DTYPE_DCS_LWRITE, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_WRTXVT2), config_WRTXVT2}, +}; + +static struct dsi_cmd_desc renesas_video_on_cmds[] = { +{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_VIDEO), config_VIDEO} +}; + +static struct dsi_cmd_desc renesas_cmd_on_cmds[] = { +{DTYPE_DCS_WRITE1, 1, 0, 0, RENESAS_CMD_DELAY, + sizeof(config_CMD_MODE), config_CMD_MODE}, +}; + +static int mipi_renesas_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + + mfd = platform_get_drvdata(pdev); + mipi = &mfd->panel_info.mipi; + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_sleep_off_cmds, + ARRAY_SIZE(renesas_sleep_off_cmds)); + + mipi_set_tx_power_mode(1); + mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_display_on_cmds, + ARRAY_SIZE(renesas_display_on_cmds)); + + if (cpu_is_msm7x25a() || cpu_is_msm7x25aa() || cpu_is_msm7x25ab()) { + mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_hvga_on_cmds, + ARRAY_SIZE(renesas_hvga_on_cmds)); + } + + if (mipi->mode == DSI_VIDEO_MODE) + mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_video_on_cmds, + ARRAY_SIZE(renesas_video_on_cmds)); + else + mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_cmd_on_cmds, + ARRAY_SIZE(renesas_cmd_on_cmds)); + mipi_set_tx_power_mode(0); + + return 0; +} + +static int mipi_renesas_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_display_off_cmds, + ARRAY_SIZE(renesas_display_off_cmds)); + + return 0; +} + +static int mipi_renesas_lcd_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + mipi_renesas_pdata = pdev->dev.platform_data; + return 0; + } + + msm_fb_add_device(pdev); + + return 0; +} + +static void mipi_renesas_set_backlight(struct msm_fb_data_type *mfd) +{ + int ret = -EPERM; + int bl_level; + + bl_level = mfd->bl_level; + + if (mipi_renesas_pdata && mipi_renesas_pdata->pmic_backlight) + ret = mipi_renesas_pdata->pmic_backlight(bl_level); + else + pr_err("%s(): Backlight level set failed", __func__); +} + +static struct platform_driver this_driver = { + .probe = mipi_renesas_lcd_probe, + .driver = { + .name = "mipi_renesas", + }, +}; + +static struct msm_fb_panel_data renesas_panel_data = { + .on = mipi_renesas_lcd_on, + .off = mipi_renesas_lcd_off, + .set_backlight = mipi_renesas_set_backlight, +}; + +static int ch_used[3]; + +int mipi_renesas_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + ch_used[channel] = TRUE; + + ret = mipi_renesas_lcd_init(); + if (ret) { + pr_err("mipi_renesas_lcd_init() failed with ret %u\n", ret); + return ret; + } + + pdev = platform_device_alloc("mipi_renesas", (panel << 8)|channel); + if (!pdev) + return -ENOMEM; + + renesas_panel_data.panel_info = *pinfo; + + ret = platform_device_add_data(pdev, &renesas_panel_data, + sizeof(renesas_panel_data)); + if (ret) { + pr_err("%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static int mipi_renesas_lcd_init(void) +{ + mipi_dsi_buf_alloc(&renesas_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&renesas_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} diff --git a/drivers/video/msm/mipi_renesas.h b/drivers/video/msm/mipi_renesas.h new file mode 100644 index 0000000000000000000000000000000000000000..67f777fef564819f622e42fafd18a5bf66f5d294 --- /dev/null +++ b/drivers/video/msm/mipi_renesas.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MIPI_RENESAS_H +#define MIPI_RENESAS_H + +#define RENESAS_FWVGA_TWO_LANE + +int mipi_renesas_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#endif /* MIPI_RENESAS_H */ diff --git a/drivers/video/msm/mipi_renesas_cmd_fwvga_pt.c b/drivers/video/msm/mipi_renesas_cmd_fwvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..1f796ab106929c8cdda0ea59517dc490989cb768 --- /dev/null +++ b/drivers/video/msm/mipi_renesas_cmd_fwvga_pt.c @@ -0,0 +1,157 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_renesas.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = { +#ifdef CONFIG_FB_MSM_MDP303 + /* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */ + /* regulator */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + {0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90, + 0x18, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x01, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +#else + /* DSI_BIT_CLK at 400MHz, 1 lane, RGB888 */ + {0x03, 0x01, 0x01, 0x00}, /* regulator */ + /* timing */ + {0x22, 0x0c, 0x7, 0x00, 0x10, 0x20, 0x10, + 0xd, 0x8, 0x2, 0x3}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xee, 0x00, 0x6, 0x00}, + /* pll control */ + {0x40, 0x2f, 0xb1, 0xda, 0x00, 0x50, 0x48, 0x63, +#if defined(RENESAS_FWVGA_TWO_LANE) + 0x33, 0x1f, 0x07, +#else /* default set to 1 lane */ + 0x30, 0x07, 0x07, +#endif + 0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0}, +#endif +}; + +static int __init mipi_cmd_renesas_fwvga_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_cmd_renesas_fwvga")) + return 0; + + pinfo.xres = 480; + pinfo.yres = 864; + pinfo.type = MIPI_CMD_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; +#ifdef CONFIG_FB_MSM_MDP303 + pinfo.lcdc.h_back_porch = 100; + pinfo.lcdc.h_front_porch = 100; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 20; + pinfo.lcdc.v_front_porch = 20; + pinfo.lcdc.v_pulse_width = 1; +#else + pinfo.lcdc.h_front_porch = 50; +#if defined(RENESAS_FWVGA_TWO_LANE) + pinfo.lcdc.h_back_porch = 400; + pinfo.lcdc.h_pulse_width = 5; + pinfo.lcdc.v_back_porch = 75; + pinfo.lcdc.v_front_porch = 5; + pinfo.lcdc.v_pulse_width = 1; +#else + pinfo.lcdc.h_back_porch = 50; + pinfo.lcdc.h_pulse_width = 20; + pinfo.lcdc.v_back_porch = 10; + pinfo.lcdc.v_front_porch = 10; + pinfo.lcdc.v_pulse_width = 5; +#endif + +#endif /* CONFIG_FB_MSM_MDP303 */ + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 100; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + +#ifdef CONFIG_FB_MSM_MDP303 + pinfo.clk_rate = 499000000; +#else + pinfo.clk_rate = 152000000; +#endif + pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */ + + pinfo.mipi.mode = DSI_CMD_MODE; + pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo.mipi.data_lane0 = TRUE; +#ifdef CONFIG_FB_MSM_MDP303 + pinfo.lcd.vsync_enable = TRUE; + pinfo.lcd.hw_vsync_mode = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2F; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.te_sel = 1; /* TE from vsync gpio */ + pinfo.mipi.interleave_max = 1; + pinfo.mipi.insert_dcs_cmd = TRUE; + pinfo.mipi.wr_mem_continue = 0x3c; + pinfo.mipi.wr_mem_start = 0x2c; + pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db; + pinfo.mipi.tx_eot_append = 0x01; + pinfo.mipi.rx_eot_ignore = 0; + pinfo.mipi.dlane_swap = 0x01; +#else +#if defined(RENESAS_FWVGA_TWO_LANE) + pinfo.mipi.data_lane1 = TRUE; +#else + pinfo.mipi.data_lane1 = FALSE; +#endif + pinfo.mipi.t_clk_post = 0x18; + pinfo.mipi.t_clk_pre = 0x14; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.te_sel = 1; /* TE from vsycn gpio */ + pinfo.mipi.interleave_max = 1; + pinfo.mipi.insert_dcs_cmd = TRUE; + pinfo.mipi.wr_mem_continue = 0x3c; + pinfo.mipi.wr_mem_start = 0x2c; + pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db; +#endif /* CONFIG_FB_MSM_MDP303 */ + + ret = mipi_renesas_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_FWVGA_PT); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_cmd_renesas_fwvga_pt_init); diff --git a/drivers/video/msm/mipi_renesas_video_fwvga_pt.c b/drivers/video/msm/mipi_renesas_video_fwvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..a8baca61da4064dc1eca3271f106ff6b2bb22b7c --- /dev/null +++ b/drivers/video/msm/mipi_renesas_video_fwvga_pt.c @@ -0,0 +1,163 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_renesas.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { +#ifdef CONFIG_FB_MSM_MDP303 + /* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */ + /* regulator */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + {0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90, + 0x18, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x00, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +#else + /* DSI_BIT_CLK at 400MHz, 1 lane, RGB888 */ + /* regulator */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + {0xaa, 0x3b, 0x1b, 0x00, 0x52, 0x58, 0x20, 0x3f, + 0x2e, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xee, 0x00, 0x86, 0x00}, + /* pll control */ + {0x40, 0xc7, 0xb0, 0xda, 0x00, 0x50, 0x48, 0x63, +#if defined(RENESAS_FWVGA_TWO_LANE) + 0x30, 0x07, 0x03, +#else + /* default set to 1 lane */ + 0x30, 0x07, 0x07, +#endif + 0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0}, +#endif +}; + +static int __init mipi_video_renesas_fwvga_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_renesas_fwvga")) + return 0; + + pinfo.xres = 480; + pinfo.yres = 864; + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; +#ifdef CONFIG_FB_MSM_MDP303 + pinfo.lcdc.h_back_porch = 100; + pinfo.lcdc.h_front_porch = 100; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 20; + pinfo.lcdc.v_front_porch = 20; + pinfo.lcdc.v_pulse_width = 1; + pinfo.clk_rate = 499000000; +#else + +#if defined(RENESAS_FWVGA_TWO_LANE) + pinfo.lcdc.h_back_porch = 400; +#else + pinfo.lcdc.h_back_porch = 50; +#endif + pinfo.lcdc.h_front_porch = 50; + +#if defined(RENESAS_FWVGA_TWO_LANE) + pinfo.lcdc.h_pulse_width = 5; +#else + pinfo.lcdc.h_pulse_width = 20; +#endif + +#if defined(RENESAS_FWVGA_TWO_LANE) + pinfo.lcdc.v_back_porch = 75; + pinfo.lcdc.v_front_porch = 5; + pinfo.lcdc.v_pulse_width = 1; +#else + pinfo.lcdc.v_back_porch = 10; + pinfo.lcdc.v_front_porch = 10; + pinfo.lcdc.v_pulse_width = 5; +#endif + +#endif + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 100; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + pinfo.mipi.pulse_mode_hsa_he = TRUE; + pinfo.mipi.hfp_power_stop = TRUE; + pinfo.mipi.hbp_power_stop = TRUE; + pinfo.mipi.hsa_power_stop = TRUE; + pinfo.mipi.eof_bllp_power_stop = TRUE; + pinfo.mipi.bllp_power_stop = TRUE; +#ifdef CONFIG_FB_MSM_MDP303 + pinfo.mipi.traffic_mode = DSI_BURST_MODE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2F; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + pinfo.mipi.dlane_swap = 0x01; + pinfo.mipi.tx_eot_append = 0x01; +#else + pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR; + pinfo.mipi.data_lane0 = TRUE; +#if defined(RENESAS_FWVGA_TWO_LANE) + pinfo.mipi.data_lane1 = TRUE; +#else + pinfo.mipi.data_lane1 = FALSE; +#endif + pinfo.mipi.t_clk_post = 0x03; + pinfo.mipi.t_clk_pre = 0x24; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; +#endif + + ret = mipi_renesas_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_FWVGA_PT); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_renesas_fwvga_pt_init); diff --git a/drivers/video/msm/mipi_simulator.c b/drivers/video/msm/mipi_simulator.c new file mode 100644 index 0000000000000000000000000000000000000000..998e7dea25429a0e08d9f79a7d46a073f7a1bcc8 --- /dev/null +++ b/drivers/video/msm/mipi_simulator.c @@ -0,0 +1,167 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_simulator.h" + +static struct dsi_buf simulator_tx_buf; +static struct dsi_buf simulator_rx_buf; +static struct msm_panel_common_pdata *mipi_simulator_pdata; + +static int mipi_simulator_lcd_init(void); + +static char display_on[2] = {0x00, 0x00}; +static char display_off[2] = {0x00, 0x00}; + +static struct dsi_cmd_desc display_on_cmds[] = { + {DTYPE_PERIPHERAL_ON, 1, 0, 0, 0, sizeof(display_on), + display_on} +}; +static struct dsi_cmd_desc display_off_cmds[] = { + {DTYPE_PERIPHERAL_OFF, 1, 0, 0, 0, sizeof(display_off), + display_off} +}; + +static int mipi_simulator_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + + mfd = platform_get_drvdata(pdev); + mipi = &mfd->panel_info.mipi; + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + pr_debug("%s:%d, debug info (mode) : %d", __func__, __LINE__, + mipi->mode); + + if (mipi->mode == DSI_VIDEO_MODE) { + mipi_dsi_cmds_tx(mfd, &simulator_tx_buf, display_on_cmds, + ARRAY_SIZE(display_on_cmds)); + } else { + pr_err("%s:%d, CMD MODE NOT SUPPORTED", __func__, __LINE__); + return -EINVAL; + } + + return 0; +} + +static int mipi_simulator_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + + mfd = platform_get_drvdata(pdev); + mipi = &mfd->panel_info.mipi; + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + pr_debug("%s:%d, debug info", __func__, __LINE__); + + if (mipi->mode == DSI_VIDEO_MODE) { + mipi_dsi_cmds_tx(mfd, &simulator_tx_buf, display_off_cmds, + ARRAY_SIZE(display_off_cmds)); + } else { + pr_debug("%s:%d, DONT REACH HERE", __func__, __LINE__); + return -EINVAL; + } + + return 0; +} + +static int mipi_simulator_lcd_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + mipi_simulator_pdata = pdev->dev.platform_data; + return 0; + } + pr_debug("%s:%d, debug info", __func__, __LINE__); + + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = mipi_simulator_lcd_probe, + .driver = { + .name = "mipi_simulator", + }, +}; + +static struct msm_fb_panel_data simulator_panel_data = { + .on = mipi_simulator_lcd_on, + .off = mipi_simulator_lcd_off, +}; + +static int ch_used[3]; + +int mipi_simulator_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + ch_used[channel] = TRUE; + + pr_debug("%s:%d, debug info", __func__, __LINE__); + ret = mipi_simulator_lcd_init(); + if (ret) { + pr_err("mipi_simulator_lcd_init() failed with ret %u\n", ret); + return ret; + } + + pdev = platform_device_alloc("mipi_simulator", (panel << 8)|channel); + if (!pdev) + return -ENOMEM; + + simulator_panel_data.panel_info = *pinfo; + + ret = platform_device_add_data(pdev, &simulator_panel_data, + sizeof(simulator_panel_data)); + if (ret) { + pr_err(KERN_ERR + "%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err(KERN_ERR + "%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static int mipi_simulator_lcd_init(void) +{ + mipi_dsi_buf_alloc(&simulator_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&simulator_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} diff --git a/drivers/video/msm/mipi_simulator.h b/drivers/video/msm/mipi_simulator.h new file mode 100644 index 0000000000000000000000000000000000000000..6c0a60c105fa4f8c402f51171e58629d97c59d97 --- /dev/null +++ b/drivers/video/msm/mipi_simulator.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MIPI_SIMULATOR_H +#define MIPI_SIMULATOR_H + +int mipi_simulator_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#endif /* MIPI_SIMULATOR_H */ diff --git a/drivers/video/msm/mipi_simulator_video.c b/drivers/video/msm/mipi_simulator_video.c new file mode 100644 index 0000000000000000000000000000000000000000..258a7ecabe5ce88b8eed483d75286288a0d90250 --- /dev/null +++ b/drivers/video/msm/mipi_simulator_video.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_simulator.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + {0x03, 0x01, 0x01, 0x00}, + {0xaa, 0x3b, 0x1b, 0x00, 0x52, 0x58, 0x20, 0x3f, + 0x2e, 0x03, 0x04}, + {0x7f, 0x00, 0x00, 0x00}, + {0xee, 0x00, 0x86, 0x00}, + {0x40, 0xc7, 0xb0, 0xda, 0x00, 0x50, 0x48, 0x63, + 0x30, 0x07, 0x03, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x54, 0x06, 0x10, 0x04, 0x0}, +}; + +static int __init mipi_video_simulator_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_simulator_vga")) + return 0; + pinfo.xres = 640; + pinfo.yres = 480; + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + + pinfo.lcdc.h_back_porch = 6; + pinfo.lcdc.h_front_porch = 6; + pinfo.lcdc.h_pulse_width = 2; + pinfo.lcdc.v_back_porch = 6; + pinfo.lcdc.v_front_porch = 6; + pinfo.lcdc.v_pulse_width = 2; + + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 15; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + pinfo.mipi.pulse_mode_hsa_he = TRUE; + pinfo.mipi.hfp_power_stop = TRUE; + pinfo.mipi.hbp_power_stop = TRUE; + pinfo.mipi.hsa_power_stop = TRUE; + pinfo.mipi.eof_bllp_power_stop = TRUE; + pinfo.mipi.bllp_power_stop = TRUE; + pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.t_clk_post = 0x03; + pinfo.mipi.t_clk_pre = 0x24; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + + ret = mipi_simulator_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_VGA); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_simulator_init); diff --git a/drivers/video/msm/mipi_tc358764_dsi2lvds.c b/drivers/video/msm/mipi_tc358764_dsi2lvds.c new file mode 100644 index 0000000000000000000000000000000000000000..0d44a3048853365f3ade6e50180b0807587578c1 --- /dev/null +++ b/drivers/video/msm/mipi_tc358764_dsi2lvds.c @@ -0,0 +1,997 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Toshiba MIPI-DSI-to-LVDS Bridge driver. + * Device Model TC358764XBG/65XBG. + * Reference document: TC358764XBG_65XBG_V119.pdf + * + * The Host sends a DSI Generic Long Write packet (Data ID = 0x29) over the + * DSI link for each write access transaction to the chip configuration + * registers. + * Payload of this packet is 16-bit register address and 32-bit data. + * Multiple data values are allowed for sequential addresses. + * + * The Host sends a DSI Generic Read packet (Data ID = 0x24) over the DSI + * link for each read request transaction to the chip configuration + * registers. Payload of this packet is further defined as follows: + * 16-bit address followed by a 32-bit value (Generic Long Read Response + * packet). + * + * The bridge supports 5 GPIO lines controlled via the GPC register. + * + * The bridge support I2C Master/Slave. + * The I2C slave can be used for read/write to the bridge register instead of + * using the DSI interface. + * I2C slave address is 0x0F (read/write 0x1F/0x1E). + * The I2C Master can be used for communication with the panel if + * it has an I2C slave. + * + * NOTE: The I2C interface is not used in this driver. + * Only the DSI interface is used for read/write the bridge registers. + * + * Pixel data can be transmitted in non-burst or burst fashion. + * Non-burst refers to pixel data packet transmission time on DSI link + * being roughly the same (to account for packet overhead time) + * as active video line time on LVDS output (i.e. DE = 1). + * And burst refers to pixel data packet transmission time on DSI link + * being less than the active video line time on LVDS output. + * Video mode transmission is further differentiated by the types of + * timing events being transmitted. + * Video pulse mode refers to the case where both sync start and sync end + * events (for frame and line) are transmitted. + * Video event mode refers to the case where only sync start events + * are transmitted. + * This is configured via register bit VPCTRL.EVTMODE. + * + */ + +/* #define DEBUG 1 */ + +/** + * Use the I2C master to control the panel. + */ +/* #define TC358764_USE_I2C_MASTER */ + +#define DRV_NAME "mipi_tc358764" + +#include +#include +#include +#include +#include "msm_fb.h" +#include "mdp4.h" +#include "mipi_dsi.h" +#include "mipi_tc358764_dsi2lvds.h" + +/* Registers definition */ + +/* DSI D-PHY Layer Registers */ +#define D0W_DPHYCONTTX 0x0004 /* Data Lane 0 DPHY Tx Control */ +#define CLW_DPHYCONTRX 0x0020 /* Clock Lane DPHY Rx Control */ +#define D0W_DPHYCONTRX 0x0024 /* Data Lane 0 DPHY Rx Control */ +#define D1W_DPHYCONTRX 0x0028 /* Data Lane 1 DPHY Rx Control */ +#define D2W_DPHYCONTRX 0x002C /* Data Lane 2 DPHY Rx Control */ +#define D3W_DPHYCONTRX 0x0030 /* Data Lane 3 DPHY Rx Control */ +#define COM_DPHYCONTRX 0x0038 /* DPHY Rx Common Control */ +#define CLW_CNTRL 0x0040 /* Clock Lane Control */ +#define D0W_CNTRL 0x0044 /* Data Lane 0 Control */ +#define D1W_CNTRL 0x0048 /* Data Lane 1 Control */ +#define D2W_CNTRL 0x004C /* Data Lane 2 Control */ +#define D3W_CNTRL 0x0050 /* Data Lane 3 Control */ +#define DFTMODE_CNTRL 0x0054 /* DFT Mode Control */ + +/* DSI PPI Layer Registers */ +#define PPI_STARTPPI 0x0104 /* START control bit of PPI-TX function. */ +#define PPI_BUSYPPI 0x0108 +#define PPI_LINEINITCNT 0x0110 /* Line Initialization Wait Counter */ +#define PPI_LPTXTIMECNT 0x0114 +#define PPI_LANEENABLE 0x0134 /* Enables each lane at the PPI layer. */ +#define PPI_TX_RX_TA 0x013C /* DSI Bus Turn Around timing parameters */ + +/* Analog timer function enable */ +#define PPI_CLS_ATMR 0x0140 /* Delay for Clock Lane in LPRX */ +#define PPI_D0S_ATMR 0x0144 /* Delay for Data Lane 0 in LPRX */ +#define PPI_D1S_ATMR 0x0148 /* Delay for Data Lane 1 in LPRX */ +#define PPI_D2S_ATMR 0x014C /* Delay for Data Lane 2 in LPRX */ +#define PPI_D3S_ATMR 0x0150 /* Delay for Data Lane 3 in LPRX */ +#define PPI_D0S_CLRSIPOCOUNT 0x0164 + +#define PPI_D1S_CLRSIPOCOUNT 0x0168 /* For lane 1 */ +#define PPI_D2S_CLRSIPOCOUNT 0x016C /* For lane 2 */ +#define PPI_D3S_CLRSIPOCOUNT 0x0170 /* For lane 3 */ + +#define CLS_PRE 0x0180 /* Digital Counter inside of PHY IO */ +#define D0S_PRE 0x0184 /* Digital Counter inside of PHY IO */ +#define D1S_PRE 0x0188 /* Digital Counter inside of PHY IO */ +#define D2S_PRE 0x018C /* Digital Counter inside of PHY IO */ +#define D3S_PRE 0x0190 /* Digital Counter inside of PHY IO */ +#define CLS_PREP 0x01A0 /* Digital Counter inside of PHY IO */ +#define D0S_PREP 0x01A4 /* Digital Counter inside of PHY IO */ +#define D1S_PREP 0x01A8 /* Digital Counter inside of PHY IO */ +#define D2S_PREP 0x01AC /* Digital Counter inside of PHY IO */ +#define D3S_PREP 0x01B0 /* Digital Counter inside of PHY IO */ +#define CLS_ZERO 0x01C0 /* Digital Counter inside of PHY IO */ +#define D0S_ZERO 0x01C4 /* Digital Counter inside of PHY IO */ +#define D1S_ZERO 0x01C8 /* Digital Counter inside of PHY IO */ +#define D2S_ZERO 0x01CC /* Digital Counter inside of PHY IO */ +#define D3S_ZERO 0x01D0 /* Digital Counter inside of PHY IO */ + +#define PPI_CLRFLG 0x01E0 /* PRE Counters has reached set values */ +#define PPI_CLRSIPO 0x01E4 /* Clear SIPO values, Slave mode use only. */ +#define HSTIMEOUT 0x01F0 /* HS Rx Time Out Counter */ +#define HSTIMEOUTENABLE 0x01F4 /* Enable HS Rx Time Out Counter */ +#define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX function */ +#define DSI_BUSYDSI 0x0208 +#define DSI_LANEENABLE 0x0210 /* Enables each lane at the Protocol layer. */ +#define DSI_LANESTATUS0 0x0214 /* Displays lane is in HS RX mode. */ +#define DSI_LANESTATUS1 0x0218 /* Displays lane is in ULPS or STOP state */ + +#define DSI_INTSTATUS 0x0220 /* Interrupt Status */ +#define DSI_INTMASK 0x0224 /* Interrupt Mask */ +#define DSI_INTCLR 0x0228 /* Interrupt Clear */ +#define DSI_LPTXTO 0x0230 /* Low Power Tx Time Out Counter */ + +#define DSIERRCNT 0x0300 /* DSI Error Count */ +#define APLCTRL 0x0400 /* Application Layer Control */ +#define RDPKTLN 0x0404 /* Command Read Packet Length */ +#define VPCTRL 0x0450 /* Video Path Control */ +#define HTIM1 0x0454 /* Horizontal Timing Control 1 */ +#define HTIM2 0x0458 /* Horizontal Timing Control 2 */ +#define VTIM1 0x045C /* Vertical Timing Control 1 */ +#define VTIM2 0x0460 /* Vertical Timing Control 2 */ +#define VFUEN 0x0464 /* Video Frame Timing Update Enable */ + +/* Mux Input Select for LVDS LINK Input */ +#define LVMX0003 0x0480 /* Bit 0 to 3 */ +#define LVMX0407 0x0484 /* Bit 4 to 7 */ +#define LVMX0811 0x0488 /* Bit 8 to 11 */ +#define LVMX1215 0x048C /* Bit 12 to 15 */ +#define LVMX1619 0x0490 /* Bit 16 to 19 */ +#define LVMX2023 0x0494 /* Bit 20 to 23 */ +#define LVMX2427 0x0498 /* Bit 24 to 27 */ + +#define LVCFG 0x049C /* LVDS Configuration */ +#define LVPHY0 0x04A0 /* LVDS PHY 0 */ +#define LVPHY1 0x04A4 /* LVDS PHY 1 */ +#define SYSSTAT 0x0500 /* System Status */ +#define SYSRST 0x0504 /* System Reset */ + +/* GPIO Registers */ +#define GPIOC 0x0520 /* GPIO Control */ +#define GPIOO 0x0524 /* GPIO Output */ +#define GPIOI 0x0528 /* GPIO Input */ + +/* I2C Registers */ +#define I2CTIMCTRL 0x0540 /* I2C IF Timing and Enable Control */ +#define I2CMADDR 0x0544 /* I2C Master Addressing */ +#define WDATAQ 0x0548 /* Write Data Queue */ +#define RDATAQ 0x054C /* Read Data Queue */ + +/* Chip ID and Revision ID Register */ +#define IDREG 0x0580 + +#define TC358764XBG_ID 0x00006500 + +/* Debug Registers */ +#define DEBUG00 0x05A0 /* Debug */ +#define DEBUG01 0x05A4 /* LVDS Data */ + +/* PWM */ +static u32 d2l_pwm_freq_hz = (3.921*1000); + +#define PWM_FREQ_HZ (d2l_pwm_freq_hz) +#define PWM_PERIOD_USEC (USEC_PER_SEC / PWM_FREQ_HZ) +#define PWM_DUTY_LEVEL (PWM_PERIOD_USEC / PWM_LEVEL) + +#define CMD_DELAY 100 +#define DSI_MAX_LANES 4 +#define KHZ 1000 +#define MHZ (1000*1000) + +/** + * Command payload for DTYPE_GEN_LWRITE (0x29) / DTYPE_GEN_READ2 (0x24). + */ +struct wr_cmd_payload { + u16 addr; + u32 data; +} __packed; + +/* + * Driver state. + */ +static struct msm_panel_common_pdata *d2l_common_pdata; +struct msm_fb_data_type *d2l_mfd; +static struct dsi_buf d2l_tx_buf; +static struct dsi_buf d2l_rx_buf; +static int led_pwm; +static struct pwm_device *bl_pwm; +static struct pwm_device *tn_pwm; +static int bl_level; +static u32 d2l_gpio_out_mask; +static u32 d2l_gpio_out_val; +static u32 d2l_3d_gpio_enable; +static u32 d2l_3d_gpio_mode; +static int d2l_enable_3d; +static struct i2c_client *d2l_i2c_client; +static struct i2c_driver d2l_i2c_slave_driver; + +static int mipi_d2l_init(void); +static int mipi_d2l_enable_3d(struct msm_fb_data_type *mfd, + bool enable, bool mode); +static u32 d2l_i2c_read_reg(struct i2c_client *client, u16 reg); +static u32 d2l_i2c_write_reg(struct i2c_client *client, u16 reg, u32 val); + +/** + * Read a bridge register + * + * @param mfd + * + * @return register data value + */ +static u32 mipi_d2l_read_reg(struct msm_fb_data_type *mfd, u16 reg) +{ + u32 data; + int len = 4; + struct dsi_cmd_desc cmd_read_reg = { + DTYPE_GEN_READ2, 1, 0, 1, 0, /* cmd 0x24 */ + sizeof(reg), (char *) ®}; + + mipi_dsi_buf_init(&d2l_tx_buf); + mipi_dsi_buf_init(&d2l_rx_buf); + + /* mutex had been acquired at mipi_dsi_on */ + len = mipi_dsi_cmds_rx(mfd, &d2l_tx_buf, &d2l_rx_buf, + &cmd_read_reg, len); + + data = *(u32 *)d2l_rx_buf.data; + + if (len != 4) + pr_err("%s: invalid rlen=%d, expecting 4.\n", __func__, len); + + pr_debug("%s: reg=0x%x.data=0x%08x.\n", __func__, reg, data); + + return data; +} + +/** + * Write a bridge register + * + * @param mfd + * + * @return int + */ +static int mipi_d2l_write_reg(struct msm_fb_data_type *mfd, u16 reg, u32 data) +{ + struct wr_cmd_payload payload; + struct dsi_cmd_desc cmd_write_reg = { + DTYPE_GEN_LWRITE, 1, 0, 0, 0, + sizeof(payload), (char *)&payload}; + + payload.addr = reg; + payload.data = data; + + /* mutex had been acquired at mipi_dsi_on */ + mipi_dsi_cmds_tx(mfd, &d2l_tx_buf, &cmd_write_reg, 1); + + pr_debug("%s: reg=0x%x. data=0x%x.\n", __func__, reg, data); + + return 0; +} + +static void mipi_d2l_read_status(struct msm_fb_data_type *mfd) +{ + mipi_d2l_read_reg(mfd, DSI_LANESTATUS0); /* 0x214 */ + mipi_d2l_read_reg(mfd, DSI_LANESTATUS1); /* 0x218 */ + mipi_d2l_read_reg(mfd, DSI_INTSTATUS); /* 0x220 */ + mipi_d2l_read_reg(mfd, SYSSTAT); /* 0x500 */ +} + +static void mipi_d2l_read_status_via_i2c(struct i2c_client *client) +{ + u32 tmp = 0; + + tmp = d2l_i2c_read_reg(client, DSIERRCNT); + d2l_i2c_write_reg(client, DSIERRCNT, 0xFFFF0000); + + d2l_i2c_read_reg(client, DSI_LANESTATUS0); /* 0x214 */ + d2l_i2c_read_reg(client, DSI_LANESTATUS1); /* 0x218 */ + d2l_i2c_read_reg(client, DSI_INTSTATUS); /* 0x220 */ + d2l_i2c_read_reg(client, SYSSTAT); /* 0x500 */ + + d2l_i2c_write_reg(client, DSIERRCNT, tmp); +} +/** + * Init the D2L bridge via the DSI interface for Video. + * + * VPCTRL.EVTMODE (0x20) configuration bit is needed to determine whether + * video timing information is delivered in pulse mode or event mode. + * In pulse mode, both Sync Start and End packets are required. + * In event mode, only Sync Start packets are required. + * + * @param mfd + * + * @return int + */ +static int mipi_d2l_dsi_init_sequence(struct msm_fb_data_type *mfd) +{ + struct mipi_panel_info *mipi = &mfd->panel_info.mipi; + u32 lanes_enable; + u32 vpctrl; + u32 htime1; + u32 vtime1; + u32 htime2; + u32 vtime2; + u32 ppi_tx_rx_ta; /* BTA Bus-Turn-Around */ + u32 lvcfg; + u32 hbpr; /* Horizontal Back Porch */ + u32 hpw; /* Horizontal Pulse Width */ + u32 vbpr; /* Vertical Back Porch */ + u32 vpw; /* Vertical Pulse Width */ + + u32 hfpr; /* Horizontal Front Porch */ + u32 hsize; /* Horizontal Active size */ + u32 vfpr; /* Vertical Front Porch */ + u32 vsize; /* Vertical Active size */ + bool vesa_rgb888 = false; + + lanes_enable = 0x01; /* clock-lane enable */ + lanes_enable |= (mipi->data_lane0 << 1); + lanes_enable |= (mipi->data_lane1 << 2); + lanes_enable |= (mipi->data_lane2 << 3); + lanes_enable |= (mipi->data_lane3 << 4); + + if (mipi->traffic_mode == DSI_NON_BURST_SYNCH_EVENT) + vpctrl = 0x01000120; + else if (mipi->traffic_mode == DSI_NON_BURST_SYNCH_PULSE) + vpctrl = 0x01000100; + else { + pr_err("%s.unsupported traffic_mode %d.\n", + __func__, mipi->traffic_mode); + return -EINVAL; + } + + if (mfd->panel_info.clk_rate > 800*1000*1000) { + pr_err("%s.unsupported clk_rate %d.\n", + __func__, mfd->panel_info.clk_rate); + return -EINVAL; + } + + pr_debug("%s.xres=%d.yres=%d.fps=%d.dst_format=%d.\n", + __func__, + mfd->panel_info.xres, + mfd->panel_info.yres, + mfd->panel_info.mipi.frame_rate, + mfd->panel_info.mipi.dst_format); + + hbpr = mfd->panel_info.lcdc.h_back_porch; + hpw = mfd->panel_info.lcdc.h_pulse_width; + vbpr = mfd->panel_info.lcdc.v_back_porch; + vpw = mfd->panel_info.lcdc.v_pulse_width; + + htime1 = (hbpr << 16) + hpw; + vtime1 = (vbpr << 16) + vpw; + + hfpr = mfd->panel_info.lcdc.h_front_porch; + hsize = mfd->panel_info.xres; + vfpr = mfd->panel_info.lcdc.v_front_porch; + vsize = mfd->panel_info.yres; + + htime2 = (hfpr << 16) + hsize; + vtime2 = (vfpr << 16) + vsize; + + lvcfg = 0x0003; /* PCLK=DCLK/3, Dual Link, LVEN */ + vpctrl = 0x01000120; /* Output RGB888 , Event-Mode , */ + ppi_tx_rx_ta = 0x00040004; + + if (mfd->panel_info.xres == 1366) { + ppi_tx_rx_ta = 0x00040004; + lvcfg = 0x01; /* LVEN */ + vesa_rgb888 = true; + } + + if (mfd->panel_info.xres == 1200) { + lvcfg = 0x0103; /* PCLK=DCLK/4, Dual Link, LVEN */ + vesa_rgb888 = true; + } + + pr_debug("%s.htime1=0x%x.\n", __func__, htime1); + pr_debug("%s.vtime1=0x%x.\n", __func__, vtime1); + pr_debug("%s.vpctrl=0x%x.\n", __func__, vpctrl); + pr_debug("%s.lvcfg=0x%x.\n", __func__, lvcfg); + + mipi_d2l_write_reg(mfd, SYSRST, 0xFF); + msleep(30); + + if (vesa_rgb888) { + /* VESA format instead of JEIDA format for RGB888 */ + mipi_d2l_write_reg(mfd, LVMX0003, 0x03020100); + mipi_d2l_write_reg(mfd, LVMX0407, 0x08050704); + mipi_d2l_write_reg(mfd, LVMX0811, 0x0F0E0A09); + mipi_d2l_write_reg(mfd, LVMX1215, 0x100D0C0B); + mipi_d2l_write_reg(mfd, LVMX1619, 0x12111716); + mipi_d2l_write_reg(mfd, LVMX2023, 0x1B151413); + mipi_d2l_write_reg(mfd, LVMX2427, 0x061A1918); + } + + mipi_d2l_write_reg(mfd, PPI_TX_RX_TA, ppi_tx_rx_ta); /* BTA */ + mipi_d2l_write_reg(mfd, PPI_LPTXTIMECNT, 0x00000004); + mipi_d2l_write_reg(mfd, PPI_D0S_CLRSIPOCOUNT, 0x00000003); + mipi_d2l_write_reg(mfd, PPI_D1S_CLRSIPOCOUNT, 0x00000003); + mipi_d2l_write_reg(mfd, PPI_D2S_CLRSIPOCOUNT, 0x00000003); + mipi_d2l_write_reg(mfd, PPI_D3S_CLRSIPOCOUNT, 0x00000003); + mipi_d2l_write_reg(mfd, PPI_LANEENABLE, lanes_enable); + mipi_d2l_write_reg(mfd, DSI_LANEENABLE, lanes_enable); + mipi_d2l_write_reg(mfd, PPI_STARTPPI, 0x00000001); + mipi_d2l_write_reg(mfd, DSI_STARTDSI, 0x00000001); + + mipi_d2l_write_reg(mfd, VPCTRL, vpctrl); /* RGB888 + Event mode */ + mipi_d2l_write_reg(mfd, HTIM1, htime1); + mipi_d2l_write_reg(mfd, VTIM1, vtime1); + mipi_d2l_write_reg(mfd, HTIM2, htime2); + mipi_d2l_write_reg(mfd, VTIM2, vtime2); + mipi_d2l_write_reg(mfd, VFUEN, 0x00000001); + mipi_d2l_write_reg(mfd, LVCFG, lvcfg); /* Enables LVDS tx */ + + return 0; +} + +/** + * Set Backlight level. + * + * @param pwm + * @param level + * + * @return int + */ +static int mipi_d2l_set_backlight_level(struct pwm_device *pwm, int level) +{ + int ret = 0; + + pr_debug("%s: level=%d.\n", __func__, level); + + if ((pwm == NULL) || (level > PWM_LEVEL) || (level < 0)) { + pr_err("%s.pwm=NULL.\n", __func__); + return -EINVAL; + } + + ret = pwm_config(pwm, PWM_DUTY_LEVEL * level, PWM_PERIOD_USEC); + if (ret) { + pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret); + return ret; + } + + ret = pwm_enable(pwm); + if (ret) { + pr_err("%s: pwm_enable() failed err=%d\n", + __func__, ret); + return ret; + } + + return 0; +} + +/** + * Set TN CLK. + * + * @param pwm + * @param level + * + * @return int + */ +static int mipi_d2l_set_tn_clk(struct pwm_device *pwm, u32 usec) +{ + int ret = 0; + + pr_debug("%s: usec=%d.\n", __func__, usec); + + ret = pwm_config(pwm, usec/2 , usec); + if (ret) { + pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret); + return ret; + } + + ret = pwm_enable(pwm); + if (ret) { + pr_err("%s: pwm_enable() failed err=%d\n", + __func__, ret); + return ret; + } + + return 0; +} + +/** + * LCD ON. + * + * Set LCD On via MIPI interface or I2C-Slave interface. + * Set Backlight on. + * + * @param pdev + * + * @return int + */ +static int mipi_d2l_lcd_on(struct platform_device *pdev) +{ + int ret = 0; + u32 chip_id; + struct msm_fb_data_type *mfd; + + pr_info("%s.\n", __func__); + + /* wait for valid clock before sending data over DSI or I2C. */ + msleep(30); + + mfd = platform_get_drvdata(pdev); + d2l_mfd = mfd; + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + chip_id = mipi_d2l_read_reg(mfd, IDREG); + + + if (chip_id != TC358764XBG_ID) { + pr_err("%s: invalid chip_id=0x%x", __func__, chip_id); + return -ENODEV; + } + + ret = mipi_d2l_dsi_init_sequence(mfd); + if (ret) + return ret; + + mipi_d2l_write_reg(mfd, GPIOC, d2l_gpio_out_mask); + /* Set gpio#4=U/D=0, gpio#3=L/R=1 , gpio#2,1=CABC=0, gpio#0=NA. */ + mipi_d2l_write_reg(mfd, GPIOO, d2l_gpio_out_val); + + d2l_pwm_freq_hz = (3.921*1000); + + if (bl_level == 0) + bl_level = PWM_LEVEL * 2 / 3 ; /* Default ON value */ + + /* Set backlight via PWM */ + if (bl_pwm) { + ret = mipi_d2l_set_backlight_level(bl_pwm, bl_level); + if (ret) + pr_err("%s.mipi_d2l_set_backlight_level.ret=%d", + __func__, ret); + } + + mipi_d2l_read_status(mfd); + + mipi_d2l_enable_3d(mfd, false, false); + + /* Add I2C driver only after DSI-CLK is running */ + i2c_add_driver(&d2l_i2c_slave_driver); + + pr_info("%s.ret=%d.\n", __func__, ret); + + return ret; +} + +/** + * LCD OFF. + * + * @param pdev + * + * @return int + */ +static int mipi_d2l_lcd_off(struct platform_device *pdev) +{ + int ret; + struct msm_fb_data_type *mfd; + + pr_info("%s.\n", __func__); + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + ret = mipi_d2l_set_backlight_level(bl_pwm, 1); + + pr_info("%s.ret=%d.\n", __func__, ret); + + return ret; +} + +static void mipi_d2l_set_backlight(struct msm_fb_data_type *mfd) +{ + int level = mfd->bl_level; + + pr_debug("%s.lvl=%d.\n", __func__, level); + + mipi_d2l_set_backlight_level(bl_pwm, level); + + bl_level = level; +} + +static struct msm_fb_panel_data d2l_panel_data = { + .on = mipi_d2l_lcd_on, + .off = mipi_d2l_lcd_off, + .set_backlight = mipi_d2l_set_backlight, +}; + +static u32 d2l_i2c_read_reg(struct i2c_client *client, u16 reg) +{ + int rc; + u32 val = 0; + u8 buf[6]; + + if (client == NULL) { + pr_err("%s.invalid i2c client.\n", __func__); + return -EINVAL; + } + + buf[0] = reg >> 8; + buf[1] = reg & 0xFF; + + rc = i2c_master_send(client, buf, sizeof(reg)); + rc = i2c_master_recv(client, buf, 4); + + if (rc >= 0) { + val = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); + pr_debug("%s.reg=0x%x.val=0x%x.\n", __func__, reg, val); + } else + pr_err("%s.fail.reg=0x%x.\n", __func__, reg); + + return val; +} + +static u32 d2l_i2c_write_reg(struct i2c_client *client, u16 reg, u32 val) +{ + int rc; + u8 buf[6]; + + if (client == NULL) { + pr_err("%s.invalid i2c client.\n", __func__); + return -EINVAL; + } + + buf[0] = reg >> 8; + buf[1] = reg & 0xFF; + + buf[2] = (val >> 0) & 0xFF; + buf[3] = (val >> 8) & 0xFF; + buf[4] = (val >> 16) & 0xFF; + buf[5] = (val >> 24) & 0xFF; + + rc = i2c_master_send(client, buf, sizeof(buf)); + + if (rc >= 0) + pr_debug("%s.reg=0x%x.val=0x%x.\n", __func__, reg, val); + else + pr_err("%s.fail.reg=0x%x.\n", __func__, reg); + + return val; +} + +static int d2l_i2c_slave_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + static const u32 i2c_funcs = I2C_FUNC_I2C; + + d2l_i2c_client = client; + + if (!i2c_check_functionality(client->adapter, i2c_funcs)) { + pr_err("%s.i2c_check_functionality failed.\n", __func__); + return -ENOSYS; + } else { + pr_debug("%s.i2c_check_functionality OK.\n", __func__); + } + + d2l_i2c_read_reg(client, IDREG); + + mipi_d2l_read_status_via_i2c(d2l_i2c_client); + + return 0; +} + +static int d2l_i2c_slave_remove(struct i2c_client *client) +{ + d2l_i2c_client = NULL; + + return 0; +} + +static const struct i2c_device_id d2l_i2c_id[] = { + {"tc358764-i2c", 0}, + {} +}; + +static struct i2c_driver d2l_i2c_slave_driver = { + .driver = { + .name = "tc358764-i2c", + .owner = THIS_MODULE + }, + .probe = d2l_i2c_slave_probe, + .remove = d2l_i2c_slave_remove, + .id_table = d2l_i2c_id, +}; + +static int mipi_d2l_enable_3d(struct msm_fb_data_type *mfd, + bool enable, bool mode) +{ + u32 tn_usec = 1000000 / 66; /* 66 HZ */ + + pr_debug("%s.enable=%d.mode=%d.\n", __func__, enable, mode); + + gpio_direction_output(d2l_3d_gpio_enable, enable); + gpio_direction_output(d2l_3d_gpio_mode, mode); + + mipi_d2l_set_tn_clk(tn_pwm, tn_usec); + + return 0; +} + +static ssize_t mipi_d2l_enable_3d_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf((char *)buf, sizeof(buf), "%u\n", d2l_enable_3d); +} + +static ssize_t mipi_d2l_enable_3d_write(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret = -1; + u32 data = 0; + + if (sscanf((char *)buf, "%u", &data) != 1) { + dev_err(dev, "%s. Invalid input.\n", __func__); + ret = -EINVAL; + } else { + d2l_enable_3d = data; + if (data == 1) /* LANDSCAPE */ + mipi_d2l_enable_3d(d2l_mfd, true, true); + else if (data == 2) /* PORTRAIT */ + mipi_d2l_enable_3d(d2l_mfd, true, false); + else if (data == 0) + mipi_d2l_enable_3d(d2l_mfd, false, false); + else if (data == 9) + mipi_d2l_read_status_via_i2c(d2l_i2c_client); + else + pr_err("%s.Invalid value=%d.\n", __func__, data); + } + + return count; +} + +static struct device_attribute mipi_d2l_3d_barrier_attributes[] = { + __ATTR(enable_3d_barrier, 0666, + mipi_d2l_enable_3d_read, + mipi_d2l_enable_3d_write), +}; + +static int mipi_dsi_3d_barrier_sysfs_register(struct device *dev) +{ + int ret; + + pr_debug("%s.d2l_3d_gpio_enable=%d.\n", __func__, d2l_3d_gpio_enable); + pr_debug("%s.d2l_3d_gpio_mode=%d.\n", __func__, d2l_3d_gpio_mode); + + ret = device_create_file(dev, mipi_d2l_3d_barrier_attributes); + if (ret) { + pr_err("%s.failed to create 3D sysfs.\n", __func__); + goto err_device_create_file; + } + + ret = gpio_request(d2l_3d_gpio_enable, "d2l_3d_gpio_enable"); + if (ret) { + pr_err("%s.failed to get d2l_3d_gpio_enable=%d.\n", + __func__, d2l_3d_gpio_enable); + goto err_d2l_3d_gpio_enable; + } + + ret = gpio_request(d2l_3d_gpio_mode, "d2l_3d_gpio_mode"); + if (ret) { + pr_err("%s.failed to get d2l_3d_gpio_mode=%d.\n", + __func__, d2l_3d_gpio_mode); + goto err_d2l_3d_gpio_mode; + } + + return 0; + +err_d2l_3d_gpio_mode: + gpio_free(d2l_3d_gpio_enable); +err_d2l_3d_gpio_enable: + device_remove_file(dev, mipi_d2l_3d_barrier_attributes); +err_device_create_file: + + return ret; +} + +/** + * Probe for device. + * + * Both the "target" and "panel" device use the same probe function. + * "Target" device has id=0, "Panel" devic has non-zero id. + * Target device should register first, passing msm_panel_common_pdata. + * Panel device passing msm_panel_info. + * + * @param pdev + * + * @return int + */ +static int mipi_d2l_probe(struct platform_device *pdev) +{ + int ret = 0; + struct msm_panel_info *pinfo = NULL; + + pr_debug("%s.id=%d.\n", __func__, pdev->id); + + if (pdev->id == 0) { + d2l_common_pdata = pdev->dev.platform_data; + + if (d2l_common_pdata == NULL) { + pr_err("%s: no PWM gpio specified.\n", __func__); + return 0; + } + + led_pwm = d2l_common_pdata->gpio_num[0]; + d2l_gpio_out_mask = d2l_common_pdata->gpio_num[1] >> 8; + d2l_gpio_out_val = d2l_common_pdata->gpio_num[1] & 0xFF; + d2l_3d_gpio_enable = d2l_common_pdata->gpio_num[2]; + d2l_3d_gpio_mode = d2l_common_pdata->gpio_num[3]; + + mipi_dsi_buf_alloc(&d2l_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&d2l_rx_buf, DSI_BUF_SIZE); + + return 0; + } + + if (d2l_common_pdata == NULL) { + pr_err("%s: d2l_common_pdata is NULL.\n", __func__); + return -ENODEV; + } + + bl_pwm = NULL; + if (led_pwm >= 0) { + bl_pwm = pwm_request(led_pwm, "lcd-backlight"); + if (bl_pwm == NULL || IS_ERR(bl_pwm)) { + pr_err("%s pwm_request() failed.id=%d.bl_pwm=%d.\n", + __func__, led_pwm, (int) bl_pwm); + bl_pwm = NULL; + return -EIO; + } else { + pr_debug("%s.pwm_request() ok.pwm-id=%d.\n", + __func__, led_pwm); + + } + } else { + pr_err("%s. led_pwm is invalid.\n", __func__); + } + + tn_pwm = pwm_request(1, "3D_TN_clk"); + if (tn_pwm == NULL || IS_ERR(tn_pwm)) { + pr_err("%s pwm_request() failed.id=%d.tn_pwm=%d.\n", + __func__, 1, (int) tn_pwm); + tn_pwm = NULL; + return -EIO; + } else { + pr_debug("%s.pwm_request() ok.pwm-id=%d.\n", __func__, 1); + + } + + pinfo = pdev->dev.platform_data; + + if (pinfo == NULL) { + pr_err("%s: pinfo is NULL.\n", __func__); + return -ENODEV; + } + + d2l_panel_data.panel_info = *pinfo; + + pdev->dev.platform_data = &d2l_panel_data; + + msm_fb_add_device(pdev); + + if (pinfo->is_3d_panel) + mipi_dsi_3d_barrier_sysfs_register(&(pdev->dev)); + + return ret; +} + +/** + * Device removal notification handler. + * + * @param pdev + * + * @return int + */ +static int mipi_d2l_remove(struct platform_device *pdev) +{ + /* Note: There are no APIs to remove fb device and free DSI buf. */ + pr_debug("%s.\n", __func__); + + if (bl_pwm) { + pwm_free(bl_pwm); + bl_pwm = NULL; + } + + return 0; +} + +/** + * Register the panel device. + * + * @param pinfo + * @param channel_id + * @param panel_id + * + * @return int + */ +int mipi_tc358764_dsi2lvds_register(struct msm_panel_info *pinfo, + u32 channel_id, u32 panel_id) +{ + struct platform_device *pdev = NULL; + int ret; + /* Use DSI-to-LVDS bridge */ + const char driver_name[] = "mipi_tc358764"; + + pr_debug("%s.\n", __func__); + ret = mipi_d2l_init(); + if (ret) { + pr_err("mipi_d2l_init() failed with ret %u\n", ret); + return ret; + } + + /* Note: the device id should be non-zero */ + pdev = platform_device_alloc(driver_name, (panel_id << 8)|channel_id); + if (pdev == NULL) + return -ENOMEM; + + pdev->dev.platform_data = pinfo; + + ret = platform_device_add(pdev); + if (ret) { + pr_err("%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static struct platform_driver d2l_driver = { + .probe = mipi_d2l_probe, + .remove = mipi_d2l_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +/** + * Module Init + * + * @return int + */ +static int mipi_d2l_init(void) +{ + pr_debug("%s.\n", __func__); + + return platform_driver_register(&d2l_driver); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Toshiba MIPI-DSI-to-LVDS bridge driver"); +MODULE_AUTHOR("Amir Samuelov "); diff --git a/drivers/video/msm/mipi_tc358764_dsi2lvds.h b/drivers/video/msm/mipi_tc358764_dsi2lvds.h new file mode 100644 index 0000000000000000000000000000000000000000..671c191d52ac31b49afcba191707a150020d5322 --- /dev/null +++ b/drivers/video/msm/mipi_tc358764_dsi2lvds.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MIPI_TC358764_DSI2LVDS_H +#define MIPI_TC358764_DSI2LVDS_H + +#define PWM_LEVEL 255 + +int mipi_tc358764_dsi2lvds_register(struct msm_panel_info *pinfo, + u32 channel_id, u32 panel_id); +#endif /* MIPI_TC358764_DSI2LVDS_H */ diff --git a/drivers/video/msm/mipi_toshiba.c b/drivers/video/msm/mipi_toshiba.c new file mode 100644 index 0000000000000000000000000000000000000000..41c50984f1f1906d7be4234944699dd6b7bc8139 --- /dev/null +++ b/drivers/video/msm/mipi_toshiba.c @@ -0,0 +1,355 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_toshiba.h" + +static struct pwm_device *bl_lpm; +static struct mipi_dsi_panel_platform_data *mipi_toshiba_pdata; + +#define TM_GET_PID(id) (((id) & 0xff00)>>8) + +static struct dsi_buf toshiba_tx_buf; +static struct dsi_buf toshiba_rx_buf; +static int mipi_toshiba_lcd_init(void); + +#ifdef TOSHIBA_CMDS_UNUSED +static char one_lane[3] = {0xEF, 0x60, 0x62}; +static char dmode_wqvga[2] = {0xB3, 0x01}; +static char intern_wr_clk1_wqvga[3] = {0xef, 0x2f, 0x22}; +static char intern_wr_clk2_wqvga[3] = {0xef, 0x6e, 0x33}; +static char hor_addr_2A_wqvga[5] = {0x2A, 0x00, 0x00, 0x00, 0xef}; +static char hor_addr_2B_wqvga[5] = {0x2B, 0x00, 0x00, 0x01, 0xaa}; +static char if_sel_cmd[2] = {0x53, 0x00}; +#endif + +static char exit_sleep[2] = {0x11, 0x00}; +static char display_on[2] = {0x29, 0x00}; +static char display_off[2] = {0x28, 0x00}; +static char enter_sleep[2] = {0x10, 0x00}; + +static char mcap_off[2] = {0xb2, 0x00}; +static char ena_test_reg[3] = {0xEF, 0x01, 0x01}; +static char two_lane[3] = {0xEF, 0x60, 0x63}; +static char non_burst_sync_pulse[3] = {0xef, 0x61, 0x09}; +static char dmode_wvga[2] = {0xB3, 0x00}; +static char intern_wr_clk1_wvga[3] = {0xef, 0x2f, 0xcc}; +static char intern_wr_clk2_wvga[3] = {0xef, 0x6e, 0xdd}; +static char hor_addr_2A_wvga[5] = {0x2A, 0x00, 0x00, 0x01, 0xdf}; +static char hor_addr_2B_wvga[5] = {0x2B, 0x00, 0x00, 0x03, 0x55}; +static char if_sel_video[2] = {0x53, 0x01}; + +static struct dsi_cmd_desc toshiba_wvga_display_on_cmds[] = { + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(mcap_off), mcap_off}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(ena_test_reg), ena_test_reg}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(two_lane), two_lane}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(non_burst_sync_pulse), + non_burst_sync_pulse}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dmode_wvga), dmode_wvga}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(intern_wr_clk1_wvga), + intern_wr_clk1_wvga}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(intern_wr_clk2_wvga), + intern_wr_clk2_wvga}, + {DTYPE_DCS_LWRITE, 1, 0, 0, 0, sizeof(hor_addr_2A_wvga), + hor_addr_2A_wvga}, + {DTYPE_DCS_LWRITE, 1, 0, 0, 0, sizeof(hor_addr_2B_wvga), + hor_addr_2B_wvga}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(if_sel_video), if_sel_video}, + {DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(display_on), display_on} +}; + +static char mcap_start[2] = {0xb0, 0x04}; +static char num_out_pixelform[3] = {0xb3, 0x00, 0x87}; +static char dsi_ctrl[3] = {0xb6, 0x30, 0x83}; +static char panel_driving[7] = {0xc0, 0x01, 0x00, 0x85, 0x00, 0x00, 0x00}; +static char dispV_timing[5] = {0xc1, 0x00, 0x10, 0x00, 0x01}; +static char dispCtrl[3] = {0xc3, 0x00, 0x19}; +static char test_mode_c4[2] = {0xc4, 0x03}; +static char dispH_timing[15] = { + /* TYPE_DCS_LWRITE */ + 0xc5, 0x00, 0x01, 0x05, + 0x04, 0x5e, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x17, + 0x05, 0x00, 0x00 +}; +static char test_mode_c6[2] = {0xc6, 0x00}; +static char gamma_setA[13] = { + 0xc8, 0x0a, 0x15, 0x18, + 0x1b, 0x1c, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00 +}; +static char gamma_setB[13] = { + 0xc9, 0x0d, 0x1d, 0x1f, + 0x1f, 0x1f, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00 +}; +static char gamma_setC[13] = { + 0xca, 0x1e, 0x1f, 0x1e, + 0x1d, 0x1d, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00 +}; +static char powerSet_ChrgPmp[5] = {0xd0, 0x02, 0x00, 0xa3, 0xb8}; +static char testMode_d1[6] = {0xd1, 0x10, 0x14, 0x53, 0x64, 0x00}; +static char powerSet_SrcAmp[3] = {0xd2, 0xb3, 0x00}; +static char powerInt_PS[3] = {0xd3, 0x33, 0x03}; +static char vreg[2] = {0xd5, 0x00}; +static char test_mode_d6[2] = {0xd6, 0x01}; +static char timingCtrl_d7[9] = { + 0xd7, 0x09, 0x00, 0x84, + 0x81, 0x61, 0xbc, 0xb5, + 0x05 +}; +static char timingCtrl_d8[7] = { + 0xd8, 0x04, 0x25, 0x90, + 0x4c, 0x92, 0x00 +}; +static char timingCtrl_d9[4] = {0xd9, 0x5b, 0x7f, 0x05}; +static char white_balance[6] = {0xcb, 0x00, 0x00, 0x00, 0x1c, 0x00}; +static char vcs_settings[2] = {0xdd, 0x53}; +static char vcom_dc_settings[2] = {0xde, 0x43}; +static char testMode_e3[5] = {0xe3, 0x00, 0x00, 0x00, 0x00}; +static char testMode_e4[6] = {0xe4, 0x00, 0x00, 0x22, 0xaa, 0x00}; +static char testMode_e5[2] = {0xe5, 0x00}; +static char testMode_fa[4] = {0xfa, 0x00, 0x00, 0x00}; +static char testMode_fd[5] = {0xfd, 0x00, 0x00, 0x00, 0x00}; +static char testMode_fe[5] = {0xfe, 0x00, 0x00, 0x00, 0x00}; +static char mcap_end[2] = {0xb0, 0x03}; +static char set_add_mode[2] = {0x36, 0x0}; +static char set_pixel_format[2] = {0x3a, 0x70}; + + +static struct dsi_cmd_desc toshiba_wsvga_display_on_cmds[] = { + {DTYPE_GEN_WRITE2, 1, 0, 0, 10, sizeof(mcap_start), mcap_start}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 10, sizeof(num_out_pixelform), + num_out_pixelform}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 10, sizeof(dsi_ctrl), dsi_ctrl}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(panel_driving), panel_driving}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dispV_timing), dispV_timing}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dispCtrl), dispCtrl}, + {DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(test_mode_c4), test_mode_c4}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dispH_timing), dispH_timing}, + {DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(test_mode_c6), test_mode_c6}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(gamma_setA), gamma_setA}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(gamma_setB), gamma_setB}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(gamma_setC), gamma_setC}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(powerSet_ChrgPmp), + powerSet_ChrgPmp}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_d1), testMode_d1}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(powerSet_SrcAmp), + powerSet_SrcAmp}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(powerInt_PS), powerInt_PS}, + {DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(vreg), vreg}, + {DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(test_mode_d6), test_mode_d6}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(timingCtrl_d7), timingCtrl_d7}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(timingCtrl_d8), timingCtrl_d8}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(timingCtrl_d9), timingCtrl_d9}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(white_balance), white_balance}, + {DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(vcs_settings), vcs_settings}, + {DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(vcom_dc_settings), + vcom_dc_settings}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_e3), testMode_e3}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_e4), testMode_e4}, + {DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(testMode_e5), testMode_e5}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_fa), testMode_fa}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_fd), testMode_fd}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(testMode_fe), testMode_fe}, + {DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(mcap_end), mcap_end}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_add_mode), set_add_mode}, + {DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_pixel_format), + set_pixel_format}, + {DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 50, sizeof(display_on), display_on} +}; + +static struct dsi_cmd_desc toshiba_display_off_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 50, sizeof(display_off), display_off}, + {DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(enter_sleep), enter_sleep} +}; + +static int mipi_toshiba_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (TM_GET_PID(mfd->panel.id) == MIPI_DSI_PANEL_WVGA_PT) + mipi_dsi_cmds_tx(mfd, &toshiba_tx_buf, + toshiba_wvga_display_on_cmds, + ARRAY_SIZE(toshiba_wvga_display_on_cmds)); + else if (TM_GET_PID(mfd->panel.id) == MIPI_DSI_PANEL_WSVGA_PT || + TM_GET_PID(mfd->panel.id) == MIPI_DSI_PANEL_WUXGA) + mipi_dsi_cmds_tx(mfd, &toshiba_tx_buf, + toshiba_wsvga_display_on_cmds, + ARRAY_SIZE(toshiba_wsvga_display_on_cmds)); + else + return -EINVAL; + + return 0; +} + +static int mipi_toshiba_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi_dsi_cmds_tx(mfd, &toshiba_tx_buf, toshiba_display_off_cmds, + ARRAY_SIZE(toshiba_display_off_cmds)); + + return 0; +} + +void mipi_bklight_pwm_cfg(void) +{ + if (mipi_toshiba_pdata && mipi_toshiba_pdata->dsi_pwm_cfg) + mipi_toshiba_pdata->dsi_pwm_cfg(); +} + +static void mipi_toshiba_set_backlight(struct msm_fb_data_type *mfd) +{ + int ret; + static int bklight_pwm_cfg; + + if (bklight_pwm_cfg == 0) { + mipi_bklight_pwm_cfg(); + bklight_pwm_cfg++; + } + + if (bl_lpm) { + ret = pwm_config(bl_lpm, MIPI_TOSHIBA_PWM_DUTY_LEVEL * + mfd->bl_level, MIPI_TOSHIBA_PWM_PERIOD_USEC); + if (ret) { + pr_err("pwm_config on lpm failed %d\n", ret); + return; + } + if (mfd->bl_level) { + ret = pwm_enable(bl_lpm); + if (ret) + pr_err("pwm enable/disable on lpm failed" + "for bl %d\n", mfd->bl_level); + } else { + pwm_disable(bl_lpm); + } + } +} + +static int mipi_toshiba_lcd_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + mipi_toshiba_pdata = pdev->dev.platform_data; + return 0; + } + + if (mipi_toshiba_pdata == NULL) { + pr_err("%s.invalid platform data.\n", __func__); + return -ENODEV; + } + + if (mipi_toshiba_pdata != NULL) + bl_lpm = pwm_request(mipi_toshiba_pdata->gpio[0], + "backlight"); + + if (bl_lpm == NULL || IS_ERR(bl_lpm)) { + pr_err("%s pwm_request() failed\n", __func__); + bl_lpm = NULL; + } + pr_debug("bl_lpm = %p lpm = %d\n", bl_lpm, + mipi_toshiba_pdata->gpio[0]); + + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = mipi_toshiba_lcd_probe, + .driver = { + .name = "mipi_toshiba", + }, +}; + +static struct msm_fb_panel_data toshiba_panel_data = { + .on = mipi_toshiba_lcd_on, + .off = mipi_toshiba_lcd_off, + .set_backlight = mipi_toshiba_set_backlight, +}; + +static int ch_used[3]; + +int mipi_toshiba_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + ch_used[channel] = TRUE; + + ret = mipi_toshiba_lcd_init(); + if (ret) { + pr_err("mipi_toshiba_lcd_init() failed with ret %u\n", ret); + return ret; + } + + pdev = platform_device_alloc("mipi_toshiba", (panel << 8)|channel); + if (!pdev) + return -ENOMEM; + + toshiba_panel_data.panel_info = *pinfo; + + ret = platform_device_add_data(pdev, &toshiba_panel_data, + sizeof(toshiba_panel_data)); + if (ret) { + printk(KERN_ERR + "%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + printk(KERN_ERR + "%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static int mipi_toshiba_lcd_init(void) +{ + mipi_dsi_buf_alloc(&toshiba_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&toshiba_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} diff --git a/drivers/video/msm/mipi_toshiba.h b/drivers/video/msm/mipi_toshiba.h new file mode 100644 index 0000000000000000000000000000000000000000..ea7fddd2308be446e003796c299c631db30fa385 --- /dev/null +++ b/drivers/video/msm/mipi_toshiba.h @@ -0,0 +1,30 @@ + +/* Copyright (c) 2009-2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MIPI_TOSHIBA_H +#define MIPI_TOSHIBA_H + +#include +#include + +int mipi_toshiba_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#define MIPI_TOSHIBA_PWM_FREQ_HZ 3921 +#define MIPI_TOSHIBA_PWM_PERIOD_USEC (USEC_PER_SEC / MIPI_TOSHIBA_PWM_FREQ_HZ) +#define MIPI_TOSHIBA_PWM_LEVEL 255 +#define MIPI_TOSHIBA_PWM_DUTY_LEVEL \ + (MIPI_TOSHIBA_PWM_PERIOD_USEC / MIPI_TOSHIBA_PWM_LEVEL) + +#endif /* MIPI_TOSHIBA_H */ diff --git a/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c b/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..2a36d94cf724bd80556557c2e527450ba66bbaba --- /dev/null +++ b/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c @@ -0,0 +1,105 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_toshiba.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* 600*1024, RGB888, 3 Lane 55 fps video mode */ + /* regulator */ + {0x09, 0x08, 0x05, 0x00, 0x20}, + /* timing */ + {0xab, 0x8a, 0x18, 0x00, 0x92, 0x97, 0x1b, 0x8c, + 0x0c, 0x03, 0x04, 0xa0}, + /* phy ctrl */ + {0x5f, 0x00, 0x00, 0x10}, + /* strength */ + {0xff, 0x00, 0x06, 0x00}, + /* pll control */ + {0x0, 0x7f, 0x31, 0xda, 0x00, 0x50, 0x48, 0x63, + 0x41, 0x0f, 0x01, + 0x00, 0x14, 0x03, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01 }, +}; + +static int __init mipi_video_toshiba_wsvga_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_toshiba_wsvga")) + return 0; + + pinfo.xres = 600; + pinfo.yres = 1024; + /* + * + * Panel's Horizontal input timing requirement is to + * include dummy(pad) data of 200 clk in addition to + * width and porch/sync width values + */ + pinfo.lcdc.xres_pad = 200; + pinfo.lcdc.yres_pad = 0; + + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 16; + pinfo.lcdc.h_front_porch = 23; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 2; + pinfo.lcdc.v_front_porch = 7; + pinfo.lcdc.v_pulse_width = 2; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = MIPI_TOSHIBA_PWM_LEVEL; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + pinfo.clk_rate = 384000000; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + pinfo.mipi.pulse_mode_hsa_he = FALSE; + pinfo.mipi.hfp_power_stop = FALSE; + pinfo.mipi.hbp_power_stop = FALSE; + pinfo.mipi.hsa_power_stop = FALSE; + pinfo.mipi.eof_bllp_power_stop = FALSE; + pinfo.mipi.bllp_power_stop = FALSE; + pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.data_lane2 = TRUE; + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2d; + pinfo.mipi.esc_byte_ratio = 4; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = 0; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 55; + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + pinfo.mipi.tx_eot_append = TRUE; + + ret = mipi_toshiba_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WSVGA_PT); + if (ret) + printk(KERN_ERR "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_toshiba_wsvga_pt_init); diff --git a/drivers/video/msm/mipi_toshiba_video_wuxga.c b/drivers/video/msm/mipi_toshiba_video_wuxga.c new file mode 100644 index 0000000000000000000000000000000000000000..ed7895ec37a09d477180b6b7d40f565d3f330b1e --- /dev/null +++ b/drivers/video/msm/mipi_toshiba_video_wuxga.c @@ -0,0 +1,98 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_toshiba.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* 1920*1200, RGB888, 4 Lane 60 fps video mode */ + /* regulator */ + {0x03, 0x0a, 0x04, 0x00, 0x20}, + /* timing */ + {0x66, 0x26, 0x1F, 0x00, 0x55, 0x9C, 0x16, 0x90, + 0x23, 0x03, 0x04, 0xa0}, + /* phy ctrl */ + {0x5f, 0x00, 0x00, 0x10}, + /* strength */ + {0xff, 0x00, 0x06, 0x00}, + /* pll control */ + {0x0, 0xD7, 0x1, 0x19, 0x00, 0x50, 0x48, 0x63, + 0x41, 0x0f, 0x01, + 0x00, 0x14, 0x03, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01 }, +}; + +static int __init mipi_video_toshiba_wuxga_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_toshiba_wuxga")) + return 0; + + pinfo.xres = 1920; + pinfo.yres = 1200; + + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 50; + pinfo.lcdc.h_front_porch = 50; + pinfo.lcdc.h_pulse_width = 170; + pinfo.lcdc.v_back_porch = 7; + pinfo.lcdc.v_front_porch = 8; + pinfo.lcdc.v_pulse_width = 30; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = MIPI_TOSHIBA_PWM_LEVEL; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + pinfo.clk_rate = 981560000; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + pinfo.mipi.pulse_mode_hsa_he = TRUE; + pinfo.mipi.hfp_power_stop = FALSE; + pinfo.mipi.hbp_power_stop = FALSE; + pinfo.mipi.hsa_power_stop = FALSE; + pinfo.mipi.eof_bllp_power_stop = TRUE; + pinfo.mipi.bllp_power_stop = TRUE; + pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.data_lane2 = TRUE; + pinfo.mipi.data_lane3 = TRUE; + pinfo.mipi.tx_eot_append = TRUE; + pinfo.mipi.t_clk_post = 0x04; + pinfo.mipi.t_clk_pre = 0x1c; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + pinfo.mipi.esc_byte_ratio = 9; + + ret = mipi_toshiba_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WUXGA); + if (ret) + printk(KERN_ERR "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_toshiba_wuxga_init); diff --git a/drivers/video/msm/mipi_toshiba_video_wvga_pt.c b/drivers/video/msm/mipi_toshiba_video_wvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..fdd4d1c4d321e1f4084444a1a87d5e40df935ae1 --- /dev/null +++ b/drivers/video/msm/mipi_toshiba_video_wvga_pt.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2009-2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_toshiba.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* 480*854, RGB888, 2 Lane 60 fps video mode */ + {0x03, 0x01, 0x01, 0x00}, /* regulator */ + /* timing */ + {0x6a, 0x22, 0x0f, 0x00, 0x30, 0x38, 0x13, 0x26, + 0x1b, 0x03, 0x04}, + {0x7f, 0x00, 0x00, 0x00}, /* phy ctrl */ + {0xee, 0x03, 0x86, 0x03}, /* strength */ + /* pll control */ + +#define DSI_BIT_CLK_380MHZ + +#if defined(DSI_BIT_CLK_366MHZ) + {0x41, 0xdb, 0xb2, 0xf5, 0x00, 0x50, 0x48, 0x63, + 0x31, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x03, 0x03, 0x54, 0x06, 0x10, 0x04, 0x03 }, +#elif defined(DSI_BIT_CLK_380MHZ) + {0x41, 0xf7, 0xb2, 0xf5, 0x00, 0x50, 0x48, 0x63, + 0x31, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x03, 0x03, 0x54, 0x06, 0x10, 0x04, 0x03 }, +#elif defined(DSI_BIT_CLK_400MHZ) + {0x41, 0x8f, 0xb1, 0xda, 0x00, 0x50, 0x48, 0x63, + 0x31, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x03, 0x03, 0x54, 0x06, 0x10, 0x04, 0x03 }, +#else /* 200 mhz */ + {0x41, 0x8f, 0xb1, 0xda, 0x00, 0x50, 0x48, 0x63, + 0x33, 0x1f, 0x0f, + 0x05, 0x14, 0x03, 0x03, 0x03, 0x54, 0x06, 0x10, 0x04, 0x03 }, +#endif +}; + +static int __init mipi_video_toshiba_wvga_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_toshiba_wvga")) + return 0; + + pinfo.xres = 480; + pinfo.yres = 864; /* 856 for V1 surf */ + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 64; + pinfo.lcdc.h_front_porch = 64; + pinfo.lcdc.h_pulse_width = 16; + pinfo.lcdc.v_back_porch = 8; + pinfo.lcdc.v_front_porch = 4; + pinfo.lcdc.v_pulse_width = 1; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 15; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + pinfo.mipi.pulse_mode_hsa_he = TRUE; + pinfo.mipi.hfp_power_stop = FALSE; + pinfo.mipi.hbp_power_stop = FALSE; + pinfo.mipi.hsa_power_stop = FALSE; + pinfo.mipi.eof_bllp_power_stop = TRUE; + pinfo.mipi.bllp_power_stop = TRUE; + pinfo.mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.t_clk_post = 0x04; + pinfo.mipi.t_clk_pre = 0x17; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + + ret = mipi_toshiba_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WVGA_PT); + if (ret) + printk(KERN_ERR "%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_toshiba_wvga_pt_init); diff --git a/drivers/video/msm/mipi_truly.c b/drivers/video/msm/mipi_truly.c new file mode 100644 index 0000000000000000000000000000000000000000..fa42e4f172528c5bc4b3f23295e902ebc5d8a007 --- /dev/null +++ b/drivers/video/msm/mipi_truly.c @@ -0,0 +1,259 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_truly.h" + +static struct msm_panel_common_pdata *mipi_truly_pdata; +static struct dsi_buf truly_tx_buf; +static struct dsi_buf truly_rx_buf; + +#define TRULY_CMD_DELAY 0 +#define TRULY_SLEEP_OFF_DELAY 150 +#define TRULY_DISPLAY_ON_DELAY 150 +#define GPIO_TRULY_LCD_RESET 129 + +static int prev_bl = 17; + +static char extend_cmd_enable[4] = {0xB9, 0xFF, 0x83, 0x69}; +static char display_setting[16] = { + 0xB2, 0x00, 0x23, 0x62, + 0x62, 0x70, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, + 0x03, 0x03, 0x00, 0x01, +}; +static char wave_cycle_setting[6] = {0xB4, 0x00, 0x1D, 0x5F, 0x0E, 0x06}; +static char gip_setting[27] = { + 0xD5, 0x00, 0x04, 0x03, + 0x00, 0x01, 0x05, 0x1C, + 0x70, 0x01, 0x03, 0x00, + 0x00, 0x40, 0x06, 0x51, + 0x07, 0x00, 0x00, 0x41, + 0x06, 0x50, 0x07, 0x07, + 0x0F, 0x04, 0x00, +}; +static char power_setting[20] = { + 0xB1, 0x01, 0x00, 0x34, + 0x06, 0x00, 0x0F, 0x0F, + 0x2A, 0x32, 0x3F, 0x3F, + 0x07, 0x3A, 0x01, 0xE6, + 0xE6, 0xE6, 0xE6, 0xE6, +}; +static char vcom_setting[3] = {0xB6, 0x56, 0x56}; +static char pannel_setting[2] = {0xCC, 0x02}; +static char gamma_setting[35] = { + 0xE0, 0x00, 0x1D, 0x22, + 0x38, 0x3D, 0x3F, 0x2E, + 0x4A, 0x06, 0x0D, 0x0F, + 0x13, 0x15, 0x13, 0x16, + 0x10, 0x19, 0x00, 0x1D, + 0x22, 0x38, 0x3D, 0x3F, + 0x2E, 0x4A, 0x06, 0x0D, + 0x0F, 0x13, 0x15, 0x13, + 0x16, 0x10, 0x19, +}; +static char mipi_setting[14] = { + 0xBA, 0x00, 0xA0, 0xC6, + 0x00, 0x0A, 0x00, 0x10, + 0x30, 0x6F, 0x02, 0x11, + 0x18, 0x40, +}; +static char exit_sleep[2] = {0x11, 0x00}; +static char display_on[2] = {0x29, 0x00}; +static char display_off[2] = {0x28, 0x00}; +static char enter_sleep[2] = {0x10, 0x00}; + +static struct dsi_cmd_desc truly_display_off_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 10, sizeof(display_off), display_off}, + {DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(enter_sleep), enter_sleep} +}; + +static struct dsi_cmd_desc truly_display_on_cmds[] = { + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(extend_cmd_enable), extend_cmd_enable}, + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(display_setting), display_setting}, + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(wave_cycle_setting), wave_cycle_setting}, + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(gip_setting), gip_setting}, + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(power_setting), power_setting}, + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(vcom_setting), vcom_setting}, + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(pannel_setting), pannel_setting}, + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(gamma_setting), gamma_setting}, + {DTYPE_GEN_LWRITE, 1, 0, 0, TRULY_CMD_DELAY, + sizeof(mipi_setting), mipi_setting}, + {DTYPE_DCS_WRITE, 1, 0, 0, TRULY_SLEEP_OFF_DELAY, + sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, TRULY_DISPLAY_ON_DELAY, + sizeof(display_on), display_on}, +}; + +static int mipi_truly_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + msleep(20); + mipi_dsi_cmds_tx(mfd, &truly_tx_buf, truly_display_on_cmds, + ARRAY_SIZE(truly_display_on_cmds)); + + return 0; +} + +static int mipi_truly_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi_dsi_cmds_tx(mfd, &truly_tx_buf, truly_display_off_cmds, + ARRAY_SIZE(truly_display_off_cmds)); + + return 0; +} + +#define BL_LEVEL 17 +static void mipi_truly_set_backlight(struct msm_fb_data_type *mfd) +{ + int step = 0, i = 0; + int bl_level = mfd->bl_level; + + /* real backlight level, 1 - max, 16 - min, 17 - off */ + bl_level = BL_LEVEL - bl_level; + + if (bl_level > prev_bl) { + step = bl_level - prev_bl; + if (bl_level == BL_LEVEL) + step--; + } else if (bl_level < prev_bl) { + step = bl_level + 16 - prev_bl; + } else { + pr_debug("%s: no change\n", __func__); + return; + } + + if (bl_level == BL_LEVEL) { + /* turn off backlight */ + mipi_truly_pdata->pmic_backlight(0); + } else { + if (prev_bl == BL_LEVEL) { + /* turn on backlight */ + mipi_truly_pdata->pmic_backlight(1); + udelay(30); + } + /* adjust backlight level */ + for (i = 0; i < step; i++) { + mipi_truly_pdata->pmic_backlight(0); + udelay(1); + mipi_truly_pdata->pmic_backlight(1); + udelay(1); + } + } + msleep(20); + prev_bl = bl_level; + + return; +} + +static int mipi_truly_lcd_probe(struct platform_device *pdev) +{ + if (pdev->id == 0) { + mipi_truly_pdata = pdev->dev.platform_data; + return 0; + } + + msm_fb_add_device(pdev); + + return 0; +} + +static struct platform_driver this_driver = { + .probe = mipi_truly_lcd_probe, + .driver = { + .name = "mipi_truly", + }, +}; + +static struct msm_fb_panel_data truly_panel_data = { + .on = mipi_truly_lcd_on, + .off = mipi_truly_lcd_off, + .set_backlight = mipi_truly_set_backlight, +}; + +static int ch_used[3]; + +int mipi_truly_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + ch_used[channel] = TRUE; + + pdev = platform_device_alloc("mipi_truly", (panel << 8)|channel); + + if (!pdev) + return -ENOMEM; + + truly_panel_data.panel_info = *pinfo; + + ret = platform_device_add_data(pdev, &truly_panel_data, + sizeof(truly_panel_data)); + if (ret) { + pr_err("%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + + if (ret) { + pr_err("%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} + +static int __init mipi_truly_lcd_init(void) +{ + mipi_dsi_buf_alloc(&truly_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&truly_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} + +module_init(mipi_truly_lcd_init); diff --git a/drivers/video/msm/mipi_truly.h b/drivers/video/msm/mipi_truly.h new file mode 100644 index 0000000000000000000000000000000000000000..4c12ff9975507fd28dc94cdfcc485366c0ef9ced --- /dev/null +++ b/drivers/video/msm/mipi_truly.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MIPI_TRULY_H +#define MIPI_TRULY_H + +/* #define MIPI_TRULY_FAKE_PANEL */ /* FAKE PANEL for test */ + +int mipi_truly_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#endif /* MIPI_TRULY_H */ diff --git a/drivers/video/msm/mipi_truly_tft540960_1_e.c b/drivers/video/msm/mipi_truly_tft540960_1_e.c new file mode 100644 index 0000000000000000000000000000000000000000..6de37381ac1e850c8f297602287fe0966ab5e035 --- /dev/null +++ b/drivers/video/msm/mipi_truly_tft540960_1_e.c @@ -0,0 +1,817 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_truly_tft540960_1_e.h" + +static struct msm_panel_common_pdata *mipi_truly_pdata; +static struct dsi_buf truly_tx_buf; +static struct dsi_buf truly_rx_buf; + +#define TRULY_CMD_DELAY 0 +#define MIPI_SETTING_DELAY 10 +#define TRULY_SLEEP_OFF_DELAY 150 +#define TRULY_DISPLAY_ON_DELAY 150 + +/* common setting */ +static char exit_sleep[2] = {0x11, 0x00}; +static char display_on[2] = {0x29, 0x00}; +static char display_off[2] = {0x28, 0x00}; +static char enter_sleep[2] = {0x10, 0x00}; +static char write_ram[2] = {0x2c, 0x00}; /* write ram */ + +static struct dsi_cmd_desc truly_display_off_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(display_off), display_off}, + {DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(enter_sleep), enter_sleep} +}; + + +/* TFT540960_1_E CMD mode */ +static char cmd0[5] = { + 0xFF, 0xAA, 0x55, 0x25, + 0x01, +}; + +static char cmd2[5] = { + 0xF3, 0x02, 0x03, 0x07, + 0x45, +}; + +static char cmd3[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x00, +}; + +static char cmd4[2] = { + 0xB1, 0xeC, +}; + +/* add 0X BD command */ +static char cmd26_2[6] = { + 0xBD, 0x01, 0x48, 0x10, 0x38, 0x01 /* 59 HZ */ +}; + +static char cmd5[5] = { + 0xB8, 0x01, 0x02, 0x02, + 0x02, +}; + +static char cmd6[4] = { + 0xBC, 0x05, 0x05, 0x05, +}; + +static char cmd7[2] = { + 0x4C, 0x11, +}; + +static char cmd8[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x01, +}; + +static char cmd9[4] = { + 0xB0, 0x05, 0x05, 0x05, +}; + +static char cmd10[4] = { + 0xB6, 0x44, 0x44, 0x44, +}; +static char cmd11[4] = { + 0xB1, 0x05, 0x05, 0x05, +}; + +static char cmd12[4] = { + 0xB7, 0x34, 0x34, 0x34, +}; + +static char cmd13[4] = { + 0xB3, 0x10, 0x10, 0x10, +}; + +static char cmd14[4] = { + 0xB9, 0x34, 0x34, 0x34, +}; + +static char cmd15[4] = { + 0xB4, 0x0A, 0x0A, 0x0A, +}; + +static char cmd16[4] = { + 0xBA, 0x14, 0x14, 0x14, +}; +static char cmd17[4] = { + 0xBC, 0x00, 0xA0, 0x00, +}; + +static char cmd18[4] = { + 0xBD, 0x00, 0xA0, 0x00, +}; + +static char cmd19[2] = { + 0xBE, 0x45, +}; + +static char cmd20[17] = { + 0xD1, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char cmd21[17] = { + 0xD2, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char cmd22[17] = { + 0xD3, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char cmd23[5] = { + 0xD4, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char cmd24[17] = { + 0xD5, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; +static char cmd25[17] = { + 0xD6, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char cmd26[17] = { + 0xD7, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; +static char cmd27[5] = { + 0xD8, 0x03, 0xB0, 0x03, + 0xF4, +}; + + +static char cmd28[17] = { + 0xD9, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char cmd29[17] = { + 0xDD, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; +static char cmd30[17] = { + 0xDE, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char cmd31[5] = { + 0xDF, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char cmd32[17] = { + 0xE0, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char cmd33[17] = { + 0xE1, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char cmd34[17] = { + 0xE2, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char cmd35[5] = { + 0xE3, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char cmd36[17] = { + 0xE4, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; +static char cmd37[17] = { + 0xE5, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char cmd38[17] = { + 0xE6, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char cmd39[5] = { + 0xE7, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char cmd40[17] = { + 0xE8, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char cmd41[17] = { + 0xE9, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char cmd42[17] = { + 0xEA, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char cmd43[5] = { + 0xEB, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char cmd44[2] = { + 0x3A, 0x07, +}; + +static char cmd45[2] = { + 0x35, 0x00, +}; + + +static struct dsi_cmd_desc truly_cmd_display_on_cmds[] = { + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd0), cmd0}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd2), cmd2}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd3), cmd3}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd4), cmd4}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd26_2), cmd26_2}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd5), cmd5}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd6), cmd6}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd7), cmd7}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd8), cmd8}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd9), cmd9}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd10), cmd10}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd11), cmd11}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd12), cmd12}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd13), cmd13}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd14), cmd14}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd15), cmd15}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd16), cmd16}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd17), cmd17}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd18), cmd18}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd19), cmd19}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd20), cmd20}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd21), cmd21}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd22), cmd22}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd23), cmd23}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd24), cmd24}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd25), cmd25}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd26), cmd26}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd27), cmd27}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd28), cmd28}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd29), cmd29}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd30), cmd30}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd31), cmd31}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd32), cmd32}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd33), cmd33}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd34), cmd34}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd35), cmd35}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd36), cmd36}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd37), cmd37}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd38), cmd38}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd39), cmd39}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd40), cmd40}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd41), cmd41}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd42), cmd42}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd43), cmd43}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd44), cmd44}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(cmd45), cmd45}, + {DTYPE_DCS_WRITE, 1, 0, 0, TRULY_SLEEP_OFF_DELAY, sizeof(exit_sleep), + exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, TRULY_CMD_DELAY, sizeof(display_on), + display_on}, + {DTYPE_DCS_WRITE, 1, 0, 0, TRULY_CMD_DELAY, sizeof(write_ram), + write_ram}, + +}; + +/* TFT540960_1_E VIDEO mode */ +static char video0[5] = { + 0xFF, 0xAA, 0x55, 0x25, + 0x01, +}; + +static char video2[5] = { + 0xF3, 0x02, 0x03, 0x07, + 0x15, +}; + +static char video3[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x00, +}; + +static char video4[2] = { + 0xB1, 0xFC, +}; + +static char video5[5] = { + 0xB8, 0x01, 0x02, 0x02, + 0x02, +}; + +static char video6[4] = { + 0xBC, 0x05, 0x05, 0x05, +}; + +static char video7[2] = { + 0x4C, 0x11, +}; + +static char video8[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x01, +}; + +static char video9[4] = { + 0xB0, 0x05, 0x05, 0x05, +}; + +static char video10[4] = { + 0xB6, 0x44, 0x44, 0x44, +}; + +static char video11[4] = { + 0xB1, 0x05, 0x05, 0x05, +}; + +static char video12[4] = { + 0xB7, 0x34, 0x34, 0x34, +}; + +static char video13[4] = { + 0xB3, 0x10, 0x10, 0x10, +}; + +static char video14[4] = { + 0xB9, 0x34, 0x34, 0x34, +}; + +static char video15[4] = { + 0xB4, 0x0A, 0x0A, 0x0A, +}; + +static char video16[4] = { + 0xBA, 0x14, 0x14, 0x14, +}; + +static char video17[4] = { + 0xBC, 0x00, 0xA0, 0x00, +}; + +static char video18[4] = { + 0xBD, 0x00, 0xA0, 0x00, +}; + +static char video19[2] = { + 0xBE, 0x45, +}; + +static char video20[17] = { + 0xD1, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char video21[17] = { + 0xD2, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char video22[17] = { + 0xD3, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char video23[5] = { + 0xD4, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char video24[17] = { + 0xD5, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char video25[17] = { + 0xD6, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char video26[17] = { + 0xD7, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char video27[5] = { + 0xD8, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char video28[17] = { + 0xD9, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char video29[17] = { + 0xDD, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char video30[17] = { + 0xDE, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char video31[5] = { + 0xDF, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char video32[17] = { + 0xE0, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char video33[17] = { + 0xE1, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char video34[17] = { + 0xE2, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char video35[5] = { + 0xE3, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char video36[17] = { + 0xE4, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char video37[17] = { + 0xE5, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char video38[17] = { + 0xE6, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char video39[5] = { + 0xE7, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char video40[17] = { + 0xE8, 0x00, 0x32, 0x00, + 0x41, 0x00, 0x54, 0x00, + 0x67, 0x00, 0x7A, 0x00, + 0x98, 0x00, 0xB0, 0x00, + 0xDB, +}; + +static char video41[17] = { + 0xE9, 0x01, 0x01, 0x01, + 0x3F, 0x01, 0x70, 0x01, + 0xB4, 0x01, 0xEC, 0x01, + 0xED, 0x02, 0x1E, 0x02, + 0x51, +}; + +static char video42[17] = { + 0xEA, 0x02, 0x6C, 0x02, + 0x8D, 0x02, 0xA5, 0x02, + 0xC9, 0x02, 0xEA, 0x03, + 0x19, 0x03, 0x45, 0x03, + 0x7A, +}; + +static char video43[5] = { + 0xEB, 0x03, 0xB0, 0x03, + 0xF4, +}; + +static char video44[2] = { + 0x3A, 0x07, +}; + +static char video45[2] = { + 0x35, 0x00, +}; + +static struct dsi_cmd_desc truly_video_display_on_cmds[] = { + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video0), video0}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video2), video2}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video3), video3}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video4), video4}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video5), video5}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video6), video6}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video7), video7}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video8), video8}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video9), video9}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video10), video10}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video11), video11}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video12), video12}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video13), video13}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video14), video14}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video15), video15}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video16), video16}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video17), video17}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video18), video18}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video19), video19}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video20), video20}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video21), video21}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video22), video22}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video23), video23}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video24), video24}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video25), video25}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video26), video26}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video27), video27}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video28), video28}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video29), video29}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video30), video30}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video31), video31}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video32), video32}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video33), video33}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video34), video34}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video35), video35}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video36), video36}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video37), video37}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video38), video38}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video39), video39}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video40), video40}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video41), video41}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video42), video42}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video43), video43}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video44), video44}, + {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(video45), video45}, + + {DTYPE_DCS_WRITE, 1, 0, 0, 150, sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 50, sizeof(display_on), display_on}, +}; + +static int mipi_truly_lcd_on(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct mipi_panel_info *mipi; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi = &mfd->panel_info.mipi; + pr_info("%s: mode = %d\n", __func__, mipi->mode); + msleep(120); + + if (mipi->mode == DSI_VIDEO_MODE) { + mipi_dsi_cmds_tx(mfd, &truly_tx_buf, + truly_video_display_on_cmds, + ARRAY_SIZE(truly_video_display_on_cmds)); + } else if (mipi->mode == DSI_CMD_MODE) { + mipi_dsi_cmds_tx(mfd, &truly_tx_buf, + truly_cmd_display_on_cmds, + ARRAY_SIZE(truly_cmd_display_on_cmds)); + } + + return 0; +} + +static int mipi_truly_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mipi_dsi_cmds_tx(mfd, &truly_tx_buf, truly_display_off_cmds, + ARRAY_SIZE(truly_display_off_cmds)); + + return 0; +} + +static int mipi_truly_lcd_probe(struct platform_device *pdev) +{ + int rc = 0; + + if (pdev->id == 0) { + mipi_truly_pdata = pdev->dev.platform_data; + if (mipi_truly_pdata->bl_lock) + spin_lock_init(&mipi_truly_pdata->bl_spinlock); + return rc; + } + + msm_fb_add_device(pdev); + + return rc; +} + +static struct platform_driver this_driver = { + .probe = mipi_truly_lcd_probe, + .driver = { + .name = "mipi_truly_tft540960_1_e", + }, +}; + +static void mipi_truly_set_backlight(struct msm_fb_data_type *mfd) +{ + int bl_level; + unsigned long flags; + bl_level = mfd->bl_level; + + if (mipi_truly_pdata->bl_lock) { + spin_lock_irqsave(&mipi_truly_pdata->bl_spinlock, flags); + mipi_truly_pdata->pmic_backlight(bl_level); + spin_unlock_irqrestore(&mipi_truly_pdata->bl_spinlock, flags); + } else + mipi_truly_pdata->pmic_backlight(bl_level); +} + +static struct msm_fb_panel_data truly_panel_data = { + .on = mipi_truly_lcd_on, + .off = mipi_truly_lcd_off, + .set_backlight = mipi_truly_set_backlight, +}; + +static int ch_used[3]; + +static int mipi_truly_tft540960_1_e_lcd_init(void) +{ + mipi_dsi_buf_alloc(&truly_tx_buf, DSI_BUF_SIZE); + mipi_dsi_buf_alloc(&truly_rx_buf, DSI_BUF_SIZE); + + return platform_driver_register(&this_driver); +} +int mipi_truly_tft540960_1_e_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel) +{ + struct platform_device *pdev = NULL; + int ret; + + if ((channel >= 3) || ch_used[channel]) + return -ENODEV; + + ch_used[channel] = TRUE; + + ret = mipi_truly_tft540960_1_e_lcd_init(); + if (ret) { + pr_err("%s: platform_device_register failed!\n", __func__); + return ret; + } + + pdev = platform_device_alloc("mipi_truly_tft540960_1_e", + (panel << 8)|channel); + if (!pdev) + return -ENOMEM; + + truly_panel_data.panel_info = *pinfo; + + ret = platform_device_add_data(pdev, &truly_panel_data, + sizeof(truly_panel_data)); + if (ret) { + pr_err("%s: platform_device_add_data failed!\n", __func__); + goto err_device_put; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("%s: platform_device_register failed!\n", __func__); + goto err_device_put; + } + + return 0; + +err_device_put: + platform_device_put(pdev); + return ret; +} diff --git a/drivers/video/msm/mipi_truly_tft540960_1_e.h b/drivers/video/msm/mipi_truly_tft540960_1_e.h new file mode 100644 index 0000000000000000000000000000000000000000..d7329d6a81489464b87dae1dbfc6da57265f5c04 --- /dev/null +++ b/drivers/video/msm/mipi_truly_tft540960_1_e.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MIPI_TRULY_H +#define MIPI_TRULY_H + +int mipi_truly_tft540960_1_e_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#endif /* MIPI_TRULY_H */ diff --git a/drivers/video/msm/mipi_truly_tft540960_1_e_cmd_qhd_pt.c b/drivers/video/msm/mipi_truly_tft540960_1_e_cmd_qhd_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..1e42c9b56affd1e2ac927aa85ba5b74f4b3fd606 --- /dev/null +++ b/drivers/video/msm/mipi_truly_tft540960_1_e_cmd_qhd_pt.c @@ -0,0 +1,98 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_truly_tft540960_1_e.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = { + /* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */ + /* regulator */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + {0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90, + 0x18, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x01, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +}; + +static int mipi_cmd_truly_qhd_pt_init(void) +{ + int ret; + if (msm_fb_detect_client("mipi_cmd_truly_qhd")) + return 0; + + pinfo.xres = 540; + pinfo.yres = 960; + pinfo.type = MIPI_CMD_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 100; + pinfo.lcdc.h_front_porch = 100; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 20; + pinfo.lcdc.v_front_porch = 20; + pinfo.lcdc.v_pulse_width = 1; + + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 31; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.clk_rate = 499000000; + + pinfo.lcd.vsync_enable = TRUE; + pinfo.lcd.hw_vsync_mode = TRUE; + pinfo.lcd.refx100 = 6100; /* adjust refx100 to prevent tearing */ + + pinfo.mipi.mode = DSI_CMD_MODE; + pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2F; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW_TE; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.te_sel = 1; /* TE from vsync gpio */ + pinfo.mipi.interleave_max = 1; + pinfo.mipi.insert_dcs_cmd = TRUE; + pinfo.mipi.wr_mem_continue = 0x3c; + pinfo.mipi.wr_mem_start = 0x2c; + pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db; + pinfo.mipi.tx_eot_append = 0x01; + pinfo.mipi.rx_eot_ignore = 0x0; + pinfo.mipi.dlane_swap = 0x01; + + ret = mipi_truly_tft540960_1_e_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WVGA_PT); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_cmd_truly_qhd_pt_init); diff --git a/drivers/video/msm/mipi_truly_tft540960_1_e_video_qhd_pt.c b/drivers/video/msm/mipi_truly_tft540960_1_e_video_qhd_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..2c5ccad2c9b1adba38898d88a1da96d03efe1d5c --- /dev/null +++ b/drivers/video/msm/mipi_truly_tft540960_1_e_video_qhd_pt.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_truly_tft540960_1_e.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */ + /* regulator */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + {0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90, + 0x18, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x00, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +}; + +static int mipi_video_truly_qhd_pt_init(void) +{ + int ret; + if (msm_fb_detect_client("mipi_video_truly_qhd")) + return 0; + + pinfo.xres = 540; + pinfo.yres = 960; + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 100; + pinfo.lcdc.h_front_porch = 100; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 20; + pinfo.lcdc.v_front_porch = 20; + pinfo.lcdc.v_pulse_width = 1; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + /* number of dot_clk cycles HSYNC active edge + is delayed from VSYNC active edge */ + pinfo.lcdc.hsync_skew = 0; + pinfo.clk_rate = 699000000; + pinfo.lcd.refx100 = 6000; /* FB driver calc FPS based on this value */ + pinfo.bl_max = 31; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + /* send HSA and HE following VS/VE packet */ + pinfo.mipi.pulse_mode_hsa_he = TRUE; + pinfo.mipi.hfp_power_stop = TRUE; /* LP-11 during the HFP period */ + pinfo.mipi.hbp_power_stop = TRUE; /* LP-11 during the HBP period */ + pinfo.mipi.hsa_power_stop = TRUE; /* LP-11 during the HSA period */ + /* LP-11 or let Command Mode Engine send packets in + HS or LP mode for the BLLP of the last line of a frame */ + pinfo.mipi.eof_bllp_power_stop = TRUE; + /* LP-11 or let Command Mode Engine send packets in + HS or LP mode for packets sent during BLLP period */ + pinfo.mipi.bllp_power_stop = TRUE; + + pinfo.mipi.traffic_mode = DSI_BURST_MODE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; /* RGB */ + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2f; + + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; + + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + pinfo.mipi.dlane_swap = 0x01; + /* append EOT at the end of data burst */ + pinfo.mipi.tx_eot_append = 0x01; + + ret = mipi_truly_tft540960_1_e_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WVGA_PT); + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_truly_qhd_pt_init); diff --git a/drivers/video/msm/mipi_truly_video_wvga_pt.c b/drivers/video/msm/mipi_truly_video_wvga_pt.c new file mode 100644 index 0000000000000000000000000000000000000000..3fc14d2e8782b2d43355be3f3e7f4a0266b819e1 --- /dev/null +++ b/drivers/video/msm/mipi_truly_video_wvga_pt.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mipi_truly.h" + +static struct msm_panel_info pinfo; + +static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = { + /* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */ + /* regulator */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + {0xb9, 0x8e, 0x1f, 0x00, 0x98, 0x9c, 0x22, 0x90, + 0x18, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x00, 0xec, 0x31, 0xd2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0f, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +}; + +static int __init mipi_video_truly_wvga_pt_init(void) +{ + int ret; + + if (msm_fb_detect_client("mipi_video_truly_wvga")) + return 0; + + pinfo.xres = 480; + pinfo.yres = 800; + pinfo.type = MIPI_VIDEO_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + pinfo.lcdc.h_back_porch = 100; + pinfo.lcdc.h_front_porch = 100; + pinfo.lcdc.h_pulse_width = 8; + pinfo.lcdc.v_back_porch = 20; + pinfo.lcdc.v_front_porch = 20; + pinfo.lcdc.v_pulse_width = 1; + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + /* number of dot_clk cycles HSYNC active edge + is delayed from VSYNC active edge */ + pinfo.lcdc.hsync_skew = 0; + pinfo.clk_rate = 499000000; + pinfo.bl_max = 15; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.mipi.mode = DSI_VIDEO_MODE; + pinfo.mipi.pulse_mode_hsa_he = TRUE; /* send HSA and HE following + VS/VE packet */ + pinfo.mipi.hfp_power_stop = TRUE; /* LP-11 during the HFP period */ + pinfo.mipi.hbp_power_stop = TRUE; /* LP-11 during the HBP period */ + pinfo.mipi.hsa_power_stop = TRUE; /* LP-11 during the HSA period */ + /* LP-11 or let Command Mode Engine send packets in + HS or LP mode for the BLLP of the last line of a frame */ + pinfo.mipi.eof_bllp_power_stop = TRUE; + /* LP-11 or let Command Mode Engine send packets in + HS or LP mode for packets sent during BLLP period */ + pinfo.mipi.bllp_power_stop = TRUE; + + pinfo.mipi.traffic_mode = DSI_BURST_MODE; + pinfo.mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_RGB; /* RGB */ + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2f; + + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.frame_rate = 60; /* FIXME */ + + pinfo.mipi.dsi_phy_db = &dsi_video_mode_phy_db; + pinfo.mipi.dlane_swap = 0x01; + pinfo.mipi.tx_eot_append = 0x01; /* append EOT at the end + of data burst */ + + ret = mipi_truly_device_register(&pinfo, MIPI_DSI_PRIM, + MIPI_DSI_PANEL_WVGA_PT); + + if (ret) + pr_err("%s: failed to register device!\n", __func__); + + return ret; +} + +module_init(mipi_video_truly_wvga_pt_init); diff --git a/drivers/video/msm/msm_dss_io_7x27a.c b/drivers/video/msm/msm_dss_io_7x27a.c new file mode 100644 index 0000000000000000000000000000000000000000..436c12a1eee3a8de7b00e23f8e20a1ed094ed0c5 --- /dev/null +++ b/drivers/video/msm/msm_dss_io_7x27a.c @@ -0,0 +1,470 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include "msm_fb.h" +#include "mipi_dsi.h" + +/* multimedia sub system sfpb */ +char *mmss_sfpb_base; +void __iomem *periph_base; + +static struct dsi_clk_desc dsicore_clk; +static struct dsi_clk_desc dsi_pclk; + +static struct clk *dsi_byte_div_clk; +static struct clk *dsi_esc_clk; +static struct clk *dsi_pixel_clk; +static struct clk *dsi_clk; +static struct clk *dsi_ref_clk; +static struct clk *mdp_dsi_pclk; +static struct clk *ahb_m_clk; +static struct clk *ahb_s_clk; +static struct clk *ebi1_dsi_clk; +int mipi_dsi_clk_on; + +int mipi_dsi_clk_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + dsi_esc_clk = clk_get(dev, "esc_clk"); + if (IS_ERR_OR_NULL(dsi_esc_clk)) { + printk(KERN_ERR "can't find dsi_esc_clk\n"); + dsi_esc_clk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_byte_div_clk = clk_get(dev, "byte_clk"); + if (IS_ERR_OR_NULL(dsi_byte_div_clk)) { + pr_err("can't find dsi_byte_div_clk\n"); + dsi_byte_div_clk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_pixel_clk = clk_get(dev, "pixel_clk"); + if (IS_ERR_OR_NULL(dsi_pixel_clk)) { + pr_err("can't find dsi_pixel_clk\n"); + dsi_pixel_clk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_clk = clk_get(dev, "core_clk"); + if (IS_ERR_OR_NULL(dsi_clk)) { + pr_err("can't find dsi_clk\n"); + dsi_clk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_ref_clk = clk_get(dev, "ref_clk"); + if (IS_ERR_OR_NULL(dsi_ref_clk)) { + pr_err("can't find dsi_ref_clk\n"); + dsi_ref_clk = NULL; + goto mipi_dsi_clk_err; + } + + mdp_dsi_pclk = clk_get(dev, "mdp_clk"); + if (IS_ERR_OR_NULL(mdp_dsi_pclk)) { + pr_err("can't find mdp_dsi_pclk\n"); + mdp_dsi_pclk = NULL; + goto mipi_dsi_clk_err; + } + + ahb_m_clk = clk_get(dev, "master_iface_clk"); + if (IS_ERR_OR_NULL(ahb_m_clk)) { + pr_err("can't find ahb_m_clk\n"); + ahb_m_clk = NULL; + goto mipi_dsi_clk_err; + } + + ahb_s_clk = clk_get(dev, "slave_iface_clk"); + if (IS_ERR_OR_NULL(ahb_s_clk)) { + pr_err("can't find ahb_s_clk\n"); + ahb_s_clk = NULL; + goto mipi_dsi_clk_err; + } + + ebi1_dsi_clk = clk_get(dev, "mem_clk"); + if (IS_ERR_OR_NULL(ebi1_dsi_clk)) { + pr_err("can't find ebi1_dsi_clk\n"); + ebi1_dsi_clk = NULL; + goto mipi_dsi_clk_err; + } + + return 0; + +mipi_dsi_clk_err: + mipi_dsi_clk_deinit(NULL); + return -EPERM; +} + +void mipi_dsi_clk_deinit(struct device *dev) +{ + if (mdp_dsi_pclk) + clk_put(mdp_dsi_pclk); + if (ahb_m_clk) + clk_put(ahb_m_clk); + if (ahb_s_clk) + clk_put(ahb_s_clk); + if (dsi_ref_clk) + clk_put(dsi_ref_clk); + if (dsi_byte_div_clk) + clk_put(dsi_byte_div_clk); + if (dsi_esc_clk) + clk_put(dsi_esc_clk); + if (ebi1_dsi_clk) + clk_put(ebi1_dsi_clk); +} + +static void mipi_dsi_clk_ctrl(struct dsi_clk_desc *clk, int clk_en) +{ + uint32 data; + if (clk_en) { + data = (clk->pre_div_func) << 24 | + (clk->m) << 16 | (clk->n) << 8 | + ((clk->d) * 2); + clk_set_rate(dsi_clk, data); + clk_enable(dsi_clk); + } else + clk_disable(dsi_clk); +} + +static void mipi_dsi_pclk_ctrl(struct dsi_clk_desc *clk, int clk_en) +{ + uint32 data; + + if (clk_en) { + data = (clk->pre_div_func) << 24 | (clk->m) << 16 + | (clk->n) << 8 | ((clk->d) * 2); + if ((clk_set_rate(dsi_pixel_clk, data)) < 0) + pr_err("%s: pixel clk set rate failed\n", __func__); + if (clk_enable(dsi_pixel_clk)) + pr_err("%s clk enable failed\n", __func__); + } else { + clk_disable(dsi_pixel_clk); + } +} + +static void mipi_dsi_calibration(void) +{ + MIPI_OUTP(MIPI_DSI_BASE + 0xf8, 0x00a105a1); /* cal_hw_ctrl */ +} + +#define PREF_DIV_RATIO 19 +struct dsiphy_pll_divider_config pll_divider_config; + +int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes, + uint32 *expected_dsi_pclk) +{ + u32 fb_divider, rate, vco; + u32 div_ratio = 0; + struct dsi_clk_mnd_table const *mnd_entry = mnd_table; + if (pll_divider_config.clk_rate == 0) + pll_divider_config.clk_rate = 454000000; + + rate = pll_divider_config.clk_rate / 1000000; /* In Mhz */ + + if (rate < 125) { + vco = rate * 8; + div_ratio = 8; + } else if (rate < 250) { + vco = rate * 4; + div_ratio = 4; + } else if (rate < 500) { + vco = rate * 2; + div_ratio = 2; + } else { + vco = rate * 1; + div_ratio = 1; + } + + /* find the mnd settings from mnd_table entry */ + for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) { + if (((mnd_entry->lanes) == lanes) && + ((mnd_entry->bpp) == bpp)) + break; + } + + if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) { + pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n", + __func__, lanes, bpp); + return -EINVAL; + } + fb_divider = ((vco * PREF_DIV_RATIO) / 27); + pll_divider_config.fb_divider = fb_divider; + pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO; + pll_divider_config.bit_clk_divider = div_ratio; + pll_divider_config.byte_clk_divider = + pll_divider_config.bit_clk_divider * 8; + pll_divider_config.dsi_clk_divider = + (mnd_entry->dsiclk_div) * div_ratio; + + if ((mnd_entry->dsiclk_d == 0) + || (mnd_entry->dsiclk_m == 1)) { + dsicore_clk.mnd_mode = 0; + dsicore_clk.src = 0x3; + dsicore_clk.pre_div_func = (mnd_entry->dsiclk_n - 1); + } else { + dsicore_clk.mnd_mode = 2; + dsicore_clk.src = 0x3; + dsicore_clk.m = mnd_entry->dsiclk_m; + dsicore_clk.n = mnd_entry->dsiclk_n; + dsicore_clk.d = mnd_entry->dsiclk_d; + } + + if ((mnd_entry->pclk_d == 0) + || (mnd_entry->pclk_m == 1)) { + dsi_pclk.mnd_mode = 0; + dsi_pclk.src = 0x3; + dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1); + *expected_dsi_pclk = ((vco * 1000000) / + ((pll_divider_config.dsi_clk_divider) + * (mnd_entry->pclk_n))); + } else { + dsi_pclk.mnd_mode = 2; + dsi_pclk.src = 0x3; + dsi_pclk.m = mnd_entry->pclk_m; + dsi_pclk.n = mnd_entry->pclk_n; + dsi_pclk.d = mnd_entry->pclk_d; + *expected_dsi_pclk = ((vco * 1000000 * dsi_pclk.m) / + ((pll_divider_config.dsi_clk_divider) + * (mnd_entry->pclk_n))); + } + dsicore_clk.m = 1; + dsicore_clk.n = 1; + dsicore_clk.d = 2; + dsicore_clk.pre_div_func = 0; + + dsi_pclk.m = 1; + dsi_pclk.n = 3; + dsi_pclk.d = 2; + dsi_pclk.pre_div_func = 0; + return 0; +} + +void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info, + int target_type) +{ + struct mipi_dsi_phy_ctrl *pd; + int i, off; + + MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */ + wmb(); + usleep(1000); + MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */ + wmb(); + usleep(1000); + MIPI_OUTP(MIPI_DSI_BASE + 0x2cc, 0x0003);/* regulator_ctrl_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x2d0, 0x0001);/* regulator_ctrl_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x2d4, 0x0001);/* regulator_ctrl_2 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x2d8, 0x0000);/* regulator_ctrl_3 */ +#ifdef DSI_POWER + MIPI_OUTP(MIPI_DSI_BASE + 0x2dc, 0x0100);/* regulator_ctrl_4 */ +#endif + + pd = (panel_info->mipi).dsi_phy_db; + + off = 0x02cc; /* regulator ctrl 0 */ + for (i = 0; i < 4; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->regulator[i]); + wmb(); + off += 4; + } + + off = 0x0260; /* phy timig ctrl 0 */ + for (i = 0; i < 11; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->timing[i]); + wmb(); + off += 4; + } + + off = 0x0290; /* ctrl 0 */ + for (i = 0; i < 4; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->ctrl[i]); + wmb(); + off += 4; + } + + off = 0x02a0; /* strength 0 */ + for (i = 0; i < 4; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->strength[i]); + wmb(); + off += 4; + } + + mipi_dsi_calibration(); + + off = 0x0204; /* pll ctrl 1, skip 0 */ + for (i = 1; i < 21; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->pll[i]); + wmb(); + off += 4; + } + + MIPI_OUTP(MIPI_DSI_BASE + 0x100, 0x67); + + /* pll ctrl 0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pd->pll[0]); + wmb(); +} + +void cont_splash_clk_ctrl(int enable) +{ +} + +void mipi_dsi_prepare_clocks(void) +{ + clk_prepare(dsi_ref_clk); + clk_prepare(ahb_m_clk); + clk_prepare(ahb_s_clk); + clk_prepare(ebi1_dsi_clk); + clk_prepare(mdp_dsi_pclk); + clk_prepare(dsi_byte_div_clk); + clk_prepare(dsi_esc_clk); + clk_prepare(dsi_clk); + clk_prepare(dsi_pixel_clk); +} + +void mipi_dsi_unprepare_clocks(void) +{ + clk_unprepare(dsi_esc_clk); + clk_unprepare(dsi_byte_div_clk); + clk_unprepare(mdp_dsi_pclk); + clk_unprepare(ebi1_dsi_clk); + clk_unprepare(ahb_m_clk); + clk_unprepare(ahb_s_clk); + clk_unprepare(dsi_ref_clk); + clk_unprepare(dsi_clk); + clk_unprepare(dsi_pixel_clk); +} + +void mipi_dsi_ahb_ctrl(u32 enable) +{ + static int ahb_ctrl_done; + if (enable) { + if (ahb_ctrl_done) { + pr_info("%s: ahb clks already ON\n", __func__); + return; + } + clk_enable(dsi_ref_clk); + clk_enable(ahb_m_clk); + clk_enable(ahb_s_clk); + ahb_ctrl_done = 1; + } else { + if (ahb_ctrl_done == 0) { + pr_info("%s: ahb clks already OFF\n", __func__); + return; + } + clk_disable(ahb_m_clk); + clk_disable(ahb_s_clk); + clk_disable(dsi_ref_clk); + ahb_ctrl_done = 0; + } +} + +void mipi_dsi_clk_enable(void) +{ + unsigned data = 0; + uint32 pll_ctrl; + + if (mipi_dsi_clk_on) { + pr_info("%s: mipi_dsi_clks already ON\n", __func__); + return; + } + if (clk_set_rate(ebi1_dsi_clk, 65000000)) /* 65 MHz */ + pr_err("%s: ebi1_dsi_clk set rate failed\n", __func__); + clk_enable(ebi1_dsi_clk); + + pll_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0200); + MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pll_ctrl | 0x01); + mb(); + + clk_set_rate(dsi_byte_div_clk, data); + clk_set_rate(dsi_esc_clk, data); + clk_enable(mdp_dsi_pclk); + clk_enable(dsi_byte_div_clk); + clk_enable(dsi_esc_clk); + mipi_dsi_pclk_ctrl(&dsi_pclk, 1); + mipi_dsi_clk_ctrl(&dsicore_clk, 1); + mipi_dsi_clk_on = 1; +} + +void mipi_dsi_clk_disable(void) +{ + if (mipi_dsi_clk_on == 0) { + pr_info("%s: mipi_dsi_clks already OFF\n", __func__); + return; + } + mipi_dsi_pclk_ctrl(&dsi_pclk, 0); + mipi_dsi_clk_ctrl(&dsicore_clk, 0); + clk_disable(dsi_esc_clk); + clk_disable(dsi_byte_div_clk); + clk_disable(mdp_dsi_pclk); + /* DSIPHY_PLL_CTRL_0, disable dsi pll */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0200, 0x40); + if (clk_set_rate(ebi1_dsi_clk, 0)) + pr_err("%s: ebi1_dsi_clk set rate failed\n", __func__); + clk_disable(ebi1_dsi_clk); + mipi_dsi_clk_on = 0; +} + +void mipi_dsi_phy_ctrl(int on) +{ + if (on) { + /* DSIPHY_PLL_CTRL_5 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x050); + + /* DSIPHY_TPA_CTRL_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0258, 0x00f); + + /* DSIPHY_TPA_CTRL_2 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x025c, 0x000); + } else { + /* DSIPHY_PLL_CTRL_5 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x05f); + + /* DSIPHY_TPA_CTRL_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0258, 0x08f); + + /* DSIPHY_TPA_CTRL_2 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x025c, 0x001); + + /* DSIPHY_REGULATOR_CTRL_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x02cc, 0x02); + + /* DSIPHY_CTRL_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0290, 0x00); + + /* DSIPHY_CTRL_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0294, 0x7f); + + /* disable dsi clk */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0118, 0); + } +} + +#ifdef CONFIG_FB_MSM_MDP303 +void update_lane_config(struct msm_panel_info *pinfo) +{ + struct mipi_dsi_phy_ctrl *pd; + + pd = (pinfo->mipi).dsi_phy_db; + pinfo->mipi.data_lane1 = FALSE; + pd->pll[10] |= 0x08; + + pinfo->yres = 320; + pinfo->lcdc.h_back_porch = 15; + pinfo->lcdc.h_front_porch = 21; + pinfo->lcdc.h_pulse_width = 5; + pinfo->lcdc.v_back_porch = 50; + pinfo->lcdc.v_front_porch = 101; + pinfo->lcdc.v_pulse_width = 50; +} +#endif diff --git a/drivers/video/msm/msm_dss_io_8960.c b/drivers/video/msm/msm_dss_io_8960.c new file mode 100644 index 0000000000000000000000000000000000000000..c2220688f43176fe85478ee82daf0855de995743 --- /dev/null +++ b/drivers/video/msm/msm_dss_io_8960.c @@ -0,0 +1,830 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include "msm_fb.h" +#include "mdp.h" +#include "mdp4.h" +#include "mipi_dsi.h" +#include "hdmi_msm.h" +#include + +/* HDMI PHY macros */ +#define HDMI_PHY_REG_0 (0x00000400) +#define HDMI_PHY_REG_1 (0x00000404) +#define HDMI_PHY_REG_2 (0x00000408) +#define HDMI_PHY_REG_3 (0x0000040c) +#define HDMI_PHY_REG_4 (0x00000410) +#define HDMI_PHY_REG_5 (0x00000414) +#define HDMI_PHY_REG_6 (0x00000418) +#define HDMI_PHY_REG_7 (0x0000041c) +#define HDMI_PHY_REG_8 (0x00000420) +#define HDMI_PHY_REG_9 (0x00000424) +#define HDMI_PHY_REG_10 (0x00000428) +#define HDMI_PHY_REG_11 (0x0000042c) +#define HDMI_PHY_REG_12 (0x00000430) +#define HDMI_PHY_REG_BIST_CFG (0x00000434) +#define HDMI_PHY_DEBUG_BUS_SEL (0x00000438) +#define HDMI_PHY_REG_MISC0 (0x0000043c) +#define HDMI_PHY_REG_13 (0x00000440) +#define HDMI_PHY_REG_14 (0x00000444) +#define HDMI_PHY_REG_15 (0x00000448) +#define HDMI_PHY_CTRL (0x000002D4) + +/* HDMI PHY/PLL bit field macros */ +#define HDMI_PHY_PLL_STATUS0 (0x00000598) +#define SW_RESET BIT(2) +#define SW_RESET_PLL BIT(0) +#define PWRDN_B BIT(7) + +/* multimedia sub system clock control */ +char *mmss_cc_base = MSM_MMSS_CLK_CTL_BASE; +/* multimedia sub system sfpb */ +char *mmss_sfpb_base; +void __iomem *periph_base; + +static struct dsi_clk_desc dsicore_clk; +static struct dsi_clk_desc dsi_pclk; + +static struct clk *dsi_byte_div_clk; +static struct clk *dsi_esc_clk; +static struct clk *dsi_m_pclk; +static struct clk *dsi_s_pclk; + +static struct clk *amp_pclk; +int mipi_dsi_clk_on; + +int mipi_dsi_clk_init(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct device *dev = &pdev->dev; + + mfd = platform_get_drvdata(pdev); + + amp_pclk = clk_get(dev, "arb_clk"); + if (IS_ERR_OR_NULL(amp_pclk)) { + pr_err("can't find amp_pclk\n"); + amp_pclk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_m_pclk = clk_get(dev, "master_iface_clk"); + if (IS_ERR_OR_NULL(dsi_m_pclk)) { + pr_err("can't find dsi_m_pclk\n"); + dsi_m_pclk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_s_pclk = clk_get(dev, "slave_iface_clk"); + if (IS_ERR_OR_NULL(dsi_s_pclk)) { + pr_err("can't find dsi_s_pclk\n"); + dsi_s_pclk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_byte_div_clk = clk_get(dev, "byte_clk"); + if (IS_ERR(dsi_byte_div_clk)) { + pr_err("can't find dsi_byte_div_clk\n"); + dsi_byte_div_clk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_esc_clk = clk_get(dev, "esc_clk"); + if (IS_ERR(dsi_esc_clk)) { + printk(KERN_ERR "can't find dsi_esc_clk\n"); + dsi_esc_clk = NULL; + goto mipi_dsi_clk_err; + } + + return 0; + +mipi_dsi_clk_err: + mipi_dsi_clk_deinit(dev); + return -EPERM; +} + +void mipi_dsi_clk_deinit(struct device *dev) +{ + if (amp_pclk) + clk_put(amp_pclk); + if (amp_pclk) + clk_put(dsi_m_pclk); + if (dsi_s_pclk) + clk_put(dsi_s_pclk); + if (dsi_byte_div_clk) + clk_put(dsi_byte_div_clk); + if (dsi_esc_clk) + clk_put(dsi_esc_clk); +} + +static void mipi_dsi_clk_ctrl(struct dsi_clk_desc *clk, int clk_en) +{ + char *cc, *ns, *md; + int pmxo_sel = 0; + char mnd_en = 1, root_en = 1; + uint32 data, val; + + cc = mmss_cc_base + 0x004c; + md = mmss_cc_base + 0x0050; + ns = mmss_cc_base + 0x0054; + + if (clk_en) { + if (clk->mnd_mode == 0) { + data = clk->pre_div_func << 14; + data |= clk->src; + MIPI_OUTP_SECURE(ns, data); + MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8) + | (clk->mnd_mode << 6) + | (root_en << 2) | clk_en)); + } else { + val = clk->d * 2; + data = (~val) & 0x0ff; + data |= clk->m << 8; + MIPI_OUTP_SECURE(md, data); + + val = clk->n - clk->m; + data = (~val) & 0x0ff; + data <<= 24; + data |= clk->src; + MIPI_OUTP_SECURE(ns, data); + + MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8) + | (clk->mnd_mode << 6) + | (mnd_en << 5) + | (root_en << 2) | clk_en)); + } + } else + MIPI_OUTP_SECURE(cc, 0); + + wmb(); +} + +static void mipi_dsi_sfpb_cfg(void) +{ + char *sfpb; + int data; + + sfpb = mmss_sfpb_base + 0x058; + + data = MIPI_INP(sfpb); + data |= 0x01800; + MIPI_OUTP(sfpb, data); + wmb(); +} + +static void mipi_dsi_pclk_ctrl(struct dsi_clk_desc *clk, int clk_en) +{ + char *cc, *ns, *md; + char mnd_en = 1, root_en = 1; + uint32 data, val; + + cc = mmss_cc_base + 0x0130; + md = mmss_cc_base + 0x0134; + ns = mmss_cc_base + 0x0138; + + if (clk_en) { + if (clk->mnd_mode == 0) { + data = clk->pre_div_func << 12; + data |= clk->src; + MIPI_OUTP_SECURE(ns, data); + MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6) + | (root_en << 2) | clk_en)); + } else { + val = clk->d * 2; + data = (~val) & 0x0ff; + data |= clk->m << 8; + MIPI_OUTP_SECURE(md, data); + + val = clk->n - clk->m; + data = (~val) & 0x0ff; + data <<= 24; + data |= clk->src; + MIPI_OUTP_SECURE(ns, data); + + MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6) + | (mnd_en << 5) + | (root_en << 2) | clk_en)); + } + } else + MIPI_OUTP_SECURE(cc, 0); + + wmb(); +} + +static void mipi_dsi_ahb_en(void) +{ + char *ahb; + + ahb = mmss_cc_base + 0x08; + + pr_debug("%s: ahb=%x %x\n", + __func__, (int) ahb, MIPI_INP_SECURE(ahb)); +} + +void mipi_dsi_lane_cfg(void) +{ + int i, ln_offset; + + ln_offset = 0x300; + for (i = 0; i < 4; i++) { + /* DSI1_DSIPHY_LN_CFG0 */ + MIPI_OUTP(MIPI_DSI_BASE + ln_offset, 0x80); + /* DSI1_DSIPHY_LN_CFG1 */ + MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x04, 0x45); + /* DSI1_DSIPHY_LN_CFG2 */ + MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x08, 0x0); + /* DSI1_DSIPHY_LN_TEST_DATAPATH */ + MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x0c, 0x0); + /* DSI1_DSIPHY_LN_TEST_STR0 */ + MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x14, 0x1); + /* DSI1_DSIPHY_LN_TEST_STR1 */ + MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x18, 0x66); + ln_offset += 0x40; + } + + MIPI_OUTP(MIPI_DSI_BASE + 0x0400, 0x40); /* DSI1_DSIPHY_LNCK_CFG0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0404, 0x67); /* DSI1_DSIPHY_LNCK_CFG1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0408, 0x0); /* DSI1_DSIPHY_LNCK_CFG2 */ + /* DSI1_DSIPHY_LNCK_TEST_DATAPATH */ + MIPI_OUTP(MIPI_DSI_BASE + 0x040c, 0x0); + MIPI_OUTP(MIPI_DSI_BASE + 0x0414, 0x1); /* DSI1_DSIPHY_LNCK_TEST_STR0 */ + /* DSI1_DSIPHY_LNCK_TEST_STR1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0418, 0x88); +} + +void mipi_dsi_bist_ctrl(void) +{ + MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0f); /* DSI1_DSIPHY_BIST_CTRL4 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0490, 0x03); /* DSI1_DSIPHY_BIST_CTRL1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x048c, 0x03); /* DSI1_DSIPHY_BIST_CTRL0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0); /* DSI1_DSIPHY_BIST_CTRL4 */ +} + +static void mipi_dsi_calibration(void) +{ + int i = 0; + uint32 term_cnt = 5000; + int cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550); + + /* DSI1_DSIPHY_REGULATOR_CAL_PWR_CFG */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0518, 0x03); + + /* DSI1_DSIPHY_CAL_SW_CFG2 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0534, 0x0); + /* DSI1_DSIPHY_CAL_HW_CFG1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x053c, 0x5a); + /* DSI1_DSIPHY_CAL_HW_CFG3 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0544, 0x10); + /* DSI1_DSIPHY_CAL_HW_CFG4 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0548, 0x01); + /* DSI1_DSIPHY_CAL_HW_CFG0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0538, 0x01); + + /* DSI1_DSIPHY_CAL_HW_TRIGGER */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0528, 0x01); + usleep_range(5000, 5000); + /* DSI1_DSIPHY_CAL_HW_TRIGGER */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0528, 0x00); + + cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550); + while (cal_busy & 0x10) { + i++; + if (i > term_cnt) { + pr_err("DSI1 PHY REGULATOR NOT READY," + "exceeded polling TIMEOUT!\n"); + break; + } + cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550); + } +} + +void mipi_dsi_phy_rdy_poll(void) +{ + uint32 phy_pll_busy; + uint32 i = 0; + uint32 term_cnt = 0xFFFFFF; + + phy_pll_busy = MIPI_INP(MIPI_DSI_BASE + 0x280); + while (!(phy_pll_busy & 0x1)) { + i++; + if (i > term_cnt) { + pr_err("DSI1 PHY NOT READY, exceeded polling TIMEOUT!\n"); + break; + } + phy_pll_busy = MIPI_INP(MIPI_DSI_BASE + 0x280); + } +} + +#define PREF_DIV_RATIO 27 +struct dsiphy_pll_divider_config pll_divider_config; + +int mipi_dsi_phy_pll_config(u32 clk_rate) +{ + struct dsiphy_pll_divider_config *dividers; + u32 fb_divider, tmp; + dividers = &pll_divider_config; + + /* DSIPHY_PLL_CTRL_x: 1 2 3 8 9 10 */ + /* masks 0xff 0x07 0x3f 0x0f 0xff 0xff */ + + /* DSIPHY_PLL_CTRL_1 */ + fb_divider = ((dividers->fb_divider) / 2) - 1; + MIPI_OUTP(MIPI_DSI_BASE + 0x204, fb_divider & 0xff); + + /* DSIPHY_PLL_CTRL_2 */ + tmp = MIPI_INP(MIPI_DSI_BASE + 0x208); + tmp &= ~0x07; + tmp |= (fb_divider >> 8) & 0x07; + MIPI_OUTP(MIPI_DSI_BASE + 0x208, tmp); + + /* DSIPHY_PLL_CTRL_3 */ + tmp = MIPI_INP(MIPI_DSI_BASE + 0x20c); + tmp &= ~0x3f; + tmp |= (dividers->ref_divider_ratio - 1) & 0x3f; + MIPI_OUTP(MIPI_DSI_BASE + 0x20c, tmp); + + /* DSIPHY_PLL_CTRL_8 */ + tmp = MIPI_INP(MIPI_DSI_BASE + 0x220); + tmp &= ~0x0f; + tmp |= (dividers->bit_clk_divider - 1) & 0x0f; + MIPI_OUTP(MIPI_DSI_BASE + 0x220, tmp); + + /* DSIPHY_PLL_CTRL_9 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x224, (dividers->byte_clk_divider - 1)); + + /* DSIPHY_PLL_CTRL_10 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x228, (dividers->dsi_clk_divider - 1)); + + return 0; +} + +int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes, + uint32 *expected_dsi_pclk) +{ + u32 fb_divider, rate, vco; + u32 div_ratio = 0; + struct dsi_clk_mnd_table const *mnd_entry = mnd_table; + if (pll_divider_config.clk_rate == 0) + pll_divider_config.clk_rate = 454000000; + + rate = pll_divider_config.clk_rate / 1000000; /* In Mhz */ + + if (rate < 125) { + vco = rate * 8; + div_ratio = 8; + } else if (rate < 250) { + vco = rate * 4; + div_ratio = 4; + } else if (rate < 600) { + vco = rate * 2; + div_ratio = 2; + } else { + vco = rate * 1; + div_ratio = 1; + } + + /* find the mnd settings from mnd_table entry */ + for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) { + if (((mnd_entry->lanes) == lanes) && + ((mnd_entry->bpp) == bpp)) + break; + } + + if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) { + pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n", + __func__, lanes, bpp); + return -EINVAL; + } + fb_divider = ((vco * PREF_DIV_RATIO) / 27); + pll_divider_config.fb_divider = fb_divider; + pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO; + pll_divider_config.bit_clk_divider = div_ratio; + pll_divider_config.byte_clk_divider = + pll_divider_config.bit_clk_divider * 8; + pll_divider_config.dsi_clk_divider = + (mnd_entry->dsiclk_div) * div_ratio; + + if (mnd_entry->dsiclk_d == 0) { + dsicore_clk.mnd_mode = 0; + dsicore_clk.src = 0x3; + dsicore_clk.pre_div_func = (mnd_entry->dsiclk_n - 1); + } else { + dsicore_clk.mnd_mode = 2; + dsicore_clk.src = 0x3; + dsicore_clk.m = mnd_entry->dsiclk_m; + dsicore_clk.n = mnd_entry->dsiclk_n; + dsicore_clk.d = mnd_entry->dsiclk_d; + } + + if ((mnd_entry->pclk_d == 0) + || (mnd_entry->pclk_m == 1)) { + dsi_pclk.mnd_mode = 0; + dsi_pclk.src = 0x3; + dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1); + *expected_dsi_pclk = ((vco * 1000000) / + ((pll_divider_config.dsi_clk_divider) + * (mnd_entry->pclk_n))); + } else { + dsi_pclk.mnd_mode = 2; + dsi_pclk.src = 0x3; + dsi_pclk.m = mnd_entry->pclk_m; + dsi_pclk.n = mnd_entry->pclk_n; + dsi_pclk.d = mnd_entry->pclk_d; + *expected_dsi_pclk = ((vco * 1000000 * dsi_pclk.m) / + ((pll_divider_config.dsi_clk_divider) + * (mnd_entry->pclk_n))); + } + return 0; +} + +static void mipi_dsi_configure_serdes(void) +{ + void __iomem *cc; + + /* PHY registers programemd thru S2P interface */ + if (periph_base) { + MIPI_OUTP(periph_base + 0x2c, 0x000000b6); + MIPI_OUTP(periph_base + 0x2c, 0x000001b5); + MIPI_OUTP(periph_base + 0x2c, 0x000001b4); + MIPI_OUTP(periph_base + 0x2c, 0x000003b3); + MIPI_OUTP(periph_base + 0x2c, 0x000003a2); + MIPI_OUTP(periph_base + 0x2c, 0x000002a1); + MIPI_OUTP(periph_base + 0x2c, 0x000008a0); + MIPI_OUTP(periph_base + 0x2c, 0x00000d9f); + MIPI_OUTP(periph_base + 0x2c, 0x0000109e); + MIPI_OUTP(periph_base + 0x2c, 0x0000209d); + MIPI_OUTP(periph_base + 0x2c, 0x0000109c); + MIPI_OUTP(periph_base + 0x2c, 0x0000079a); + MIPI_OUTP(periph_base + 0x2c, 0x00000c99); + MIPI_OUTP(periph_base + 0x2c, 0x00002298); + MIPI_OUTP(periph_base + 0x2c, 0x000000a7); + MIPI_OUTP(periph_base + 0x2c, 0x000000a6); + MIPI_OUTP(periph_base + 0x2c, 0x000000a5); + MIPI_OUTP(periph_base + 0x2c, 0x00007fa4); + MIPI_OUTP(periph_base + 0x2c, 0x0000eea8); + MIPI_OUTP(periph_base + 0x2c, 0x000006aa); + MIPI_OUTP(periph_base + 0x2c, 0x00002095); + MIPI_OUTP(periph_base + 0x2c, 0x00000493); + MIPI_OUTP(periph_base + 0x2c, 0x00001092); + MIPI_OUTP(periph_base + 0x2c, 0x00000691); + MIPI_OUTP(periph_base + 0x2c, 0x00005490); + MIPI_OUTP(periph_base + 0x2c, 0x0000038d); + MIPI_OUTP(periph_base + 0x2c, 0x0000148c); + MIPI_OUTP(periph_base + 0x2c, 0x0000058b); + MIPI_OUTP(periph_base + 0x2c, 0x0000078a); + MIPI_OUTP(periph_base + 0x2c, 0x00001f89); + MIPI_OUTP(periph_base + 0x2c, 0x00003388); + MIPI_OUTP(periph_base + 0x2c, 0x00006387); + MIPI_OUTP(periph_base + 0x2c, 0x00004886); + MIPI_OUTP(periph_base + 0x2c, 0x00005085); + MIPI_OUTP(periph_base + 0x2c, 0x00000084); + MIPI_OUTP(periph_base + 0x2c, 0x0000da83); + MIPI_OUTP(periph_base + 0x2c, 0x0000b182); + MIPI_OUTP(periph_base + 0x2c, 0x00002f81); + MIPI_OUTP(periph_base + 0x2c, 0x00004080); + MIPI_OUTP(periph_base + 0x2c, 0x00004180); + MIPI_OUTP(periph_base + 0x2c, 0x000006aa); + } + + cc = MIPI_DSI_BASE + 0x0130; + MIPI_OUTP(cc, 0x806c11c8); + MIPI_OUTP(cc, 0x804c11c8); + MIPI_OUTP(cc, 0x806d0080); + MIPI_OUTP(cc, 0x804d0080); + MIPI_OUTP(cc, 0x00000000); + MIPI_OUTP(cc, 0x807b1597); + MIPI_OUTP(cc, 0x805b1597); + MIPI_OUTP(cc, 0x807c0080); + MIPI_OUTP(cc, 0x805c0080); + MIPI_OUTP(cc, 0x00000000); + MIPI_OUTP(cc, 0x807911c8); + MIPI_OUTP(cc, 0x805911c8); + MIPI_OUTP(cc, 0x807a0080); + MIPI_OUTP(cc, 0x805a0080); + MIPI_OUTP(cc, 0x00000000); + MIPI_OUTP(cc, 0x80721555); + MIPI_OUTP(cc, 0x80521555); + MIPI_OUTP(cc, 0x80730000); + MIPI_OUTP(cc, 0x80530000); + MIPI_OUTP(cc, 0x00000000); +} + +void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info, + int target_type) +{ + struct mipi_dsi_phy_ctrl *pd; + int i, off; + + MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */ + wmb(); + usleep(1); + MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */ + wmb(); + usleep(1); + MIPI_OUTP(MIPI_DSI_BASE + 0x500, 0x0003);/* regulator_ctrl_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x504, 0x0001);/* regulator_ctrl_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x508, 0x0001);/* regulator_ctrl_2 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x50c, 0x0000);/* regulator_ctrl_3 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x510, 0x0100);/* regulator_ctrl_4 */ + + MIPI_OUTP(MIPI_DSI_BASE + 0x4b0, 0x04);/* DSIPHY_LDO_CNTRL */ + + pd = (panel_info->mipi).dsi_phy_db; + + off = 0x0480; /* strength 0 - 2 */ + for (i = 0; i < 3; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->strength[i]); + wmb(); + off += 4; + } + + off = 0x0470; /* ctrl 0 - 3 */ + for (i = 0; i < 4; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->ctrl[i]); + wmb(); + off += 4; + } + + off = 0x0500; /* regulator ctrl 0 - 4 */ + for (i = 0; i < 5; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->regulator[i]); + wmb(); + off += 4; + } + mipi_dsi_calibration(); + mipi_dsi_lane_cfg(); /* lane cfgs */ + mipi_dsi_bist_ctrl(); /* bist ctrl */ + + off = 0x0204; /* pll ctrl 1 - 19, skip 0 */ + for (i = 1; i < 20; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->pll[i]); + wmb(); + off += 4; + } + + if (panel_info) + mipi_dsi_phy_pll_config(panel_info->clk_rate); + + /* pll ctrl 0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x200, pd->pll[0]); + wmb(); + + off = 0x0440; /* phy timing ctrl 0 - 11 */ + for (i = 0; i < 12; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->timing[i]); + wmb(); + off += 4; + } + + if (target_type == 1) + mipi_dsi_configure_serdes(); +} + +void cont_splash_clk_ctrl(int enable) +{ + static int cont_splash_clks_enabled; + if (enable && !cont_splash_clks_enabled) { + clk_prepare_enable(dsi_byte_div_clk); + clk_prepare_enable(dsi_esc_clk); + cont_splash_clks_enabled = 1; + } else if (!enable && cont_splash_clks_enabled) { + clk_disable_unprepare(dsi_byte_div_clk); + clk_disable_unprepare(dsi_esc_clk); + cont_splash_clks_enabled = 0; + } +} + +void mipi_dsi_prepare_clocks(void) +{ + clk_prepare(amp_pclk); + clk_prepare(dsi_m_pclk); + clk_prepare(dsi_s_pclk); + clk_prepare(dsi_byte_div_clk); + clk_prepare(dsi_esc_clk); +} + +void mipi_dsi_unprepare_clocks(void) +{ + clk_unprepare(dsi_esc_clk); + clk_unprepare(dsi_byte_div_clk); + clk_unprepare(dsi_m_pclk); + clk_unprepare(dsi_s_pclk); + clk_unprepare(amp_pclk); +} + +void mipi_dsi_ahb_ctrl(u32 enable) +{ + static int ahb_ctrl_done; + if (enable) { + if (ahb_ctrl_done) { + pr_info("%s: ahb clks already ON\n", __func__); + return; + } + clk_enable(amp_pclk); /* clock for AHB-master to AXI */ + clk_enable(dsi_m_pclk); + clk_enable(dsi_s_pclk); + mipi_dsi_ahb_en(); + mipi_dsi_sfpb_cfg(); + ahb_ctrl_done = 1; + } else { + if (ahb_ctrl_done == 0) { + pr_info("%s: ahb clks already OFF\n", __func__); + return; + } + clk_disable(dsi_m_pclk); + clk_disable(dsi_s_pclk); + clk_disable(amp_pclk); /* clock for AHB-master to AXI */ + ahb_ctrl_done = 0; + } +} + +void mipi_dsi_clk_enable(void) +{ + u32 pll_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0200); + if (mipi_dsi_clk_on) { + pr_info("%s: mipi_dsi_clks already ON\n", __func__); + return; + } + MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pll_ctrl | 0x01); + mipi_dsi_phy_rdy_poll(); + + if (clk_set_rate(dsi_byte_div_clk, 1) < 0) /* divided by 1 */ + pr_err("%s: dsi_byte_div_clk - " + "clk_set_rate failed\n", __func__); + if (clk_set_rate(dsi_esc_clk, esc_byte_ratio) < 0) /* divided by esc */ + pr_err("%s: dsi_esc_clk - " /* clk ratio */ + "clk_set_rate failed\n", __func__); + mipi_dsi_pclk_ctrl(&dsi_pclk, 1); + mipi_dsi_clk_ctrl(&dsicore_clk, 1); + clk_enable(dsi_byte_div_clk); + clk_enable(dsi_esc_clk); + mipi_dsi_clk_on = 1; + mdp4_stat.dsi_clk_on++; +} + +void mipi_dsi_clk_disable(void) +{ + if (mipi_dsi_clk_on == 0) { + pr_info("%s: mipi_dsi_clks already OFF\n", __func__); + return; + } + clk_disable(dsi_esc_clk); + clk_disable(dsi_byte_div_clk); + mipi_dsi_pclk_ctrl(&dsi_pclk, 0); + mipi_dsi_clk_ctrl(&dsicore_clk, 0); + /* DSIPHY_PLL_CTRL_0, disable dsi pll */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0200, 0x0); + mipi_dsi_clk_on = 0; + mdp4_stat.dsi_clk_off++; +} + +void mipi_dsi_phy_ctrl(int on) +{ + if (on) { + /* DSIPHY_PLL_CTRL_5 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x050); + } else { + /* DSIPHY_PLL_CTRL_5 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x05f); + + /* DSIPHY_REGULATOR_CTRL_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0500, 0x02); + + /* DSIPHY_CTRL_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0470, 0x00); + + /* DSIPHY_CTRL_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0474, 0x7f); + + /* disable dsi clk */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0118, 0); + } +} + +#ifdef CONFIG_FB_MSM_HDMI_COMMON +void hdmi_phy_reset(void) +{ + unsigned int phy_reset_polarity = 0x0; + unsigned int pll_reset_polarity = 0x0; + + unsigned int val = HDMI_INP_ND(HDMI_PHY_CTRL); + + phy_reset_polarity = val >> 3 & 0x1; + pll_reset_polarity = val >> 1 & 0x1; + + if (phy_reset_polarity == 0) + HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET); + else + HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET)); + + if (pll_reset_polarity == 0) + HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET_PLL); + else + HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET_PLL)); + + msleep(100); + + if (phy_reset_polarity == 0) + HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET)); + else + HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET); + + if (pll_reset_polarity == 0) + HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET_PLL)); + else + HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET_PLL); +} + +void hdmi_msm_reset_core(void) +{ + hdmi_msm_set_mode(FALSE); + hdmi_msm_clk(0); + udelay(5); + hdmi_msm_clk(1); + + clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_ASSERT); + udelay(20); + clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_DEASSERT); +} + +void hdmi_msm_init_phy(int video_format) +{ + uint32 offset; + pr_err("Video format is : %u\n", video_format); + + HDMI_OUTP(HDMI_PHY_REG_0, 0x1B); + HDMI_OUTP(HDMI_PHY_REG_1, 0xf2); + + offset = HDMI_PHY_REG_4; + while (offset <= HDMI_PHY_REG_11) { + HDMI_OUTP(offset, 0x0); + offset += 0x4; + } + + HDMI_OUTP(HDMI_PHY_REG_3, 0x20); +} + +void hdmi_msm_powerdown_phy(void) +{ + /* Power down PHY */ + HDMI_OUTP_ND(HDMI_PHY_REG_2, 0x7F); /*0b01111111*/ +} + +void hdmi_frame_ctrl_cfg(const struct hdmi_disp_mode_timing_type *timing) +{ + /* 0x02C8 HDMI_FRAME_CTRL + * 31 INTERLACED_EN Interlaced or progressive enable bit + * 0: Frame in progressive + * 1: Frame is interlaced + * 29 HSYNC_HDMI_POL HSYNC polarity fed to HDMI core + * 0: Active Hi Hsync, detect the rising edge of hsync + * 1: Active lo Hsync, Detect the falling edge of Hsync + * 28 VSYNC_HDMI_POL VSYNC polarity fed to HDMI core + * 0: Active Hi Vsync, detect the rising edge of vsync + * 1: Active Lo Vsync, Detect the falling edge of Vsync + * 12 RGB_MUX_SEL ALPHA mdp4 input is RGB, mdp4 input is BGR + */ + HDMI_OUTP(0x02C8, + ((timing->interlaced << 31) & 0x80000000) + | ((timing->active_low_h << 29) & 0x20000000) + | ((timing->active_low_v << 28) & 0x10000000)); +} + +void hdmi_msm_phy_status_poll(void) +{ + unsigned int lock_det, phy_ready; + lock_det = 0x1 & HDMI_INP_ND(HDMI_PHY_PLL_STATUS0); + if (lock_det) { + pr_debug("HDMI Phy PLL Lock Detect Bit is set\n"); + } else { + pr_debug("HDMI Phy Lock Detect Bit is not set," + "waiting for lock detection\n"); + do { + lock_det = 0x1 & \ + HDMI_INP_ND(HDMI_PHY_PLL_STATUS0); + } while (!lock_det); + } + + phy_ready = 0x1 & HDMI_INP_ND(HDMI_PHY_REG_15); + if (phy_ready) { + pr_debug("HDMI Phy Status bit is set and ready\n"); + } else { + pr_debug("HDMI Phy Status bit is not set," + "waiting for ready status\n"); + do { + phy_ready = 0x1 & HDMI_INP_ND(HDMI_PHY_REG_15); + } while (!phy_ready); + } +} + +#endif diff --git a/drivers/video/msm/msm_dss_io_8x60.c b/drivers/video/msm/msm_dss_io_8x60.c new file mode 100644 index 0000000000000000000000000000000000000000..360b60bb5c920bc55d3cbca1e223a360735558e6 --- /dev/null +++ b/drivers/video/msm/msm_dss_io_8x60.c @@ -0,0 +1,690 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "hdmi_msm.h" +#include + +/* multimedia sub system clock control */ +char *mmss_cc_base = MSM_MMSS_CLK_CTL_BASE; +/* multimedia sub system sfpb */ +char *mmss_sfpb_base; +void __iomem *periph_base; + +static struct dsi_clk_desc dsicore_clk; +static struct dsi_clk_desc dsi_pclk; + +static struct clk *dsi_byte_div_clk; +static struct clk *dsi_esc_clk; +static struct clk *dsi_m_pclk; +static struct clk *dsi_s_pclk; + +static struct clk *amp_pclk; +int mipi_dsi_clk_on; + +int mipi_dsi_clk_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + amp_pclk = clk_get(dev, "arb_clk"); + if (IS_ERR_OR_NULL(amp_pclk)) { + pr_err("can't find amp_pclk\n"); + amp_pclk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_m_pclk = clk_get(dev, "master_iface_clk"); + if (IS_ERR_OR_NULL(dsi_m_pclk)) { + pr_err("can't find dsi_m_pclk\n"); + dsi_m_pclk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_s_pclk = clk_get(dev, "slave_iface_clk"); + if (IS_ERR_OR_NULL(dsi_s_pclk)) { + pr_err("can't find dsi_s_pclk\n"); + dsi_s_pclk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_byte_div_clk = clk_get(dev, "byte_clk"); + if (IS_ERR_OR_NULL(dsi_byte_div_clk)) { + pr_err("can't find dsi_byte_div_clk\n"); + dsi_byte_div_clk = NULL; + goto mipi_dsi_clk_err; + } + + dsi_esc_clk = clk_get(dev, "esc_clk"); + if (IS_ERR_OR_NULL(dsi_esc_clk)) { + printk(KERN_ERR "can't find dsi_esc_clk\n"); + dsi_esc_clk = NULL; + goto mipi_dsi_clk_err; + } + + return 0; + +mipi_dsi_clk_err: + mipi_dsi_clk_deinit(NULL); + return -EPERM; +} + +void mipi_dsi_clk_deinit(struct device *dev) +{ + if (amp_pclk) + clk_put(amp_pclk); + if (dsi_m_pclk) + clk_put(dsi_m_pclk); + if (dsi_s_pclk) + clk_put(dsi_s_pclk); + if (dsi_byte_div_clk) + clk_put(dsi_byte_div_clk); + if (dsi_esc_clk) + clk_put(dsi_esc_clk); +} + +static void mipi_dsi_clk_ctrl(struct dsi_clk_desc *clk, int clk_en) +{ + char *cc, *ns, *md; + int pmxo_sel = 0; + char mnd_en = 1, root_en = 1; + uint32 data, val; + + cc = mmss_cc_base + 0x004c; + md = mmss_cc_base + 0x0050; + ns = mmss_cc_base + 0x0054; + + if (clk_en) { + if (clk->mnd_mode == 0) { + data = clk->pre_div_func << 14; + data |= clk->src; + MIPI_OUTP_SECURE(ns, data); + MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8) + | (clk->mnd_mode << 6) + | (root_en << 2) | clk_en)); + } else { + val = clk->d * 2; + data = (~val) & 0x0ff; + data |= clk->m << 8; + MIPI_OUTP_SECURE(md, data); + + val = clk->n - clk->m; + data = (~val) & 0x0ff; + data <<= 24; + data |= clk->src; + MIPI_OUTP_SECURE(ns, data); + + MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8) + | (clk->mnd_mode << 6) + | (mnd_en << 5) + | (root_en << 2) | clk_en)); + } + + } else + MIPI_OUTP_SECURE(cc, 0); + + wmb(); +} + +static void mipi_dsi_sfpb_cfg(void) +{ + char *sfpb; + int data; + + sfpb = mmss_sfpb_base + 0x058; + + data = MIPI_INP(sfpb); + data |= 0x01800; + MIPI_OUTP(sfpb, data); + wmb(); +} + +static void mipi_dsi_pclk_ctrl(struct dsi_clk_desc *clk, int clk_en) +{ + char *cc, *ns, *md; + char mnd_en = 1, root_en = 1; + uint32 data, val; + + cc = mmss_cc_base + 0x0130; + md = mmss_cc_base + 0x0134; + ns = mmss_cc_base + 0x0138; + + if (clk_en) { + if (clk->mnd_mode == 0) { + data = clk->pre_div_func << 12; + data |= clk->src; + MIPI_OUTP_SECURE(ns, data); + MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6) + | (root_en << 2) | clk_en)); + } else { + val = clk->d * 2; + data = (~val) & 0x0ff; + data |= clk->m << 8; + MIPI_OUTP_SECURE(md, data); + + val = clk->n - clk->m; + data = (~val) & 0x0ff; + data <<= 24; + data |= clk->src; + MIPI_OUTP_SECURE(ns, data); + + MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6) + | (mnd_en << 5) + | (root_en << 2) | clk_en)); + } + + } else + MIPI_OUTP_SECURE(cc, 0); + + wmb(); +} + +static void mipi_dsi_ahb_en(void) +{ + char *ahb; + + ahb = mmss_cc_base + 0x08; + + pr_debug("%s: ahb=%x %x\n", + __func__, (int) ahb, MIPI_INP_SECURE(ahb)); +} + +static void mipi_dsi_calibration(void) +{ + uint32 data; + + MIPI_OUTP(MIPI_DSI_BASE + 0xf4, 0x0000ff11); /* cal_ctrl */ + MIPI_OUTP(MIPI_DSI_BASE + 0xf0, 0x01); /* cal_hw_trigger */ + + while (1) { + data = MIPI_INP(MIPI_DSI_BASE + 0xfc); /* cal_status */ + if ((data & 0x10000000) == 0) + break; + + udelay(10); + } +} + +#define PREF_DIV_RATIO 27 +struct dsiphy_pll_divider_config pll_divider_config; + + +int mipi_dsi_phy_pll_config(u32 clk_rate) +{ + struct dsiphy_pll_divider_config *dividers; + u32 fb_divider, tmp; + dividers = &pll_divider_config; + + /* DSIPHY_PLL_CTRL_x: 1 2 3 8 9 10 */ + /* masks 0xff 0x07 0x3f 0x0f 0xff 0xff */ + + /* DSIPHY_PLL_CTRL_1 */ + fb_divider = ((dividers->fb_divider) / 2) - 1; + MIPI_OUTP(MIPI_DSI_BASE + 0x204, fb_divider & 0xff); + + /* DSIPHY_PLL_CTRL_2 */ + tmp = MIPI_INP(MIPI_DSI_BASE + 0x208); + tmp &= ~0x07; + tmp |= (fb_divider >> 8) & 0x07; + MIPI_OUTP(MIPI_DSI_BASE + 0x208, tmp); + + /* DSIPHY_PLL_CTRL_3 */ + tmp = MIPI_INP(MIPI_DSI_BASE + 0x20c); + tmp &= ~0x3f; + tmp |= (dividers->ref_divider_ratio - 1) & 0x3f; + MIPI_OUTP(MIPI_DSI_BASE + 0x20c, tmp); + + /* DSIPHY_PLL_CTRL_8 */ + tmp = MIPI_INP(MIPI_DSI_BASE + 0x220); + tmp &= ~0x0f; + tmp |= (dividers->bit_clk_divider - 1) & 0x0f; + MIPI_OUTP(MIPI_DSI_BASE + 0x220, tmp); + + /* DSIPHY_PLL_CTRL_9 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x224, (dividers->byte_clk_divider - 1)); + + /* DSIPHY_PLL_CTRL_10 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x228, (dividers->dsi_clk_divider - 1)); + + return 0; +} + +int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes, + uint32 *expected_dsi_pclk) +{ + u32 fb_divider, rate, vco; + u32 div_ratio = 0; + struct dsi_clk_mnd_table const *mnd_entry = mnd_table; + if (pll_divider_config.clk_rate == 0) + pll_divider_config.clk_rate = 454000000; + + rate = pll_divider_config.clk_rate / 1000000; /* In Mhz */ + + if (rate < 125) { + vco = rate * 8; + div_ratio = 8; + } else if (rate < 250) { + vco = rate * 4; + div_ratio = 4; + } else if (rate < 500) { + vco = rate * 2; + div_ratio = 2; + } else { + vco = rate * 1; + div_ratio = 1; + } + + /* find the mnd settings from mnd_table entry */ + for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) { + if (((mnd_entry->lanes) == lanes) && + ((mnd_entry->bpp) == bpp)) + break; + } + + if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) { + pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n", + __func__, lanes, bpp); + return -EINVAL; + } + fb_divider = ((vco * PREF_DIV_RATIO) / 27); + pll_divider_config.fb_divider = fb_divider; + pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO; + pll_divider_config.bit_clk_divider = div_ratio; + pll_divider_config.byte_clk_divider = + pll_divider_config.bit_clk_divider * 8; + pll_divider_config.dsi_clk_divider = + (mnd_entry->dsiclk_div) * div_ratio; + + if ((mnd_entry->dsiclk_d == 0) + || (mnd_entry->dsiclk_m == 1)) { + dsicore_clk.mnd_mode = 0; + dsicore_clk.src = 0x3; + dsicore_clk.pre_div_func = (mnd_entry->dsiclk_n - 1); + } else { + dsicore_clk.mnd_mode = 2; + dsicore_clk.src = 0x3; + dsicore_clk.m = mnd_entry->dsiclk_m; + dsicore_clk.n = mnd_entry->dsiclk_n; + dsicore_clk.d = mnd_entry->dsiclk_d; + } + + if ((mnd_entry->pclk_d == 0) + || (mnd_entry->pclk_m == 1)) { + dsi_pclk.mnd_mode = 0; + dsi_pclk.src = 0x3; + dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1); + *expected_dsi_pclk = ((vco * 1000000) / + ((pll_divider_config.dsi_clk_divider) + * (mnd_entry->pclk_n))); + } else { + dsi_pclk.mnd_mode = 2; + dsi_pclk.src = 0x3; + dsi_pclk.m = mnd_entry->pclk_m; + dsi_pclk.n = mnd_entry->pclk_n; + dsi_pclk.d = mnd_entry->pclk_d; + *expected_dsi_pclk = ((vco * 1000000 * dsi_pclk.m) / + ((pll_divider_config.dsi_clk_divider) + * (mnd_entry->pclk_n))); + } + return 0; +} + +void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info, + int target_type) +{ + struct mipi_dsi_phy_ctrl *pd; + int i, off; + + MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */ + wmb(); + usleep(1); + MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */ + wmb(); + usleep(1); + MIPI_OUTP(MIPI_DSI_BASE + 0x2cc, 0x0003);/* regulator_ctrl_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x2d0, 0x0001);/* regulator_ctrl_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x2d4, 0x0001);/* regulator_ctrl_2 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x2d8, 0x0000);/* regulator_ctrl_3 */ +#ifdef DSI_POWER + MIPI_OUTP(MIPI_DSI_BASE + 0x2dc, 0x0100);/* regulator_ctrl_4 */ +#endif + + pd = (panel_info->mipi).dsi_phy_db; + + off = 0x02cc; /* regulator ctrl 0 */ + for (i = 0; i < 4; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->regulator[i]); + wmb(); + off += 4; + } + + off = 0x0260; /* phy timig ctrl 0 */ + for (i = 0; i < 11; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->timing[i]); + wmb(); + off += 4; + } + + off = 0x0290; /* ctrl 0 */ + for (i = 0; i < 4; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->ctrl[i]); + wmb(); + off += 4; + } + + off = 0x02a0; /* strength 0 */ + for (i = 0; i < 4; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->strength[i]); + wmb(); + off += 4; + } + + mipi_dsi_calibration(); + + off = 0x0204; /* pll ctrl 1, skip 0 */ + for (i = 1; i < 21; i++) { + MIPI_OUTP(MIPI_DSI_BASE + off, pd->pll[i]); + wmb(); + off += 4; + } + + if (panel_info) + mipi_dsi_phy_pll_config(panel_info->clk_rate); + + /* pll ctrl 0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x200, pd->pll[0]); + wmb(); +} + +void cont_splash_clk_ctrl(int enable) +{ +} + +void mipi_dsi_prepare_clocks(void) +{ + clk_prepare(amp_pclk); + clk_prepare(dsi_m_pclk); + clk_prepare(dsi_s_pclk); + clk_prepare(dsi_byte_div_clk); + clk_prepare(dsi_esc_clk); +} + +void mipi_dsi_unprepare_clocks(void) +{ + clk_unprepare(dsi_esc_clk); + clk_unprepare(dsi_byte_div_clk); + clk_unprepare(dsi_m_pclk); + clk_unprepare(dsi_s_pclk); + clk_unprepare(amp_pclk); +} + +void mipi_dsi_ahb_ctrl(u32 enable) +{ + static int ahb_ctrl_done; + if (enable) { + if (ahb_ctrl_done) { + pr_info("%s: ahb clks already ON\n", __func__); + return; + } + clk_enable(amp_pclk); /* clock for AHB-master to AXI */ + clk_enable(dsi_m_pclk); + clk_enable(dsi_s_pclk); + mipi_dsi_ahb_en(); + mipi_dsi_sfpb_cfg(); + ahb_ctrl_done = 1; + } else { + if (ahb_ctrl_done == 0) { + pr_info("%s: ahb clks already OFF\n", __func__); + return; + } + clk_disable(dsi_m_pclk); + clk_disable(dsi_s_pclk); + clk_disable(amp_pclk); /* clock for AHB-master to AXI */ + ahb_ctrl_done = 0; + } +} + +void mipi_dsi_clk_enable(void) +{ + u32 pll_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0200); + if (mipi_dsi_clk_on) { + pr_info("%s: mipi_dsi_clks already ON\n", __func__); + return; + } + MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pll_ctrl | 0x01); + mb(); + + if (clk_set_rate(dsi_byte_div_clk, 1) < 0) /* divided by 1 */ + pr_err("%s: clk_set_rate failed\n", __func__); + mipi_dsi_pclk_ctrl(&dsi_pclk, 1); + mipi_dsi_clk_ctrl(&dsicore_clk, 1); + clk_enable(dsi_byte_div_clk); + clk_enable(dsi_esc_clk); + mipi_dsi_clk_on = 1; +} + +void mipi_dsi_clk_disable(void) +{ + if (mipi_dsi_clk_on == 0) { + pr_info("%s: mipi_dsi_clks already OFF\n", __func__); + return; + } + clk_disable(dsi_esc_clk); + clk_disable(dsi_byte_div_clk); + + mipi_dsi_pclk_ctrl(&dsi_pclk, 0); + mipi_dsi_clk_ctrl(&dsicore_clk, 0); + /* DSIPHY_PLL_CTRL_0, disable dsi pll */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0200, 0x40); + mipi_dsi_clk_on = 0; +} + +void mipi_dsi_phy_ctrl(int on) +{ + if (on) { + /* DSIPHY_PLL_CTRL_5 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x050); + + /* DSIPHY_TPA_CTRL_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0258, 0x00f); + + /* DSIPHY_TPA_CTRL_2 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x025c, 0x000); + } else { + /* DSIPHY_PLL_CTRL_5 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x05f); + + /* DSIPHY_TPA_CTRL_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0258, 0x08f); + + /* DSIPHY_TPA_CTRL_2 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x025c, 0x001); + + /* DSIPHY_REGULATOR_CTRL_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x02cc, 0x02); + + /* DSIPHY_CTRL_0 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0290, 0x00); + + /* DSIPHY_CTRL_1 */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0294, 0x7f); + + /* disable dsi clk */ + MIPI_OUTP(MIPI_DSI_BASE + 0x0118, 0); + } +} + +#ifdef CONFIG_FB_MSM_HDMI_COMMON +#define SW_RESET BIT(2) +void hdmi_phy_reset(void) +{ + unsigned int phy_reset_polarity = 0x0; + unsigned int val = HDMI_INP_ND(0x2D4); + + phy_reset_polarity = val >> 3 & 0x1; + + if (phy_reset_polarity == 0) + HDMI_OUTP(0x2D4, val | SW_RESET); + else + HDMI_OUTP(0x2D4, val & (~SW_RESET)); + + msleep(100); + + if (phy_reset_polarity == 0) + HDMI_OUTP(0x2D4, val & (~SW_RESET)); + else + HDMI_OUTP(0x2D4, val | SW_RESET); +} + +void hdmi_msm_reset_core(void) +{ + hdmi_msm_set_mode(FALSE); + hdmi_msm_clk(0); + udelay(5); + hdmi_msm_clk(1); + + clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_ASSERT); + clk_reset(hdmi_msm_state->hdmi_m_pclk, CLK_RESET_ASSERT); + clk_reset(hdmi_msm_state->hdmi_s_pclk, CLK_RESET_ASSERT); + udelay(20); + clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_DEASSERT); + clk_reset(hdmi_msm_state->hdmi_m_pclk, CLK_RESET_DEASSERT); + clk_reset(hdmi_msm_state->hdmi_s_pclk, CLK_RESET_DEASSERT); +} + +void hdmi_msm_init_phy(int video_format) +{ + uint32 offset; + /* De-serializer delay D/C for non-lbk mode + * PHY REG0 = (DESER_SEL(0) | DESER_DEL_CTRL(3) + * | AMUX_OUT_SEL(0)) + */ + HDMI_OUTP_ND(0x0300, 0x0C); /*0b00001100*/ + + if (video_format == HDMI_VFRMT_720x480p60_16_9) { + /* PHY REG1 = DTEST_MUX_SEL(5) | PLL_GAIN_SEL(0) + * | OUTVOL_SWING_CTRL(3) + */ + HDMI_OUTP_ND(0x0304, 0x53); /*0b01010011*/ + } else { + /* If the freq. is less than 120MHz, use low gain 0 + * for board with termination + * PHY REG1 = DTEST_MUX_SEL(5) | PLL_GAIN_SEL(0) + * | OUTVOL_SWING_CTRL(4) + */ + HDMI_OUTP_ND(0x0304, 0x54); /*0b01010100*/ + } + + /* No matter what, start from the power down mode + * PHY REG2 = PD_PWRGEN | PD_PLL | PD_DRIVE_4 | PD_DRIVE_3 + * | PD_DRIVE_2 | PD_DRIVE_1 | PD_DESER + */ + HDMI_OUTP_ND(0x0308, 0x7F); /*0b01111111*/ + + /* Turn PowerGen on + * PHY REG2 = PD_PLL | PD_DRIVE_4 | PD_DRIVE_3 + * | PD_DRIVE_2 | PD_DRIVE_1 | PD_DESER + */ + HDMI_OUTP_ND(0x0308, 0x3F); /*0b00111111*/ + + /* Turn PLL power on + * PHY REG2 = PD_DRIVE_4 | PD_DRIVE_3 + * | PD_DRIVE_2 | PD_DRIVE_1 | PD_DESER + */ + HDMI_OUTP_ND(0x0308, 0x1F); /*0b00011111*/ + + /* Write to HIGH after PLL power down de-assert + * PHY REG3 = PLL_ENABLE + */ + HDMI_OUTP_ND(0x030C, 0x01); + /* ASIC power on; PHY REG9 = 0 */ + HDMI_OUTP_ND(0x0324, 0x00); + /* Enable PLL lock detect, PLL lock det will go high after lock + * Enable the re-time logic + * PHY REG12 = PLL_LOCK_DETECT_EN | RETIMING_ENABLE + */ + HDMI_OUTP_ND(0x0330, 0x03); /*0b00000011*/ + + /* Drivers are on + * PHY REG2 = PD_DESER + */ + HDMI_OUTP_ND(0x0308, 0x01); /*0b00000001*/ + /* If the RX detector is needed + * PHY REG2 = RCV_SENSE_EN | PD_DESER + */ + HDMI_OUTP_ND(0x0308, 0x81); /*0b10000001*/ + + offset = 0x0310; + while (offset <= 0x032C) { + HDMI_OUTP(offset, 0x0); + offset += 0x4; + } + + /* If we want to use lock enable based on counting + * PHY REG12 = FORCE_LOCK | PLL_LOCK_DETECT_EN | RETIMING_ENABLE + */ + HDMI_OUTP_ND(0x0330, 0x13); /*0b00010011*/ +} + +void hdmi_msm_powerdown_phy(void) +{ + /* Assert RESET PHY from controller */ + HDMI_OUTP_ND(0x02D4, 0x4); + udelay(10); + /* De-assert RESET PHY from controller */ + HDMI_OUTP_ND(0x02D4, 0x0); + /* Turn off Driver */ + HDMI_OUTP_ND(0x0308, 0x1F); + udelay(10); + /* Disable PLL */ + HDMI_OUTP_ND(0x030C, 0x00); + /* Power down PHY */ + HDMI_OUTP_ND(0x0308, 0x7F); /*0b01111111*/ +} + +void hdmi_frame_ctrl_cfg(const struct hdmi_disp_mode_timing_type *timing) +{ + /* 0x02C8 HDMI_FRAME_CTRL + * 31 INTERLACED_EN Interlaced or progressive enable bit + * 0: Frame in progressive + * 1: Frame is interlaced + * 29 HSYNC_HDMI_POL HSYNC polarity fed to HDMI core + * 0: Active Hi Hsync, detect the rising edge of hsync + * 1: Active lo Hsync, Detect the falling edge of Hsync + * 28 VSYNC_HDMI_POL VSYNC polarity fed to HDMI core + * 0: Active Hi Vsync, detect the rising edge of vsync + * 1: Active Lo Vsync, Detect the falling edge of Vsync + * 12 RGB_MUX_SEL ALPHA mdp4 input is RGB, mdp4 input is BGR + */ + HDMI_OUTP(0x02C8, + ((timing->interlaced << 31) & 0x80000000) + | ((timing->active_low_h << 29) & 0x20000000) + | ((timing->active_low_v << 28) & 0x10000000) + | (1 << 12)); +} + +void hdmi_msm_phy_status_poll(void) +{ + unsigned int phy_ready; + phy_ready = 0x1 & HDMI_INP_ND(0x33c); + if (phy_ready) { + pr_debug("HDMI Phy Status bit is set and ready\n"); + } else { + pr_debug("HDMI Phy Status bit is not set," + "waiting for ready status\n"); + do { + phy_ready = 0x1 & HDMI_INP_ND(0x33c); + } while (!phy_ready); + } +} +#endif diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c new file mode 100644 index 0000000000000000000000000000000000000000..c2ebb7d4f8ce438afef4a08ebcf5ee5316101956 --- /dev/null +++ b/drivers/video/msm/msm_fb.c @@ -0,0 +1,3771 @@ +/* drivers/video/msm/msm_fb.c + * + * Core MSM framebuffer driver. + * + * Copyright (C) 2007 Google Incorporated + * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSM_FB_C +#include "msm_fb.h" +#include "mddihosti.h" +#include "tvenc.h" +#include "mdp.h" +#include "mdp4.h" + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_NUM 3 +#endif + +static unsigned char *fbram; +static unsigned char *fbram_phys; +static int fbram_size; +static boolean bf_supported; + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; + +int vsync_mode = 1; + +#define MAX_BLIT_REQ 256 + +#define MAX_FBI_LIST 32 +static struct fb_info *fbi_list[MAX_FBI_LIST]; +static int fbi_list_index; + +static struct msm_fb_data_type *mfd_list[MAX_FBI_LIST]; +static int mfd_list_index; + +static u32 msm_fb_pseudo_palette[16] = { + 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + +static struct ion_client *iclient; + +u32 msm_fb_debug_enabled; +/* Setting msm_fb_msg_level to 8 prints out ALL messages */ +u32 msm_fb_msg_level = 7; + +/* Setting mddi_msg_level to 8 prints out ALL messages */ +u32 mddi_msg_level = 5; + +extern int32 mdp_block_power_cnt[MDP_MAX_BLOCK]; +extern unsigned long mdp_timer_duration; + +static int msm_fb_register(struct msm_fb_data_type *mfd); +static int msm_fb_open(struct fb_info *info, int user); +static int msm_fb_release(struct fb_info *info, int user); +static int msm_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); +static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd); +int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd); +static int msm_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); +static int msm_fb_set_par(struct fb_info *info); +static int msm_fb_blank_sub(int blank_mode, struct fb_info *info, + boolean op_enable); +static int msm_fb_suspend_sub(struct msm_fb_data_type *mfd); +static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg); +static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma); + +#ifdef MSM_FB_ENABLE_DBGFS + +#define MSM_FB_MAX_DBGFS 1024 +#define MAX_BACKLIGHT_BRIGHTNESS 255 + +int msm_fb_debugfs_file_index; +struct dentry *msm_fb_debugfs_root; +struct dentry *msm_fb_debugfs_file[MSM_FB_MAX_DBGFS]; + +DEFINE_MUTEX(msm_fb_notify_update_sem); +void msmfb_no_update_notify_timer_cb(unsigned long data) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; + if (!mfd) + pr_err("%s mfd NULL\n", __func__); + complete(&mfd->msmfb_no_update_notify); +} + +struct dentry *msm_fb_get_debugfs_root(void) +{ + if (msm_fb_debugfs_root == NULL) + msm_fb_debugfs_root = debugfs_create_dir("msm_fb", NULL); + + return msm_fb_debugfs_root; +} + +void msm_fb_debugfs_file_create(struct dentry *root, const char *name, + u32 *var) +{ + if (msm_fb_debugfs_file_index >= MSM_FB_MAX_DBGFS) + return; + + msm_fb_debugfs_file[msm_fb_debugfs_file_index++] = + debugfs_create_u32(name, S_IRUGO | S_IWUSR, root, var); +} +#endif + +int msm_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (!mfd->cursor_update) + return -ENODEV; + + return mfd->cursor_update(info, cursor); +} + +static int msm_fb_resource_initialized; + +#ifndef CONFIG_FB_BACKLIGHT +static int lcd_backlight_registered; + +static void msm_fb_set_bl_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent); + int bl_lvl; + + if (value > MAX_BACKLIGHT_BRIGHTNESS) + value = MAX_BACKLIGHT_BRIGHTNESS; + + /* This maps android backlight level 0 to 255 into + driver backlight level 0 to bl_max with rounding */ + bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS) + /(2 * MAX_BACKLIGHT_BRIGHTNESS); + + if (!bl_lvl && value) + bl_lvl = 1; + + msm_fb_set_backlight(mfd, bl_lvl); +} + +static struct led_classdev backlight_led = { + .name = "lcd-backlight", + .brightness = MAX_BACKLIGHT_BRIGHTNESS, + .brightness_set = msm_fb_set_bl_brightness, +}; +#endif + +static struct msm_fb_platform_data *msm_fb_pdata; +unsigned char hdmi_prim_display; + +int msm_fb_detect_client(const char *name) +{ + int ret = 0; + u32 len; +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + u32 id; +#endif + if (!msm_fb_pdata) + return -EPERM; + + len = strnlen(name, PANEL_NAME_MAX_LEN); + if (strnlen(msm_fb_pdata->prim_panel_name, PANEL_NAME_MAX_LEN)) { + pr_err("\n name = %s, prim_display = %s", + name, msm_fb_pdata->prim_panel_name); + if (!strncmp((char *)msm_fb_pdata->prim_panel_name, + name, len)) { + if (!strncmp((char *)msm_fb_pdata->prim_panel_name, + "hdmi_msm", len)) + hdmi_prim_display = 1; + return 0; + } else { + ret = -EPERM; + } + } + + if (strnlen(msm_fb_pdata->ext_panel_name, PANEL_NAME_MAX_LEN)) { + pr_err("\n name = %s, ext_display = %s", + name, msm_fb_pdata->ext_panel_name); + if (!strncmp((char *)msm_fb_pdata->ext_panel_name, name, len)) + return 0; + else + ret = -EPERM; + } + + if (ret) + return ret; + + ret = -EPERM; + if (msm_fb_pdata && msm_fb_pdata->detect_client) { + ret = msm_fb_pdata->detect_client(name); + + /* if it's non mddi panel, we need to pre-scan + mddi client to see if we can disable mddi host */ + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + if (!ret && msm_fb_pdata->mddi_prescan) + id = mddi_get_client_id(); +#endif + } + + return ret; +} + +static ssize_t msm_fb_msm_fb_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct msm_fb_panel_data *pdata = + (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + switch (pdata->panel_info.type) { + case NO_PANEL: + ret = snprintf(buf, PAGE_SIZE, "no panel\n"); + break; + case MDDI_PANEL: + ret = snprintf(buf, PAGE_SIZE, "mddi panel\n"); + break; + case EBI2_PANEL: + ret = snprintf(buf, PAGE_SIZE, "ebi2 panel\n"); + break; + case LCDC_PANEL: + ret = snprintf(buf, PAGE_SIZE, "lcdc panel\n"); + break; + case EXT_MDDI_PANEL: + ret = snprintf(buf, PAGE_SIZE, "ext mddi panel\n"); + break; + case TV_PANEL: + ret = snprintf(buf, PAGE_SIZE, "tv panel\n"); + break; + case HDMI_PANEL: + ret = snprintf(buf, PAGE_SIZE, "hdmi panel\n"); + break; + case LVDS_PANEL: + ret = snprintf(buf, PAGE_SIZE, "lvds panel\n"); + break; + case DTV_PANEL: + ret = snprintf(buf, PAGE_SIZE, "dtv panel\n"); + break; + case MIPI_VIDEO_PANEL: + ret = snprintf(buf, PAGE_SIZE, "mipi dsi video panel\n"); + break; + case MIPI_CMD_PANEL: + ret = snprintf(buf, PAGE_SIZE, "mipi dsi cmd panel\n"); + break; + case WRITEBACK_PANEL: + ret = snprintf(buf, PAGE_SIZE, "writeback panel\n"); + break; + default: + ret = snprintf(buf, PAGE_SIZE, "unknown panel\n"); + break; + } + + return ret; +} + +static DEVICE_ATTR(msm_fb_type, S_IRUGO, msm_fb_msm_fb_type, NULL); +static struct attribute *msm_fb_attrs[] = { + &dev_attr_msm_fb_type.attr, + NULL, +}; +static struct attribute_group msm_fb_attr_group = { + .attrs = msm_fb_attrs, +}; + +static int msm_fb_create_sysfs(struct platform_device *pdev) +{ + int rc; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + rc = sysfs_create_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group); + if (rc) + MSM_FB_ERR("%s: sysfs group creation failed, rc=%d\n", __func__, + rc); + return rc; +} +static void msm_fb_remove_sysfs(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + sysfs_remove_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group); +} + +static int msm_fb_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + int rc; + int err = 0; + + MSM_FB_DEBUG("msm_fb_probe\n"); + + if ((pdev->id == 0) && (pdev->num_resources > 0)) { + msm_fb_pdata = pdev->dev.platform_data; + fbram_size = + pdev->resource[0].end - pdev->resource[0].start + 1; + fbram_phys = (char *)pdev->resource[0].start; + fbram = __va(fbram_phys); + + if (!fbram) { + printk(KERN_ERR "fbram ioremap failed!\n"); + return -ENOMEM; + } + MSM_FB_DEBUG("msm_fb_probe: phy_Addr = 0x%x virt = 0x%x\n", + (int)fbram_phys, (int)fbram); + + iclient = msm_ion_client_create(-1, pdev->name); + if (IS_ERR_OR_NULL(iclient)) { + pr_err("msm_ion_client_create() return" + " error, val %p\n", iclient); + iclient = NULL; + } + + msm_fb_resource_initialized = 1; + return 0; + } + + if (!msm_fb_resource_initialized) + return -EPERM; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + mfd->panel_info.frame_count = 0; + mfd->bl_level = 0; +#ifdef CONFIG_FB_MSM_OVERLAY + mfd->overlay_play_enable = 1; +#endif + + bf_supported = mdp4_overlay_borderfill_supported(); + + rc = msm_fb_register(mfd); + if (rc) + return rc; + err = pm_runtime_set_active(mfd->fbi->dev); + if (err < 0) + printk(KERN_ERR "pm_runtime: fail to set active.\n"); + pm_runtime_enable(mfd->fbi->dev); +#ifdef CONFIG_FB_BACKLIGHT + msm_fb_config_backlight(mfd); +#else + /* android supports only one lcd-backlight/lcd for now */ + if (!lcd_backlight_registered) { + if (led_classdev_register(&pdev->dev, &backlight_led)) + printk(KERN_ERR "led_classdev_register failed\n"); + else + lcd_backlight_registered = 1; + } +#endif + + pdev_list[pdev_list_cnt++] = pdev; + msm_fb_create_sysfs(pdev); + return 0; +} + +static int msm_fb_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + MSM_FB_DEBUG("msm_fb_remove\n"); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + msm_fb_remove_sysfs(pdev); + + pm_runtime_disable(mfd->fbi->dev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (msm_fb_suspend_sub(mfd)) + printk(KERN_ERR "msm_fb_remove: can't stop the device %d\n", mfd->index); + + if (mfd->channel_irq != 0) + free_irq(mfd->channel_irq, (void *)mfd); + + if (mfd->vsync_width_boundary) + vfree(mfd->vsync_width_boundary); + + if (mfd->vsync_resync_timer.function) + del_timer(&mfd->vsync_resync_timer); + + if (mfd->refresh_timer.function) + del_timer(&mfd->refresh_timer); + + if (mfd->dma_hrtimer.function) + hrtimer_cancel(&mfd->dma_hrtimer); + + if (mfd->msmfb_no_update_notify_timer.function) + del_timer(&mfd->msmfb_no_update_notify_timer); + complete(&mfd->msmfb_no_update_notify); + complete(&mfd->msmfb_update_notify); + + /* remove /dev/fb* */ + unregister_framebuffer(mfd->fbi); + +#ifdef CONFIG_FB_BACKLIGHT + /* remove /sys/class/backlight */ + backlight_device_unregister(mfd->fbi->bl_dev); +#else + if (lcd_backlight_registered) { + lcd_backlight_registered = 0; + led_classdev_unregister(&backlight_led); + } +#endif + +#ifdef MSM_FB_ENABLE_DBGFS + if (mfd->sub_dir) + debugfs_remove(mfd->sub_dir); +#endif + + return 0; +} + +#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) +static int msm_fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct msm_fb_data_type *mfd; + int ret = 0; + + MSM_FB_DEBUG("msm_fb_suspend\n"); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + console_lock(); + fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED); + + ret = msm_fb_suspend_sub(mfd); + if (ret != 0) { + printk(KERN_ERR "msm_fb: failed to suspend! %d\n", ret); + fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING); + } else { + pdev->dev.power.power_state = state; + } + + console_unlock(); + return ret; +} +#else +#define msm_fb_suspend NULL +#endif + +static int msm_fb_suspend_sub(struct msm_fb_data_type *mfd) +{ + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + if (mfd->msmfb_no_update_notify_timer.function) + del_timer(&mfd->msmfb_no_update_notify_timer); + complete(&mfd->msmfb_no_update_notify); + + /* + * suspend this channel + */ + mfd->suspend.sw_refreshing_enable = mfd->sw_refreshing_enable; + mfd->suspend.op_enable = mfd->op_enable; + mfd->suspend.panel_power_on = mfd->panel_power_on; + + if (mfd->op_enable) { + ret = + msm_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, + mfd->suspend.op_enable); + if (ret) { + MSM_FB_INFO + ("msm_fb_suspend: can't turn off display!\n"); + return ret; + } + mfd->op_enable = FALSE; + } + /* + * try to power down + */ + mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + /* + * detach display channel irq if there's any + * or wait until vsync-resync completes + */ + if ((mfd->dest == DISPLAY_LCD)) { + if (mfd->panel_info.lcd.vsync_enable) { + if (mfd->panel_info.lcd.hw_vsync_mode) { + if (mfd->channel_irq != 0) + disable_irq(mfd->channel_irq); + } else { + volatile boolean vh_pending; + do { + vh_pending = mfd->vsync_handler_pending; + } while (vh_pending); + } + } + } + + return 0; +} + +#ifdef CONFIG_PM +static int msm_fb_resume_sub(struct msm_fb_data_type *mfd) +{ + int ret = 0; + struct msm_fb_panel_data *pdata = NULL; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + /* attach display channel irq if there's any */ + if (mfd->channel_irq != 0) + enable_irq(mfd->channel_irq); + + /* resume state var recover */ + mfd->sw_refreshing_enable = mfd->suspend.sw_refreshing_enable; + mfd->op_enable = mfd->suspend.op_enable; + + if (mfd->suspend.panel_power_on) { + ret = + msm_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi, + mfd->op_enable); + if (ret) + MSM_FB_INFO("msm_fb_resume: can't turn on display!\n"); + } else { + if (pdata->power_ctrl) + pdata->power_ctrl(TRUE); + } + + return ret; +} +#endif + +#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) +static int msm_fb_resume(struct platform_device *pdev) +{ + /* This resume function is called when interrupt is enabled. + */ + int ret = 0; + struct msm_fb_data_type *mfd; + + MSM_FB_DEBUG("msm_fb_resume\n"); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + console_lock(); + ret = msm_fb_resume_sub(mfd); + pdev->dev.power.power_state = PMSG_ON; + fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING); + console_unlock(); + + return ret; +} +#else +#define msm_fb_resume NULL +#endif + +static int msm_fb_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int msm_fb_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static int msm_fb_runtime_idle(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: idling...\n"); + return 0; +} + +#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL)) +static int msm_fb_ext_suspend(struct device *dev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + if (mfd->panel_info.type == HDMI_PANEL || + mfd->panel_info.type == DTV_PANEL) + ret = msm_fb_suspend_sub(mfd); + + return ret; +} + +static int msm_fb_ext_resume(struct device *dev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + if (mfd->panel_info.type == HDMI_PANEL || + mfd->panel_info.type == DTV_PANEL) + ret = msm_fb_resume_sub(mfd); + + return ret; +} +#endif + +static struct dev_pm_ops msm_fb_dev_pm_ops = { + .runtime_suspend = msm_fb_runtime_suspend, + .runtime_resume = msm_fb_runtime_resume, + .runtime_idle = msm_fb_runtime_idle, +#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL)) + .suspend = msm_fb_ext_suspend, + .resume = msm_fb_ext_resume, +#endif +}; + +static struct platform_driver msm_fb_driver = { + .probe = msm_fb_probe, + .remove = msm_fb_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = msm_fb_suspend, + .resume = msm_fb_resume, +#endif + .shutdown = NULL, + .driver = { + /* Driver name must match the device name added in platform.c. */ + .name = "msm_fb", + .pm = &msm_fb_dev_pm_ops, + }, +}; + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_FB_MSM_MDP303) +static void memset32_io(u32 __iomem *_ptr, u32 val, size_t count) +{ + count >>= 2; + while (count--) + writel(val, _ptr++); +} +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void msmfb_early_suspend(struct early_suspend *h) +{ + struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, + early_suspend); +#if defined(CONFIG_FB_MSM_MDP303) + /* + * For MDP with overlay, set framebuffer with black pixels + * to show black screen on HDMI. + */ + struct fb_info *fbi = mfd->fbi; + switch (mfd->fbi->var.bits_per_pixel) { + case 32: + memset32_io((void *)fbi->screen_base, 0xFF000000, + fbi->fix.smem_len); + break; + default: + memset32_io((void *)fbi->screen_base, 0x00, fbi->fix.smem_len); + break; + } +#endif + msm_fb_suspend_sub(mfd); +} + +static void msmfb_early_resume(struct early_suspend *h) +{ + struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, + early_suspend); + msm_fb_resume_sub(mfd); +} +#endif + +static int unset_bl_level, bl_updated; +static int bl_level_old; + +void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl) +{ + struct msm_fb_panel_data *pdata; + + if (!mfd->panel_power_on || !bl_updated) { + unset_bl_level = bkl_lvl; + return; + } else { + unset_bl_level = 0; + } + + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + if ((pdata) && (pdata->set_backlight)) { + down(&mfd->sem); + if (bl_level_old == bkl_lvl) { + up(&mfd->sem); + return; + } + mfd->bl_level = bkl_lvl; + pdata->set_backlight(mfd); + bl_level_old = mfd->bl_level; + up(&mfd->sem); + } +} + +static int msm_fb_blank_sub(int blank_mode, struct fb_info *info, + boolean op_enable) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msm_fb_panel_data *pdata = NULL; + int ret = 0; + + if (!op_enable) + return -EPERM; + + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + if ((!pdata) || (!pdata->on) || (!pdata->off)) { + printk(KERN_ERR "msm_fb_blank_sub: no panel operation detected!\n"); + return -ENODEV; + } + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + if (!mfd->panel_power_on) { + msleep(16); + ret = pdata->on(mfd->pdev); + if (ret == 0) { + mfd->panel_power_on = TRUE; + +/* ToDo: possible conflict with android which doesn't expect sw refresher */ +/* + if (!mfd->hw_refresh) + { + if ((ret = msm_fb_resume_sw_refresher(mfd)) != 0) + { + MSM_FB_INFO("msm_fb_blank_sub: msm_fb_resume_sw_refresher failed = %d!\n",ret); + } + } +*/ + } + } + break; + + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + case FB_BLANK_POWERDOWN: + default: + if (mfd->panel_power_on) { + int curr_pwr_state; + + mfd->op_enable = FALSE; + curr_pwr_state = mfd->panel_power_on; + mfd->panel_power_on = FALSE; + bl_updated = 0; + + msleep(16); + ret = pdata->off(mfd->pdev); + if (ret) + mfd->panel_power_on = curr_pwr_state; + + mfd->op_enable = TRUE; + } else { + if (pdata->power_ctrl) + pdata->power_ctrl(FALSE); + } + break; + } + + return ret; +} + +int calc_fb_offset(struct msm_fb_data_type *mfd, struct fb_info *fbi, int bpp) +{ + struct msm_panel_info *panel_info = &mfd->panel_info; + int remainder, yres, offset; + + if (panel_info->mode2_yres != 0) { + yres = panel_info->mode2_yres; + remainder = (fbi->fix.line_length*yres) & (PAGE_SIZE - 1); + } else { + yres = panel_info->yres; + remainder = (fbi->fix.line_length*yres) & (PAGE_SIZE - 1); + } + + if (!remainder) + remainder = PAGE_SIZE; + + if (fbi->var.yoffset < yres) { + offset = (fbi->var.xoffset * bpp); + /* iBuf->buf += fbi->var.xoffset * bpp + 0 * + yres * fbi->fix.line_length; */ + } else if (fbi->var.yoffset >= yres && fbi->var.yoffset < 2 * yres) { + offset = (fbi->var.xoffset * bpp + yres * + fbi->fix.line_length + PAGE_SIZE - remainder); + } else { + offset = (fbi->var.xoffset * bpp + 2 * yres * + fbi->fix.line_length + 2 * (PAGE_SIZE - remainder)); + } + return offset; +} + +static void msm_fb_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + cfb_fillrect(info, rect); + if (!mfd->hw_refresh && (info->var.yoffset == 0) && + !mfd->sw_currently_refreshing) { + struct fb_var_screeninfo var; + + var = info->var; + var.reserved[0] = 0x54445055; + var.reserved[1] = (rect->dy << 16) | (rect->dx); + var.reserved[2] = ((rect->dy + rect->height) << 16) | + (rect->dx + rect->width); + + msm_fb_pan_display(&var, info); + } +} + +static void msm_fb_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + cfb_copyarea(info, area); + if (!mfd->hw_refresh && (info->var.yoffset == 0) && + !mfd->sw_currently_refreshing) { + struct fb_var_screeninfo var; + + var = info->var; + var.reserved[0] = 0x54445055; + var.reserved[1] = (area->dy << 16) | (area->dx); + var.reserved[2] = ((area->dy + area->height) << 16) | + (area->dx + area->width); + + msm_fb_pan_display(&var, info); + } +} + +static void msm_fb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + cfb_imageblit(info, image); + if (!mfd->hw_refresh && (info->var.yoffset == 0) && + !mfd->sw_currently_refreshing) { + struct fb_var_screeninfo var; + + var = info->var; + var.reserved[0] = 0x54445055; + var.reserved[1] = (image->dy << 16) | (image->dx); + var.reserved[2] = ((image->dy + image->height) << 16) | + (image->dx + image->width); + + msm_fb_pan_display(&var, info); + } +} + +static int msm_fb_blank(int blank_mode, struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + return msm_fb_blank_sub(blank_mode, info, mfd->op_enable); +} + +static int msm_fb_set_lut(struct fb_cmap *cmap, struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (!mfd->lut_update) + return -ENODEV; + + mfd->lut_update(info, cmap); + return 0; +} + +/* + * Custom Framebuffer mmap() function for MSM driver. + * Differs from standard mmap() function by allowing for customized + * page-protection. + */ +static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma) +{ + /* Get frame buffer memory range. */ + unsigned long start = info->fix.smem_start; + u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + if (off >= len) { + /* memory mapped io */ + off -= len; + if (info->var.accel_flags) { + mutex_unlock(&info->lock); + return -EINVAL; + } + start = info->fix.mmio_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); + } + + /* Set VM flags. */ + start &= PAGE_MASK; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + /* This is an IO map - tell maydump to skip this VMA */ + vma->vm_flags |= VM_IO | VM_RESERVED; + + /* Set VM page protection */ + if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE) + vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE) + vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE) + vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* Remap the frame buffer I/O range */ + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static struct fb_ops msm_fb_ops = { + .owner = THIS_MODULE, + .fb_open = msm_fb_open, + .fb_release = msm_fb_release, + .fb_read = NULL, + .fb_write = NULL, + .fb_cursor = NULL, + .fb_check_var = msm_fb_check_var, /* vinfo check */ + .fb_set_par = msm_fb_set_par, /* set the video mode according to info->var */ + .fb_setcolreg = NULL, /* set color register */ + .fb_blank = msm_fb_blank, /* blank display */ + .fb_pan_display = msm_fb_pan_display, /* pan display */ + .fb_fillrect = msm_fb_fillrect, /* Draws a rectangle */ + .fb_copyarea = msm_fb_copyarea, /* Copy data from area to another */ + .fb_imageblit = msm_fb_imageblit, /* Draws a image to the display */ + .fb_rotate = NULL, + .fb_sync = NULL, /* wait for blit idle, optional */ + .fb_ioctl = msm_fb_ioctl, /* perform fb specific ioctl (optional) */ + .fb_mmap = msm_fb_mmap, +}; + +static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp) +{ + /* The adreno GPU hardware requires that the pitch be aligned to + 32 pixels for color buffers, so for the cases where the GPU + is writing directly to fb0, the framebuffer pitch + also needs to be 32 pixel aligned */ + + if (fb_index == 0) + return ALIGN(xres, 32) * bpp; + else + return xres * bpp; +} + +static int msm_fb_register(struct msm_fb_data_type *mfd) +{ + int ret = -ENODEV; + int bpp; + struct msm_panel_info *panel_info = &mfd->panel_info; + struct fb_info *fbi = mfd->fbi; + struct fb_fix_screeninfo *fix; + struct fb_var_screeninfo *var; + int *id; + int fbram_offset; + int remainder, remainder_mode2; + + /* + * fb info initialization + */ + fix = &fbi->fix; + var = &fbi->var; + + fix->type_aux = 0; /* if type == FB_TYPE_INTERLEAVED_PLANES */ + fix->visual = FB_VISUAL_TRUECOLOR; /* True Color */ + fix->ywrapstep = 0; /* No support */ + fix->mmio_start = 0; /* No MMIO Address */ + fix->mmio_len = 0; /* No MMIO Address */ + fix->accel = FB_ACCEL_NONE;/* FB_ACCEL_MSM needes to be added in fb.h */ + + var->xoffset = 0, /* Offset from virtual to visible */ + var->yoffset = 0, /* resolution */ + var->grayscale = 0, /* No graylevels */ + var->nonstd = 0, /* standard pixel format */ + var->activate = FB_ACTIVATE_VBL, /* activate it at vsync */ + var->height = -1, /* height of picture in mm */ + var->width = -1, /* width of picture in mm */ + var->accel_flags = 0, /* acceleration flags */ + var->sync = 0, /* see FB_SYNC_* */ + var->rotate = 0, /* angle we rotate counter clockwise */ + mfd->op_enable = FALSE; + + switch (mfd->fb_imgType) { + case MDP_RGB_565: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 2; + break; + + case MDP_RGB_888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 3; + break; + + case MDP_ARGB_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 24; + var->transp.length = 8; + bpp = 4; + break; + + case MDP_RGBA_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 8; + var->green.offset = 16; + var->red.offset = 24; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 8; + bpp = 4; + break; + + case MDP_YCRYCB_H2V1: + /* ToDo: need to check TV-Out YUV422i framebuffer format */ + /* we might need to create new type define */ + fix->type = FB_TYPE_INTERLEAVED_PLANES; + fix->xpanstep = 2; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + /* how about R/G/B offset? */ + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 2; + break; + + default: + MSM_FB_ERR("msm_fb_init: fb %d unkown image type!\n", + mfd->index); + return ret; + } + + fix->type = panel_info->is_3d_panel; + + fix->line_length = msm_fb_line_length(mfd->index, panel_info->xres, + bpp); + + /* Make sure all buffers can be addressed on a page boundary by an x + * and y offset */ + + remainder = (fix->line_length * panel_info->yres) & (PAGE_SIZE - 1); + /* PAGE_SIZE is a power of 2 */ + if (!remainder) + remainder = PAGE_SIZE; + remainder_mode2 = (fix->line_length * + panel_info->mode2_yres) & (PAGE_SIZE - 1); + if (!remainder_mode2) + remainder_mode2 = PAGE_SIZE; + + /* + * calculate smem_len based on max size of two supplied modes. + * Only fb0 has mem. fb1 and fb2 don't have mem. + */ + if (!bf_supported || mfd->index == 0) + fix->smem_len = MAX((msm_fb_line_length(mfd->index, + panel_info->xres, + bpp) * + panel_info->yres + PAGE_SIZE - + remainder) * mfd->fb_page, + (msm_fb_line_length(mfd->index, + panel_info->mode2_xres, + bpp) * + panel_info->mode2_yres + PAGE_SIZE - + remainder_mode2) * mfd->fb_page); + else if (mfd->index == 1 || mfd->index == 2) { + pr_debug("%s:%d no memory is allocated for fb%d!\n", + __func__, __LINE__, mfd->index); + fix->smem_len = 0; + } + + mfd->var_xres = panel_info->xres; + mfd->var_yres = panel_info->yres; + mfd->var_frame_rate = panel_info->frame_rate; + + var->pixclock = mfd->panel_info.clk_rate; + mfd->var_pixclock = var->pixclock; + + var->xres = panel_info->xres; + var->yres = panel_info->yres; + var->xres_virtual = panel_info->xres; + var->yres_virtual = panel_info->yres * mfd->fb_page + + ((PAGE_SIZE - remainder)/fix->line_length) * mfd->fb_page; + var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */ + if (mfd->dest == DISPLAY_LCD) { + if (panel_info->type == MDDI_PANEL && panel_info->mddi.is_type1) + var->reserved[3] = panel_info->lcd.refx100 / (100 * 2); + else + var->reserved[3] = panel_info->lcd.refx100 / 100; + } else { + if (panel_info->type == MIPI_VIDEO_PANEL) { + var->reserved[3] = panel_info->mipi.frame_rate; + } else { + var->reserved[3] = panel_info->clk_rate / + ((panel_info->lcdc.h_back_porch + + panel_info->lcdc.h_front_porch + + panel_info->lcdc.h_pulse_width + + panel_info->xres) * + (panel_info->lcdc.v_back_porch + + panel_info->lcdc.v_front_porch + + panel_info->lcdc.v_pulse_width + + panel_info->yres)); + } + } + pr_debug("reserved[3] %u\n", var->reserved[3]); + + /* + * id field for fb app + */ + id = (int *)&mfd->panel; + + switch (mdp_rev) { + case MDP_REV_20: + snprintf(fix->id, sizeof(fix->id), "msmfb20_%x", (__u32) *id); + break; + case MDP_REV_22: + snprintf(fix->id, sizeof(fix->id), "msmfb22_%x", (__u32) *id); + break; + case MDP_REV_30: + snprintf(fix->id, sizeof(fix->id), "msmfb30_%x", (__u32) *id); + break; + case MDP_REV_303: + snprintf(fix->id, sizeof(fix->id), "msmfb303_%x", (__u32) *id); + break; + case MDP_REV_31: + snprintf(fix->id, sizeof(fix->id), "msmfb31_%x", (__u32) *id); + break; + case MDP_REV_40: + snprintf(fix->id, sizeof(fix->id), "msmfb40_%x", (__u32) *id); + break; + case MDP_REV_41: + snprintf(fix->id, sizeof(fix->id), "msmfb41_%x", (__u32) *id); + break; + case MDP_REV_42: + snprintf(fix->id, sizeof(fix->id), "msmfb42_%x", (__u32) *id); + break; + case MDP_REV_43: + snprintf(fix->id, sizeof(fix->id), "msmfb43_%x", (__u32) *id); + break; + case MDP_REV_44: + snprintf(fix->id, sizeof(fix->id), "msmfb44_%x", (__u32) *id); + break; + default: + snprintf(fix->id, sizeof(fix->id), "msmfb0_%x", (__u32) *id); + break; + } + + fbi->fbops = &msm_fb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = msm_fb_pseudo_palette; + + mfd->ref_cnt = 0; + mfd->sw_currently_refreshing = FALSE; + mfd->sw_refreshing_enable = TRUE; + mfd->panel_power_on = FALSE; + + mfd->pan_waiting = FALSE; + init_completion(&mfd->pan_comp); + init_completion(&mfd->refresher_comp); + sema_init(&mfd->sem, 1); + + init_timer(&mfd->msmfb_no_update_notify_timer); + mfd->msmfb_no_update_notify_timer.function = + msmfb_no_update_notify_timer_cb; + mfd->msmfb_no_update_notify_timer.data = (unsigned long)mfd; + init_completion(&mfd->msmfb_update_notify); + init_completion(&mfd->msmfb_no_update_notify); + + fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram; + fbram += fbram_offset; + fbram_phys += fbram_offset; + fbram_size -= fbram_offset; + + if (!bf_supported || mfd->index == 0) + if (fbram_size < fix->smem_len) { + pr_err("error: no more framebuffer memory!\n"); + return -ENOMEM; + } + + fbi->screen_base = fbram; + fbi->fix.smem_start = (unsigned long)fbram_phys; + + msm_iommu_map_contig_buffer(fbi->fix.smem_start, + DISPLAY_DOMAIN, + GEN_POOL, + fbi->fix.smem_len, + SZ_4K, + 1, + &(mfd->display_iova)); + + msm_iommu_map_contig_buffer(fbi->fix.smem_start, + ROTATOR_DOMAIN, + GEN_POOL, + fbi->fix.smem_len, + SZ_4K, + 1, + &(mfd->rotator_iova)); + + if (!bf_supported || mfd->index == 0) + memset(fbi->screen_base, 0x0, fix->smem_len); + + mfd->op_enable = TRUE; + mfd->panel_power_on = FALSE; + + /* cursor memory allocation */ + if (mfd->cursor_update) { + mfd->cursor_buf = dma_alloc_coherent(NULL, + MDP_CURSOR_SIZE, + (dma_addr_t *) &mfd->cursor_buf_phys, + GFP_KERNEL); + if (!mfd->cursor_buf) + mfd->cursor_update = 0; + } + + if (mfd->lut_update) { + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret) + printk(KERN_ERR "%s: fb_alloc_cmap() failed!\n", + __func__); + } + + if (register_framebuffer(fbi) < 0) { + if (mfd->lut_update) + fb_dealloc_cmap(&fbi->cmap); + + if (mfd->cursor_buf) + dma_free_coherent(NULL, + MDP_CURSOR_SIZE, + mfd->cursor_buf, + (dma_addr_t) mfd->cursor_buf_phys); + + mfd->op_enable = FALSE; + return -EPERM; + } + + fbram += fix->smem_len; + fbram_phys += fix->smem_len; + fbram_size -= fix->smem_len; + + MSM_FB_INFO + ("FrameBuffer[%d] %dx%d size=%d bytes is registered successfully!\n", + mfd->index, fbi->var.xres, fbi->var.yres, fbi->fix.smem_len); + +#ifdef CONFIG_FB_MSM_LOGO + /* Flip buffer */ + if (!load_565rle_image(INIT_IMAGE_FILE, bf_supported)) + ; +#endif + ret = 0; + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (mfd->panel_info.type != DTV_PANEL) { + mfd->early_suspend.suspend = msmfb_early_suspend; + mfd->early_suspend.resume = msmfb_early_resume; + mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2; + register_early_suspend(&mfd->early_suspend); + } +#endif + +#ifdef MSM_FB_ENABLE_DBGFS + { + struct dentry *root; + struct dentry *sub_dir; + char sub_name[2]; + + root = msm_fb_get_debugfs_root(); + if (root != NULL) { + sub_name[0] = (char)(mfd->index + 0x30); + sub_name[1] = '\0'; + sub_dir = debugfs_create_dir(sub_name, root); + } else { + sub_dir = NULL; + } + + mfd->sub_dir = sub_dir; + + if (sub_dir) { + msm_fb_debugfs_file_create(sub_dir, "op_enable", + (u32 *) &mfd->op_enable); + msm_fb_debugfs_file_create(sub_dir, "panel_power_on", + (u32 *) &mfd-> + panel_power_on); + msm_fb_debugfs_file_create(sub_dir, "ref_cnt", + (u32 *) &mfd->ref_cnt); + msm_fb_debugfs_file_create(sub_dir, "fb_imgType", + (u32 *) &mfd->fb_imgType); + msm_fb_debugfs_file_create(sub_dir, + "sw_currently_refreshing", + (u32 *) &mfd-> + sw_currently_refreshing); + msm_fb_debugfs_file_create(sub_dir, + "sw_refreshing_enable", + (u32 *) &mfd-> + sw_refreshing_enable); + + msm_fb_debugfs_file_create(sub_dir, "xres", + (u32 *) &mfd->panel_info. + xres); + msm_fb_debugfs_file_create(sub_dir, "yres", + (u32 *) &mfd->panel_info. + yres); + msm_fb_debugfs_file_create(sub_dir, "bpp", + (u32 *) &mfd->panel_info. + bpp); + msm_fb_debugfs_file_create(sub_dir, "type", + (u32 *) &mfd->panel_info. + type); + msm_fb_debugfs_file_create(sub_dir, "wait_cycle", + (u32 *) &mfd->panel_info. + wait_cycle); + msm_fb_debugfs_file_create(sub_dir, "pdest", + (u32 *) &mfd->panel_info. + pdest); + msm_fb_debugfs_file_create(sub_dir, "backbuff", + (u32 *) &mfd->panel_info. + fb_num); + msm_fb_debugfs_file_create(sub_dir, "clk_rate", + (u32 *) &mfd->panel_info. + clk_rate); + msm_fb_debugfs_file_create(sub_dir, "frame_count", + (u32 *) &mfd->panel_info. + frame_count); + + + switch (mfd->dest) { + case DISPLAY_LCD: + msm_fb_debugfs_file_create(sub_dir, + "vsync_enable", + (u32 *)&mfd->panel_info.lcd.vsync_enable); + msm_fb_debugfs_file_create(sub_dir, + "refx100", + (u32 *) &mfd->panel_info.lcd. refx100); + msm_fb_debugfs_file_create(sub_dir, + "v_back_porch", + (u32 *) &mfd->panel_info.lcd.v_back_porch); + msm_fb_debugfs_file_create(sub_dir, + "v_front_porch", + (u32 *) &mfd->panel_info.lcd.v_front_porch); + msm_fb_debugfs_file_create(sub_dir, + "v_pulse_width", + (u32 *) &mfd->panel_info.lcd.v_pulse_width); + msm_fb_debugfs_file_create(sub_dir, + "hw_vsync_mode", + (u32 *) &mfd->panel_info.lcd.hw_vsync_mode); + msm_fb_debugfs_file_create(sub_dir, + "vsync_notifier_period", (u32 *) + &mfd->panel_info.lcd.vsync_notifier_period); + break; + + case DISPLAY_LCDC: + msm_fb_debugfs_file_create(sub_dir, + "h_back_porch", + (u32 *) &mfd->panel_info.lcdc.h_back_porch); + msm_fb_debugfs_file_create(sub_dir, + "h_front_porch", + (u32 *) &mfd->panel_info.lcdc.h_front_porch); + msm_fb_debugfs_file_create(sub_dir, + "h_pulse_width", + (u32 *) &mfd->panel_info.lcdc.h_pulse_width); + msm_fb_debugfs_file_create(sub_dir, + "v_back_porch", + (u32 *) &mfd->panel_info.lcdc.v_back_porch); + msm_fb_debugfs_file_create(sub_dir, + "v_front_porch", + (u32 *) &mfd->panel_info.lcdc.v_front_porch); + msm_fb_debugfs_file_create(sub_dir, + "v_pulse_width", + (u32 *) &mfd->panel_info.lcdc.v_pulse_width); + msm_fb_debugfs_file_create(sub_dir, + "border_clr", + (u32 *) &mfd->panel_info.lcdc.border_clr); + msm_fb_debugfs_file_create(sub_dir, + "underflow_clr", + (u32 *) &mfd->panel_info.lcdc.underflow_clr); + msm_fb_debugfs_file_create(sub_dir, + "hsync_skew", + (u32 *) &mfd->panel_info.lcdc.hsync_skew); + break; + + default: + break; + } + } + } +#endif /* MSM_FB_ENABLE_DBGFS */ + + return ret; +} + +static int msm_fb_open(struct fb_info *info, int user) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int result; + + result = pm_runtime_get_sync(info->dev); + + if (result < 0) { + printk(KERN_ERR "pm_runtime: fail to wake up\n"); + } + + if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */ + mfd->ref_cnt++; + return 0; + } + + if (!mfd->ref_cnt) { + if (!bf_supported || + (info->node != 1 && info->node != 2)) + mdp_set_dma_pan_info(info, NULL, TRUE); + else + pr_debug("%s:%d no mdp_set_dma_pan_info %d\n", + __func__, __LINE__, info->node); + + if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) { + printk(KERN_ERR "msm_fb_open: can't turn on display!\n"); + return -1; + } + } + + mfd->ref_cnt++; + return 0; +} + +static int msm_fb_release(struct fb_info *info, int user) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret = 0; + + if (!mfd->ref_cnt) { + MSM_FB_INFO("msm_fb_release: try to close unopened fb %d!\n", + mfd->index); + return -EINVAL; + } + + mfd->ref_cnt--; + + if (!mfd->ref_cnt) { + if ((ret = + msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, + mfd->op_enable)) != 0) { + printk(KERN_ERR "msm_fb_release: can't turn off display!\n"); + return ret; + } + } + + pm_runtime_put(info->dev); + return ret; +} + +DEFINE_SEMAPHORE(msm_fb_pan_sem); + +static int msm_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mdp_dirty_region dirty; + struct mdp_dirty_region *dirtyPtr = NULL; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msm_fb_panel_data *pdata; + + /* + * If framebuffer is 1 or 2, io pen display is not allowed. + */ + if (bf_supported && + (info->node == 1 || info->node == 2)) { + pr_err("%s: no pan display for fb%d!", + __func__, info->node); + return -EPERM; + } + + if (info->node != 0 || mfd->cont_splash_done) /* primary */ + if ((!mfd->op_enable) || (!mfd->panel_power_on)) + return -EPERM; + + if (var->xoffset > (info->var.xres_virtual - info->var.xres)) + return -EINVAL; + + if (var->yoffset > (info->var.yres_virtual - info->var.yres)) + return -EINVAL; + + if (info->fix.xpanstep) + info->var.xoffset = + (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep; + + if (info->fix.ypanstep) + info->var.yoffset = + (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep; + + /* "UPDT" */ + if (var->reserved[0] == 0x54445055) { + + dirty.xoffset = var->reserved[1] & 0xffff; + dirty.yoffset = (var->reserved[1] >> 16) & 0xffff; + + if ((var->reserved[2] & 0xffff) <= dirty.xoffset) + return -EINVAL; + if (((var->reserved[2] >> 16) & 0xffff) <= dirty.yoffset) + return -EINVAL; + + dirty.width = (var->reserved[2] & 0xffff) - dirty.xoffset; + dirty.height = + ((var->reserved[2] >> 16) & 0xffff) - dirty.yoffset; + info->var.yoffset = var->yoffset; + + if (dirty.xoffset < 0) + return -EINVAL; + + if (dirty.yoffset < 0) + return -EINVAL; + + if ((dirty.xoffset + dirty.width) > info->var.xres) + return -EINVAL; + + if ((dirty.yoffset + dirty.height) > info->var.yres) + return -EINVAL; + + if ((dirty.width <= 0) || (dirty.height <= 0)) + return -EINVAL; + + dirtyPtr = &dirty; + } + complete(&mfd->msmfb_update_notify); + mutex_lock(&msm_fb_notify_update_sem); + if (mfd->msmfb_no_update_notify_timer.function) + del_timer(&mfd->msmfb_no_update_notify_timer); + + mfd->msmfb_no_update_notify_timer.expires = + jiffies + ((1000 * HZ) / 1000); + add_timer(&mfd->msmfb_no_update_notify_timer); + mutex_unlock(&msm_fb_notify_update_sem); + + down(&msm_fb_pan_sem); + + if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */ + mdp_set_dma_pan_info(info, NULL, TRUE); + if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) { + pr_err("%s: can't turn on display!\n", __func__); + return -EINVAL; + } + } + + mdp_set_dma_pan_info(info, dirtyPtr, + (var->activate == FB_ACTIVATE_VBL)); + mdp_dma_pan_update(info); + up(&msm_fb_pan_sem); + + if (unset_bl_level && !bl_updated) { + pdata = (struct msm_fb_panel_data *)mfd->pdev-> + dev.platform_data; + if ((pdata) && (pdata->set_backlight)) { + down(&mfd->sem); + mfd->bl_level = unset_bl_level; + pdata->set_backlight(mfd); + bl_level_old = unset_bl_level; + up(&mfd->sem); + bl_updated = 1; + } + } + + ++mfd->panel_info.frame_count; + return 0; +} + +static int msm_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (var->rotate != FB_ROTATE_UR) + return -EINVAL; + if (var->grayscale != info->var.grayscale) + return -EINVAL; + + switch (var->bits_per_pixel) { + case 16: + if ((var->green.offset != 5) || + !((var->blue.offset == 11) + || (var->blue.offset == 0)) || + !((var->red.offset == 11) + || (var->red.offset == 0)) || + (var->blue.length != 5) || + (var->green.length != 6) || + (var->red.length != 5) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0) || + (var->transp.offset != 0) || + (var->transp.length != 0)) + return -EINVAL; + break; + + case 24: + if ((var->blue.offset != 0) || + (var->green.offset != 8) || + (var->red.offset != 16) || + (var->blue.length != 8) || + (var->green.length != 8) || + (var->red.length != 8) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0) || + !(((var->transp.offset == 0) && + (var->transp.length == 0)) || + ((var->transp.offset == 24) && + (var->transp.length == 8)))) + return -EINVAL; + break; + + case 32: + /* Figure out if the user meant RGBA or ARGB + and verify the position of the RGB components */ + + if (var->transp.offset == 24) { + if ((var->blue.offset != 0) || + (var->green.offset != 8) || + (var->red.offset != 16)) + return -EINVAL; + } else if (var->transp.offset == 0) { + if ((var->blue.offset != 8) || + (var->green.offset != 16) || + (var->red.offset != 24)) + return -EINVAL; + } else + return -EINVAL; + + /* Check the common values for both RGBA and ARGB */ + + if ((var->blue.length != 8) || + (var->green.length != 8) || + (var->red.length != 8) || + (var->transp.length != 8) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0)) + return -EINVAL; + + break; + + default: + return -EINVAL; + } + + if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0)) + return -EINVAL; + + if (!bf_supported || + (info->node != 1 && info->node != 2)) + if (info->fix.smem_len < + (var->xres_virtual* + var->yres_virtual* + (var->bits_per_pixel/8))) + return -EINVAL; + + if ((var->xres == 0) || (var->yres == 0)) + return -EINVAL; + + if ((var->xres > MAX(mfd->panel_info.xres, + mfd->panel_info.mode2_xres)) || + (var->yres > MAX(mfd->panel_info.yres, + mfd->panel_info.mode2_yres))) + return -EINVAL; + + if (var->xoffset > (var->xres_virtual - var->xres)) + return -EINVAL; + + if (var->yoffset > (var->yres_virtual - var->yres)) + return -EINVAL; + + return 0; +} + +int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd + , struct fb_info *info) +{ + int panel_height, panel_width, var_frame_rate, fps_mod; + struct fb_var_screeninfo *var = &info->var; + fps_mod = 0; + if ((mfd->panel_info.type == DTV_PANEL) || + (mfd->panel_info.type == HDMI_PANEL)) { + panel_height = var->yres + var->upper_margin + + var->vsync_len + var->lower_margin; + panel_width = var->xres + var->right_margin + + var->hsync_len + var->left_margin; + var_frame_rate = ((var->pixclock)/(panel_height * panel_width)); + if (mfd->var_frame_rate != var_frame_rate) { + fps_mod = 1; + mfd->var_frame_rate = var_frame_rate; + } + } + return fps_mod; +} + +static int msm_fb_set_par(struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_var_screeninfo *var = &info->var; + int old_imgType; + int blank = 0; + + old_imgType = mfd->fb_imgType; + switch (var->bits_per_pixel) { + case 16: + if (var->red.offset == 0) + mfd->fb_imgType = MDP_BGR_565; + else + mfd->fb_imgType = MDP_RGB_565; + break; + + case 24: + if ((var->transp.offset == 0) && (var->transp.length == 0)) + mfd->fb_imgType = MDP_RGB_888; + else if ((var->transp.offset == 24) && + (var->transp.length == 8)) { + mfd->fb_imgType = MDP_ARGB_8888; + info->var.bits_per_pixel = 32; + } + break; + + case 32: + if (var->transp.offset == 24) + mfd->fb_imgType = MDP_ARGB_8888; + else + mfd->fb_imgType = MDP_RGBA_8888; + break; + + default: + return -EINVAL; + } + + if ((mfd->var_pixclock != var->pixclock) || + (mfd->hw_refresh && ((mfd->fb_imgType != old_imgType) || + (mfd->var_pixclock != var->pixclock) || + (mfd->var_xres != var->xres) || + (mfd->var_yres != var->yres) || + (msm_fb_check_frame_rate(mfd, info))))) { + mfd->var_xres = var->xres; + mfd->var_yres = var->yres; + mfd->var_pixclock = var->pixclock; + blank = 1; + } + mfd->fbi->fix.line_length = msm_fb_line_length(mfd->index, var->xres, + var->bits_per_pixel/8); + + if (blank) { + msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); + msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable); + } + + return 0; +} + +static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd) +{ + if (mfd->hw_refresh) + return -EPERM; + + if (mfd->sw_currently_refreshing) { + down(&mfd->sem); + mfd->sw_currently_refreshing = FALSE; + up(&mfd->sem); + + /* wait until the refresher finishes the last job */ + wait_for_completion_killable(&mfd->refresher_comp); + } + + return 0; +} + +int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd) +{ + boolean do_refresh; + + if (mfd->hw_refresh) + return -EPERM; + + down(&mfd->sem); + if ((!mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) { + do_refresh = TRUE; + mfd->sw_currently_refreshing = TRUE; + } else { + do_refresh = FALSE; + } + up(&mfd->sem); + + if (do_refresh) + mdp_refresh_screen((unsigned long)mfd); + + return 0; +} + +#if defined CONFIG_FB_MSM_MDP31 +static int mdp_blit_split_height(struct fb_info *info, + struct mdp_blit_req *req) +{ + int ret; + struct mdp_blit_req splitreq; + int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; + int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; + + splitreq = *req; + /* break dest roi at height*/ + d_x_0 = d_x_1 = req->dst_rect.x; + d_w_0 = d_w_1 = req->dst_rect.w; + d_y_0 = req->dst_rect.y; + if (req->dst_rect.h % 32 == 3) + d_h_1 = (req->dst_rect.h - 3) / 2 - 1; + else if (req->dst_rect.h % 32 == 2) + d_h_1 = (req->dst_rect.h - 2) / 2 - 6; + else + d_h_1 = (req->dst_rect.h - 1) / 2 - 1; + d_h_0 = req->dst_rect.h - d_h_1; + d_y_1 = d_y_0 + d_h_0; + if (req->dst_rect.h == 3) { + d_h_1 = 2; + d_h_0 = 2; + d_y_1 = d_y_0 + 1; + } + + /* blit first region */ + if (((splitreq.flags & 0x07) == 0x04) || + ((splitreq.flags & 0x07) == 0x0)) { + + if (splitreq.flags & MDP_ROT_90) { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_h_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } else { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_h_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } + + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + + if (splitreq.flags & MDP_ROT_90) { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_h_0) / req->dst_rect.h; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_h_0 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } else { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_h_0) / req->dst_rect.h; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_h_0 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } + ret = mdp_ppp_blit(info, &splitreq); + if (ret) + return ret; + + /* blit second region */ + if (((splitreq.flags & 0x07) == 0x04) || + ((splitreq.flags & 0x07) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } + ret = mdp_ppp_blit(info, &splitreq); + return ret; +} +#endif + +int mdp_blit(struct fb_info *info, struct mdp_blit_req *req) +{ + int ret; +#if defined CONFIG_FB_MSM_MDP31 || defined CONFIG_FB_MSM_MDP30 + unsigned int remainder = 0, is_bpp_4 = 0; + struct mdp_blit_req splitreq; + int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; + int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; + + if (req->flags & MDP_ROT_90) { + if (((req->dst_rect.h == 1) && ((req->src_rect.w != 1) || + (req->dst_rect.w != req->src_rect.h))) || + ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.h != req->src_rect.w)))) { + printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } else { + if (((req->dst_rect.w == 1) && ((req->src_rect.w != 1) || + (req->dst_rect.h != req->src_rect.h))) || + ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.w != req->src_rect.w)))) { + printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } +#endif + if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) { + printk(KERN_ERR "mpd_ppp: src img of zero size!\n"); + return -EINVAL; + } + if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0)) + return 0; + +#if defined CONFIG_FB_MSM_MDP31 + /* MDP width split workaround */ + remainder = (req->dst_rect.w)%32; + ret = mdp_get_bytes_per_pixel(req->dst.format, + (struct msm_fb_data_type *)info->par); + if (ret <= 0) { + printk(KERN_ERR "mdp_ppp: incorrect bpp!\n"); + return -EINVAL; + } + is_bpp_4 = (ret == 4) ? 1 : 0; + + if ((is_bpp_4 && (remainder == 6 || remainder == 14 || + remainder == 22 || remainder == 30)) || remainder == 3 || + (remainder == 1 && req->dst_rect.w != 1) || + (remainder == 2 && req->dst_rect.w != 2)) { + /* make new request as provide by user */ + splitreq = *req; + + /* break dest roi at width*/ + d_y_0 = d_y_1 = req->dst_rect.y; + d_h_0 = d_h_1 = req->dst_rect.h; + d_x_0 = req->dst_rect.x; + + if (remainder == 14) + d_w_1 = (req->dst_rect.w - 14) / 2 + 4; + else if (remainder == 22) + d_w_1 = (req->dst_rect.w - 22) / 2 + 10; + else if (remainder == 30) + d_w_1 = (req->dst_rect.w - 30) / 2 + 10; + else if (remainder == 6) + d_w_1 = req->dst_rect.w / 2 - 1; + else if (remainder == 3) + d_w_1 = (req->dst_rect.w - 3) / 2 - 1; + else if (remainder == 2) + d_w_1 = (req->dst_rect.w - 2) / 2 - 6; + else + d_w_1 = (req->dst_rect.w - 1) / 2 - 1; + d_w_0 = req->dst_rect.w - d_w_1; + d_x_1 = d_x_0 + d_w_0; + if (req->dst_rect.w == 3) { + d_w_1 = 2; + d_w_0 = 2; + d_x_1 = d_x_0 + 1; + } + + /* blit first region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x0)) { + + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_1) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_1) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_0) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_0 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_0) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_0 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } + + if ((splitreq.dst_rect.h % 32 == 3) || + ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) || + ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2)) + ret = mdp_blit_split_height(info, &splitreq); + else + ret = mdp_ppp_blit(info, &splitreq); + if (ret) + return ret; + /* blit second region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } + if (((splitreq.dst_rect.h % 32) == 3) || + ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) || + ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2)) + ret = mdp_blit_split_height(info, &splitreq); + else + ret = mdp_ppp_blit(info, &splitreq); + if (ret) + return ret; + } else if ((req->dst_rect.h % 32) == 3 || + ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) || + ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2)) + ret = mdp_blit_split_height(info, req); + else + ret = mdp_ppp_blit(info, req); + return ret; +#elif defined CONFIG_FB_MSM_MDP30 + /* MDP width split workaround */ + remainder = (req->dst_rect.w)%16; + ret = mdp_get_bytes_per_pixel(req->dst.format, + (struct msm_fb_data_type *)info->par); + if (ret <= 0) { + printk(KERN_ERR "mdp_ppp: incorrect bpp!\n"); + return -EINVAL; + } + is_bpp_4 = (ret == 4) ? 1 : 0; + + if ((is_bpp_4 && (remainder == 6 || remainder == 14))) { + + /* make new request as provide by user */ + splitreq = *req; + + /* break dest roi at width*/ + d_y_0 = d_y_1 = req->dst_rect.y; + d_h_0 = d_h_1 = req->dst_rect.h; + d_x_0 = req->dst_rect.x; + + if (remainder == 14 || remainder == 6) + d_w_1 = req->dst_rect.w / 2; + else + d_w_1 = (req->dst_rect.w - 1) / 2 - 1; + + d_w_0 = req->dst_rect.w - d_w_1; + d_x_1 = d_x_0 + d_w_0; + + /* blit first region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x05) || + ((splitreq.flags & 0x07) == 0x02) || + ((splitreq.flags & 0x07) == 0x0)) { + + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_1) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_1) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_0) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_0 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_0) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_0 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } + + /* No need to split in height */ + ret = mdp_ppp_blit(info, &splitreq); + + if (ret) + return ret; + + /* blit second region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x05) || + ((splitreq.flags & 0x07) == 0x02) || + ((splitreq.flags & 0x07) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } + + /* No need to split in height ... just width */ + ret = mdp_ppp_blit(info, &splitreq); + + if (ret) + return ret; + + } else + ret = mdp_ppp_blit(info, req); + return ret; +#else + ret = mdp_ppp_blit(info, req); + return ret; +#endif +} + +typedef void (*msm_dma_barrier_function_pointer) (void *, size_t); + +static inline void msm_fb_dma_barrier_for_rect(struct fb_info *info, + struct mdp_img *img, struct mdp_rect *rect, + msm_dma_barrier_function_pointer dma_barrier_fp + ) +{ + /* + * Compute the start and end addresses of the rectangles. + * NOTE: As currently implemented, the data between + * the end of one row and the start of the next is + * included in the address range rather than + * doing multiple calls for each row. + */ + unsigned long start; + size_t size; + char * const pmem_start = info->screen_base; + int bytes_per_pixel = mdp_get_bytes_per_pixel(img->format, + (struct msm_fb_data_type *)info->par); + if (bytes_per_pixel <= 0) { + printk(KERN_ERR "%s incorrect bpp!\n", __func__); + return; + } + start = (unsigned long)pmem_start + img->offset + + (img->width * rect->y + rect->x) * bytes_per_pixel; + size = (rect->h * img->width + rect->w) * bytes_per_pixel; + (*dma_barrier_fp) ((void *) start, size); + +} + +static inline void msm_dma_nc_pre(void) +{ + dmb(); +} +static inline void msm_dma_wt_pre(void) +{ + dmb(); +} +static inline void msm_dma_todevice_wb_pre(void *start, size_t size) +{ + dma_cache_pre_ops(start, size, DMA_TO_DEVICE); +} + +static inline void msm_dma_fromdevice_wb_pre(void *start, size_t size) +{ + dma_cache_pre_ops(start, size, DMA_FROM_DEVICE); +} + +static inline void msm_dma_nc_post(void) +{ + dmb(); +} + +static inline void msm_dma_fromdevice_wt_post(void *start, size_t size) +{ + dma_cache_post_ops(start, size, DMA_FROM_DEVICE); +} + +static inline void msm_dma_todevice_wb_post(void *start, size_t size) +{ + dma_cache_post_ops(start, size, DMA_TO_DEVICE); +} + +static inline void msm_dma_fromdevice_wb_post(void *start, size_t size) +{ + dma_cache_post_ops(start, size, DMA_FROM_DEVICE); +} + +/* + * Do the write barriers required to guarantee data is committed to RAM + * (from CPU cache or internal buffers) before a DMA operation starts. + * NOTE: As currently implemented, the data between + * the end of one row and the start of the next is + * included in the address range rather than + * doing multiple calls for each row. +*/ +static void msm_fb_ensure_memory_coherency_before_dma(struct fb_info *info, + struct mdp_blit_req *req_list, + int req_list_count) +{ +#ifdef CONFIG_ARCH_QSD8X50 + int i; + + /* + * Normally, do the requested barriers for each address + * range that corresponds to a rectangle. + * + * But if at least one write barrier is requested for data + * going to or from the device but no address range is + * needed for that barrier, then do the barrier, but do it + * only once, no matter how many requests there are. + */ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + switch (mfd->mdp_fb_page_protection) { + default: + case MDP_FB_PAGE_PROTECTION_NONCACHED: + case MDP_FB_PAGE_PROTECTION_WRITECOMBINE: + /* + * The following barrier is only done at most once, + * since further calls would be redundant. + */ + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags + & MDP_NO_DMA_BARRIER_START)) { + msm_dma_nc_pre(); + break; + } + } + break; + + case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE: + /* + * The following barrier is only done at most once, + * since further calls would be redundant. + */ + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags + & MDP_NO_DMA_BARRIER_START)) { + msm_dma_wt_pre(); + break; + } + } + break; + + case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE: + case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE: + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags & + MDP_NO_DMA_BARRIER_START)) { + + msm_fb_dma_barrier_for_rect(info, + &(req_list[i].src), + &(req_list[i].src_rect), + msm_dma_todevice_wb_pre + ); + + msm_fb_dma_barrier_for_rect(info, + &(req_list[i].dst), + &(req_list[i].dst_rect), + msm_dma_todevice_wb_pre + ); + } + } + break; + } +#else + dmb(); +#endif +} + + +/* + * Do the write barriers required to guarantee data will be re-read from RAM by + * the CPU after a DMA operation ends. + * NOTE: As currently implemented, the data between + * the end of one row and the start of the next is + * included in the address range rather than + * doing multiple calls for each row. +*/ +static void msm_fb_ensure_memory_coherency_after_dma(struct fb_info *info, + struct mdp_blit_req *req_list, + int req_list_count) +{ +#ifdef CONFIG_ARCH_QSD8X50 + int i; + + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + switch (mfd->mdp_fb_page_protection) { + default: + case MDP_FB_PAGE_PROTECTION_NONCACHED: + case MDP_FB_PAGE_PROTECTION_WRITECOMBINE: + /* + * The following barrier is only done at most once, + * since further calls would be redundant. + */ + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags + & MDP_NO_DMA_BARRIER_END)) { + msm_dma_nc_post(); + break; + } + } + break; + + case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE: + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags & + MDP_NO_DMA_BARRIER_END)) { + + msm_fb_dma_barrier_for_rect(info, + &(req_list[i].dst), + &(req_list[i].dst_rect), + msm_dma_fromdevice_wt_post + ); + } + } + break; + case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE: + case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE: + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags & + MDP_NO_DMA_BARRIER_END)) { + + msm_fb_dma_barrier_for_rect(info, + &(req_list[i].dst), + &(req_list[i].dst_rect), + msm_dma_fromdevice_wb_post + ); + } + } + break; + } +#else + dmb(); +#endif +} + +/* + * NOTE: The userspace issues blit operations in a sequence, the sequence + * start with a operation marked START and ends in an operation marked + * END. It is guranteed by the userspace that all the blit operations + * between START and END are only within the regions of areas designated + * by the START and END operations and that the userspace doesnt modify + * those areas. Hence it would be enough to perform barrier/cache operations + * only on the START and END operations. + */ +static int msmfb_blit(struct fb_info *info, void __user *p) +{ + /* + * CAUTION: The names of the struct types intentionally *DON'T* match + * the names of the variables declared -- they appear to be swapped. + * Read the code carefully and you should see that the variable names + * make sense. + */ + const int MAX_LIST_WINDOW = 16; + struct mdp_blit_req req_list[MAX_LIST_WINDOW]; + struct mdp_blit_req_list req_list_header; + + int count, i, req_list_count; + if (bf_supported && + (info->node == 1 || info->node == 2)) { + pr_err("%s: no pan display for fb%d.", + __func__, info->node); + return -EPERM; + } + /* Get the count size for the total BLIT request. */ + if (copy_from_user(&req_list_header, p, sizeof(req_list_header))) + return -EFAULT; + p += sizeof(req_list_header); + count = req_list_header.count; + if (count < 0 || count >= MAX_BLIT_REQ) + return -EINVAL; + while (count > 0) { + /* + * Access the requests through a narrow window to decrease copy + * overhead and make larger requests accessible to the + * coherency management code. + * NOTE: The window size is intended to be larger than the + * typical request size, but not require more than 2 + * kbytes of stack storage. + */ + req_list_count = count; + if (req_list_count > MAX_LIST_WINDOW) + req_list_count = MAX_LIST_WINDOW; + if (copy_from_user(&req_list, p, + sizeof(struct mdp_blit_req)*req_list_count)) + return -EFAULT; + + /* + * Ensure that any data CPU may have previously written to + * internal state (but not yet committed to memory) is + * guaranteed to be committed to memory now. + */ + msm_fb_ensure_memory_coherency_before_dma(info, + req_list, req_list_count); + + /* + * Do the blit DMA, if required -- returning early only if + * there is a failure. + */ + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags & MDP_NO_BLIT)) { + /* Do the actual blit. */ + int ret = mdp_blit(info, &(req_list[i])); + + /* + * Note that early returns don't guarantee + * memory coherency. + */ + if (ret) + return ret; + } + } + + /* + * Ensure that CPU cache and other internal CPU state is + * updated to reflect any change in memory modified by MDP blit + * DMA. + */ + msm_fb_ensure_memory_coherency_after_dma(info, + req_list, + req_list_count); + + /* Go to next window of requests. */ + count -= req_list_count; + p += sizeof(struct mdp_blit_req)*req_list_count; + } + return 0; +} + +#ifdef CONFIG_FB_MSM_OVERLAY +static int msmfb_overlay_get(struct fb_info *info, void __user *p) +{ + struct mdp_overlay req; + int ret; + + if (copy_from_user(&req, p, sizeof(req))) + return -EFAULT; + + ret = mdp4_overlay_get(info, &req); + if (ret) { + printk(KERN_ERR "%s: ioctl failed \n", + __func__); + return ret; + } + if (copy_to_user(p, &req, sizeof(req))) { + printk(KERN_ERR "%s: copy2user failed \n", + __func__); + return -EFAULT; + } + + return 0; +} + +static int msmfb_overlay_set(struct fb_info *info, void __user *p) +{ + struct mdp_overlay req; + int ret; + + if (copy_from_user(&req, p, sizeof(req))) + return -EFAULT; + + ret = mdp4_overlay_set(info, &req); + if (ret) { + printk(KERN_ERR "%s: ioctl failed, rc=%d\n", + __func__, ret); + return ret; + } + + if (copy_to_user(p, &req, sizeof(req))) { + printk(KERN_ERR "%s: copy2user failed \n", + __func__); + return -EFAULT; + } + + return 0; +} + +static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp) +{ + int ret, ndx; + + ret = copy_from_user(&ndx, argp, sizeof(ndx)); + if (ret) { + printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n", + __func__); + return ret; + } + + return mdp4_overlay_unset(info, ndx); +} + +static int msmfb_overlay_play_wait(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_data req; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mfd->overlay_play_enable == 0) /* nothing to do */ + return 0; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s:msmfb_overlay_wait ioctl failed", __func__); + return ret; + } + + ret = mdp4_overlay_play_wait(info, &req); + + return ret; +} + +static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_data req; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msm_fb_panel_data *pdata; + + if (mfd->overlay_play_enable == 0) /* nothing to do */ + return 0; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n", + __func__); + return ret; + } + + complete(&mfd->msmfb_update_notify); + mutex_lock(&msm_fb_notify_update_sem); + if (mfd->msmfb_no_update_notify_timer.function) + del_timer(&mfd->msmfb_no_update_notify_timer); + + mfd->msmfb_no_update_notify_timer.expires = + jiffies + ((1000 * HZ) / 1000); + add_timer(&mfd->msmfb_no_update_notify_timer); + mutex_unlock(&msm_fb_notify_update_sem); + + if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */ + mdp_set_dma_pan_info(info, NULL, TRUE); + if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) { + pr_err("%s: can't turn on display!\n", __func__); + return -EINVAL; + } + } + + ret = mdp4_overlay_play(info, &req); + + if (unset_bl_level && !bl_updated) { + pdata = (struct msm_fb_panel_data *)mfd->pdev-> + dev.platform_data; + if ((pdata) && (pdata->set_backlight)) { + down(&mfd->sem); + mfd->bl_level = unset_bl_level; + pdata->set_backlight(mfd); + bl_level_old = unset_bl_level; + up(&mfd->sem); + bl_updated = 1; + } + } + + return ret; +} + +static int msmfb_overlay_play_enable(struct fb_info *info, unsigned long *argp) +{ + int ret, enable; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + ret = copy_from_user(&enable, argp, sizeof(enable)); + if (ret) { + printk(KERN_ERR "%s:msmfb_overlay_play_enable ioctl failed \n", + __func__); + return ret; + } + + mfd->overlay_play_enable = enable; + + return 0; +} + +static int msmfb_overlay_blt(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_blt req; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s: failed\n", __func__); + return ret; + } + + ret = mdp4_overlay_blt(info, &req); + + return ret; +} + +static int msmfb_overlay_blt_off(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_blt req; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s: failed\n", __func__); + return ret; + } + + ret = mdp4_overlay_blt_offset(info, &req); + + ret = copy_to_user(argp, &req, sizeof(req)); + if (ret) + printk(KERN_ERR "%s:msmfb_overlay_blt_off ioctl failed\n", + __func__); + + return ret; +} + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info) +{ + return mdp4_writeback_init(info); +} +static int msmfb_overlay_ioctl_writeback_start( + struct fb_info *info) +{ + int ret = 0; + ret = mdp4_writeback_start(info); + if (ret) + goto error; +error: + if (ret) + pr_err("%s:msmfb_writeback_start " + " ioctl failed\n", __func__); + return ret; +} + +static int msmfb_overlay_ioctl_writeback_stop( + struct fb_info *info) +{ + int ret = 0; + ret = mdp4_writeback_stop(info); + if (ret) + goto error; + +error: + if (ret) + pr_err("%s:msmfb_writeback_stop ioctl failed\n", + __func__); + return ret; +} + +static int msmfb_overlay_ioctl_writeback_queue_buffer( + struct fb_info *info, unsigned long *argp) +{ + int ret = 0; + struct msmfb_data data; + + ret = copy_from_user(&data, argp, sizeof(data)); + if (ret) + goto error; + + ret = mdp4_writeback_queue_buffer(info, &data); + if (ret) + goto error; + +error: + if (ret) + pr_err("%s:msmfb_writeback_queue_buffer ioctl failed\n", + __func__); + return ret; +} + +static int msmfb_overlay_ioctl_writeback_dequeue_buffer( + struct fb_info *info, unsigned long *argp) +{ + int ret = 0; + struct msmfb_data data; + + ret = copy_from_user(&data, argp, sizeof(data)); + if (ret) + goto error; + + ret = mdp4_writeback_dequeue_buffer(info, &data); + if (ret) + goto error; + + ret = copy_to_user(argp, &data, sizeof(data)); + if (ret) + goto error; + +error: + if (ret) + pr_err("%s:msmfb_writeback_dequeue_buffer ioctl failed\n", + __func__); + return ret; +} +static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info) +{ + return mdp4_writeback_terminate(info); +} + +#else +static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info) +{ + return -ENOTSUPP; +} +static int msmfb_overlay_ioctl_writeback_start( + struct fb_info *info) +{ + return -ENOTSUPP; +} + +static int msmfb_overlay_ioctl_writeback_stop( + struct fb_info *info) +{ + return -ENOTSUPP; +} + +static int msmfb_overlay_ioctl_writeback_queue_buffer( + struct fb_info *info, unsigned long *argp) +{ + return -ENOTSUPP; +} + +static int msmfb_overlay_ioctl_writeback_dequeue_buffer( + struct fb_info *info, unsigned long *argp) +{ + return -ENOTSUPP; +} +static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info) +{ + return -ENOTSUPP; +} +#endif + +static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_3d req; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s:msmfb_overlay_3d_ctrl ioctl failed\n", + __func__); + return ret; + } + + ret = mdp4_overlay_3d_sbys(info, &req); + + return ret; +} + +static int msmfb_mixer_info(struct fb_info *info, unsigned long *argp) +{ + int ret, cnt; + struct msmfb_mixer_info_req req; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s: failed\n", __func__); + return ret; + } + + cnt = mdp4_mixer_info(req.mixer_num, req.info); + req.cnt = cnt; + ret = copy_to_user(argp, &req, sizeof(req)); + if (ret) + pr_err("%s:msmfb_overlay_blt_off ioctl failed\n", + __func__); + + return cnt; +} + +#endif + +DEFINE_SEMAPHORE(msm_fb_ioctl_ppp_sem); +DEFINE_MUTEX(msm_fb_ioctl_lut_sem); + +/* Set color conversion matrix from user space */ + +#ifndef CONFIG_FB_MSM_MDP40 +static void msmfb_set_color_conv(struct mdp_ccs *p) +{ + int i; + + if (p->direction == MDP_CCS_RGB2YUV) { + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* RGB->YUV primary forward matrix */ + for (i = 0; i < MDP_CCS_SIZE; i++) + writel(p->ccs[i], MDP_CSC_PFMVn(i)); + + #ifdef CONFIG_FB_MSM_MDP31 + for (i = 0; i < MDP_BV_SIZE; i++) + writel(p->bv[i], MDP_CSC_POST_BV2n(i)); + #endif + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } else { + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* YUV->RGB primary reverse matrix */ + for (i = 0; i < MDP_CCS_SIZE; i++) + writel(p->ccs[i], MDP_CSC_PRMVn(i)); + for (i = 0; i < MDP_BV_SIZE; i++) + writel(p->bv[i], MDP_CSC_PRE_BV1n(i)); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } +} +#else +static void msmfb_set_color_conv(struct mdp_csc *p) +{ + mdp4_vg_csc_update(p); +} +#endif + +static int msmfb_notify_update(struct fb_info *info, unsigned long *argp) +{ + int ret, notify; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + ret = copy_from_user(¬ify, argp, sizeof(int)); + if (ret) { + pr_err("%s:ioctl failed\n", __func__); + return ret; + } + + if (notify > NOTIFY_UPDATE_STOP) + return -EINVAL; + + if (notify == NOTIFY_UPDATE_START) { + INIT_COMPLETION(mfd->msmfb_update_notify); + wait_for_completion_interruptible(&mfd->msmfb_update_notify); + } else { + INIT_COMPLETION(mfd->msmfb_no_update_notify); + wait_for_completion_interruptible(&mfd->msmfb_no_update_notify); + } + return 0; +} + +static int msmfb_handle_pp_ioctl(struct msmfb_mdp_pp *pp_ptr) +{ + int ret = -1; + + if (!pp_ptr) + return ret; + + switch (pp_ptr->op) { +#ifdef CONFIG_FB_MSM_MDP40 + case mdp_op_csc_cfg: + ret = mdp4_csc_config(&(pp_ptr->data.csc_cfg_data)); + break; + + case mdp_op_pcc_cfg: + ret = mdp4_pcc_cfg(&(pp_ptr->data.pcc_cfg_data)); + break; + + case mdp_op_lut_cfg: + switch (pp_ptr->data.lut_cfg_data.lut_type) { + case mdp_lut_igc: + ret = mdp4_igc_lut_config( + (struct mdp_igc_lut_data *) + &pp_ptr->data.lut_cfg_data.data); + break; + + case mdp_lut_pgc: + ret = mdp4_argc_cfg( + &pp_ptr->data.lut_cfg_data.data.pgc_lut_data); + break; + + case mdp_lut_hist: + ret = mdp_hist_lut_config( + (struct mdp_hist_lut_data *) + &pp_ptr->data.lut_cfg_data.data); + break; + + default: + break; + } + break; + case mdp_op_qseed_cfg: + ret = mdp4_qseed_cfg((struct mdp_qseed_cfg_data *) + &pp_ptr->data.qseed_cfg_data); + break; +#endif + default: + pr_warn("Unsupported request to MDP_PP IOCTL.\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + void __user *argp = (void __user *)arg; + struct fb_cursor cursor; + struct fb_cmap cmap; + struct mdp_histogram_data hist; + struct mdp_histogram_start_req hist_req; + uint32_t block; +#ifndef CONFIG_FB_MSM_MDP40 + struct mdp_ccs ccs_matrix; +#else + struct mdp_csc csc_matrix; +#endif + struct mdp_page_protection fb_page_protection; + struct msmfb_mdp_pp mdp_pp; + int ret = 0; + + switch (cmd) { +#ifdef CONFIG_FB_MSM_OVERLAY + case MSMFB_OVERLAY_GET: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_get(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_OVERLAY_SET: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_set(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_OVERLAY_UNSET: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_unset(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_OVERLAY_PLAY: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_play(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_OVERLAY_PLAY_ENABLE: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_play_enable(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_OVERLAY_PLAY_WAIT: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_play_wait(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_OVERLAY_BLT: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_blt(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_OVERLAY_BLT_OFFSET: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_blt_off(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_OVERLAY_3D: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_overlay_3d_sbys(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_MIXER_INFO: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_mixer_info(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_WRITEBACK_INIT: + ret = msmfb_overlay_ioctl_writeback_init(info); + break; + case MSMFB_WRITEBACK_START: + ret = msmfb_overlay_ioctl_writeback_start( + info); + break; + case MSMFB_WRITEBACK_STOP: + ret = msmfb_overlay_ioctl_writeback_stop( + info); + break; + case MSMFB_WRITEBACK_QUEUE_BUFFER: + ret = msmfb_overlay_ioctl_writeback_queue_buffer( + info, argp); + break; + case MSMFB_WRITEBACK_DEQUEUE_BUFFER: + ret = msmfb_overlay_ioctl_writeback_dequeue_buffer( + info, argp); + break; + case MSMFB_WRITEBACK_TERMINATE: + ret = msmfb_overlay_ioctl_writeback_terminate(info); + break; +#endif + case MSMFB_BLIT: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_blit(info, argp); + up(&msm_fb_ioctl_ppp_sem); + + break; + + /* Ioctl for setting ccs matrix from user space */ + case MSMFB_SET_CCS_MATRIX: +#ifndef CONFIG_FB_MSM_MDP40 + ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix)); + if (ret) { + printk(KERN_ERR + "%s:MSMFB_SET_CCS_MATRIX ioctl failed \n", + __func__); + return ret; + } + + down(&msm_fb_ioctl_ppp_sem); + if (ccs_matrix.direction == MDP_CCS_RGB2YUV) + mdp_ccs_rgb2yuv = ccs_matrix; + else + mdp_ccs_yuv2rgb = ccs_matrix; + + msmfb_set_color_conv(&ccs_matrix) ; + up(&msm_fb_ioctl_ppp_sem); +#else + ret = copy_from_user(&csc_matrix, argp, sizeof(csc_matrix)); + if (ret) { + pr_err("%s:MSMFB_SET_CSC_MATRIX ioctl failed\n", + __func__); + return ret; + } + down(&msm_fb_ioctl_ppp_sem); + msmfb_set_color_conv(&csc_matrix); + up(&msm_fb_ioctl_ppp_sem); + +#endif + + break; + + /* Ioctl for getting ccs matrix to user space */ + case MSMFB_GET_CCS_MATRIX: +#ifndef CONFIG_FB_MSM_MDP40 + ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix)) ; + if (ret) { + printk(KERN_ERR + "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n", + __func__); + return ret; + } + + down(&msm_fb_ioctl_ppp_sem); + if (ccs_matrix.direction == MDP_CCS_RGB2YUV) + ccs_matrix = mdp_ccs_rgb2yuv; + else + ccs_matrix = mdp_ccs_yuv2rgb; + + ret = copy_to_user(argp, &ccs_matrix, sizeof(ccs_matrix)); + + if (ret) { + printk(KERN_ERR + "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n", + __func__); + return ret ; + } + up(&msm_fb_ioctl_ppp_sem); +#else + ret = -EINVAL; +#endif + + break; + + case MSMFB_GRP_DISP: +#ifdef CONFIG_FB_MSM_MDP22 + { + unsigned long grp_id; + + ret = copy_from_user(&grp_id, argp, sizeof(grp_id)); + if (ret) + return ret; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + writel(grp_id, MDP_FULL_BYPASS_WORD43); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, + FALSE); + break; + } +#else + return -EFAULT; +#endif + case MSMFB_SUSPEND_SW_REFRESHER: + if (!mfd->panel_power_on) + return -EPERM; + + mfd->sw_refreshing_enable = FALSE; + ret = msm_fb_stop_sw_refresher(mfd); + break; + + case MSMFB_RESUME_SW_REFRESHER: + if (!mfd->panel_power_on) + return -EPERM; + + mfd->sw_refreshing_enable = TRUE; + ret = msm_fb_resume_sw_refresher(mfd); + break; + + case MSMFB_CURSOR: + ret = copy_from_user(&cursor, argp, sizeof(cursor)); + if (ret) + return ret; + + ret = msm_fb_cursor(info, &cursor); + break; + + case MSMFB_SET_LUT: + ret = copy_from_user(&cmap, argp, sizeof(cmap)); + if (ret) + return ret; + + mutex_lock(&msm_fb_ioctl_lut_sem); + ret = msm_fb_set_lut(&cmap, info); + mutex_unlock(&msm_fb_ioctl_lut_sem); + break; + + case MSMFB_HISTOGRAM: + if (!mfd->panel_power_on) + return -EPERM; + + if (!mfd->do_histogram) + return -ENODEV; + + ret = copy_from_user(&hist, argp, sizeof(hist)); + if (ret) + return ret; + + ret = mfd->do_histogram(info, &hist); + break; + + case MSMFB_HISTOGRAM_START: + if (!mfd->panel_power_on) + return -EPERM; + + if (!mfd->start_histogram) + return -ENODEV; + + ret = copy_from_user(&hist_req, argp, sizeof(hist_req)); + if (ret) + return ret; + + ret = mfd->start_histogram(&hist_req); + break; + + case MSMFB_HISTOGRAM_STOP: + if (!mfd->stop_histogram) + return -ENODEV; + + ret = copy_from_user(&block, argp, sizeof(int)); + if (ret) + return ret; + + ret = mfd->stop_histogram(info, block); + break; + + + case MSMFB_GET_PAGE_PROTECTION: + fb_page_protection.page_protection + = mfd->mdp_fb_page_protection; + ret = copy_to_user(argp, &fb_page_protection, + sizeof(fb_page_protection)); + if (ret) + return ret; + break; + + case MSMFB_NOTIFY_UPDATE: + ret = msmfb_notify_update(info, argp); + break; + + case MSMFB_SET_PAGE_PROTECTION: +#if defined CONFIG_ARCH_QSD8X50 || defined CONFIG_ARCH_MSM8X60 + ret = copy_from_user(&fb_page_protection, argp, + sizeof(fb_page_protection)); + if (ret) + return ret; + + /* Validate the proposed page protection settings. */ + switch (fb_page_protection.page_protection) { + case MDP_FB_PAGE_PROTECTION_NONCACHED: + case MDP_FB_PAGE_PROTECTION_WRITECOMBINE: + case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE: + /* Write-back cache (read allocate) */ + case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE: + /* Write-back cache (write allocate) */ + case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE: + mfd->mdp_fb_page_protection = + fb_page_protection.page_protection; + break; + default: + ret = -EINVAL; + break; + } +#else + /* + * Don't allow caching until 7k DMA cache operations are + * available. + */ + ret = -EINVAL; +#endif + break; + + case MSMFB_MDP_PP: + ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp)); + if (ret) + return ret; + + ret = msmfb_handle_pp_ioctl(&mdp_pp); + break; + + default: + MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd); + ret = -EINVAL; + break; + } + + return ret; +} + +static int msm_fb_register_driver(void) +{ + return platform_driver_register(&msm_fb_driver); +} + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +struct fb_info *msm_fb_get_writeback_fb(void) +{ + int c = 0; + for (c = 0; c < fbi_list_index; ++c) { + struct msm_fb_data_type *mfd; + mfd = (struct msm_fb_data_type *)fbi_list[c]->par; + if (mfd->panel.type == WRITEBACK_PANEL) + return fbi_list[c]; + } + + return NULL; +} +EXPORT_SYMBOL(msm_fb_get_writeback_fb); + +int msm_fb_writeback_start(struct fb_info *info) +{ + return mdp4_writeback_start(info); +} +EXPORT_SYMBOL(msm_fb_writeback_start); + +int msm_fb_writeback_queue_buffer(struct fb_info *info, + struct msmfb_data *data) +{ + return mdp4_writeback_queue_buffer(info, data); +} +EXPORT_SYMBOL(msm_fb_writeback_queue_buffer); + +int msm_fb_writeback_dequeue_buffer(struct fb_info *info, + struct msmfb_data *data) +{ + return mdp4_writeback_dequeue_buffer(info, data); +} +EXPORT_SYMBOL(msm_fb_writeback_dequeue_buffer); + +int msm_fb_writeback_stop(struct fb_info *info) +{ + return mdp4_writeback_stop(info); +} +EXPORT_SYMBOL(msm_fb_writeback_stop); +int msm_fb_writeback_init(struct fb_info *info) +{ + return mdp4_writeback_init(info); +} +EXPORT_SYMBOL(msm_fb_writeback_init); +int msm_fb_writeback_terminate(struct fb_info *info) +{ + return mdp4_writeback_terminate(info); +} +EXPORT_SYMBOL(msm_fb_writeback_terminate); +#endif + +struct platform_device *msm_fb_add_device(struct platform_device *pdev) +{ + struct msm_fb_panel_data *pdata; + struct platform_device *this_dev = NULL; + struct fb_info *fbi; + struct msm_fb_data_type *mfd = NULL; + u32 type, id, fb_num; + + if (!pdev) + return NULL; + id = pdev->id; + + pdata = pdev->dev.platform_data; + if (!pdata) + return NULL; + type = pdata->panel_info.type; + +#if defined MSM_FB_NUM + /* + * over written fb_num which defined + * at panel_info + * + */ + if (type == HDMI_PANEL || type == DTV_PANEL || + type == TV_PANEL || type == WRITEBACK_PANEL) { + if (hdmi_prim_display) + pdata->panel_info.fb_num = 2; + else + pdata->panel_info.fb_num = 1; + } + else + pdata->panel_info.fb_num = MSM_FB_NUM; + + MSM_FB_INFO("setting pdata->panel_info.fb_num to %d. type: %d\n", + pdata->panel_info.fb_num, type); +#endif + fb_num = pdata->panel_info.fb_num; + + if (fb_num <= 0) + return NULL; + + if (fbi_list_index >= MAX_FBI_LIST) { + printk(KERN_ERR "msm_fb: no more framebuffer info list!\n"); + return NULL; + } + /* + * alloc panel device data + */ + this_dev = msm_fb_device_alloc(pdata, type, id); + + if (!this_dev) { + printk(KERN_ERR + "%s: msm_fb_device_alloc failed!\n", __func__); + return NULL; + } + + /* + * alloc framebuffer info + par data + */ + fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL); + if (fbi == NULL) { + platform_device_put(this_dev); + printk(KERN_ERR "msm_fb: can't alloca framebuffer info data!\n"); + return NULL; + } + + mfd = (struct msm_fb_data_type *)fbi->par; + mfd->key = MFD_KEY; + mfd->fbi = fbi; + mfd->panel.type = type; + mfd->panel.id = id; + mfd->fb_page = fb_num; + mfd->index = fbi_list_index; + mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE; + mfd->iclient = iclient; + /* link to the latest pdev */ + mfd->pdev = this_dev; + + mfd_list[mfd_list_index++] = mfd; + fbi_list[fbi_list_index++] = fbi; + + /* + * set driver data + */ + platform_set_drvdata(this_dev, mfd); + + if (platform_device_add(this_dev)) { + printk(KERN_ERR "msm_fb: platform_device_add failed!\n"); + platform_device_put(this_dev); + framebuffer_release(fbi); + fbi_list_index--; + return NULL; + } + return this_dev; +} +EXPORT_SYMBOL(msm_fb_add_device); + +int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num, + int subsys_id) +{ + struct fb_info *info; + struct msm_fb_data_type *mfd; + + if (fb_num > MAX_FBI_LIST || + (subsys_id != DISPLAY_SUBSYSTEM_ID && + subsys_id != ROTATOR_SUBSYSTEM_ID)) { + pr_err("%s(): Invalid parameters\n", __func__); + return -1; + } + + info = fbi_list[fb_num]; + if (!info) { + pr_err("%s(): info is NULL\n", __func__); + return -1; + } + + mfd = (struct msm_fb_data_type *)info->par; + + if (subsys_id == DISPLAY_SUBSYSTEM_ID) { + if (mfd->display_iova) + *start = mfd->display_iova; + else + *start = info->fix.smem_start; + } else { + if (mfd->rotator_iova) + *start = mfd->rotator_iova; + else + *start = info->fix.smem_start; + } + + *len = info->fix.smem_len; + + return 0; +} +EXPORT_SYMBOL(get_fb_phys_info); + +int __init msm_fb_init(void) +{ + int rc = -ENODEV; + + if (msm_fb_register_driver()) + return rc; + +#ifdef MSM_FB_ENABLE_DBGFS + { + struct dentry *root; + + if ((root = msm_fb_get_debugfs_root()) != NULL) { + msm_fb_debugfs_file_create(root, + "msm_fb_msg_printing_level", + (u32 *) &msm_fb_msg_level); + msm_fb_debugfs_file_create(root, + "mddi_msg_printing_level", + (u32 *) &mddi_msg_level); + msm_fb_debugfs_file_create(root, "msm_fb_debug_enabled", + (u32 *) &msm_fb_debug_enabled); + } + } +#endif + + return 0; +} + +/* Called by v4l2 driver to enable/disable overlay pipe */ +int msm_fb_v4l2_enable(struct mdp_overlay *req, bool enable, void **par) +{ + int err = 0; +#ifdef CONFIG_FB_MSM_MDP40 + struct mdp4_overlay_pipe *pipe; + if (enable) { + + err = mdp4_v4l2_overlay_set(fbi_list[0], req, &pipe); + + *(struct mdp4_overlay_pipe **)par = pipe; + + } else { + pipe = *(struct mdp4_overlay_pipe **)par; + mdp4_v4l2_overlay_clear(pipe); + } +#else +#ifdef CONFIG_FB_MSM_MDP30 + if (enable) + err = mdp_ppp_v4l2_overlay_set(fbi_list[0], req); + else + err = mdp_ppp_v4l2_overlay_clear(); +#else + err = -EINVAL; +#endif +#endif + + return err; +} +EXPORT_SYMBOL(msm_fb_v4l2_enable); + +/* Called by v4l2 driver to provide a frame for display */ +int msm_fb_v4l2_update(void *par, + unsigned long srcp0_addr, unsigned long srcp0_size, + unsigned long srcp1_addr, unsigned long srcp1_size, + unsigned long srcp2_addr, unsigned long srcp2_size) +{ +#ifdef CONFIG_FB_MSM_MDP40 + struct mdp4_overlay_pipe *pipe = (struct mdp4_overlay_pipe *)par; + return mdp4_v4l2_overlay_play(fbi_list[0], pipe, + srcp0_addr, srcp1_addr, + srcp2_addr); +#else +#ifdef CONFIG_FB_MSM_MDP30 + return mdp_ppp_v4l2_overlay_play(fbi_list[0], + srcp0_addr, srcp0_size, + srcp1_addr, srcp1_size); +#else + return -EINVAL; +#endif +#endif +} +EXPORT_SYMBOL(msm_fb_v4l2_update); + +module_init(msm_fb_init); diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h new file mode 100644 index 0000000000000000000000000000000000000000..e6d908aad6d3998f03a0c361eefe9be0d0b3757f --- /dev/null +++ b/drivers/video/msm/msm_fb.h @@ -0,0 +1,223 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MSM_FB_H +#define MSM_FB_H + +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include "msm_fb_panel.h" +#include "mdp.h" + +#define MSM_FB_DEFAULT_PAGE_SIZE 2 +#define MFD_KEY 0x11161126 +#define MSM_FB_MAX_DEV_LIST 32 + +struct disp_info_type_suspend { + boolean op_enable; + boolean sw_refreshing_enable; + boolean panel_power_on; +}; + +struct msmfb_writeback_data_list { + struct list_head registered_entry; + struct list_head active_entry; + void *addr; + struct file *pmem_file; + struct msmfb_data buf_info; + struct msmfb_img img; + int state; +}; + + +struct msm_fb_data_type { + __u32 key; + __u32 index; + __u32 ref_cnt; + __u32 fb_page; + + panel_id_type panel; + struct msm_panel_info panel_info; + + DISP_TARGET dest; + struct fb_info *fbi; + + boolean op_enable; + uint32 fb_imgType; + boolean sw_currently_refreshing; + boolean sw_refreshing_enable; + boolean hw_refresh; +#ifdef CONFIG_FB_MSM_OVERLAY + int overlay_play_enable; +#endif + + MDPIBUF ibuf; + boolean ibuf_flushed; + struct timer_list refresh_timer; + struct completion refresher_comp; + + boolean pan_waiting; + struct completion pan_comp; + + /* vsync */ + boolean use_mdp_vsync; + __u32 vsync_gpio; + __u32 total_lcd_lines; + __u32 total_porch_lines; + __u32 lcd_ref_usec_time; + __u32 refresh_timer_duration; + + struct hrtimer dma_hrtimer; + + boolean panel_power_on; + struct work_struct dma_update_worker; + struct semaphore sem; + + struct timer_list vsync_resync_timer; + boolean vsync_handler_pending; + struct work_struct vsync_resync_worker; + + ktime_t last_vsync_timetick; + + __u32 *vsync_width_boundary; + + unsigned int pmem_id; + struct disp_info_type_suspend suspend; + + __u32 channel_irq; + + struct mdp_dma_data *dma; + void (*dma_fnc) (struct msm_fb_data_type *mfd); + int (*cursor_update) (struct fb_info *info, + struct fb_cursor *cursor); + int (*lut_update) (struct fb_info *info, + struct fb_cmap *cmap); + int (*do_histogram) (struct fb_info *info, + struct mdp_histogram_data *hist); + int (*start_histogram) (struct mdp_histogram_start_req *req); + int (*stop_histogram) (struct fb_info *info, uint32_t block); + void *cursor_buf; + void *cursor_buf_phys; + + void *cmd_port; + void *data_port; + void *data_port_phys; + + __u32 bl_level; + + struct platform_device *pdev; + + __u32 var_xres; + __u32 var_yres; + __u32 var_pixclock; + __u32 var_frame_rate; + +#ifdef MSM_FB_ENABLE_DBGFS + struct dentry *sub_dir; +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#ifdef CONFIG_FB_MSM_MDDI + struct early_suspend mddi_early_suspend; + struct early_suspend mddi_ext_early_suspend; +#endif +#endif + u32 mdp_fb_page_protection; + + struct clk *ebi1_clk; + boolean dma_update_flag; + struct timer_list msmfb_no_update_notify_timer; + struct completion msmfb_update_notify; + struct completion msmfb_no_update_notify; + struct mutex writeback_mutex; + struct mutex unregister_mutex; + struct list_head writeback_busy_queue; + struct list_head writeback_free_queue; + struct list_head writeback_register_queue; + wait_queue_head_t wait_q; + struct ion_client *iclient; + unsigned long display_iova; + unsigned long rotator_iova; + struct mdp_buf_type *ov0_wb_buf; + struct mdp_buf_type *ov1_wb_buf; + u32 ov_start; + u32 mem_hid; + u32 mdp_rev; + u32 use_ov0_blt, ov0_blt_state; + u32 use_ov1_blt, ov1_blt_state; + u32 writeback_state; + bool writeback_active_cnt; + int cont_splash_done; +}; + +struct dentry *msm_fb_get_debugfs_root(void); +void msm_fb_debugfs_file_create(struct dentry *root, const char *name, + u32 *var); +void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl); + +struct platform_device *msm_fb_add_device(struct platform_device *pdev); +struct fb_info *msm_fb_get_writeback_fb(void); +int msm_fb_writeback_init(struct fb_info *info); +int msm_fb_writeback_start(struct fb_info *info); +int msm_fb_writeback_queue_buffer(struct fb_info *info, + struct msmfb_data *data); +int msm_fb_writeback_dequeue_buffer(struct fb_info *info, + struct msmfb_data *data); +int msm_fb_writeback_stop(struct fb_info *info); +int msm_fb_writeback_terminate(struct fb_info *info); +int msm_fb_detect_client(const char *name); +int calc_fb_offset(struct msm_fb_data_type *mfd, struct fb_info *fbi, int bpp); + +#ifdef CONFIG_FB_BACKLIGHT +void msm_fb_config_backlight(struct msm_fb_data_type *mfd); +#endif + +void fill_black_screen(void); +void unfill_black_screen(void); +int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd, + struct fb_info *info); + +#ifdef CONFIG_FB_MSM_LOGO +#define INIT_IMAGE_FILE "/initlogo.rle" +int load_565rle_image(char *filename, bool bf_supported); +#endif + +#endif /* MSM_FB_H */ diff --git a/drivers/video/msm/msm_fb_bl.c b/drivers/video/msm/msm_fb_bl.c new file mode 100644 index 0000000000000000000000000000000000000000..f4835ee1134b037ead9d9802d4486987222792f7 --- /dev/null +++ b/drivers/video/msm/msm_fb_bl.c @@ -0,0 +1,75 @@ +/* Copyright (c) 2008-2009, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb.h" + +static int msm_fb_bl_get_brightness(struct backlight_device *pbd) +{ + return pbd->props.brightness; +} + +static int msm_fb_bl_update_status(struct backlight_device *pbd) +{ + struct msm_fb_data_type *mfd = bl_get_data(pbd); + __u32 bl_lvl; + + bl_lvl = pbd->props.brightness; + bl_lvl = mfd->fbi->bl_curve[bl_lvl]; + msm_fb_set_backlight(mfd, bl_lvl); + return 0; +} + +static struct backlight_ops msm_fb_bl_ops = { + .get_brightness = msm_fb_bl_get_brightness, + .update_status = msm_fb_bl_update_status, +}; + +void msm_fb_config_backlight(struct msm_fb_data_type *mfd) +{ + struct msm_fb_panel_data *pdata; + struct backlight_device *pbd; + struct fb_info *fbi; + char name[16]; + struct backlight_properties props; + + fbi = mfd->fbi; + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + if ((pdata) && (pdata->set_backlight)) { + snprintf(name, sizeof(name), "msmfb_bl%d", mfd->index); + props.max_brightness = FB_BACKLIGHT_LEVELS - 1; + props.brightness = FB_BACKLIGHT_LEVELS - 1; + pbd = + backlight_device_register(name, fbi->dev, mfd, + &msm_fb_bl_ops, &props); + if (!IS_ERR(pbd)) { + fbi->bl_dev = pbd; + fb_bl_default_curve(fbi, + 0, + mfd->panel_info.bl_min, + mfd->panel_info.bl_max); + } else { + fbi->bl_dev = NULL; + printk(KERN_ERR "msm_fb: backlight_device_register failed!\n"); + } + } +} diff --git a/drivers/video/msm/msm_fb_def.h b/drivers/video/msm/msm_fb_def.h new file mode 100644 index 0000000000000000000000000000000000000000..dcd648bab809b68fd859677704f15b27c7d96570 --- /dev/null +++ b/drivers/video/msm/msm_fb_def.h @@ -0,0 +1,204 @@ +/* Copyright (c) 2008-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MSM_FB_DEF_H +#define MSM_FB_DEF_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" +#include +#include +#include + +#include +#include + +#include + + +typedef s64 int64; +typedef s32 int32; +typedef s16 int16; +typedef s8 int8; + +typedef u64 uint64; +typedef u32 uint32; +typedef u16 uint16; +typedef u8 uint8; + +typedef s32 int4; +typedef s16 int2; +typedef s8 int1; + +typedef u32 uint4; +typedef u16 uint2; +typedef u8 uint1; + +typedef u32 dword; +typedef u16 word; +typedef u8 byte; + +typedef unsigned int boolean; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define MSM_FB_ENABLE_DBGFS +#define FEATURE_MDDI + +#if defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGB565) +#define MSMFB_DEFAULT_TYPE MDP_RGB_565 +#elif defined(CONFIG_FB_MSM_DEFAULT_DEPTH_ARGB8888) +#define MSMFB_DEFAULT_TYPE MDP_ARGB_8888 +#elif defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888) +#define MSMFB_DEFAULT_TYPE MDP_RGBA_8888 +#else +#define MSMFB_DEFAULT_TYPE MDP_RGB_565 +#endif + +#define outp32(addr, val) writel(val, addr) +#define outp16(addr, val) writew(val, addr) +#define outp8(addr, val) writeb(val, addr) +#define outp(addr, val) outp32(addr, val) + +#ifndef MAX +#define MAX( x, y ) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef MIN +#define MIN( x, y ) (((x) < (y)) ? (x) : (y)) +#endif + +/*--------------------------------------------------------------------------*/ + +#define inp32(addr) readl(addr) +#define inp16(addr) readw(addr) +#define inp8(addr) readb(addr) +#define inp(addr) inp32(addr) + +#define inpw(port) readw(port) +#define outpw(port, val) writew(val, port) +#define inpdw(port) readl(port) +#define outpdw(port, val) writel(val, port) + + +#define clk_busy_wait(x) msleep_interruptible((x)/1000) + +#define memory_barrier() + +#define assert(expr) \ + if(!(expr)) { \ + printk(KERN_ERR "msm_fb: assertion failed! %s,%s,%s,line=%d\n",\ + #expr, __FILE__, __func__, __LINE__); \ + } + +#define ASSERT(x) assert(x) + +#define DISP_EBI2_LOCAL_DEFINE +#ifdef DISP_EBI2_LOCAL_DEFINE +#define LCD_PRIM_BASE_PHYS 0x98000000 +#define LCD_SECD_BASE_PHYS 0x9c000000 +#define EBI2_PRIM_LCD_RS_PIN 0x20000 +#define EBI2_SECD_LCD_RS_PIN 0x20000 + +#define EBI2_PRIM_LCD_CLR 0xC0 +#define EBI2_PRIM_LCD_SEL 0x40 + +#define EBI2_SECD_LCD_CLR 0x300 +#define EBI2_SECD_LCD_SEL 0x100 +#endif + +extern u32 msm_fb_msg_level; + +/* + * Message printing priorities: + * LEVEL 0 KERN_EMERG (highest priority) + * LEVEL 1 KERN_ALERT + * LEVEL 2 KERN_CRIT + * LEVEL 3 KERN_ERR + * LEVEL 4 KERN_WARNING + * LEVEL 5 KERN_NOTICE + * LEVEL 6 KERN_INFO + * LEVEL 7 KERN_DEBUG (Lowest priority) + */ +#define MSM_FB_EMERG(msg, ...) \ + if (msm_fb_msg_level > 0) \ + printk(KERN_EMERG msg, ## __VA_ARGS__); +#define MSM_FB_ALERT(msg, ...) \ + if (msm_fb_msg_level > 1) \ + printk(KERN_ALERT msg, ## __VA_ARGS__); +#define MSM_FB_CRIT(msg, ...) \ + if (msm_fb_msg_level > 2) \ + printk(KERN_CRIT msg, ## __VA_ARGS__); +#define MSM_FB_ERR(msg, ...) \ + if (msm_fb_msg_level > 3) \ + printk(KERN_ERR msg, ## __VA_ARGS__); +#define MSM_FB_WARNING(msg, ...) \ + if (msm_fb_msg_level > 4) \ + printk(KERN_WARNING msg, ## __VA_ARGS__); +#define MSM_FB_NOTICE(msg, ...) \ + if (msm_fb_msg_level > 5) \ + printk(KERN_NOTICE msg, ## __VA_ARGS__); +#define MSM_FB_INFO(msg, ...) \ + if (msm_fb_msg_level > 6) \ + printk(KERN_INFO msg, ## __VA_ARGS__); +#define MSM_FB_DEBUG(msg, ...) \ + if (msm_fb_msg_level > 7) \ + printk(KERN_DEBUG msg, ## __VA_ARGS__); + +#ifdef MSM_FB_C +unsigned char *msm_mdp_base; +unsigned char *msm_pmdh_base; +unsigned char *msm_emdh_base; +unsigned char *mipi_dsi_base; +#else +extern unsigned char *msm_mdp_base; +extern unsigned char *msm_pmdh_base; +extern unsigned char *msm_emdh_base; +extern unsigned char *mipi_dsi_base; +#endif + +#undef ENABLE_MDDI_MULTI_READ_WRITE +#undef ENABLE_FWD_LINK_SKEW_CALIBRATION + +#endif /* MSM_FB_DEF_H */ diff --git a/drivers/video/msm/msm_fb_panel.c b/drivers/video/msm/msm_fb_panel.c new file mode 100644 index 0000000000000000000000000000000000000000..8e6a8226f45f626d36472484acc0539b6eb56949 --- /dev/null +++ b/drivers/video/msm/msm_fb_panel.c @@ -0,0 +1,148 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_fb_panel.h" + +int panel_next_on(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_panel_data *pdata; + struct msm_fb_panel_data *next_pdata; + struct platform_device *next_pdev; + + pdata = (struct msm_fb_panel_data *)pdev->dev.platform_data; + + if (pdata) { + next_pdev = pdata->next; + if (next_pdev) { + next_pdata = + (struct msm_fb_panel_data *)next_pdev->dev. + platform_data; + if ((next_pdata) && (next_pdata->on)) + ret = next_pdata->on(next_pdev); + } + } + + return ret; +} + +int panel_next_off(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_panel_data *pdata; + struct msm_fb_panel_data *next_pdata; + struct platform_device *next_pdev; + + pdata = (struct msm_fb_panel_data *)pdev->dev.platform_data; + + if (pdata) { + next_pdev = pdata->next; + if (next_pdev) { + next_pdata = + (struct msm_fb_panel_data *)next_pdev->dev. + platform_data; + if ((next_pdata) && (next_pdata->on)) + ret = next_pdata->off(next_pdev); + } + } + + return ret; +} + +struct platform_device *msm_fb_device_alloc(struct msm_fb_panel_data *pdata, + u32 type, u32 id) +{ + struct platform_device *this_dev = NULL; + char dev_name[16]; + + switch (type) { + case EBI2_PANEL: + snprintf(dev_name, sizeof(dev_name), "ebi2_lcd"); + break; + + case MDDI_PANEL: + snprintf(dev_name, sizeof(dev_name), "mddi"); + break; + + case EXT_MDDI_PANEL: + snprintf(dev_name, sizeof(dev_name), "mddi_ext"); + break; + + case TV_PANEL: + snprintf(dev_name, sizeof(dev_name), "tvenc"); + break; + + case HDMI_PANEL: + case LCDC_PANEL: + snprintf(dev_name, sizeof(dev_name), "lcdc"); + break; + + case LVDS_PANEL: + snprintf(dev_name, sizeof(dev_name), "lvds"); + break; + + case DTV_PANEL: + snprintf(dev_name, sizeof(dev_name), "dtv"); + break; + + case MIPI_VIDEO_PANEL: + case MIPI_CMD_PANEL: + snprintf(dev_name, sizeof(dev_name), "mipi_dsi"); + break; + case WRITEBACK_PANEL: + snprintf(dev_name, sizeof(dev_name), "writeback"); + break; + + default: + return NULL; + } + + if (pdata != NULL) + pdata->next = NULL; + else + return NULL; + + this_dev = + platform_device_alloc(dev_name, ((u32) type << 16) | (u32) id); + + if (this_dev) { + if (platform_device_add_data + (this_dev, pdata, sizeof(struct msm_fb_panel_data))) { + printk + ("msm_fb_device_alloc: platform_device_add_data failed!\n"); + platform_device_put(this_dev); + return NULL; + } + } + + return this_dev; +} diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h new file mode 100644 index 0000000000000000000000000000000000000000..61ddc1373ead67cd38de5403ef5f1da0372bca31 --- /dev/null +++ b/drivers/video/msm/msm_fb_panel.h @@ -0,0 +1,213 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MSM_FB_PANEL_H +#define MSM_FB_PANEL_H + +#include "msm_fb_def.h" + +struct msm_fb_data_type; + +typedef void (*msm_fb_vsync_handler_type) (void *arg); + +/* panel id type */ +typedef struct panel_id_s { + uint16 id; + uint16 type; +} panel_id_type; + +/* panel type list */ +#define NO_PANEL 0xffff /* No Panel */ +#define MDDI_PANEL 1 /* MDDI */ +#define EBI2_PANEL 2 /* EBI2 */ +#define LCDC_PANEL 3 /* internal LCDC type */ +#define EXT_MDDI_PANEL 4 /* Ext.MDDI */ +#define TV_PANEL 5 /* TV */ +#define HDMI_PANEL 6 /* HDMI TV */ +#define DTV_PANEL 7 /* DTV */ +#define MIPI_VIDEO_PANEL 8 /* MIPI */ +#define MIPI_CMD_PANEL 9 /* MIPI */ +#define WRITEBACK_PANEL 10 /* Wifi display */ +#define LVDS_PANEL 11 /* LVDS */ + +/* panel class */ +typedef enum { + DISPLAY_LCD = 0, /* lcd = ebi2/mddi */ + DISPLAY_LCDC, /* lcdc */ + DISPLAY_TV, /* TV Out */ + DISPLAY_EXT_MDDI, /* External MDDI */ +} DISP_TARGET; + +/* panel device locaiton */ +typedef enum { + DISPLAY_1 = 0, /* attached as first device */ + DISPLAY_2, /* attached on second device */ + DISPLAY_3, /* attached on third writeback device */ + MAX_PHYS_TARGET_NUM, +} DISP_TARGET_PHYS; + +/* panel info type */ +struct lcd_panel_info { + __u32 vsync_enable; + __u32 refx100; + __u32 v_back_porch; + __u32 v_front_porch; + __u32 v_pulse_width; + __u32 hw_vsync_mode; + __u32 vsync_notifier_period; + __u32 rev; +}; + +struct lcdc_panel_info { + __u32 h_back_porch; + __u32 h_front_porch; + __u32 h_pulse_width; + __u32 v_back_porch; + __u32 v_front_porch; + __u32 v_pulse_width; + __u32 border_clr; + __u32 underflow_clr; + __u32 hsync_skew; + /* Pad width */ + uint32 xres_pad; + /* Pad height */ + uint32 yres_pad; +}; + +struct mddi_panel_info { + __u32 vdopkt; + boolean is_type1; +}; + +struct mipi_panel_info { + char mode; /* video/cmd */ + char interleave_mode; + char crc_check; + char ecc_check; + char dst_format; /* shared by video and command */ + char data_lane0; + char data_lane1; + char data_lane2; + char data_lane3; + char dlane_swap; /* data lane swap */ + char rgb_swap; + char b_sel; + char g_sel; + char r_sel; + char rx_eot_ignore; + char tx_eot_append; + char t_clk_post; /* 0xc0, DSI_CLKOUT_TIMING_CTRL */ + char t_clk_pre; /* 0xc0, DSI_CLKOUT_TIMING_CTRL */ + char vc; /* virtual channel */ + struct mipi_dsi_phy_ctrl *dsi_phy_db; + /* video mode */ + char pulse_mode_hsa_he; + char hfp_power_stop; + char hbp_power_stop; + char hsa_power_stop; + char eof_bllp_power_stop; + char bllp_power_stop; + char traffic_mode; + char frame_rate; + /* command mode */ + char interleave_max; + char insert_dcs_cmd; + char wr_mem_continue; + char wr_mem_start; + char te_sel; + char stream; /* 0 or 1 */ + char mdp_trigger; + char dma_trigger; + uint32 dsi_pclk_rate; + /* byte to esc clk ratio */ + uint32 esc_byte_ratio; + /* The packet-size should not bet changed */ + char no_max_pkt_size; + /* Clock required during LP commands */ + char force_clk_lane_hs; +}; + +enum lvds_mode { + LVDS_SINGLE_CHANNEL_MODE, + LVDS_DUAL_CHANNEL_MODE, +}; + +struct lvds_panel_info { + enum lvds_mode channel_mode; + /* Channel swap in dual mode */ + char channel_swap; +}; + +struct msm_panel_info { + __u32 xres; + __u32 yres; + __u32 bpp; + __u32 mode2_xres; + __u32 mode2_yres; + __u32 mode2_bpp; + __u32 type; + __u32 wait_cycle; + DISP_TARGET_PHYS pdest; + __u32 bl_max; + __u32 bl_min; + __u32 fb_num; + __u32 clk_rate; + __u32 clk_min; + __u32 clk_max; + __u32 frame_count; + __u32 is_3d_panel; + __u32 frame_rate; + + + struct mddi_panel_info mddi; + struct lcd_panel_info lcd; + struct lcdc_panel_info lcdc; + struct mipi_panel_info mipi; + struct lvds_panel_info lvds; +}; + +#define MSM_FB_SINGLE_MODE_PANEL(pinfo) \ + do { \ + (pinfo)->mode2_xres = 0; \ + (pinfo)->mode2_yres = 0; \ + (pinfo)->mode2_bpp = 0; \ + } while (0) + +struct msm_fb_panel_data { + struct msm_panel_info panel_info; + void (*set_rect) (int x, int y, int xres, int yres); + void (*set_vsync_notifier) (msm_fb_vsync_handler_type, void *arg); + void (*set_backlight) (struct msm_fb_data_type *); + + /* function entry chain */ + int (*on) (struct platform_device *pdev); + int (*off) (struct platform_device *pdev); + int (*power_ctrl) (boolean enable); + struct platform_device *next; + int (*clk_func) (int enable); +}; + +/*=========================================================================== + FUNCTIONS PROTOTYPES +============================================================================*/ +struct platform_device *msm_fb_device_alloc(struct msm_fb_panel_data *pdata, + u32 type, u32 id); +int panel_next_on(struct platform_device *pdev); +int panel_next_off(struct platform_device *pdev); + +int lcdc_device_register(struct msm_panel_info *pinfo); + +int mddi_toshiba_device_register(struct msm_panel_info *pinfo, + u32 channel, u32 panel); + +#endif /* MSM_FB_PANEL_H */ diff --git a/drivers/video/msm/tvenc.c b/drivers/video/msm/tvenc.c new file mode 100644 index 0000000000000000000000000000000000000000..2f3ee972730d4e88cfe9b9ea853b1f9a4a201a56 --- /dev/null +++ b/drivers/video/msm/tvenc.c @@ -0,0 +1,522 @@ +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define TVENC_C +#include "tvenc.h" +#include "msm_fb.h" +#include "mdp4.h" +/* AXI rate in KHz */ +#define MSM_SYSTEM_BUS_RATE 128000000 + +static int tvenc_probe(struct platform_device *pdev); +static int tvenc_remove(struct platform_device *pdev); + +static int tvenc_off(struct platform_device *pdev); +static int tvenc_on(struct platform_device *pdev); + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; + +static struct clk *tvenc_clk; +static struct clk *tvdac_clk; +static struct clk *tvenc_pclk; +static struct clk *mdp_tv_clk; +#ifdef CONFIG_FB_MSM_MDP40 +static struct clk *tv_src_clk; +#endif + +#ifdef CONFIG_MSM_BUS_SCALING +static uint32_t tvenc_bus_scale_handle; +#endif + +static int tvenc_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int tvenc_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static struct dev_pm_ops tvenc_dev_pm_ops = { + .runtime_suspend = tvenc_runtime_suspend, + .runtime_resume = tvenc_runtime_resume, +}; + +static struct platform_driver tvenc_driver = { + .probe = tvenc_probe, + .remove = tvenc_remove, + .suspend = NULL, + .resume = NULL, + .shutdown = NULL, + .driver = { + .name = "tvenc", + .pm = &tvenc_dev_pm_ops + }, +}; + +int tvenc_set_encoder_clock(boolean clock_on) +{ + int ret = 0; + if (clock_on) { +#ifdef CONFIG_FB_MSM_MDP40 + /* Consolidated clock used by both HDMI & TV encoder. + Clock exists only in MDP4 and not in older versions */ + ret = clk_set_rate(tv_src_clk, 27000000); + if (ret) { + pr_err("%s: tvsrc_clk set rate failed! %d\n", + __func__, ret); + goto tvsrc_err; + } +#endif + ret = clk_prepare_enable(tvenc_clk); + if (ret) { + pr_err("%s: tvenc_clk enable failed! %d\n", + __func__, ret); + goto tvsrc_err; + } + + if (!IS_ERR(tvenc_pclk)) { + ret = clk_prepare_enable(tvenc_pclk); + if (ret) { + pr_err("%s: tvenc_pclk enable failed! %d\n", + __func__, ret); + goto tvencp_err; + } + } + return ret; + } else { + if (!IS_ERR(tvenc_pclk)) + clk_disable_unprepare(tvenc_pclk); + clk_disable_unprepare(tvenc_clk); + return ret; + } +tvencp_err: + clk_disable_unprepare(tvenc_clk); +tvsrc_err: + return ret; +} + +int tvenc_set_clock(boolean clock_on) +{ + int ret = 0; + if (clock_on) { + if (tvenc_pdata->poll) { + ret = tvenc_set_encoder_clock(CLOCK_ON); + if (ret) { + pr_err("%s: TVenc clock(s) enable failed! %d\n", + __func__, ret); + goto tvenc_err; + } + } + ret = clk_prepare_enable(tvdac_clk); + if (ret) { + pr_err("%s: tvdac_clk enable failed! %d\n", + __func__, ret); + goto tvdac_err; + } + if (!IS_ERR(mdp_tv_clk)) { + ret = clk_prepare_enable(mdp_tv_clk); + if (ret) { + pr_err("%s: mdp_tv_clk enable failed! %d\n", + __func__, ret); + goto mdptv_err; + } + } + return ret; + } else { + if (!IS_ERR(mdp_tv_clk)) + clk_disable_unprepare(mdp_tv_clk); + clk_disable_unprepare(tvdac_clk); + if (tvenc_pdata->poll) + tvenc_set_encoder_clock(CLOCK_OFF); + return ret; + } + +mdptv_err: + clk_disable_unprepare(tvdac_clk); +tvdac_err: + tvenc_set_encoder_clock(CLOCK_OFF); +tvenc_err: + return ret; +} + +static int tvenc_off(struct platform_device *pdev) +{ + int ret = 0; + + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + + ret = panel_next_off(pdev); + if (ret) + pr_err("%s: tvout_off failed! %d\n", + __func__, ret); + + tvenc_set_clock(CLOCK_OFF); + + if (tvenc_pdata && tvenc_pdata->pm_vid_en) + ret = tvenc_pdata->pm_vid_en(0); +#ifdef CONFIG_MSM_BUS_SCALING + if (tvenc_bus_scale_handle > 0) + msm_bus_scale_client_update_request(tvenc_bus_scale_handle, + 0); +#else + if (mfd->ebi1_clk) + clk_disable_unprepare(mfd->ebi1_clk); +#endif + + if (ret) + pr_err("%s: pm_vid_en(off) failed! %d\n", + __func__, ret); + mdp4_extn_disp = 0; + return ret; +} + +static int tvenc_on(struct platform_device *pdev) +{ + int ret = 0; + +#ifndef CONFIG_MSM_BUS_SCALING + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); +#endif + +#ifdef CONFIG_MSM_BUS_SCALING + if (tvenc_bus_scale_handle > 0) + msm_bus_scale_client_update_request(tvenc_bus_scale_handle, + 1); +#else + if (mfd->ebi1_clk) + clk_prepare_enable(mfd->ebi1_clk); +#endif + mdp4_extn_disp = 1; + if (tvenc_pdata && tvenc_pdata->pm_vid_en) + ret = tvenc_pdata->pm_vid_en(1); + if (ret) { + pr_err("%s: pm_vid_en(on) failed! %d\n", + __func__, ret); + return ret; + } + + ret = tvenc_set_clock(CLOCK_ON); + if (ret) { + pr_err("%s: tvenc_set_clock(CLOCK_ON) failed! %d\n", + __func__, ret); + tvenc_pdata->pm_vid_en(0); + goto error; + } + + ret = panel_next_on(pdev); + if (ret) { + pr_err("%s: tvout_on failed! %d\n", + __func__, ret); + tvenc_set_clock(CLOCK_OFF); + tvenc_pdata->pm_vid_en(0); + } + +error: + return ret; + +} + +void tvenc_gen_test_pattern(struct msm_fb_data_type *mfd) +{ + uint32 reg = 0, i; + + reg = readl(MSM_TV_ENC_CTL); + reg |= TVENC_CTL_TEST_PATT_EN; + + for (i = 0; i < 3; i++) { + TV_OUT(TV_ENC_CTL, 0); /* disable TV encoder */ + + switch (i) { + /* + * TV Encoder - Color Bar Test Pattern + */ + case 0: + reg |= TVENC_CTL_TPG_CLRBAR; + break; + /* + * TV Encoder - Red Frame Test Pattern + */ + case 1: + reg |= TVENC_CTL_TPG_REDCLR; + break; + /* + * TV Encoder - Modulated Ramp Test Pattern + */ + default: + reg |= TVENC_CTL_TPG_MODRAMP; + break; + } + + TV_OUT(TV_ENC_CTL, reg); + mdelay(5000); + + switch (i) { + /* + * TV Encoder - Color Bar Test Pattern + */ + case 0: + reg &= ~TVENC_CTL_TPG_CLRBAR; + break; + /* + * TV Encoder - Red Frame Test Pattern + */ + case 1: + reg &= ~TVENC_CTL_TPG_REDCLR; + break; + /* + * TV Encoder - Modulated Ramp Test Pattern + */ + default: + reg &= ~TVENC_CTL_TPG_MODRAMP; + break; + } + } +} + +static int tvenc_resource_initialized; + +static int tvenc_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + struct platform_device *mdp_dev = NULL; + struct msm_fb_panel_data *pdata = NULL; + int rc, ret; + struct clk *ebi1_clk = NULL; + + if (pdev->id == 0) { + tvenc_base = ioremap(pdev->resource[0].start, + pdev->resource[0].end - + pdev->resource[0].start + 1); + if (!tvenc_base) { + pr_err("tvenc_base ioremap failed!\n"); + return -ENOMEM; + } + + tvenc_clk = clk_get(&pdev->dev, "enc_clk"); + tvdac_clk = clk_get(&pdev->dev, "dac_clk"); + tvenc_pclk = clk_get(&pdev->dev, "iface_clk"); + mdp_tv_clk = clk_get(&pdev->dev, "mdp_clk"); + +#ifndef CONFIG_MSM_BUS_SCALING + ebi1_clk = clk_get(&pdev->dev, "mem_clk"); + if (IS_ERR(ebi1_clk)) { + rc = PTR_ERR(ebi1_clk); + goto tvenc_probe_err; + } + clk_set_rate(ebi1_clk, MSM_SYSTEM_BUS_RATE); +#endif + +#ifdef CONFIG_FB_MSM_MDP40 + tv_src_clk = clk_get(&pdev->dev, "src_clk"); + if (IS_ERR(tv_src_clk)) + tv_src_clk = tvenc_clk; /* Fallback to slave */ +#endif + + if (IS_ERR(tvenc_clk)) { + pr_err("%s: error: can't get tvenc_clk!\n", __func__); + return PTR_ERR(tvenc_clk); + } + + if (IS_ERR(tvdac_clk)) { + pr_err("%s: error: can't get tvdac_clk!\n", __func__); + return PTR_ERR(tvdac_clk); + } + + if (IS_ERR(tvenc_pclk)) { + ret = PTR_ERR(tvenc_pclk); + if (-ENOENT == ret) + pr_info("%s: tvenc_pclk does not exist!\n", + __func__); + else { + pr_err("%s: error: can't get tvenc_pclk!\n", + __func__); + return ret; + } + } + + if (IS_ERR(mdp_tv_clk)) { + ret = PTR_ERR(mdp_tv_clk); + if (-ENOENT == ret) + pr_info("%s: mdp_tv_clk does not exist!\n", + __func__); + else { + pr_err("%s: error: can't get mdp_tv_clk!\n", + __func__); + return ret; + } + } + + tvenc_pdata = pdev->dev.platform_data; + tvenc_resource_initialized = 1; + return 0; + } + + if (!tvenc_resource_initialized) + return -EPERM; + + mfd = platform_get_drvdata(pdev); + mfd->ebi1_clk = ebi1_clk; + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + if (tvenc_base == NULL) + return -ENOMEM; + + mdp_dev = platform_device_alloc("mdp", pdev->id); + if (!mdp_dev) + return -ENOMEM; + + /* + * link to the latest pdev + */ + mfd->pdev = mdp_dev; + mfd->dest = DISPLAY_TV; + + /* + * alloc panel device data + */ + if (platform_device_add_data + (mdp_dev, pdev->dev.platform_data, + sizeof(struct msm_fb_panel_data))) { + pr_err("tvenc_probe: platform_device_add_data failed!\n"); + platform_device_put(mdp_dev); + return -ENOMEM; + } + /* + * data chain + */ + pdata = mdp_dev->dev.platform_data; + pdata->on = tvenc_on; + pdata->off = tvenc_off; + pdata->next = pdev; + + /* + * get/set panel specific fb info + */ + mfd->panel_info = pdata->panel_info; +#ifdef CONFIG_FB_MSM_MDP40 + mfd->fb_imgType = MDP_RGB_565; /* base layer */ +#else + mfd->fb_imgType = MDP_YCRYCB_H2V1; +#endif + +#ifdef CONFIG_MSM_BUS_SCALING + if (!tvenc_bus_scale_handle && tvenc_pdata && + tvenc_pdata->bus_scale_table) { + tvenc_bus_scale_handle = + msm_bus_scale_register_client( + tvenc_pdata->bus_scale_table); + if (!tvenc_bus_scale_handle) { + printk(KERN_ERR "%s not able to get bus scale\n", + __func__); + } + } +#endif + + /* + * set driver data + */ + platform_set_drvdata(mdp_dev, mfd); + + /* + * register in mdp driver + */ + rc = platform_device_add(mdp_dev); + if (rc) + goto tvenc_probe_err; + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + + + pdev_list[pdev_list_cnt++] = pdev; + + return 0; + +tvenc_probe_err: +#ifdef CONFIG_MSM_BUS_SCALING + if (tvenc_pdata && tvenc_pdata->bus_scale_table && + tvenc_bus_scale_handle > 0) { + msm_bus_scale_unregister_client(tvenc_bus_scale_handle); + tvenc_bus_scale_handle = 0; + } +#endif + platform_device_put(mdp_dev); + return rc; +} + +static int tvenc_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = platform_get_drvdata(pdev); + +#ifdef CONFIG_MSM_BUS_SCALING + if (tvenc_pdata && tvenc_pdata->bus_scale_table && + tvenc_bus_scale_handle > 0) { + msm_bus_scale_unregister_client(tvenc_bus_scale_handle); + tvenc_bus_scale_handle = 0; + } +#else + clk_put(mfd->ebi1_clk); +#endif + + pm_runtime_disable(&pdev->dev); + return 0; +} + +static int tvenc_register_driver(void) +{ + return platform_driver_register(&tvenc_driver); +} + +static int __init tvenc_driver_init(void) +{ + return tvenc_register_driver(); +} + +module_init(tvenc_driver_init); diff --git a/drivers/video/msm/tvenc.h b/drivers/video/msm/tvenc.h new file mode 100644 index 0000000000000000000000000000000000000000..0368cb860b8eb27e8fb6444ac997db5719e39e27 --- /dev/null +++ b/drivers/video/msm/tvenc.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2008-2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef TVENC_H +#define TVENC_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "msm_fb_panel.h" + +#define NTSC_M 0 /* North America, Korea */ +#define NTSC_J 1 /* Japan */ +#define PAL_BDGHIN 2 /* Non-argentina PAL-N */ +#define PAL_M 3 /* PAL-M */ +#define PAL_N 4 /* Argentina PAL-N */ + +#define CLOCK_OFF 0 +#define CLOCK_ON 1 + +/* 3.57954545 Mhz */ +#define TVENC_CTL_TV_MODE_NTSC_M_PAL60 0 +/* 3.57961149 Mhz */ +#define TVENC_CTL_TV_MODE_PAL_M BIT(0) +/*non-Argintina = 4.3361875 Mhz */ +#define TVENC_CTL_TV_MODE_PAL_BDGHIN BIT(1) +/*Argentina = 3.582055625 Mhz */ +#define TVENC_CTL_TV_MODE_PAL_N (BIT(1)|BIT(0)) + +#define TVENC_CTL_ENC_EN BIT(2) +#define TVENC_CTL_CC_EN BIT(3) +#define TVENC_CTL_CGMS_EN BIT(4) +#define TVENC_CTL_MACRO_EN BIT(5) +#define TVENC_CTL_Y_FILTER_W_NOTCH BIT(6) +#define TVENC_CTL_Y_FILTER_WO_NOTCH 0 +#define TVENC_CTL_Y_FILTER_EN BIT(7) +#define TVENC_CTL_CR_FILTER_EN BIT(8) +#define TVENC_CTL_CB_FILTER_EN BIT(9) +#define TVENC_CTL_SINX_FILTER_EN BIT(10) +#define TVENC_CTL_TEST_PATT_EN BIT(11) +#define TVENC_CTL_OUTPUT_INV BIT(12) +#define TVENC_CTL_PAL60_MODE BIT(13) +#define TVENC_CTL_NTSCJ_MODE BIT(14) +#define TVENC_CTL_S_VIDEO_EN BIT(19) + + +#define TVENC_CTL_TPG_CLRBAR 0 +#define TVENC_CTL_TPG_MODRAMP BIT(15) +#define TVENC_CTL_TPG_REDCLR BIT(16) +#define TVENC_CTL_TPG_NTSC_CBAR (BIT(16)|BIT(15)) +#define TVENC_CTL_TPG_BLACK BIT(17) +#define TVENC_CTL_TPG_WHITE100 (BIT(17)|BIT(15)) +#define TVENC_CTL_TPG_YELLOW75 (BIT(17)|BIT(16)) +#define TVENC_CTL_TPG_CYAN75 (BIT(17)|BIT(16)|BIT(15)) +#define TVENC_CTL_TPG_GREEN75 BIT(18) +#define TVENC_CTL_TPG_MAGENTA75 (BIT(18)|BIT(15)) +#define TVENC_CTL_TPG_RED75 (BIT(18)|BIT(16)) +#define TVENC_CTL_TPG_BLUE75 (BIT(18)|BIT(16)|BIT(15)) +#define TVENC_CTL_TPG_WHITE75 (BIT(18)|BIT(17)) +#define TVENC_CTL_TPG_WHITE_TRSTN (BIT(18)|BIT(17)|BIT(15)) + +#define TVENC_LOAD_DETECT_EN BIT(8) + +#ifdef TVENC_C +void *tvenc_base; +struct tvenc_platform_data *tvenc_pdata; +#else +extern void *tvenc_base; +extern struct tvenc_platform_data *tvenc_pdata; +#endif + +#define TV_OUT(reg, v) writel(v, tvenc_base + MSM_##reg) +#define TV_IN(reg) readl(tvenc_base + MSM_##reg) + +#define MSM_TV_ENC_CTL 0x00 +#define MSM_TV_LEVEL 0x04 +#define MSM_TV_GAIN 0x08 +#define MSM_TV_OFFSET 0x0c +#define MSM_TV_CGMS 0x10 +#define MSM_TV_SYNC_1 0x14 +#define MSM_TV_SYNC_2 0x18 +#define MSM_TV_SYNC_3 0x1c +#define MSM_TV_SYNC_4 0x20 +#define MSM_TV_SYNC_5 0x24 +#define MSM_TV_SYNC_6 0x28 +#define MSM_TV_SYNC_7 0x2c +#define MSM_TV_BURST_V1 0x30 +#define MSM_TV_BURST_V2 0x34 +#define MSM_TV_BURST_V3 0x38 +#define MSM_TV_BURST_V4 0x3c +#define MSM_TV_BURST_H 0x40 +#define MSM_TV_SOL_REQ_ODD 0x44 +#define MSM_TV_SOL_REQ_EVEN 0x48 +#define MSM_TV_DAC_CTL 0x4c +#define MSM_TV_TEST_MUX 0x50 +#define MSM_TV_TEST_MODE 0x54 +#define MSM_TV_TEST_MISR_RESET 0x58 +#define MSM_TV_TEST_EXPORT_MISR 0x5c +#define MSM_TV_TEST_MISR_CURR_VAL 0x60 +#define MSM_TV_TEST_SOF_CFG 0x64 +#define MSM_TV_DAC_INTF 0x100 + +#define MSM_TV_INTR_ENABLE 0x200 +#define MSM_TV_INTR_STATUS 0x204 +#define MSM_TV_INTR_CLEAR 0x208 + +int tvenc_set_encoder_clock(boolean clock_on); +int tvenc_set_clock(boolean clock_on); +#endif /* TVENC_H */ diff --git a/drivers/video/msm/tvout_msm.c b/drivers/video/msm/tvout_msm.c new file mode 100644 index 0000000000000000000000000000000000000000..983c585e0981fa9940746f0251f4e1d8a0b4b9bf --- /dev/null +++ b/drivers/video/msm/tvout_msm.c @@ -0,0 +1,652 @@ +/* Copyright (c) 2008-2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include "msm_fb.h" +#include "tvenc.h" +#include "external_common.h" + +#define TVOUT_HPD_DUTY_CYCLE 3000 + +#define TV_DIMENSION_MAX_WIDTH 720 +#define TV_DIMENSION_MAX_HEIGHT 576 + +struct tvout_msm_state_type { + struct external_common_state_type common; + struct platform_device *pdev; + struct timer_list hpd_state_timer; + struct timer_list hpd_work_timer; + struct work_struct hpd_work; + uint32 hpd_int_status; + uint32 prev_hpd_int_status; + uint32 five_retry; + int irq; + uint16 y_res; + boolean hpd_initialized; + boolean disp_powered_up; +#ifdef CONFIG_SUSPEND + boolean pm_suspended; +#endif + +}; + +static struct tvout_msm_state_type *tvout_msm_state; +static DEFINE_MUTEX(tvout_msm_state_mutex); + +static int tvout_off(struct platform_device *pdev); +static int tvout_on(struct platform_device *pdev); +static void tvout_check_status(void); + +static void tvout_msm_turn_on(boolean power_on) +{ + uint32 reg_val = 0; + reg_val = TV_IN(TV_ENC_CTL); + if (power_on) { + DEV_DBG("%s: TV Encoder turned on\n", __func__); + reg_val |= TVENC_CTL_ENC_EN; + } else { + DEV_DBG("%s: TV Encoder turned off\n", __func__); + reg_val = 0; + } + /* Enable TV Encoder*/ + TV_OUT(TV_ENC_CTL, reg_val); +} + +static void tvout_check_status() +{ + tvout_msm_state->hpd_int_status &= 0x05; + /* hpd_int_status could either be 0x05 or 0x04 for a cable + plug-out event when cable detect is driven by polling. */ + if ((((tvout_msm_state->hpd_int_status == 0x05) || + (tvout_msm_state->hpd_int_status == 0x04)) && + (tvout_msm_state->prev_hpd_int_status == BIT(2))) || + ((tvout_msm_state->hpd_int_status == 0x01) && + (tvout_msm_state->prev_hpd_int_status == BIT(0)))) { + DEV_DBG("%s: cable event sent already!", __func__); + return; + } + + if (tvout_msm_state->hpd_int_status & BIT(2)) { + DEV_DBG("%s: cable plug-out\n", __func__); + mutex_lock(&external_common_state_hpd_mutex); + external_common_state->hpd_state = FALSE; + mutex_unlock(&external_common_state_hpd_mutex); + kobject_uevent(external_common_state->uevent_kobj, + KOBJ_OFFLINE); + tvout_msm_state->prev_hpd_int_status = BIT(2); + } else if (tvout_msm_state->hpd_int_status & BIT(0)) { + DEV_DBG("%s: cable plug-in\n", __func__); + mutex_lock(&external_common_state_hpd_mutex); + external_common_state->hpd_state = TRUE; + mutex_unlock(&external_common_state_hpd_mutex); + kobject_uevent(external_common_state->uevent_kobj, + KOBJ_ONLINE); + tvout_msm_state->prev_hpd_int_status = BIT(0); + } +} + +/* ISR for TV out cable detect */ +static irqreturn_t tvout_msm_isr(int irq, void *dev_id) +{ + tvout_msm_state->hpd_int_status = TV_IN(TV_INTR_STATUS); + TV_OUT(TV_INTR_CLEAR, tvout_msm_state->hpd_int_status); + DEV_DBG("%s: ISR: 0x%02x\n", __func__, + tvout_msm_state->hpd_int_status & 0x05); + + if (tvenc_pdata->poll) + if (!tvout_msm_state || !tvout_msm_state->disp_powered_up) { + DEV_DBG("%s: ISR ignored, display not yet powered on\n", + __func__); + return IRQ_HANDLED; + } + if (tvout_msm_state->hpd_int_status & BIT(0) || + tvout_msm_state->hpd_int_status & BIT(2)) { + /* Use .75sec to debounce the interrupt */ + mod_timer(&tvout_msm_state->hpd_state_timer, jiffies + + msecs_to_jiffies(750)); + } + + return IRQ_HANDLED; +} + +/* Interrupt debounce timer */ +static void tvout_msm_hpd_state_timer(unsigned long data) +{ +#ifdef CONFIG_SUSPEND + mutex_lock(&tvout_msm_state_mutex); + if (tvout_msm_state->pm_suspended) { + mutex_unlock(&tvout_msm_state_mutex); + DEV_WARN("%s: ignored, pm_suspended\n", __func__); + return; + } + mutex_unlock(&tvout_msm_state_mutex); +#endif + + if (tvenc_pdata->poll) + if (!tvout_msm_state || !tvout_msm_state->disp_powered_up) { + DEV_DBG("%s: ignored, display powered off\n", __func__); + return; + } + + /* TV_INTR_STATUS[0x204] + When a TV_ENC interrupt occurs, then reading this register will + indicate what caused the interrupt since that each bit indicates + the source of the interrupt that had happened. If multiple + interrupt sources had happened, then multiple bits of this + register will be set + Bit 0 : Load present on Video1 + Bit 1 : Load present on Video2 + Bit 2 : Load removed on Video1 + Bit 3 : Load removed on Video2 + */ + + /* Locking interrupt status is not required because + last status read after debouncing is used */ + if ((tvout_msm_state->hpd_int_status & 0x05) == 0x05) { + /* SW-workaround :If the status read after debouncing is + 0x05(indicating both load present & load removed- which can't + happen in reality), force an update. If status remains 0x05 + after retry, it's a cable unplug event */ + if (++tvout_msm_state->five_retry < 2) { + uint32 reg; + DEV_DBG("tvout: Timer: 0x05\n"); + TV_OUT(TV_INTR_CLEAR, 0xf); + reg = TV_IN(TV_DAC_INTF); + TV_OUT(TV_DAC_INTF, reg & ~TVENC_LOAD_DETECT_EN); + TV_OUT(TV_INTR_CLEAR, 0xf); + reg = TV_IN(TV_DAC_INTF); + TV_OUT(TV_DAC_INTF, reg | TVENC_LOAD_DETECT_EN); + return; + } + } + tvout_msm_state->five_retry = 0; + tvout_check_status(); +} + +static void tvout_msm_hpd_work(struct work_struct *work) +{ + uint32 reg; + +#ifdef CONFIG_SUSPEND + mutex_lock(&tvout_msm_state_mutex); + if (tvout_msm_state->pm_suspended) { + mutex_unlock(&tvout_msm_state_mutex); + DEV_WARN("%s: ignored, pm_suspended\n", __func__); + return; + } + mutex_unlock(&tvout_msm_state_mutex); +#endif + + /* Enable power lines & clocks */ + tvenc_pdata->pm_vid_en(1); + tvenc_set_clock(CLOCK_ON); + + /* Enable encoder to get a stable interrupt */ + reg = TV_IN(TV_ENC_CTL); + TV_OUT(TV_ENC_CTL, reg | TVENC_CTL_ENC_EN); + + /* SW- workaround to update status register */ + reg = TV_IN(TV_DAC_INTF); + TV_OUT(TV_DAC_INTF, reg & ~TVENC_LOAD_DETECT_EN); + TV_OUT(TV_INTR_CLEAR, 0xf); + reg = TV_IN(TV_DAC_INTF); + TV_OUT(TV_DAC_INTF, reg | TVENC_LOAD_DETECT_EN); + + tvout_msm_state->hpd_int_status = TV_IN(TV_INTR_STATUS); + + /* Disable TV encoder */ + reg = TV_IN(TV_ENC_CTL); + TV_OUT(TV_ENC_CTL, reg & ~TVENC_CTL_ENC_EN); + + /*Disable power lines & clocks */ + tvenc_set_clock(CLOCK_OFF); + tvenc_pdata->pm_vid_en(0); + + DEV_DBG("%s: ISR: 0x%02x\n", __func__, + tvout_msm_state->hpd_int_status & 0x05); + + mod_timer(&tvout_msm_state->hpd_work_timer, jiffies + + msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE)); + + tvout_check_status(); +} + +static void tvout_msm_hpd_work_timer(unsigned long data) +{ + schedule_work(&tvout_msm_state->hpd_work); +} + +static int tvout_on(struct platform_device *pdev) +{ + uint32 reg = 0; + struct fb_var_screeninfo *var; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + +#ifdef CONFIG_SUSPEND + mutex_lock(&tvout_msm_state_mutex); + if (tvout_msm_state->pm_suspended) { + mutex_unlock(&tvout_msm_state_mutex); + DEV_WARN("%s: ignored, pm_suspended\n", __func__); + return -ENODEV; + } + mutex_unlock(&tvout_msm_state_mutex); +#endif + + var = &mfd->fbi->var; + if (var->reserved[3] >= NTSC_M && var->reserved[3] <= PAL_N) + external_common_state->video_resolution = var->reserved[3]; + + tvout_msm_state->pdev = pdev; + if (del_timer(&tvout_msm_state->hpd_work_timer)) + DEV_DBG("%s: work timer stopped\n", __func__); + + TV_OUT(TV_ENC_CTL, 0); /* disable TV encoder */ + + switch (external_common_state->video_resolution) { + case NTSC_M: + case NTSC_J: + TV_OUT(TV_CGMS, 0x0); + /* NTSC Timing */ + TV_OUT(TV_SYNC_1, 0x0020009e); + TV_OUT(TV_SYNC_2, 0x011306B4); + TV_OUT(TV_SYNC_3, 0x0006000C); + TV_OUT(TV_SYNC_4, 0x0028020D); + TV_OUT(TV_SYNC_5, 0x005E02FB); + TV_OUT(TV_SYNC_6, 0x0006000C); + TV_OUT(TV_SYNC_7, 0x00000012); + TV_OUT(TV_BURST_V1, 0x0013020D); + TV_OUT(TV_BURST_V2, 0x0014020C); + TV_OUT(TV_BURST_V3, 0x0013020D); + TV_OUT(TV_BURST_V4, 0x0014020C); + TV_OUT(TV_BURST_H, 0x00AE00F2); + TV_OUT(TV_SOL_REQ_ODD, 0x00280208); + TV_OUT(TV_SOL_REQ_EVEN, 0x00290209); + + reg |= TVENC_CTL_TV_MODE_NTSC_M_PAL60; + + if (external_common_state->video_resolution == NTSC_M) { + /* Cr gain 11, Cb gain C6, y_gain 97 */ + TV_OUT(TV_GAIN, 0x0081B697); + } else { + /* Cr gain 11, Cb gain C6, y_gain 97 */ + TV_OUT(TV_GAIN, 0x008bc4a3); + reg |= TVENC_CTL_NTSCJ_MODE; + } + + var->yres = 480; + break; + case PAL_BDGHIN: + case PAL_N: + /* PAL Timing */ + TV_OUT(TV_SYNC_1, 0x00180097); + TV_OUT(TV_SYNC_3, 0x0005000a); + TV_OUT(TV_SYNC_4, 0x00320271); + TV_OUT(TV_SYNC_5, 0x005602f9); + TV_OUT(TV_SYNC_6, 0x0005000a); + TV_OUT(TV_SYNC_7, 0x0000000f); + TV_OUT(TV_BURST_V1, 0x0012026e); + TV_OUT(TV_BURST_V2, 0x0011026d); + TV_OUT(TV_BURST_V3, 0x00100270); + TV_OUT(TV_BURST_V4, 0x0013026f); + TV_OUT(TV_SOL_REQ_ODD, 0x0030026e); + TV_OUT(TV_SOL_REQ_EVEN, 0x0031026f); + + if (external_common_state->video_resolution == PAL_BDGHIN) { + /* Cr gain 11, Cb gain C6, y_gain 97 */ + TV_OUT(TV_GAIN, 0x0088c1a0); + TV_OUT(TV_CGMS, 0x00012345); + TV_OUT(TV_SYNC_2, 0x011f06c0); + TV_OUT(TV_BURST_H, 0x00af00ea); + reg |= TVENC_CTL_TV_MODE_PAL_BDGHIN; + } else { + /* Cr gain 11, Cb gain C6, y_gain 97 */ + TV_OUT(TV_GAIN, 0x0081b697); + TV_OUT(TV_CGMS, 0x000af317); + TV_OUT(TV_SYNC_2, 0x12006c0); + TV_OUT(TV_BURST_H, 0x00af00fa); + reg |= TVENC_CTL_TV_MODE_PAL_N; + } + var->yres = 576; + break; + case PAL_M: + /* Cr gain 11, Cb gain C6, y_gain 97 */ + TV_OUT(TV_GAIN, 0x0081b697); + TV_OUT(TV_CGMS, 0x000af317); + TV_OUT(TV_TEST_MUX, 0x000001c3); + TV_OUT(TV_TEST_MODE, 0x00000002); + /* PAL Timing */ + TV_OUT(TV_SYNC_1, 0x0020009e); + TV_OUT(TV_SYNC_2, 0x011306b4); + TV_OUT(TV_SYNC_3, 0x0006000c); + TV_OUT(TV_SYNC_4, 0x0028020D); + TV_OUT(TV_SYNC_5, 0x005e02fb); + TV_OUT(TV_SYNC_6, 0x0006000c); + TV_OUT(TV_SYNC_7, 0x00000012); + TV_OUT(TV_BURST_V1, 0x0012020b); + TV_OUT(TV_BURST_V2, 0x0016020c); + TV_OUT(TV_BURST_V3, 0x00150209); + TV_OUT(TV_BURST_V4, 0x0013020c); + TV_OUT(TV_BURST_H, 0x00bf010b); + TV_OUT(TV_SOL_REQ_ODD, 0x00280208); + TV_OUT(TV_SOL_REQ_EVEN, 0x00290209); + + reg |= TVENC_CTL_TV_MODE_PAL_M; + var->yres = 480; + break; + default: + return -ENODEV; + } + + reg |= TVENC_CTL_Y_FILTER_EN | TVENC_CTL_CR_FILTER_EN | + TVENC_CTL_CB_FILTER_EN | TVENC_CTL_SINX_FILTER_EN; + + /* DC offset to 0. */ + TV_OUT(TV_LEVEL, 0x00000000); + TV_OUT(TV_OFFSET, 0x008080f0); + +#ifdef CONFIG_FB_MSM_TVOUT_SVIDEO + reg |= TVENC_CTL_S_VIDEO_EN; +#endif +#if defined(CONFIG_FB_MSM_MDP31) + TV_OUT(TV_DAC_INTF, 0x29); +#endif + TV_OUT(TV_ENC_CTL, reg); + + if (!tvout_msm_state->hpd_initialized) { + tvout_msm_state->hpd_initialized = TRUE; + /* Load detect enable */ + reg = TV_IN(TV_DAC_INTF); + reg |= TVENC_LOAD_DETECT_EN; + TV_OUT(TV_DAC_INTF, reg); + } + + tvout_msm_state->disp_powered_up = TRUE; + tvout_msm_turn_on(TRUE); + + if (tvenc_pdata->poll) { + /* Enable Load present & removal interrupts for Video1 */ + TV_OUT(TV_INTR_ENABLE, 0x5); + + /* Enable interrupts when display is on */ + enable_irq(tvout_msm_state->irq); + } + return 0; +} + +static int tvout_off(struct platform_device *pdev) +{ + /* Disable TV encoder irqs when display is off */ + if (tvenc_pdata->poll) + disable_irq(tvout_msm_state->irq); + tvout_msm_turn_on(FALSE); + tvout_msm_state->hpd_initialized = FALSE; + tvout_msm_state->disp_powered_up = FALSE; + if (tvenc_pdata->poll) { + mod_timer(&tvout_msm_state->hpd_work_timer, jiffies + + msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE)); + } + return 0; +} + +static int tvout_probe(struct platform_device *pdev) +{ + int rc = 0; + uint32 reg; + struct platform_device *fb_dev; + +#ifdef CONFIG_FB_MSM_TVOUT_NTSC_M + external_common_state->video_resolution = NTSC_M; +#elif defined CONFIG_FB_MSM_TVOUT_NTSC_J + external_common_state->video_resolution = NTSC_J; +#elif defined CONFIG_FB_MSM_TVOUT_PAL_M + external_common_state->video_resolution = PAL_M; +#elif defined CONFIG_FB_MSM_TVOUT_PAL_N + external_common_state->video_resolution = PAL_N; +#elif defined CONFIG_FB_MSM_TVOUT_PAL_BDGHIN + external_common_state->video_resolution = PAL_BDGHIN; +#endif + external_common_state->dev = &pdev->dev; + if (pdev->id == 0) { + struct resource *res; + + #define GET_RES(name, mode) do { \ + res = platform_get_resource_byname(pdev, mode, name); \ + if (!res) { \ + DEV_DBG("'" name "' resource not found\n"); \ + rc = -ENODEV; \ + goto error; \ + } \ + } while (0) + + #define GET_IRQ(var, name) do { \ + GET_RES(name, IORESOURCE_IRQ); \ + var = res->start; \ + } while (0) + + GET_IRQ(tvout_msm_state->irq, "tvout_device_irq"); + #undef GET_IRQ + #undef GET_RES + return 0; + } + + DEV_DBG("%s: tvout_msm_state->irq : %d", + __func__, tvout_msm_state->irq); + + rc = request_irq(tvout_msm_state->irq, &tvout_msm_isr, + IRQF_TRIGGER_HIGH, "tvout_msm_isr", NULL); + + if (rc) { + DEV_DBG("Init FAILED: IRQ request, rc=%d\n", rc); + goto error; + } + disable_irq(tvout_msm_state->irq); + + init_timer(&tvout_msm_state->hpd_state_timer); + tvout_msm_state->hpd_state_timer.function = + tvout_msm_hpd_state_timer; + tvout_msm_state->hpd_state_timer.data = (uint32)NULL; + tvout_msm_state->hpd_state_timer.expires = jiffies + + msecs_to_jiffies(1000); + + if (tvenc_pdata->poll) { + init_timer(&tvout_msm_state->hpd_work_timer); + tvout_msm_state->hpd_work_timer.function = + tvout_msm_hpd_work_timer; + tvout_msm_state->hpd_work_timer.data = (uint32)NULL; + tvout_msm_state->hpd_work_timer.expires = jiffies + + msecs_to_jiffies(1000); + } + fb_dev = msm_fb_add_device(pdev); + if (fb_dev) { + rc = external_common_state_create(fb_dev); + if (rc) { + DEV_ERR("Init FAILED: tvout_msm_state_create, rc=%d\n", + rc); + goto error; + } + if (tvenc_pdata->poll) { + /* Start polling timer to detect load */ + mod_timer(&tvout_msm_state->hpd_work_timer, jiffies + + msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE)); + } else { + /* Enable interrupt to detect load */ + tvenc_set_encoder_clock(CLOCK_ON); + reg = TV_IN(TV_DAC_INTF); + reg |= TVENC_LOAD_DETECT_EN; + TV_OUT(TV_DAC_INTF, reg); + TV_OUT(TV_INTR_ENABLE, 0x5); + enable_irq(tvout_msm_state->irq); + } + } else + DEV_ERR("Init FAILED: failed to add fb device\n"); +error: + return 0; +} + +static int tvout_remove(struct platform_device *pdev) +{ + external_common_state_remove(); + kfree(tvout_msm_state); + tvout_msm_state = NULL; + return 0; +} + +#ifdef CONFIG_SUSPEND +static int tvout_device_pm_suspend(struct device *dev) +{ + mutex_lock(&tvout_msm_state_mutex); + if (tvout_msm_state->pm_suspended) { + mutex_unlock(&tvout_msm_state_mutex); + return 0; + } + if (tvenc_pdata->poll) { + if (del_timer(&tvout_msm_state->hpd_work_timer)) + DEV_DBG("%s: suspending cable detect timer\n", + __func__); + } else { + disable_irq(tvout_msm_state->irq); + tvenc_set_encoder_clock(CLOCK_OFF); + } + tvout_msm_state->pm_suspended = TRUE; + mutex_unlock(&tvout_msm_state_mutex); + return 0; +} + +static int tvout_device_pm_resume(struct device *dev) +{ + mutex_lock(&tvout_msm_state_mutex); + if (!tvout_msm_state->pm_suspended) { + mutex_unlock(&tvout_msm_state_mutex); + return 0; + } + + if (tvenc_pdata->poll) { + tvout_msm_state->pm_suspended = FALSE; + mod_timer(&tvout_msm_state->hpd_work_timer, jiffies + + msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE)); + mutex_unlock(&tvout_msm_state_mutex); + DEV_DBG("%s: resuming cable detect timer\n", __func__); + } else { + tvenc_set_encoder_clock(CLOCK_ON); + tvout_msm_state->pm_suspended = FALSE; + mutex_unlock(&tvout_msm_state_mutex); + enable_irq(tvout_msm_state->irq); + DEV_DBG("%s: enable cable detect interrupt\n", __func__); + } + return 0; +} +#else +#define tvout_device_pm_suspend NULL +#define tvout_device_pm_resume NULL +#endif + + +static const struct dev_pm_ops tvout_device_pm_ops = { + .suspend = tvout_device_pm_suspend, + .resume = tvout_device_pm_resume, +}; + +static struct platform_driver this_driver = { + .probe = tvout_probe, + .remove = tvout_remove, + .driver = { + .name = "tvout_device", + .pm = &tvout_device_pm_ops, + }, +}; + +static struct msm_fb_panel_data tvout_panel_data = { + .panel_info.xres = TV_DIMENSION_MAX_WIDTH, + .panel_info.yres = TV_DIMENSION_MAX_HEIGHT, + .panel_info.type = TV_PANEL, + .panel_info.pdest = DISPLAY_2, + .panel_info.wait_cycle = 0, +#ifdef CONFIG_FB_MSM_MDP40 + .panel_info.bpp = 24, +#else + .panel_info.bpp = 16, +#endif + .panel_info.fb_num = 2, + .on = tvout_on, + .off = tvout_off, +}; + +static struct platform_device this_device = { + .name = "tvout_device", + .id = 1, + .dev = { + .platform_data = &tvout_panel_data, + } +}; + +static int __init tvout_init(void) +{ + int ret; + + if (msm_fb_detect_client("tvout_msm")) + return 0; + + tvout_msm_state = kzalloc(sizeof(*tvout_msm_state), GFP_KERNEL); + if (!tvout_msm_state) { + DEV_ERR("tvout_msm_init FAILED: out of memory\n"); + ret = -ENOMEM; + goto init_exit; + } + + external_common_state = &tvout_msm_state->common; + ret = platform_driver_register(&this_driver); + if (ret) { + DEV_ERR("tvout_device_init FAILED: platform_driver_register\ + rc=%d\n", ret); + goto init_exit; + } + + ret = platform_device_register(&this_device); + if (ret) { + DEV_ERR("tvout_device_init FAILED: platform_driver_register\ + rc=%d\n", ret); + platform_driver_unregister(&this_driver); + goto init_exit; + } + + INIT_WORK(&tvout_msm_state->hpd_work, tvout_msm_hpd_work); + return 0; + +init_exit: + kfree(tvout_msm_state); + tvout_msm_state = NULL; + return ret; +} + +static void __exit tvout_exit(void) +{ + platform_device_unregister(&this_device); + platform_driver_unregister(&this_driver); +} + +module_init(tvout_init); +module_exit(tvout_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); +MODULE_AUTHOR("Qualcomm Innovation Center, Inc."); +MODULE_DESCRIPTION("TV out driver");