target: msm_shared: add HDMI panel support
Enable HDMI support in display.
Change-Id: Ibfff88ac268db6d5168ace84784d5a114c4486b9
diff --git a/platform/msm_shared/mdss_hdmi.c b/platform/msm_shared/mdss_hdmi.c
new file mode 100644
index 0000000..c6a5bab
--- /dev/null
+++ b/platform/msm_shared/mdss_hdmi.c
@@ -0,0 +1,315 @@
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include <err.h>
+#include <debug.h>
+#include <reg.h>
+#include <msm_panel.h>
+#include <platform/timer.h>
+#include <platform/clock.h>
+#include <platform/iomap.h>
+
+static struct msm_fb_panel_data panel;
+
+/* AVI INFOFRAME DATA */
+#define NUM_MODES_AVI 20
+#define AVI_MAX_DATA_BYTES 13
+
+enum {
+ DATA_BYTE_1,
+ DATA_BYTE_2,
+ DATA_BYTE_3,
+ DATA_BYTE_4,
+ DATA_BYTE_5,
+ DATA_BYTE_6,
+ DATA_BYTE_7,
+ DATA_BYTE_8,
+ DATA_BYTE_9,
+ DATA_BYTE_10,
+ DATA_BYTE_11,
+ DATA_BYTE_12,
+ DATA_BYTE_13,
+};
+
+#define IFRAME_PACKET_OFFSET 0x80
+/*
+ * InfoFrame Type Code:
+ * 0x0 - Reserved
+ * 0x1 - Vendor Specific
+ * 0x2 - Auxiliary Video Information
+ * 0x3 - Source Product Description
+ * 0x4 - AUDIO
+ * 0x5 - MPEG Source
+ * 0x6 - NTSC VBI
+ * 0x7 - 0xFF - Reserved
+ */
+#define AVI_IFRAME_TYPE 0x2
+#define AVI_IFRAME_VERSION 0x2
+#define LEFT_SHIFT_BYTE(x) ((x) << 8)
+#define LEFT_SHIFT_WORD(x) ((x) << 16)
+#define LEFT_SHIFT_24BITS(x) ((x) << 24)
+
+struct mdss_hdmi_timing_info {
+ uint32_t video_format;
+ uint32_t active_h;
+ uint32_t front_porch_h;
+ uint32_t pulse_width_h;
+ uint32_t back_porch_h;
+ uint32_t active_low_h;
+ uint32_t active_v;
+ uint32_t front_porch_v;
+ uint32_t pulse_width_v;
+ uint32_t back_porch_v;
+ uint32_t active_low_v;
+ /* Must divide by 1000 to get the actual frequency in MHZ */
+ uint32_t pixel_freq;
+ /* Must divide by 1000 to get the actual frequency in HZ */
+ uint32_t refresh_rate;
+ uint32_t interlaced;
+ uint32_t supported;
+};
+
+#define HDMI_VFRMT_1280x720p60_16_9 4
+#define HDMI_RESOLUTION_DATA HDMI_VFRMT_1280x720p60_16_9##_TIMING
+
+#define HDMI_VFRMT_1280x720p60_16_9_TIMING \
+ {HDMI_VFRMT_1280x720p60_16_9, 1280, 110, 40, 220, false, \
+ 720, 5, 5, 20, false, 74250, 60000, false, true}
+
+uint32_t mdss_hdmi_avi_info_db[] = {
+ 0x10, 0x28, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0xD1, 0x02, 0x00, 0x00, 0x01, 0x05};
+
+static int mdss_hdmi_panel_clock(uint8_t enable, struct msm_panel_info *pinfo)
+{
+ return target_hdmi_panel_clock(enable, pinfo);
+}
+
+static int mdss_hdmi_enable_power(uint8_t enable)
+{
+ int ret = NO_ERROR;
+
+ ret = target_ldo_ctrl(enable);
+ if (ret) {
+ dprintf(CRITICAL, "LDO control enable failed\n");
+ return ret;
+ }
+
+ ret = target_hdmi_regulator_ctrl(enable);
+ if (ret) {
+ dprintf(CRITICAL, "hdmi regulator control enable failed\n");
+ return ret;
+ }
+
+ dprintf(SPEW, "HDMI Panel power %s done\n", enable ? "on" : "off");
+
+ return ret;
+}
+
+static void mdss_hdmi_set_mode(bool on)
+{
+ uint32_t val = 0;
+
+ if (on) {
+ val = 0x3;
+ writel(val, HDMI_CTRL);
+ } else {
+ writel(val, HDMI_CTRL);
+ }
+}
+
+static void mdss_hdmi_panel_init(struct msm_panel_info *pinfo)
+{
+ struct mdss_hdmi_timing_info tinfo = HDMI_RESOLUTION_DATA;
+
+ if (!pinfo)
+ return;
+
+ pinfo->xres = tinfo.active_h;
+ pinfo->yres = tinfo.active_v;
+ pinfo->bpp = 24;
+ pinfo->type = HDMI_PANEL;
+
+ pinfo->lcdc.h_back_porch = tinfo.back_porch_h;
+ pinfo->lcdc.h_front_porch = tinfo.front_porch_h;
+ pinfo->lcdc.h_pulse_width = tinfo.pulse_width_h;
+ pinfo->lcdc.v_back_porch = tinfo.back_porch_v;
+ pinfo->lcdc.v_front_porch = tinfo.front_porch_v;
+ pinfo->lcdc.v_pulse_width = tinfo.pulse_width_v;
+
+ pinfo->lcdc.hsync_skew = 0;
+ pinfo->lcdc.xres_pad = 0;
+ pinfo->lcdc.yres_pad = 0;
+ pinfo->lcdc.dual_pipe = 0;
+}
+
+void mdss_hdmi_display_init(uint32_t rev, void *base)
+{
+ mdss_hdmi_panel_init(&(panel.panel_info));
+
+ panel.clk_func = mdss_hdmi_panel_clock;
+ panel.power_func = mdss_hdmi_enable_power;
+
+ panel.fb.width = panel.panel_info.xres;
+ panel.fb.height = panel.panel_info.yres;
+ panel.fb.stride = panel.panel_info.xres;
+ panel.fb.bpp = panel.panel_info.bpp;
+ panel.fb.format = FB_FORMAT_RGB888;
+
+ panel.fb.base = base;
+ panel.mdp_rev = rev;
+
+ msm_display_init(&panel);
+}
+
+static int mdss_hdmi_video_setup(void)
+{
+ uint32_t total_v = 0;
+ uint32_t total_h = 0;
+ uint32_t start_h = 0;
+ uint32_t end_h = 0;
+ uint32_t start_v = 0;
+ uint32_t end_v = 0;
+
+ struct mdss_hdmi_timing_info tinfo = HDMI_RESOLUTION_DATA;
+
+ total_h = tinfo.active_h + tinfo.front_porch_h +
+ tinfo.back_porch_h + tinfo.pulse_width_h - 1;
+ total_v = tinfo.active_v + tinfo.front_porch_v +
+ tinfo.back_porch_v + tinfo.pulse_width_v - 1;
+ if (((total_v << 16) & 0xE0000000) || (total_h & 0xFFFFE000)) {
+ dprintf(CRITICAL,
+ "%s: total v=%d or h=%d is larger than supported\n",
+ __func__, total_v, total_h);
+ return ERROR;
+ }
+ writel((total_v << 16) | (total_h << 0), HDMI_TOTAL);
+
+ start_h = tinfo.back_porch_h + tinfo.pulse_width_h;
+ end_h = (total_h + 1) - tinfo.front_porch_h;
+ if (((end_h << 16) & 0xE0000000) || (start_h & 0xFFFFE000)) {
+ dprintf(CRITICAL,
+ "%s: end_h=%d or start_h=%d is larger than supported\n",
+ __func__, end_h, start_h);
+ return ERROR;
+ }
+ writel((end_h << 16) | (start_h << 0), HDMI_ACTIVE_H);
+
+ start_v = tinfo.back_porch_v + tinfo.pulse_width_v - 1;
+ end_v = total_v - tinfo.front_porch_v;
+ if (((end_v << 16) & 0xE0000000) || (start_v & 0xFFFFE000)) {
+ dprintf(CRITICAL,
+ "%s: end_v=%d or start_v=%d is larger than supported\n",
+ __func__, end_v, start_v);
+ return ERROR;
+ }
+ writel((end_v << 16) | (start_v << 0), HDMI_ACTIVE_V);
+
+ if (tinfo.interlaced) {
+ writel((total_v + 1) << 0, HDMI_V_TOTAL_F2);
+ writel(((end_v + 1) << 16) | ((start_v + 1) << 0),
+ HDMI_ACTIVE_V_F2);
+ } else {
+ writel(0, HDMI_V_TOTAL_F2);
+ writel(0, HDMI_ACTIVE_V_F2);
+ }
+
+ writel(((tinfo.interlaced << 31) & 0x80000000) |
+ ((tinfo.active_low_h << 29) & 0x20000000) |
+ ((tinfo.active_low_v << 28) & 0x10000000), HDMI_FRAME_CTRL);
+
+ return 0;
+}
+
+void mdss_hdmi_avi_info_frame(void)
+{
+ uint32_t sum;
+ uint32_t reg_val;
+ uint8_t checksum;
+ uint32_t i;
+
+ sum = IFRAME_PACKET_OFFSET + AVI_IFRAME_TYPE +
+ AVI_IFRAME_VERSION + AVI_MAX_DATA_BYTES;
+
+ for (i = 0; i < AVI_MAX_DATA_BYTES; i++)
+ sum += mdss_hdmi_avi_info_db[i];
+
+ sum &= 0xFF;
+ sum = 256 - sum;
+ checksum = (uint8_t) sum;
+
+ reg_val = checksum |
+ LEFT_SHIFT_BYTE(mdss_hdmi_avi_info_db[DATA_BYTE_1]) |
+ LEFT_SHIFT_WORD(mdss_hdmi_avi_info_db[DATA_BYTE_2]) |
+ LEFT_SHIFT_24BITS(mdss_hdmi_avi_info_db[DATA_BYTE_3]);
+ writel(reg_val, HDMI_AVI_INFO0);
+
+ reg_val = mdss_hdmi_avi_info_db[DATA_BYTE_4] |
+ LEFT_SHIFT_BYTE(mdss_hdmi_avi_info_db[DATA_BYTE_5]) |
+ LEFT_SHIFT_WORD(mdss_hdmi_avi_info_db[DATA_BYTE_6]) |
+ LEFT_SHIFT_24BITS(mdss_hdmi_avi_info_db[DATA_BYTE_7]);
+ writel(reg_val, HDMI_AVI_INFO1);
+
+ reg_val = mdss_hdmi_avi_info_db[DATA_BYTE_8] |
+ LEFT_SHIFT_BYTE(mdss_hdmi_avi_info_db[DATA_BYTE_9]) |
+ LEFT_SHIFT_WORD(mdss_hdmi_avi_info_db[DATA_BYTE_10]) |
+ LEFT_SHIFT_24BITS(mdss_hdmi_avi_info_db[DATA_BYTE_11]);
+ writel(reg_val, HDMI_AVI_INFO2);
+
+ reg_val = mdss_hdmi_avi_info_db[DATA_BYTE_12] |
+ LEFT_SHIFT_BYTE(mdss_hdmi_avi_info_db[DATA_BYTE_13]) |
+ LEFT_SHIFT_24BITS(AVI_IFRAME_VERSION);
+ writel(reg_val, HDMI_AVI_INFO3);
+
+ /* AVI InfFrame enable (every frame) */
+ writel(readl(HDMI_INFOFRAME_CTRL0) | BIT(1) | BIT(0),
+ HDMI_INFOFRAME_CTRL0);
+}
+
+int mdss_hdmi_init(void)
+{
+ uint32_t hotplug_control;
+
+ mdss_hdmi_set_mode(false);
+
+ hdmi_phy_init();
+
+ // Enable USEC REF timer
+ writel(0x0001001B, HDMI_USEC_REFTIMER);
+
+ // Video setup for HDMI
+ mdss_hdmi_video_setup();
+
+ // AVI info setup
+ mdss_hdmi_avi_info_frame();
+
+ // Write 1 to HDMI_CTRL to enable HDMI
+ mdss_hdmi_set_mode(true);
+
+ return 0;
+}