};
#endif
+struct tegra_dc_hdr {
+ bool enabled;
+ u32 eotf;
+ u32 static_metadata_id;
+ u8 static_metadata[24];
+};
+
struct tegra_dc_win {
u8 idx;
u8 ppflags; /* see TEGRA_WIN_PPFLAG* */
}
EXPORT_SYMBOL(tegra_dc_update_cmu);
+int tegra_dc_set_hdr(struct tegra_dc *dc, struct tegra_dc_hdr *hdr,
+ bool cache_dirty)
+{
+ int ret;
+
+ mutex_lock(&dc->lock);
+
+ if (!dc->enabled) {
+ mutex_unlock(&dc->lock);
+ return 0;
+ }
+ if (cache_dirty) {
+ dc->hdr.eotf = hdr->eotf;
+ dc->hdr.static_metadata_id = hdr->static_metadata_id;
+ memcpy(dc->hdr.static_metadata, hdr->static_metadata,
+ sizeof(dc->hdr.static_metadata));
+ } else if (dc->hdr.enabled == hdr->enabled) {
+ mutex_unlock(&dc->lock);
+ return 0;
+ }
+ dc->hdr.enabled = hdr->enabled;
+ dc->hdr_cache_dirty = true;
+ if (!dc->hdr.enabled)
+ memset(&dc->hdr, 0, sizeof(dc->hdr));
+ ret = _tegra_dc_config_frame_end_intr(dc, true);
+
+ mutex_unlock(&dc->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(tegra_dc_set_hdr);
+
static int _tegra_dc_update_cmu_aligned(struct tegra_dc *dc,
struct tegra_dc_cmu *cmu,
bool force)
} \
} while (0)
+static void _tegra_dc_handle_hdr(struct tegra_dc *dc)
+{
+ mutex_lock(&dc->lock);
+ if (!dc->enabled) {
+ mutex_unlock(&dc->lock);
+ return;
+ }
+
+ tegra_dc_get(dc);
+
+ if (dc->out_ops->set_hdr)
+ dc->out_ops->set_hdr(dc);
+
+ tegra_dc_put(dc);
+ mutex_unlock(&dc->lock);
+
+ return;
+}
+
static void tegra_dc_frame_end(struct work_struct *work)
{
-#ifdef CONFIG_TEGRA_DC_CMU
struct tegra_dc *dc = container_of(work,
struct tegra_dc, frame_end_work);
+#ifdef CONFIG_TEGRA_DC_CMU
u32 val;
u32 i;
tegra_dc_put(dc);
mutex_unlock(&dc->lock);
#endif
+ if (dc->hdr_cache_dirty) {
+ _tegra_dc_handle_hdr(dc);
+ _tegra_dc_config_frame_end_intr(dc, false);
+ dc->hdr_cache_dirty = false;
+ }
+ return;
}
static void tegra_dc_one_shot_worker(struct work_struct *work)
int tegra_dc_update_cmu_aligned(struct tegra_dc *dc, struct tegra_dc_cmu *cmu);
#endif
+int tegra_dc_set_hdr(struct tegra_dc *dc, struct tegra_dc_hdr *hdr,
+ bool cache_dirty);
+
struct device_node *tegra_get_panel_node_out_type_check
(struct tegra_dc *dc, u32 out_type);
/* Set up VRR mode */
void (*vrr_mode)(const struct tegra_dc *dc,
struct fb_videomode *mode);
+ int (*set_hdr)(struct tegra_dc *dc);
};
struct tegra_dc_shift_clk_div {
struct tegra_dc_blend blend;
int n_windows;
+ struct tegra_dc_hdr hdr;
#if defined(CONFIG_TEGRA_DC_CMU)
struct tegra_dc_cmu cmu;
struct notifier_block slgc_notifier;
bool vedid;
u8 *vedid_data;
+ bool hdr_cache_dirty;
};
#endif
u16 dirty_rect[4];
bool dirty_rect_valid;
u8 flags;
+ struct tegra_dc_hdr hdr_data;
+ bool hdr_cache_dirty;
};
static int tegra_dc_ext_set_vblank(struct tegra_dc_ext *ext, bool enable);
trace_sync_wt_ovr_syncpt_upd((data->win[win_num-1]).syncpt_max);
+ if (dc->enabled && !skip_flip)
+ tegra_dc_set_hdr(dc, &data->hdr_data, data->hdr_cache_dirty);
+
if (dc->enabled && !skip_flip) {
dc->blanked = false;
if (dc->out_ops && dc->out_ops->vrr_enable)
struct tegra_dc_ext_flip_user_data *flip_user_data,
int nr_user_data)
{
- /*Add support for reading user data (HDR data for intance) here*/
+ int i = 0;
+
+ for (i = 0; i < nr_user_data; i++) {
+ struct tegra_dc_hdr *hdr;
+ struct tegra_dc_ext_hdr *info;
+ switch (flip_user_data[0].data_type) {
+ case TEGRA_DC_EXT_FLIP_USER_DATA_HDR_DATA:
+ hdr = &data->hdr_data;
+ info = &flip_user_data[i].hdr_info;
+ if (flip_user_data[i].flags &
+ TEGRA_DC_EXT_FLIP_FLAG_HDR_ENABLE)
+ hdr->enabled = true;
+ if (flip_user_data[i].flags &
+ TEGRA_DC_EXT_FLIP_FLAG_HDR_DATA_UPDATED) {
+ data->hdr_cache_dirty = true;
+ hdr->eotf = info->eotf;
+ hdr->static_metadata_id =
+ info->static_metadata_id;
+ memcpy(hdr->static_metadata,
+ info->static_metadata,
+ sizeof(hdr->static_metadata));
+ }
+ break;
+ default:
+ dev_err(&data->ext->dc->ndev->dev,
+ "Invalid FLIP_USER_DATA_TYPE\n");
+ }
+ }
+ return;
}
-
static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
struct tegra_dc_ext_flip_windowattr *win,
int win_num,
HDMI_INFOFRAME_VS_AUDIO,
HDMI_INFOFRAME_LEN_AUDIO,
&to_hdmi(hda->client_data)->audio,
- sizeof(to_hdmi(hda->client_data)->audio));
+ sizeof(to_hdmi(hda->client_data)->audio),
+ false);
}
/* Send infoframe every frame, checksum hw generated */
u32 header_reg, u8 pkt_type,
u8 pkt_vs, u8 pkt_len,
void *reg_payload,
- u32 reg_payload_len)
+ u32 reg_payload_len,
+ bool sw_checksum)
{
struct tegra_dc_sor_data *sor = hdmi->sor;
u32 val;
NV_SOR_HDMI_INFOFRAME_HEADER_LEN(pkt_len);
tegra_sor_writel(sor, header_reg, val);
+ if (sw_checksum) {
+ u8 checksum = pkt_type + pkt_vs + pkt_len;
+
+ for (val = 1; val <= pkt_len; val++)
+ checksum += ((u8 *)reg_payload)[val];
+
+ /* The first byte of the payload must always be the checksum
+ * that we are going to calculate in SW */
+ ((u8 *)reg_payload)[0] = (256 - checksum);
+ }
+
for (val = 0; val < reg_payload_len; val += 4, data_reg++, data++)
tegra_sor_writel(sor, data_reg, *data);
}
HDMI_INFOFRAME_TYPE_AVI,
HDMI_INFOFRAME_VS_AVI,
HDMI_INFOFRAME_LEN_AVI,
- &hdmi->avi, sizeof(hdmi->avi));
+ &hdmi->avi, sizeof(hdmi->avi),
+ false);
/* Send infoframe every frame, checksum hw generated */
tegra_sor_writel(sor, NV_SOR_HDMI_AVI_INFOFRAME_CTRL,
HDMI_INFOFRAME_TYPE_VENDOR,
HDMI_INFOFRAME_VS_VENDOR,
HDMI_INFOFRAME_LEN_VENDOR_LLC,
- &hdmi->vsi, sizeof(hdmi->vsi));
+ &hdmi->vsi, sizeof(hdmi->vsi),
+ false);
/* Send infoframe every frame, checksum hw generated */
tegra_sor_writel(sor, NV_SOR_HDMI_VSI_INFOFRAME_CTRL,
#undef HDMI_INFOFRAME_LEN_VENDOR_LLC
}
+static void tegra_hdmi_hdr_infoframe_update(struct tegra_hdmi *hdmi)
+{
+ struct hdmi_hdr_infoframe *hdr = &hdmi->hdr;
+
+ memset(&hdmi->hdr, 0, sizeof(hdmi->hdr));
+
+ if (tegra_platform_is_linsim())
+ return;
+
+ hdr->eotf = hdmi->dc->hdr.eotf;
+ hdr->static_metadata_id = hdmi->dc->hdr.static_metadata_id;
+
+ /* PB3-14 : Group 1 : Static Metadata*/
+ hdr->display_primaries_x_0_lsb = hdmi->dc->hdr.static_metadata[0];
+ hdr->display_primaries_x_0_msb = hdmi->dc->hdr.static_metadata[1];
+ hdr->display_primaries_y_0_lsb = hdmi->dc->hdr.static_metadata[2];
+ hdr->display_primaries_y_0_msb = hdmi->dc->hdr.static_metadata[3];
+ hdr->display_primaries_x_1_lsb = hdmi->dc->hdr.static_metadata[4];
+ hdr->display_primaries_x_1_msb = hdmi->dc->hdr.static_metadata[5];
+ hdr->display_primaries_y_1_lsb = hdmi->dc->hdr.static_metadata[6];
+ hdr->display_primaries_y_1_msb = hdmi->dc->hdr.static_metadata[7];
+ hdr->display_primaries_x_2_lsb = hdmi->dc->hdr.static_metadata[8];
+ hdr->display_primaries_x_2_msb = hdmi->dc->hdr.static_metadata[9];
+ hdr->display_primaries_y_2_lsb = hdmi->dc->hdr.static_metadata[10];
+ hdr->display_primaries_y_2_msb = hdmi->dc->hdr.static_metadata[11];
+
+ /* PB15-18 : Group 2 : Static Metadata*/
+ hdr->white_point_x_lsb = hdmi->dc->hdr.static_metadata[12];
+ hdr->white_point_x_msb = hdmi->dc->hdr.static_metadata[13];
+ hdr->white_point_y_lsb = hdmi->dc->hdr.static_metadata[14];
+ hdr->white_point_y_msb = hdmi->dc->hdr.static_metadata[15];
+
+ /* PB19-20 : Group 3 : Static Metadata*/
+ hdr->max_display_mastering_luminance_lsb =
+ hdmi->dc->hdr.static_metadata[16];
+ hdr->max_display_mastering_luminance_msb =
+ hdmi->dc->hdr.static_metadata[17];
+
+ /* PB21-22 : Group 4 : Static Metadata*/
+ hdr->min_display_mastering_luminance_lsb =
+ hdmi->dc->hdr.static_metadata[18];
+ hdr->min_display_mastering_luminance_msb =
+ hdmi->dc->hdr.static_metadata[19];
+
+ /* PB23-24 : Group 5 : Static Metadata*/
+ hdr->max_content_light_level_lsb = hdmi->dc->hdr.static_metadata[20];
+ hdr->max_content_light_level_msb = hdmi->dc->hdr.static_metadata[21];
+
+ /* PB25-26 : Group 6 : Static Metadata*/
+ hdr->max_frame_avg_light_level_lsb = hdmi->dc->hdr.static_metadata[22];
+ hdr->min_frame_avg_light_level_msb = hdmi->dc->hdr.static_metadata[23];
+
+ return;
+}
+
+static void tegra_hdmi_hdr_infoframe(struct tegra_hdmi *hdmi)
+{
+ struct tegra_dc_sor_data *sor = hdmi->sor;
+
+ /* disable generic infoframe before configuring */
+ tegra_sor_writel(sor, NV_SOR_HDMI_GENERIC_CTRL, 0);
+
+ tegra_hdmi_hdr_infoframe_update(hdmi);
+
+ tegra_hdmi_infoframe_pkt_write(hdmi, NV_SOR_HDMI_GENERIC_HEADER,
+ HDMI_INFOFRAME_TYPE_HDR,
+ HDMI_INFOFRAME_VS_HDR,
+ HDMI_INFOFRAME_LEN_HDR,
+ &hdmi->hdr, sizeof(hdmi->hdr),
+ true);
+
+ /* Send infoframe every frame, checksum hw generated */
+ tegra_sor_writel(sor, NV_SOR_HDMI_GENERIC_CTRL,
+ NV_SOR_HDMI_GENERIC_CTRL_ENABLE_YES |
+ NV_SOR_HDMI_GENERIC_CTRL_OTHER_DISABLE |
+ NV_SOR_HDMI_GENERIC_CTRL_SINGLE_DISABLE);
+ return;
+}
+
__maybe_unused
static int tegra_hdmi_scdc_read(struct tegra_hdmi *hdmi,
u8 offset_data[][2], u32 n_entries)
msecs_to_jiffies(HDMI_HPD_DEBOUNCE_DELAY_MS));
}
+static int tegra_dc_hdmi_set_hdr(struct tegra_dc *dc)
+{
+ u16 ret = 0;
+ struct tegra_hdmi *hdmi = tegra_dc_get_outdata(dc);
+ ret = tegra_edid_get_ex_hdr_cap(hdmi->edid);
+ if (ret & FB_CAP_HDR)
+ tegra_hdmi_hdr_infoframe(hdmi);
+ return 0;
+}
+
static int tegra_dc_hdmi_ddc_enable(struct tegra_dc *dc)
{
struct tegra_hdmi *hdmi = tegra_dc_get_outdata(dc);
.hpd_state = tegra_dc_hdmi_hpd_state,
.vrr_enable = tegra_dc_hdmi_vrr_enable,
.vrr_mode = tegra_hdmi_update_vrr_mode,
+ .set_hdr = tegra_dc_hdmi_set_hdr,
};
HDMI_INFOFRAME_TYPE_SPD = 0x83,
HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
HDMI_INFOFRAME_TYPE_MPEG_SRC = 0x85,
+ HDMI_INFOFRAME_TYPE_HDR = 0x87,
};
enum {
HDMI_INFOFRAME_VS_SPD = 0x1,
HDMI_INFOFRAME_VS_AUDIO = 0x1,
HDMI_INFOFRAME_VS_MPEG_SRC = 0x1,
+ HDMI_INFOFRAME_VS_HDR = 0x1,
};
/* excluding checksum and header bytes */
HDMI_INFOFRAME_LEN_SPD = 25,
HDMI_INFOFRAME_LEN_AUDIO = 10,
HDMI_INFOFRAME_LEN_MPEG_SRC = 10,
+ HDMI_INFOFRAME_LEN_HDR = 26,
};
enum {
u32 reg_hole2:8;
} __packed;
+struct hdmi_hdr_infoframe {
+ /* PB0 */
+ u32 csum:8; /* checksum */
+
+ /* PB1 */
+ u32 eotf:3; /* The EOTF requested by the source */
+ u32 res1:5; /* reserved */
+
+ /* PB2 */
+ u32 static_metadata_id:3; /* The id of the following static metadata */
+ u32 res2:5; /* reserved */
+
+ /* PB3-14 : Group 1 : Static Metadata*/
+ u32 display_primaries_x_0_lsb:8;
+ u32 display_primaries_x_0_msb:8;
+ u32 display_primaries_y_0_lsb:8;
+ u32 display_primaries_y_0_msb:8;
+ u32 reg_hole1:8;
+ u32 display_primaries_x_1_lsb:8;
+ u32 display_primaries_x_1_msb:8;
+ u32 display_primaries_y_1_lsb:8;
+ u32 display_primaries_y_1_msb:8;
+ u32 display_primaries_x_2_lsb:8;
+ u32 display_primaries_x_2_msb:8;
+ u32 display_primaries_y_2_lsb:8;
+ u32 reg_hole2:8;
+ u32 display_primaries_y_2_msb:8;
+
+ /* PB15-18 : Group 2 : Static Metadata*/
+ u32 white_point_x_lsb:8;
+ u32 white_point_x_msb:8;
+ u32 white_point_y_lsb:8;
+ u32 white_point_y_msb:8;
+
+ /* PB19-20 : Group 3 : Static Metadata*/
+ u32 max_display_mastering_luminance_lsb:8;
+ u32 max_display_mastering_luminance_msb:8;
+
+ u32 reg_hole3:8;
+
+ /* PB21-22 : Group 4 : Static Metadata*/
+ u32 min_display_mastering_luminance_lsb:8;
+ u32 min_display_mastering_luminance_msb:8;
+
+ /* PB23-24 : Group 5 : Static Metadata*/
+ u32 max_content_light_level_lsb:8;
+ u32 max_content_light_level_msb:8;
+
+ /* PB25-26 : Group 6 : Static Metadata*/
+ u32 max_frame_avg_light_level_lsb:8;
+ u32 min_frame_avg_light_level_msb:8;
+
+} __packed;
+
enum {
HDMI_AUDIO_CHANNEL_CNT_STREAM, /* refer to audio stream header */
HDMI_AUDIO_CHANNEL_CNT_2,
struct tegra_hdmi_out *pdata;
struct tegra_dc_sor_data *sor;
struct hdmi_avi_infoframe avi;
+ struct hdmi_hdr_infoframe hdr;
bool enabled;
atomic_t clock_refcount;
u32 header_reg, u8 pkt_type,
u8 pkt_vs, u8 pkt_len,
void *reg_payload,
- u32 reg_payload_len);
+ u32 reg_payload_len,
+ bool sw_checksum);
#endif
#define NV_SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8)
#define NV_SOR_REFCLK_DIV_FRAC(x) (((x) & 0x3) << 6)
#define NV_SOR_AUDIO_CTRL 0xfc
+
+#define NV_SOR_HDMI_GENERIC_CTRL 0xa6
+#define NV_SOR_HDMI_GENERIC_CTRL_AUDIO_ENABLE (1 << 16)
+#define NV_SOR_HDMI_GENERIC_CTRL_AUDIO_DISABLE (0 << 16)
+#define NV_SOR_HDMI_GENERIC_CTRL_HBLANK_ENABLE (1 << 12)
+#define NV_SOR_HDMI_GENERIC_CTRL_HBLANK_DISABLE (0 << 12)
+#define NV_SOR_HDMI_GENERIC_CTRL_SINGLE_ENABLE (1 << 8)
+#define NV_SOR_HDMI_GENERIC_CTRL_SINGLE_DISABLE (0 << 8)
+#define NV_SOR_HDMI_GENERIC_CTRL_OTHER_ENABLE (1 << 4)
+#define NV_SOR_HDMI_GENERIC_CTRL_OTHER_DISABLE (0 << 4)
+#define NV_SOR_HDMI_GENERIC_CTRL_ENABLE_YES (1 << 0)
+#define NV_SOR_HDMI_GENERIC_CTRL_ENABLE_NO (0 << 0)
+#define NV_SOR_HDMI_GENERIC_HEADER 0xa8
+#define NV_SOR_HDMI_GENERIC_SUBPACK0_LOW 0xa9
+#define NV_SOR_HDMI_GENERIC_SUBPACK0_HIGH 0xaa
+#define NV_SOR_HDMI_GENERIC_SUBPACK1_LOW 0xab
+#define NV_SOR_HDMI_GENERIC_SUBPACK1_HIGH 0xac
+#define NV_SOR_HDMI_GENERIC_SUBPACK2_LOW 0xad
+#define NV_SOR_HDMI_GENERIC_SUBPACK2_HIGH 0xae
+#define NV_SOR_HDMI_GENERIC_SUBPACK3_LOW 0xaf
+#define NV_SOR_HDMI_GENERIC_SUBPACK3_HIGH 0xb0
#define NV_SOR_AUDIO_CTRL_AFIFO_FLUSH (1 << 12)
#define NV_SOR_AUDIO_CTRL_SRC_HDA (0x2 << 20)
#define NV_SOR_AUDIO_CTRL_NULL_SAMPLE_EN (1 << 29)
#define TEGRA_DC_EXT_FLIP_HEAD_FLAG_VRR_MODE (1 << 1)
/* Flag to notify attr v2 struct is being used */
#define TEGRA_DC_EXT_FLIP_HEAD_FLAG_V2_ATTR (1 << 2)
-
+/* Flag for HDR_DATA handling */
+#define TEGRA_DC_EXT_FLIP_FLAG_HDR_ENABLE (1 << 0)
+#define TEGRA_DC_EXT_FLIP_FLAG_HDR_DATA_UPDATED (1 << 1)
struct tegra_timespec {
__s32 tv_sec; /* seconds */
__u16 dirty_rect[4]; /* x,y,w,h for partial screen update. 0 ignores */
};
+enum tegra_dc_ext_flip_data_type {
+ TEGRA_DC_EXT_FLIP_USER_DATA_NONE, /* dummy value - do not use */
+ TEGRA_DC_EXT_FLIP_USER_DATA_HDR_DATA,
+};
+
+/*
+ * Static Metadata for HDR
+ * This lets us specify which HDR static metadata to specify in the infoframe.
+ * Please see CEA 861.3 for more information.
+ */
+struct tegra_dc_ext_hdr {
+ __u8 eotf;
+ __u8 static_metadata_id;
+ __u8 static_metadata[24];
+};
+
/* size of the this sturct is 32 bytes */
struct tegra_dc_ext_flip_user_data {
__u8 data_type;
union { /* data to be packed into 26 bytes */
__u8 data8[26];
__u16 data16[13];
- /*Add HDR data struct here*/
+ struct tegra_dc_ext_hdr hdr_info;
};
};