blob: 3823d3be27f25f12535d7836d506c6a06b368769 [file] [log] [blame]
/* Copyright (c) 2010-2016, 2018, 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 <linux/bitops.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/types.h>
#include "video/msm_hdmi_modes.h"
#include "mdss_debug.h"
#include "mdss_fb.h"
#include "mdss.h"
#include "mdss_hdmi_util.h"
#include "mdss_panel.h"
#include "mdss_hdmi_panel.h"
#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000
#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200
#define HDMI_TX_KHZ_TO_HZ 1000U
/* AVI INFOFRAME DATA */
#define NUM_MODES_AVI 20
#define AVI_MAX_DATA_BYTES 13
/* Line numbers at which AVI Infoframe and Vendor Infoframe will be sent */
#define AVI_IFRAME_LINE_NUMBER 1
#define VENDOR_IFRAME_LINE_NUMBER 3
#define IFRAME_CHECKSUM_32(d) \
((d & 0xff) + ((d >> 8) & 0xff) + \
((d >> 16) & 0xff) + ((d >> 24) & 0xff))
#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)
/* AVI Infoframe data byte 3, bit 7 (msb) represents ITC bit */
#define SET_ITC_BIT(byte) (byte = (byte | BIT(7)))
#define CLR_ITC_BIT(byte) (byte = (byte & ~BIT(7)))
/*
* CN represents IT content type, if ITC bit in infoframe data byte 3
* is set, CN bits will represent content type as below:
* 0b00 Graphics
* 0b01 Photo
* 0b10 Cinema
* 0b11 Game
*/
#define CONFIG_CN_BITS(bits, byte) \
(byte = (byte & ~(BIT(4) | BIT(5))) |\
((bits & (BIT(0) | BIT(1))) << 4))
struct hdmi_avi_iframe_bar_info {
bool vert_binfo_present;
bool horz_binfo_present;
u32 end_of_top_bar;
u32 start_of_bottom_bar;
u32 end_of_left_bar;
u32 start_of_right_bar;
};
struct hdmi_avi_infoframe_config {
u32 pixel_format;
u32 scan_info;
bool act_fmt_info_present;
u32 colorimetry_info;
u32 ext_colorimetry_info;
u32 rgb_quantization_range;
u32 yuv_quantization_range;
u32 scaling_info;
bool is_it_content;
u8 content_type;
u8 pixel_rpt_factor;
struct hdmi_avi_iframe_bar_info bar_info;
};
struct hdmi_video_config {
struct msm_hdmi_mode_timing_info *timing;
struct hdmi_avi_infoframe_config avi_iframe;
};
struct hdmi_panel {
struct mdss_io_data *io;
struct hdmi_util_ds_data *ds_data;
struct hdmi_panel_data *data;
struct hdmi_video_config vid_cfg;
struct hdmi_tx_ddc_ctrl *ddc;
u32 version;
u32 vic;
u8 *spd_vendor_name;
u8 *spd_product_description;
bool on;
bool scrambler_enabled;
};
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,
};
enum hdmi_quantization_range {
HDMI_QUANTIZATION_DEFAULT,
HDMI_QUANTIZATION_LIMITED_RANGE,
HDMI_QUANTIZATION_FULL_RANGE
};
enum hdmi_scaling_info {
HDMI_SCALING_NONE,
HDMI_SCALING_HORZ,
HDMI_SCALING_VERT,
HDMI_SCALING_HORZ_VERT,
};
static int hdmi_panel_get_vic(struct mdss_panel_info *pinfo,
struct hdmi_util_ds_data *ds_data)
{
int new_vic = -1;
u32 h_total, v_total;
struct msm_hdmi_mode_timing_info timing;
if (!pinfo) {
pr_err("invalid panel data\n");
return -EINVAL;
}
if (pinfo->vic) {
struct msm_hdmi_mode_timing_info info = {0};
u32 ret = hdmi_get_supported_mode(&info, ds_data, pinfo->vic);
u32 supported = info.supported;
if (!ret && supported) {
new_vic = pinfo->vic;
} else {
pr_err("invalid or not supported vic %d\n",
pinfo->vic);
return -EPERM;
}
} else {
timing.active_h = pinfo->xres;
timing.back_porch_h = pinfo->lcdc.h_back_porch;
timing.front_porch_h = pinfo->lcdc.h_front_porch;
timing.pulse_width_h = pinfo->lcdc.h_pulse_width;
h_total = timing.active_h + timing.back_porch_h +
timing.front_porch_h + timing.pulse_width_h;
pr_debug("ah=%d bph=%d fph=%d pwh=%d ht=%d\n",
timing.active_h, timing.back_porch_h,
timing.front_porch_h, timing.pulse_width_h,
h_total);
timing.active_v = pinfo->yres;
timing.back_porch_v = pinfo->lcdc.v_back_porch;
timing.front_porch_v = pinfo->lcdc.v_front_porch;
timing.pulse_width_v = pinfo->lcdc.v_pulse_width;
v_total = timing.active_v + timing.back_porch_v +
timing.front_porch_v + timing.pulse_width_v;
pr_debug("av=%d bpv=%d fpv=%d pwv=%d vt=%d\n",
timing.active_v, timing.back_porch_v,
timing.front_porch_v, timing.pulse_width_v, v_total);
timing.pixel_freq = ((unsigned long int)pinfo->clk_rate / 1000);
if (h_total && v_total) {
timing.refresh_rate = ((timing.pixel_freq * 1000) /
(h_total * v_total)) * 1000;
} else {
pr_err("cannot cal refresh rate\n");
return -EPERM;
}
pr_debug("pixel_freq=%d refresh_rate=%d\n",
timing.pixel_freq, timing.refresh_rate);
new_vic = hdmi_get_video_id_code(&timing, ds_data);
}
return new_vic;
}
static void hdmi_panel_update_dfps_data(struct hdmi_panel *panel)
{
struct mdss_panel_info *pinfo = panel->data->pinfo;
pinfo->saved_total = mdss_panel_get_htotal(pinfo, true);
pinfo->saved_fporch = panel->vid_cfg.timing->front_porch_h;
pinfo->current_fps = panel->vid_cfg.timing->refresh_rate;
pinfo->default_fps = panel->vid_cfg.timing->refresh_rate;
pinfo->lcdc.frame_rate = panel->vid_cfg.timing->refresh_rate;
}
static int hdmi_panel_config_avi(struct hdmi_panel *panel)
{
struct mdss_panel_info *pinfo = panel->data->pinfo;
struct hdmi_video_config *vid_cfg = &panel->vid_cfg;
struct hdmi_avi_infoframe_config *avi = &vid_cfg->avi_iframe;
struct msm_hdmi_mode_timing_info *timing;
u32 ret = 0;
timing = panel->vid_cfg.timing;
if (!timing) {
pr_err("fmt not supported: %d\n", panel->vic);
ret = -EPERM;
goto end;
}
/* Setup AVI Infoframe content */
avi->pixel_format = pinfo->out_format;
avi->is_it_content = panel->data->is_it_content;
avi->content_type = panel->data->content_type;
avi->scan_info = panel->data->scan_info;
avi->bar_info.end_of_top_bar = 0x0;
avi->bar_info.start_of_bottom_bar = timing->active_v + 1;
avi->bar_info.end_of_left_bar = 0;
avi->bar_info.start_of_right_bar = timing->active_h + 1;
avi->act_fmt_info_present = true;
avi->rgb_quantization_range = HDMI_QUANTIZATION_DEFAULT;
avi->yuv_quantization_range = HDMI_QUANTIZATION_DEFAULT;
avi->scaling_info = HDMI_SCALING_NONE;
avi->colorimetry_info = 0;
avi->ext_colorimetry_info = 0;
avi->pixel_rpt_factor = 0;
end:
return ret;
}
static int hdmi_panel_setup_video(struct hdmi_panel *panel)
{
u32 total_h, start_h, end_h;
u32 total_v, start_v, end_v;
u32 div = 0;
struct mdss_io_data *io = panel->io;
struct msm_hdmi_mode_timing_info *timing;
timing = panel->vid_cfg.timing;
if (!timing) {
pr_err("fmt not supported: %d\n", panel->vic);
return -EPERM;
}
/* reduce horizontal params by half for YUV420 output */
if (panel->vid_cfg.avi_iframe.pixel_format == MDP_Y_CBCR_H2V2)
div = 1;
total_h = (hdmi_tx_get_h_total(timing) >> div) - 1;
total_v = hdmi_tx_get_v_total(timing) - 1;
if (((total_v << 16) & 0xE0000000) || (total_h & 0xFFFFE000)) {
pr_err("total v=%d or h=%d is larger than supported\n",
total_v, total_h);
return -EPERM;
}
DSS_REG_W(io, HDMI_TOTAL, (total_v << 16) | (total_h << 0));
start_h = (timing->back_porch_h >> div) +
(timing->pulse_width_h >> div);
end_h = (total_h + 1) - (timing->front_porch_h >> div);
if (((end_h << 16) & 0xE0000000) || (start_h & 0xFFFFE000)) {
pr_err("end_h=%d or start_h=%d is larger than supported\n",
end_h, start_h);
return -EPERM;
}
DSS_REG_W(io, HDMI_ACTIVE_H, (end_h << 16) | (start_h << 0));
start_v = timing->back_porch_v + timing->pulse_width_v - 1;
end_v = total_v - timing->front_porch_v;
if (((end_v << 16) & 0xE0000000) || (start_v & 0xFFFFE000)) {
pr_err("end_v=%d or start_v=%d is larger than supported\n",
end_v, start_v);
return -EPERM;
}
DSS_REG_W(io, HDMI_ACTIVE_V, (end_v << 16) | (start_v << 0));
if (timing->interlaced) {
DSS_REG_W(io, HDMI_V_TOTAL_F2, (total_v + 1) << 0);
DSS_REG_W(io, HDMI_ACTIVE_V_F2,
((end_v + 1) << 16) | ((start_v + 1) << 0));
} else {
DSS_REG_W(io, HDMI_V_TOTAL_F2, 0);
DSS_REG_W(io, HDMI_ACTIVE_V_F2, 0);
}
DSS_REG_W(io, HDMI_FRAME_CTRL,
((timing->interlaced << 31) & 0x80000000) |
((timing->active_low_h << 29) & 0x20000000) |
((timing->active_low_v << 28) & 0x10000000));
hdmi_panel_update_dfps_data(panel);
return 0;
}
static void hdmi_panel_set_avi_infoframe(struct hdmi_panel *panel)
{
int i;
u8 avi_iframe[AVI_MAX_DATA_BYTES] = {0};
u8 checksum;
u32 sum, reg_val;
struct mdss_io_data *io = panel->io;
struct hdmi_avi_infoframe_config *avi;
struct msm_hdmi_mode_timing_info *timing;
avi = &panel->vid_cfg.avi_iframe;
timing = panel->vid_cfg.timing;
/*
* BYTE - 1:
* 0:1 - Scan Information
* 2:3 - Bar Info
* 4 - Active Format Info present
* 5:6 - Pixel format type;
* 7 - Reserved;
*/
avi_iframe[0] = (avi->scan_info & 0x3) |
(avi->bar_info.vert_binfo_present ? BIT(2) : 0) |
(avi->bar_info.horz_binfo_present ? BIT(3) : 0) |
(avi->act_fmt_info_present ? BIT(4) : 0);
if (avi->pixel_format == MDP_Y_CBCR_H2V2)
avi_iframe[0] |= (0x3 << 5);
else if (avi->pixel_format == MDP_Y_CBCR_H2V1)
avi_iframe[0] |= (0x1 << 5);
else if (avi->pixel_format == MDP_Y_CBCR_H1V1)
avi_iframe[0] |= (0x2 << 5);
/*
* BYTE - 2:
* 0:3 - Active format info
* 4:5 - Picture aspect ratio
* 6:7 - Colorimetry info
*/
avi_iframe[1] |= 0x08;
if (timing->ar == HDMI_RES_AR_4_3)
avi_iframe[1] |= (0x1 << 4);
else if (timing->ar == HDMI_RES_AR_16_9)
avi_iframe[1] |= (0x2 << 4);
avi_iframe[1] |= (avi->colorimetry_info & 0x3) << 6;
/*
* BYTE - 3:
* 0:1 - Scaling info
* 2:3 - Quantization range
* 4:6 - Extended Colorimetry
* 7 - IT content
*/
avi_iframe[2] |= (avi->scaling_info & 0x3) |
((avi->rgb_quantization_range & 0x3) << 2) |
((avi->ext_colorimetry_info & 0x7) << 4) |
((avi->is_it_content ? 0x1 : 0x0) << 7);
/*
* BYTE - 4:
* 0:7 - VIC
*/
if (timing->video_format < HDMI_VFRMT_END)
avi_iframe[3] = timing->video_format;
/*
* BYTE - 5:
* 0:3 - Pixel Repeat factor
* 4:5 - Content type
* 6:7 - YCC Quantization range
*/
avi_iframe[4] = (avi->pixel_rpt_factor & 0xF) |
((avi->content_type & 0x3) << 4) |
((avi->yuv_quantization_range & 0x3) << 6);
/* BYTE - 6,7: End of top bar */
avi_iframe[5] = avi->bar_info.end_of_top_bar & 0xFF;
avi_iframe[6] = ((avi->bar_info.end_of_top_bar & 0xFF00) >> 8);
/* BYTE - 8,9: Start of bottom bar */
avi_iframe[7] = avi->bar_info.start_of_bottom_bar & 0xFF;
avi_iframe[8] = ((avi->bar_info.start_of_bottom_bar & 0xFF00) >>
8);
/* BYTE - 10,11: Endof of left bar */
avi_iframe[9] = avi->bar_info.end_of_left_bar & 0xFF;
avi_iframe[10] = ((avi->bar_info.end_of_left_bar & 0xFF00) >> 8);
/* BYTE - 12,13: Start of right bar */
avi_iframe[11] = avi->bar_info.start_of_right_bar & 0xFF;
avi_iframe[12] = ((avi->bar_info.start_of_right_bar & 0xFF00) >>
8);
sum = IFRAME_PACKET_OFFSET + AVI_IFRAME_TYPE +
AVI_IFRAME_VERSION + AVI_MAX_DATA_BYTES;
for (i = 0; i < AVI_MAX_DATA_BYTES; i++)
sum += avi_iframe[i];
sum &= 0xFF;
sum = 256 - sum;
checksum = (u8) sum;
reg_val = checksum |
LEFT_SHIFT_BYTE(avi_iframe[DATA_BYTE_1]) |
LEFT_SHIFT_WORD(avi_iframe[DATA_BYTE_2]) |
LEFT_SHIFT_24BITS(avi_iframe[DATA_BYTE_3]);
DSS_REG_W(io, HDMI_AVI_INFO0, reg_val);
reg_val = avi_iframe[DATA_BYTE_4] |
LEFT_SHIFT_BYTE(avi_iframe[DATA_BYTE_5]) |
LEFT_SHIFT_WORD(avi_iframe[DATA_BYTE_6]) |
LEFT_SHIFT_24BITS(avi_iframe[DATA_BYTE_7]);
DSS_REG_W(io, HDMI_AVI_INFO1, reg_val);
reg_val = avi_iframe[DATA_BYTE_8] |
LEFT_SHIFT_BYTE(avi_iframe[DATA_BYTE_9]) |
LEFT_SHIFT_WORD(avi_iframe[DATA_BYTE_10]) |
LEFT_SHIFT_24BITS(avi_iframe[DATA_BYTE_11]);
DSS_REG_W(io, HDMI_AVI_INFO2, reg_val);
reg_val = avi_iframe[DATA_BYTE_12] |
LEFT_SHIFT_BYTE(avi_iframe[DATA_BYTE_13]) |
LEFT_SHIFT_24BITS(AVI_IFRAME_VERSION);
DSS_REG_W(io, HDMI_AVI_INFO3, reg_val);
/* AVI InfFrame enable (every frame) */
DSS_REG_W(io, HDMI_INFOFRAME_CTRL0,
DSS_REG_R(io, HDMI_INFOFRAME_CTRL0) | BIT(1) | BIT(0));
reg_val = DSS_REG_R(io, HDMI_INFOFRAME_CTRL1);
reg_val &= ~0x3F;
reg_val |= AVI_IFRAME_LINE_NUMBER;
DSS_REG_W(io, HDMI_INFOFRAME_CTRL1, reg_val);
}
static void hdmi_panel_set_vendor_specific_infoframe(void *input)
{
int i;
u8 vs_iframe[9]; /* two header + length + 6 data */
u32 sum, reg_val;
u32 hdmi_vic, hdmi_video_format, s3d_struct = 0;
struct hdmi_panel *panel = input;
struct mdss_io_data *io = panel->io;
/* HDMI Spec 1.4a Table 8-10 */
vs_iframe[0] = 0x81; /* type */
vs_iframe[1] = 0x1; /* version */
vs_iframe[2] = 0x8; /* length */
vs_iframe[3] = 0x0; /* PB0: checksum */
/* PB1..PB3: 24 Bit IEEE Registration Code 00_0C_03 */
vs_iframe[4] = 0x03;
vs_iframe[5] = 0x0C;
vs_iframe[6] = 0x00;
if ((panel->data->s3d_mode != HDMI_S3D_NONE) &&
panel->data->s3d_support) {
switch (panel->data->s3d_mode) {
case HDMI_S3D_SIDE_BY_SIDE:
s3d_struct = 0x8;
break;
case HDMI_S3D_TOP_AND_BOTTOM:
s3d_struct = 0x6;
break;
default:
s3d_struct = 0;
}
hdmi_video_format = 0x2;
hdmi_vic = 0;
/* PB5: 3D_Structure[7:4], Reserved[3:0] */
vs_iframe[8] = s3d_struct << 4;
} else {
hdmi_video_format = 0x1;
switch (panel->vic) {
case HDMI_EVFRMT_3840x2160p30_16_9:
hdmi_vic = 0x1;
break;
case HDMI_EVFRMT_3840x2160p25_16_9:
hdmi_vic = 0x2;
break;
case HDMI_EVFRMT_3840x2160p24_16_9:
hdmi_vic = 0x3;
break;
case HDMI_EVFRMT_4096x2160p24_16_9:
hdmi_vic = 0x4;
break;
default:
hdmi_video_format = 0x0;
hdmi_vic = 0x0;
}
/* PB5: HDMI_VIC */
vs_iframe[8] = hdmi_vic;
}
/* PB4: HDMI Video Format[7:5], Reserved[4:0] */
vs_iframe[7] = (hdmi_video_format << 5) & 0xE0;
/* compute checksum */
sum = 0;
for (i = 0; i < 9; i++)
sum += vs_iframe[i];
sum &= 0xFF;
sum = 256 - sum;
vs_iframe[3] = (u8)sum;
reg_val = (s3d_struct << 24) | (hdmi_vic << 16) |
(vs_iframe[3] << 8) | (hdmi_video_format << 5) |
vs_iframe[2];
DSS_REG_W(io, HDMI_VENSPEC_INFO0, reg_val);
/* vendor specific info-frame enable (every frame) */
DSS_REG_W(io, HDMI_INFOFRAME_CTRL0,
DSS_REG_R(io, HDMI_INFOFRAME_CTRL0) | BIT(13) | BIT(12));
reg_val = DSS_REG_R(io, HDMI_INFOFRAME_CTRL1);
reg_val &= ~0x3F000000;
reg_val |= (VENDOR_IFRAME_LINE_NUMBER << 24);
DSS_REG_W(io, HDMI_INFOFRAME_CTRL1, reg_val);
}
static void hdmi_panel_set_spd_infoframe(struct hdmi_panel *panel)
{
u32 packet_header = 0;
u32 check_sum = 0;
u32 packet_payload = 0;
u32 packet_control = 0;
u8 *vendor_name = NULL;
u8 *product_description = NULL;
struct mdss_io_data *io = panel->io;
vendor_name = panel->spd_vendor_name;
product_description = panel->spd_product_description;
/* 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);
DSS_REG_W(io, HDMI_GENERIC1_HDR, packet_header);
check_sum += IFRAME_CHECKSUM_32(packet_header);
packet_payload = (vendor_name[3] & 0x7f)
| ((vendor_name[4] & 0x7f) << 8)
| ((vendor_name[5] & 0x7f) << 16)
| ((vendor_name[6] & 0x7f) << 24);
DSS_REG_W(io, HDMI_GENERIC1_1, packet_payload);
check_sum += IFRAME_CHECKSUM_32(packet_payload);
/* Product Description (7-bit ASCII code) */
packet_payload = (vendor_name[7] & 0x7f)
| ((product_description[0] & 0x7f) << 8)
| ((product_description[1] & 0x7f) << 16)
| ((product_description[2] & 0x7f) << 24);
DSS_REG_W(io, HDMI_GENERIC1_2, packet_payload);
check_sum += IFRAME_CHECKSUM_32(packet_payload);
packet_payload = (product_description[3] & 0x7f)
| ((product_description[4] & 0x7f) << 8)
| ((product_description[5] & 0x7f) << 16)
| ((product_description[6] & 0x7f) << 24);
DSS_REG_W(io, HDMI_GENERIC1_3, packet_payload);
check_sum += IFRAME_CHECKSUM_32(packet_payload);
packet_payload = (product_description[7] & 0x7f)
| ((product_description[8] & 0x7f) << 8)
| ((product_description[9] & 0x7f) << 16)
| ((product_description[10] & 0x7f) << 24);
DSS_REG_W(io, HDMI_GENERIC1_4, packet_payload);
check_sum += IFRAME_CHECKSUM_32(packet_payload);
packet_payload = (product_description[11] & 0x7f)
| ((product_description[12] & 0x7f) << 8)
| ((product_description[13] & 0x7f) << 16)
| ((product_description[14] & 0x7f) << 24);
DSS_REG_W(io, HDMI_GENERIC1_5, packet_payload);
check_sum += IFRAME_CHECKSUM_32(packet_payload);
/*
* 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;
DSS_REG_W(io, HDMI_GENERIC1_6, packet_payload);
check_sum += IFRAME_CHECKSUM_32(packet_payload);
/* Vendor Name (7bit ASCII code) */
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);
DSS_REG_W(io, HDMI_GENERIC1_0, 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 = DSS_REG_R_ND(io, HDMI_GEN_PKT_CTRL);
packet_control |= ((0x1 << 24) | (1 << 5) | (1 << 4));
DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control);
}
static int hdmi_panel_setup_infoframe(struct hdmi_panel *panel)
{
int rc = 0;
if (!panel) {
pr_err("invalid input\n");
rc = -EINVAL;
goto end;
}
if (panel->data->infoframe) {
hdmi_panel_set_avi_infoframe(panel);
hdmi_panel_set_vendor_specific_infoframe(panel);
hdmi_panel_set_spd_infoframe(panel);
}
end:
return rc;
}
static int hdmi_panel_setup_scrambler(struct hdmi_panel *panel)
{
int rc = 0;
int timeout_hsync;
u32 reg_val = 0;
u32 tmds_clock_ratio = 0;
bool scrambler_on = false;
struct msm_hdmi_mode_timing_info *timing = NULL;
if (!panel) {
pr_err("invalid input\n");
return -EINVAL;
}
timing = panel->vid_cfg.timing;
if (!timing) {
pr_err("Invalid timing info\n");
return -EINVAL;
}
/* Scrambling is supported from HDMI TX 4.0 */
if (panel->version < HDMI_TX_SCRAMBLER_MIN_TX_VERSION) {
pr_debug("scrambling not supported by tx\n");
return 0;
}
if (timing->pixel_freq > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
scrambler_on = true;
tmds_clock_ratio = 1;
} else {
scrambler_on = panel->data->scrambler;
}
pr_debug("scrambler %s\n", scrambler_on ? "on" : "off");
if (scrambler_on) {
rc = hdmi_scdc_write(panel->ddc,
HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE,
tmds_clock_ratio);
if (rc) {
pr_err("TMDS CLK RATIO ERR\n");
return rc;
}
reg_val = DSS_REG_R(panel->io, HDMI_CTRL);
reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */
reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */
DSS_REG_W(panel->io, HDMI_CTRL, reg_val);
rc = hdmi_scdc_write(panel->ddc,
HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x1);
if (!rc) {
panel->scrambler_enabled = true;
} else {
pr_err("failed to enable scrambling\n");
return rc;
}
/*
* Setup hardware to periodically check for scrambler
* status bit on the sink. Sink should set this bit
* with in 200ms after scrambler is enabled.
*/
timeout_hsync = hdmi_utils_get_timeout_in_hysnc(
panel->vid_cfg.timing,
HDMI_TX_SCRAMBLER_TIMEOUT_MSEC);
if (timeout_hsync <= 0) {
pr_err("err in timeout hsync calc\n");
timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
}
pr_debug("timeout for scrambling en: %d hsyncs\n",
timeout_hsync);
rc = hdmi_setup_ddc_timers(panel->ddc,
HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, timeout_hsync);
} else {
hdmi_scdc_write(panel->ddc,
HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0);
panel->scrambler_enabled = false;
}
return rc;
}
static int hdmi_panel_update_fps(void *input, u32 fps)
{
struct hdmi_panel *panel = input;
struct mdss_panel_info *pinfo = panel->data->pinfo;
struct msm_hdmi_mode_timing_info *timing = panel->vid_cfg.timing;
u64 pclk;
int vic;
timing->back_porch_h = pinfo->lcdc.h_back_porch;
timing->front_porch_h = pinfo->lcdc.h_front_porch;
timing->pulse_width_h = pinfo->lcdc.h_pulse_width;
timing->back_porch_v = pinfo->lcdc.v_back_porch;
timing->front_porch_v = pinfo->lcdc.v_front_porch;
timing->pulse_width_v = pinfo->lcdc.v_pulse_width;
timing->refresh_rate = fps;
pclk = pinfo->clk_rate;
do_div(pclk, HDMI_TX_KHZ_TO_HZ);
timing->pixel_freq = (unsigned long) pclk;
if (hdmi_panel_setup_video(panel)) {
DEV_DBG("%s: no change in video timing\n", __func__);
goto end;
}
vic = hdmi_get_video_id_code(timing, panel->ds_data);
if (vic > 0 && panel->vic != vic) {
panel->vic = vic;
DEV_DBG("%s: switched to new resolution id %d\n",
__func__, vic);
}
pinfo->dynamic_fps = false;
end:
return panel->vic;
}
static int hdmi_panel_power_on(void *input)
{
int rc = 0;
bool res_changed = false;
struct hdmi_panel *panel = input;
struct mdss_panel_info *pinfo;
struct msm_hdmi_mode_timing_info *info;
if (!panel) {
pr_err("invalid input\n");
rc = -EINVAL;
goto err;
}
pinfo = panel->data->pinfo;
if (!pinfo) {
pr_err("invalid panel data\n");
rc = -EINVAL;
goto err;
}
if (panel->vic != panel->data->vic) {
res_changed = true;
pr_debug("switching from %d => %d\n",
panel->vic, panel->data->vic);
panel->vic = panel->data->vic;
}
if (pinfo->cont_splash_enabled) {
pinfo->cont_splash_enabled = false;
if (!res_changed) {
panel->on = true;
hdmi_panel_set_vendor_specific_infoframe(panel);
hdmi_panel_set_spd_infoframe(panel);
pr_debug("handoff done\n");
goto end;
}
}
rc = hdmi_panel_config_avi(panel);
if (rc) {
pr_err("avi config failed. rc=%d\n", rc);
goto err;
}
rc = hdmi_panel_setup_video(panel);
if (rc) {
pr_err("video setup failed. rc=%d\n", rc);
goto err;
}
rc = hdmi_panel_setup_infoframe(panel);
if (rc) {
pr_err("infoframe setup failed. rc=%d\n", rc);
goto err;
}
rc = hdmi_panel_setup_scrambler(panel);
if (rc) {
pr_err("scrambler setup failed. rc=%d\n", rc);
goto err;
}
end:
panel->on = true;
info = panel->vid_cfg.timing;
pr_debug("%dx%d%s@%dHz %dMHz %s (%d)\n",
info->active_h, info->active_v,
info->interlaced ? "i" : "p",
info->refresh_rate / 1000,
info->pixel_freq / 1000,
pinfo->out_format == MDP_Y_CBCR_H2V2 ? "Y420" : "RGB",
info->video_format);
err:
return rc;
}
static int hdmi_panel_power_off(void *input)
{
struct hdmi_panel *panel = input;
panel->on = false;
pr_debug("panel off\n");
return 0;
}
void *hdmi_panel_init(struct hdmi_panel_init_data *data)
{
struct hdmi_panel *panel = NULL;
if (!data) {
pr_err("invalid input\n");
goto end;
}
panel = kzalloc(sizeof(*panel), GFP_KERNEL);
if (!panel)
goto end;
panel->io = data->io;
panel->ds_data = data->ds_data;
panel->data = data->panel_data;
panel->spd_vendor_name = data->spd_vendor_name;
panel->spd_product_description = data->spd_product_description;
panel->version = data->version;
panel->ddc = data->ddc;
panel->vid_cfg.timing = data->timing;
if (data->ops) {
data->ops->on = hdmi_panel_power_on;
data->ops->off = hdmi_panel_power_off;
data->ops->get_vic = hdmi_panel_get_vic;
data->ops->vendor = hdmi_panel_set_vendor_specific_infoframe;
data->ops->update_fps = hdmi_panel_update_fps;
}
end:
return panel;
}
void hdmi_panel_deinit(void *input)
{
struct hdmi_panel *panel = input;
kfree(panel);
}