return -EINVAL;
 }
 
+static int hdmi_generate_avi_infoframe(const struct drm_connector *connector,
+                                      struct drm_connector_state *conn_state)
+{
+       const struct drm_display_mode *mode =
+               connector_state_get_mode(conn_state);
+       struct drm_connector_hdmi_infoframe *infoframe =
+               &conn_state->hdmi.infoframes.avi;
+       struct hdmi_avi_infoframe *frame =
+               &infoframe->data.avi;
+       bool is_limited_range = conn_state->hdmi.is_limited_range;
+       enum hdmi_quantization_range rgb_quant_range =
+               is_limited_range ? HDMI_QUANTIZATION_RANGE_LIMITED : HDMI_QUANTIZATION_RANGE_FULL;
+       int ret;
+
+       ret = drm_hdmi_avi_infoframe_from_display_mode(frame, connector, mode);
+       if (ret)
+               return ret;
+
+       frame->colorspace = conn_state->hdmi.output_format;
+
+       /*
+        * FIXME: drm_hdmi_avi_infoframe_quant_range() doesn't handle
+        * YUV formats at all at the moment, so if we ever support YUV
+        * formats this needs to be revised.
+        */
+       drm_hdmi_avi_infoframe_quant_range(frame, connector, mode, rgb_quant_range);
+       drm_hdmi_avi_infoframe_colorimetry(frame, conn_state);
+       drm_hdmi_avi_infoframe_bars(frame, conn_state);
+
+       infoframe->set = true;
+
+       return 0;
+}
+
+static int hdmi_generate_spd_infoframe(const struct drm_connector *connector,
+                                      struct drm_connector_state *conn_state)
+{
+       struct drm_connector_hdmi_infoframe *infoframe =
+               &conn_state->hdmi.infoframes.spd;
+       struct hdmi_spd_infoframe *frame =
+               &infoframe->data.spd;
+       int ret;
+
+       ret = hdmi_spd_infoframe_init(frame,
+                                     connector->hdmi.vendor,
+                                     connector->hdmi.product);
+       if (ret)
+               return ret;
+
+       frame->sdi = HDMI_SPD_SDI_PC;
+
+       infoframe->set = true;
+
+       return 0;
+}
+
+static int hdmi_generate_hdr_infoframe(const struct drm_connector *connector,
+                                      struct drm_connector_state *conn_state)
+{
+       struct drm_connector_hdmi_infoframe *infoframe =
+               &conn_state->hdmi.infoframes.hdr_drm;
+       struct hdmi_drm_infoframe *frame =
+               &infoframe->data.drm;
+       int ret;
+
+       if (connector->max_bpc < 10)
+               return 0;
+
+       if (!conn_state->hdr_output_metadata)
+               return 0;
+
+       ret = drm_hdmi_infoframe_set_hdr_metadata(frame, conn_state);
+       if (ret)
+               return ret;
+
+       infoframe->set = true;
+
+       return 0;
+}
+
+static int hdmi_generate_hdmi_vendor_infoframe(const struct drm_connector *connector,
+                                              struct drm_connector_state *conn_state)
+{
+       const struct drm_display_info *info = &connector->display_info;
+       const struct drm_display_mode *mode =
+               connector_state_get_mode(conn_state);
+       struct drm_connector_hdmi_infoframe *infoframe =
+               &conn_state->hdmi.infoframes.hdmi;
+       struct hdmi_vendor_infoframe *frame =
+               &infoframe->data.vendor.hdmi;
+       int ret;
+
+       if (!info->has_hdmi_infoframe)
+               return 0;
+
+       ret = drm_hdmi_vendor_infoframe_from_display_mode(frame, connector, mode);
+       if (ret)
+               return ret;
+
+       infoframe->set = true;
+
+       return 0;
+}
+
+static int
+hdmi_generate_infoframes(const struct drm_connector *connector,
+                        struct drm_connector_state *conn_state)
+{
+       const struct drm_display_info *info = &connector->display_info;
+       int ret;
+
+       if (!info->is_hdmi)
+               return 0;
+
+       ret = hdmi_generate_avi_infoframe(connector, conn_state);
+       if (ret)
+               return ret;
+
+       ret = hdmi_generate_spd_infoframe(connector, conn_state);
+       if (ret)
+               return ret;
+
+       /*
+        * Audio Infoframes will be generated by ALSA, and updated by
+        * drm_atomic_helper_connector_hdmi_update_audio_infoframe().
+        */
+
+       ret = hdmi_generate_hdr_infoframe(connector, conn_state);
+       if (ret)
+               return ret;
+
+       ret = hdmi_generate_hdmi_vendor_infoframe(connector, conn_state);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
 /**
  * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
  * @connector: DRM Connector
        if (ret)
                return ret;
 
+       ret = hdmi_generate_infoframes(connector, new_conn_state);
+       if (ret)
+               return ret;
+
        if (old_conn_state->hdmi.broadcast_rgb != new_conn_state->hdmi.broadcast_rgb ||
            old_conn_state->hdmi.output_bpc != new_conn_state->hdmi.output_bpc ||
            old_conn_state->hdmi.output_format != new_conn_state->hdmi.output_format) {
        return 0;
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check);
+
+#define HDMI_MAX_INFOFRAME_SIZE                29
+
+static int clear_device_infoframe(struct drm_connector *connector,
+                                 enum hdmi_infoframe_type type)
+{
+       const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
+       struct drm_device *dev = connector->dev;
+       int ret;
+
+       drm_dbg_kms(dev, "Clearing infoframe type 0x%x\n", type);
+
+       if (!funcs || !funcs->clear_infoframe) {
+               drm_dbg_kms(dev, "Function not implemented, bailing.\n");
+               return 0;
+       }
+
+       ret = funcs->clear_infoframe(connector, type);
+       if (ret) {
+               drm_dbg_kms(dev, "Call failed: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int clear_infoframe(struct drm_connector *connector,
+                          struct drm_connector_hdmi_infoframe *old_frame)
+{
+       int ret;
+
+       ret = clear_device_infoframe(connector, old_frame->data.any.type);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int write_device_infoframe(struct drm_connector *connector,
+                                 union hdmi_infoframe *frame)
+{
+       const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
+       struct drm_device *dev = connector->dev;
+       u8 buffer[HDMI_MAX_INFOFRAME_SIZE];
+       int ret;
+       int len;
+
+       drm_dbg_kms(dev, "Writing infoframe type %x\n", frame->any.type);
+
+       if (!funcs || !funcs->write_infoframe) {
+               drm_dbg_kms(dev, "Function not implemented, bailing.\n");
+               return -EINVAL;
+       }
+
+       len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
+       if (len < 0)
+               return len;
+
+       ret = funcs->write_infoframe(connector, frame->any.type, buffer, len);
+       if (ret) {
+               drm_dbg_kms(dev, "Call failed: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int write_infoframe(struct drm_connector *connector,
+                          struct drm_connector_hdmi_infoframe *new_frame)
+{
+       int ret;
+
+       ret = write_device_infoframe(connector, &new_frame->data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int write_or_clear_infoframe(struct drm_connector *connector,
+                                   struct drm_connector_hdmi_infoframe *old_frame,
+                                   struct drm_connector_hdmi_infoframe *new_frame)
+{
+       if (new_frame->set)
+               return write_infoframe(connector, new_frame);
+
+       if (old_frame->set && !new_frame->set)
+               return clear_infoframe(connector, old_frame);
+
+       return 0;
+}
+
+/**
+ * drm_atomic_helper_connector_hdmi_update_infoframes - Update the Infoframes
+ * @connector: A pointer to the HDMI connector
+ * @state: The HDMI connector state to generate the infoframe from
+ *
+ * This function is meant for HDMI connector drivers to write their
+ * infoframes. It will typically be used in a
+ * @drm_connector_helper_funcs.atomic_enable implementation.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector,
+                                                      struct drm_atomic_state *state)
+{
+       struct drm_connector_state *old_conn_state =
+               drm_atomic_get_old_connector_state(state, connector);
+       struct drm_connector_state *new_conn_state =
+               drm_atomic_get_new_connector_state(state, connector);
+       struct drm_display_info *info = &connector->display_info;
+       int ret;
+
+       if (!info->is_hdmi)
+               return 0;
+
+       mutex_lock(&connector->hdmi.infoframes.lock);
+
+       ret = write_or_clear_infoframe(connector,
+                                      &old_conn_state->hdmi.infoframes.avi,
+                                      &new_conn_state->hdmi.infoframes.avi);
+       if (ret)
+               goto out;
+
+       if (connector->hdmi.infoframes.audio.set) {
+               ret = write_infoframe(connector,
+                                     &connector->hdmi.infoframes.audio);
+               if (ret)
+                       goto out;
+       }
+
+       ret = write_or_clear_infoframe(connector,
+                                      &old_conn_state->hdmi.infoframes.hdr_drm,
+                                      &new_conn_state->hdmi.infoframes.hdr_drm);
+       if (ret)
+               goto out;
+
+       ret = write_or_clear_infoframe(connector,
+                                      &old_conn_state->hdmi.infoframes.spd,
+                                      &new_conn_state->hdmi.infoframes.spd);
+       if (ret)
+               goto out;
+
+       if (info->has_hdmi_infoframe) {
+               ret = write_or_clear_infoframe(connector,
+                                              &old_conn_state->hdmi.infoframes.hdmi,
+                                              &new_conn_state->hdmi.infoframes.hdmi);
+               if (ret)
+                       goto out;
+       }
+
+out:
+       mutex_unlock(&connector->hdmi.infoframes.lock);
+       return ret;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_infoframes);
+
+/**
+ * drm_atomic_helper_connector_hdmi_update_audio_infoframe - Update the Audio Infoframe
+ * @connector: A pointer to the HDMI connector
+ * @frame: A pointer to the audio infoframe to write
+ *
+ * This function is meant for HDMI connector drivers to update their
+ * audio infoframe. It will typically be used in one of the ALSA hooks
+ * (most likely prepare).
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int
+drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector,
+                                                       struct hdmi_audio_infoframe *frame)
+{
+       struct drm_connector_hdmi_infoframe *infoframe =
+               &connector->hdmi.infoframes.audio;
+       struct drm_display_info *info = &connector->display_info;
+       int ret;
+
+       if (!info->is_hdmi)
+               return 0;
+
+       mutex_lock(&connector->hdmi.infoframes.lock);
+
+       memcpy(&infoframe->data, frame, sizeof(infoframe->data));
+       infoframe->set = true;
+
+       ret = write_infoframe(connector, infoframe);
+
+       mutex_unlock(&connector->hdmi.infoframes.lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_audio_infoframe);
 
        INIT_LIST_HEAD(&connector->modes);
        mutex_init(&connector->mutex);
        mutex_init(&connector->edid_override_mutex);
+       mutex_init(&connector->hdmi.infoframes.lock);
        connector->edid_blob_ptr = NULL;
        connector->epoch_counter = 0;
        connector->tile_blob_ptr = NULL;
  * drmm_connector_hdmi_init - Init a preallocated HDMI connector
  * @dev: DRM device
  * @connector: A pointer to the HDMI connector to init
+ * @vendor: HDMI Controller Vendor name
+ * @product: HDMI Controller Product name
  * @funcs: callbacks for this connector
  * @hdmi_funcs: HDMI-related callbacks for this connector
  * @connector_type: user visible type of the connector
  */
 int drmm_connector_hdmi_init(struct drm_device *dev,
                             struct drm_connector *connector,
+                            const char *vendor, const char *product,
                             const struct drm_connector_funcs *funcs,
                             const struct drm_connector_hdmi_funcs *hdmi_funcs,
                             int connector_type,
 {
        int ret;
 
+       if (!vendor || !product)
+               return -EINVAL;
+
+       if ((strlen(vendor) > DRM_CONNECTOR_HDMI_VENDOR_LEN) ||
+           (strlen(product) > DRM_CONNECTOR_HDMI_PRODUCT_LEN))
+               return -EINVAL;
+
        if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA ||
              connector_type == DRM_MODE_CONNECTOR_HDMIB))
                return -EINVAL;
                return ret;
 
        connector->hdmi.supported_formats = supported_formats;
+       strtomem_pad(connector->hdmi.vendor, vendor, 0);
+       strtomem_pad(connector->hdmi.product, product, 0);
 
        /*
         * drm_connector_attach_max_bpc_property() requires the
                connector->funcs->atomic_destroy_state(connector,
                                                       connector->state);
 
+       mutex_destroy(&connector->hdmi.infoframes.lock);
        mutex_destroy(&connector->mutex);
 
        memset(connector, 0, sizeof(*connector));
 
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       connector_type,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       connector_type,
        int ret;
 
        ret = drmm_connector_hdmi_init(&priv->drm, connector,
+                                      "Vendor", "Product",
                                       &dummy_funcs,
                                       &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
 
        unsigned int hue;
 };
 
+/**
+ * struct drm_connector_hdmi_infoframe - HDMI Infoframe container
+ */
+struct drm_connector_hdmi_infoframe {
+       /**
+        * @data: HDMI Infoframe structure
+        */
+       union hdmi_infoframe data;
+
+       /**
+        * @set: Is the content of @data valid?
+        */
+       bool set;
+};
+
 /**
  * struct drm_connector_state - mutable connector state
  */
                enum drm_hdmi_broadcast_rgb broadcast_rgb;
 
                /**
-                * @is_full_range: Is the output supposed to use a full
+                * @infoframes: HDMI Infoframes matching that state
+                */
+               struct {
+                       /**
+                        * @avi: AVI Infoframes structure matching our
+                        * state.
+                        */
+                       struct drm_connector_hdmi_infoframe avi;
+
+                       /**
+                        * @hdr_drm: DRM (Dynamic Range and Mastering)
+                        * Infoframes structure matching our state.
+                        */
+                       struct drm_connector_hdmi_infoframe hdr_drm;
+
+                       /**
+                        * @spd: SPD Infoframes structure matching our
+                        * state.
+                        */
+                       struct drm_connector_hdmi_infoframe spd;
+
+                       /**
+                        * @vendor: HDMI Vendor Infoframes structure
+                        * matching our state.
+                        */
+                       struct drm_connector_hdmi_infoframe hdmi;
+               } infoframes;
+
+               /**
+                * @is_limited_range: Is the output supposed to use a limited
                 * RGB Quantization Range or not?
                 */
                bool is_limited_range;
        (*tmds_char_rate_valid)(const struct drm_connector *connector,
                                const struct drm_display_mode *mode,
                                unsigned long long tmds_rate);
+
+       /**
+        * @clear_infoframe:
+        *
+        * This callback is invoked through
+        * @drm_atomic_helper_connector_hdmi_update_infoframes during a
+        * commit to clear the infoframes into the hardware. It will be
+        * called multiple times, once for every disabled infoframe
+        * type.
+        *
+        * The @clear_infoframe callback is optional.
+        *
+        * Returns:
+        * 0 on success, a negative error code otherwise
+        */
+       int (*clear_infoframe)(struct drm_connector *connector,
+                              enum hdmi_infoframe_type type);
+
+       /**
+        * @write_infoframe:
+        *
+        * This callback is invoked through
+        * @drm_atomic_helper_connector_hdmi_update_infoframes during a
+        * commit to program the infoframes into the hardware. It will
+        * be called multiple times, once for every updated infoframe
+        * type.
+        *
+        * The @write_infoframe callback is mandatory.
+        *
+        * Returns:
+        * 0 on success, a negative error code otherwise
+        */
+       int (*write_infoframe)(struct drm_connector *connector,
+                              enum hdmi_infoframe_type type,
+                              const u8 *buffer, size_t len);
 };
 
 /**
         * @hdmi: HDMI-related variable and properties.
         */
        struct {
+#define DRM_CONNECTOR_HDMI_VENDOR_LEN  8
+               /**
+                * @vendor: HDMI Controller Vendor Name
+                */
+               unsigned char vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] __nonstring;
+
+#define DRM_CONNECTOR_HDMI_PRODUCT_LEN 16
+               /**
+                * @product: HDMI Controller Product Name
+                */
+               unsigned char product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] __nonstring;
+
                /**
                 * @supported_formats: Bitmask of @hdmi_colorspace
                 * supported by the controller.
                 * @funcs: HDMI connector Control Functions
                 */
                const struct drm_connector_hdmi_funcs *funcs;
+
+               /**
+                * @infoframes: Current Infoframes output by the connector
+                */
+               struct {
+                       /**
+                        * @lock: Mutex protecting against concurrent access to
+                        * the infoframes, most notably between KMS and ALSA.
+                        */
+                       struct mutex lock;
+
+                       /**
+                        * @audio: Current Audio Infoframes structure. Protected
+                        * by @lock.
+                        */
+                       struct drm_connector_hdmi_infoframe audio;
+               } infoframes;
        } hdmi;
 };
 
                        struct i2c_adapter *ddc);
 int drmm_connector_hdmi_init(struct drm_device *dev,
                             struct drm_connector *connector,
+                            const char *vendor, const char *product,
                             const struct drm_connector_funcs *funcs,
                             const struct drm_connector_hdmi_funcs *hdmi_funcs,
                             int connector_type,