From: Derek Foreman <[email protected]> VTEM EMPs are a type of infoframe that is required for Variable Refresh Rate. Add code to pack them.
Signed-off-by: Derek Foreman <[email protected]> Signed-off-by: Nicolas Frattaroli <[email protected]> --- drivers/video/hdmi.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/hdmi.h | 19 ++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 45b42f14a750..ed9abc7ad228 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -983,6 +983,10 @@ hdmi_infoframe_pack(union hdmi_infoframe *frame, length = hdmi_vendor_any_infoframe_pack(&frame->vendor, buffer, size); break; + case HDMI_INFOFRAME_TYPE_EMP: + length = hdmi_emp_infoframe_vtem_pack(&frame->vtem, + buffer, size); + break; default: WARN(1, "Bad infoframe type %d\n", frame->any.type); length = -EINVAL; @@ -994,9 +998,11 @@ EXPORT_SYMBOL(hdmi_infoframe_pack); static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type) { - if (type < 0x80 || type > 0x9f) + if (type < 0x7F || type > 0x9f) return "Invalid"; switch (type) { + case HDMI_INFOFRAME_TYPE_EMP: + return "EMP"; case HDMI_INFOFRAME_TYPE_VENDOR: return "Vendor"; case HDMI_INFOFRAME_TYPE_AVI: @@ -1516,6 +1522,29 @@ hdmi_vendor_any_infoframe_log(const char *level, } } +static void +hdmi_emp_infoframe_vtem_log(const char *level, + struct device *dev, + const struct hdmi_emp_infoframe_vtem *frame) +{ + hdmi_infoframe_log_header(level, dev, + (const struct hdmi_any_infoframe *)frame); + + hdmi_log(" game vrr enabled: %u\n", frame->game_vrr_en); + hdmi_log(" fva factor minus 1: %u\n", frame->fva_factor_m1); + hdmi_log(" m_const: %u\n", frame->m_const); + hdmi_log(" base vertical front porch: %u\n", frame->base_vfront); + hdmi_log(" base refresh rate: %u\n", frame->base_refresh_rate); +} + +static void +hdmi_emp_infoframe_log(const char *level, + struct device *dev, + const union hdmi_infoframe *frame) +{ + /* Only support VTEM for now */ + hdmi_emp_infoframe_vtem_log(level, dev, &frame->vtem); +} /** * hdmi_infoframe_log() - log info of HDMI infoframe * @level: logging level @@ -1542,6 +1571,9 @@ void hdmi_infoframe_log(const char *level, case HDMI_INFOFRAME_TYPE_DRM: hdmi_drm_infoframe_log(level, dev, &frame->drm); break; + case HDMI_INFOFRAME_TYPE_EMP: + hdmi_emp_infoframe_log(level, dev, frame); + break; } } EXPORT_SYMBOL(hdmi_infoframe_log); @@ -1919,3 +1951,66 @@ int hdmi_infoframe_unpack(union hdmi_infoframe *frame, return ret; } EXPORT_SYMBOL(hdmi_infoframe_unpack); + +/** + * hdmi_emp_infoframe_vtem_init() - initialize an HDMI VTEM EMP + * @emp: HDMI VTEM EMP + */ +void hdmi_emp_infoframe_vtem_init(struct hdmi_emp_infoframe_vtem *emp) +{ + memset(emp, 0, sizeof(*emp)); + emp->type = HDMI_INFOFRAME_TYPE_EMP; +} +EXPORT_SYMBOL(hdmi_emp_infoframe_vtem_init); + +/** + * hdmi_emp_infoframe_vtem_pack() - pack a vtem into a binary buffer + * @vtem: pointer to &struct hdmi_emp_infoframe_vtem to pack + * @buffer: pointer to pre-allocated output buffer + * @size: maximum size in bytes of @buffer + * + * Pack a VTEM infoframe struct into a binary buffer in a way that's suitable + * to be sent out over the wire. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_emp_infoframe_vtem_pack(struct hdmi_emp_infoframe_vtem *vtem, + void *buffer, size_t size) +{ + u8 *ptr = buffer; + + if (size < HDMI_INFOFRAME_SIZE(VTEM)) + return -EINVAL; + + /* 4 bits max */ + if (vtem->fva_factor_m1 > 0xF) + return -EINVAL; + + memset(buffer, 0, size); + + /* Header Data */ + ptr[0] = vtem->type; + ptr[1] = BIT(7) | BIT(6); + ptr[2] = 0; + + ptr[3] = BIT(7) | BIT(2); + + ptr[4] = 0; + ptr[5] = 1; + ptr[6] = 0; + ptr[7] = 1; + ptr[8] = 0; + ptr[9] = 4; + + /* This is where the VTEM part starts */ + ptr[10] = (vtem->fva_factor_m1 << 4) | + (vtem->m_const ? BIT(1) : 0) | + (vtem->game_vrr_en ? BIT(0) : 0); + ptr[11] = vtem->base_vfront; + ptr[12] = vtem->base_refresh_rate >> 8 & 0x3; + ptr[13] = vtem->base_refresh_rate & 0xFF; + + return 14; +} +EXPORT_SYMBOL(hdmi_emp_infoframe_vtem_pack); diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index b80a5ee63bb2..71e2b36896af 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -55,6 +55,7 @@ enum hdmi_packet_type { }; enum hdmi_infoframe_type { + HDMI_INFOFRAME_TYPE_EMP = 0x7F, HDMI_INFOFRAME_TYPE_VENDOR = 0x81, HDMI_INFOFRAME_TYPE_AVI = 0x82, HDMI_INFOFRAME_TYPE_SPD = 0x83, @@ -76,6 +77,7 @@ enum hdmi_infoframe_type { #define HDMI_AUDIO_INFOFRAME_SIZE 10 #define HDMI_DRM_INFOFRAME_SIZE 26 #define HDMI_VENDOR_INFOFRAME_SIZE 4 +#define HDMI_VTEM_INFOFRAME_SIZE 10 /* * HDMI 1.3a table 5-14 states that the largest InfoFrame_length is 27, @@ -235,6 +237,18 @@ struct hdmi_drm_infoframe { u16 max_fall; }; +struct hdmi_emp_infoframe_vtem { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + + bool game_vrr_en; + bool m_const; + unsigned char fva_factor_m1; + unsigned char base_vfront; + u16 base_refresh_rate; +}; + void hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame); ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, size_t size); @@ -249,6 +263,10 @@ ssize_t hdmi_drm_infoframe_pack_only(const struct hdmi_drm_infoframe *frame, int hdmi_drm_infoframe_check(struct hdmi_drm_infoframe *frame); int hdmi_drm_infoframe_unpack_only(struct hdmi_drm_infoframe *frame, const void *buffer, size_t size); +void hdmi_emp_infoframe_vtem_init(struct hdmi_emp_infoframe_vtem *frame); +ssize_t hdmi_emp_infoframe_vtem_pack(struct hdmi_emp_infoframe_vtem *frame, + void *buffer, + size_t size); enum hdmi_spd_sdi { HDMI_SPD_SDI_UNKNOWN, @@ -457,6 +475,7 @@ union hdmi_infoframe { union hdmi_vendor_any_infoframe vendor; struct hdmi_audio_infoframe audio; struct hdmi_drm_infoframe drm; + struct hdmi_emp_infoframe_vtem vtem; }; ssize_t hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, -- 2.55.0
