/** * DOC: overview * * The DRM bridge connector helper object provides a DRM connector * implementation that wraps a chain of &struct drm_bridge. The connector * operations are fully implemented based on the operations of the bridges in * the chain, and don't require any intervention from the display controller * driver at runtime. * * To use the helper, display controller drivers create a bridge connector with * a call to drm_bridge_connector_init(). This associates the newly created * connector with the chain of bridges passed to the function and registers it * with the DRM device. At that point the connector becomes fully usable, no * further operation is needed. * * The DRM bridge connector operations are implemented based on the operations * provided by the bridges in the chain. Each connector operation is delegated * to the bridge closest to the connector (at the end of the chain) that * provides the relevant functionality. * * To make use of this helper, all bridges in the chain shall report bridge * operation flags (&drm_bridge->ops) and bridge output type * (&drm_bridge->type), as well as the DRM_BRIDGE_ATTACH_NO_CONNECTOR attach * flag (none of the bridges shall create a DRM connector directly).
*/
/** * struct drm_bridge_connector - A connector backed by a chain of bridges
*/ struct drm_bridge_connector { /** * @base: The base DRM connector
*/ struct drm_connector base; /** * @encoder: * * The encoder at the start of the bridges chain.
*/ struct drm_encoder *encoder; /** * @bridge_edid: * * The last bridge in the chain (closest to the connector) that provides * EDID read support, if any (see &DRM_BRIDGE_OP_EDID).
*/ struct drm_bridge *bridge_edid; /** * @bridge_hpd: * * The last bridge in the chain (closest to the connector) that provides * hot-plug detection notification, if any (see &DRM_BRIDGE_OP_HPD).
*/ struct drm_bridge *bridge_hpd; /** * @bridge_detect: * * The last bridge in the chain (closest to the connector) that provides * connector detection, if any (see &DRM_BRIDGE_OP_DETECT).
*/ struct drm_bridge *bridge_detect; /** * @bridge_modes: * * The last bridge in the chain (closest to the connector) that provides * connector modes detection, if any (see &DRM_BRIDGE_OP_MODES).
*/ struct drm_bridge *bridge_modes; /** * @bridge_hdmi: * * The bridge in the chain that implements necessary support for the * HDMI connector infrastructure, if any (see &DRM_BRIDGE_OP_HDMI).
*/ struct drm_bridge *bridge_hdmi; /** * @bridge_hdmi_audio: * * The bridge in the chain that implements necessary support for the * HDMI Audio infrastructure, if any (see &DRM_BRIDGE_OP_HDMI_AUDIO).
*/ struct drm_bridge *bridge_hdmi_audio; /** * @bridge_dp_audio: * * The bridge in the chain that implements necessary support for the * DisplayPort Audio infrastructure, if any (see * &DRM_BRIDGE_OP_DP_AUDIO).
*/ struct drm_bridge *bridge_dp_audio; /** * @bridge_hdmi_cec: * * The bridge in the chain that implements CEC support, if any (see * DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER).
*/ struct drm_bridge *bridge_hdmi_cec;
};
/* Notify all bridges in the pipeline of hotplug events. */
drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) { if (bridge->funcs->hpd_notify)
bridge->funcs->hpd_notify(bridge, status);
}
}
if (detect) {
status = detect->funcs->detect(detect, connector);
if (hdmi)
drm_atomic_helper_connector_hdmi_hotplug(connector, status);
drm_bridge_connector_hpd_notify(connector, status);
} else { switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DPI: case DRM_MODE_CONNECTOR_LVDS: case DRM_MODE_CONNECTOR_DSI: case DRM_MODE_CONNECTOR_eDP:
status = connector_status_connected; break; default:
status = connector_status_unknown; break;
}
}
/* * If there is a HDMI bridge, EDID has been updated as a part of * the .detect(). Just update the modes here.
*/
bridge = bridge_connector->bridge_hdmi; if (bridge) return drm_edid_connector_add_modes(connector);
/* * If display exposes EDID, then we parse that in the normal way to * build table of supported modes.
*/
bridge = bridge_connector->bridge_edid; if (bridge) return drm_bridge_connector_get_modes_edid(connector, bridge);
/* * Otherwise if the display pipeline reports modes (e.g. with a fixed * resolution panel or an analog TV output), query it.
*/
bridge = bridge_connector->bridge_modes; if (bridge) return bridge->funcs->get_modes(bridge, connector);
/* * We can't retrieve modes, which can happen for instance for a DVI or * VGA output with the DDC bus unconnected. The KMS core will add the * default modes.
*/ return 0;
}
/** * drm_bridge_connector_init - Initialise a connector for a chain of bridges * @drm: the DRM device * @encoder: the encoder where the bridge chain starts * * Allocate, initialise and register a &drm_bridge_connector with the @drm * device. The connector is associated with a chain of bridges that starts at * the @encoder. All bridges in the chain shall report bridge operation flags * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of * them may create a DRM connector directly. * * Returns a pointer to the new connector on success, or a negative error * pointer otherwise.
*/ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, struct drm_encoder *encoder)
{ struct drm_bridge_connector *bridge_connector; struct drm_connector *connector; struct i2c_adapter *ddc = NULL; struct drm_bridge *bridge, *panel_bridge = NULL; unsignedint supported_formats = BIT(HDMI_COLORSPACE_RGB); unsignedint max_bpc = 8; int connector_type; int ret;
bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL); if (!bridge_connector) return ERR_PTR(-ENOMEM);
/* * Initialise connector status handling. First locate the furthest * bridges in the pipeline that support HPD and output detection. Then * initialise the connector polling mode, using HPD if available and * falling back to polling if supported. If neither HPD nor output * detection are available, we don't support hotplug detection at all.
*/
connector_type = DRM_MODE_CONNECTOR_Unknown;
drm_for_each_bridge_in_chain(encoder, bridge) { if (!bridge->interlace_allowed)
connector->interlace_allowed = false; if (!bridge->ycbcr_420_allowed)
connector->ycbcr_420_allowed = false;
if (bridge->ops & DRM_BRIDGE_OP_EDID)
bridge_connector->bridge_edid = bridge; if (bridge->ops & DRM_BRIDGE_OP_HPD)
bridge_connector->bridge_hpd = bridge; if (bridge->ops & DRM_BRIDGE_OP_DETECT)
bridge_connector->bridge_detect = bridge; if (bridge->ops & DRM_BRIDGE_OP_MODES)
bridge_connector->bridge_modes = bridge; if (bridge->ops & DRM_BRIDGE_OP_HDMI) { if (bridge_connector->bridge_hdmi) return ERR_PTR(-EBUSY); if (!bridge->funcs->hdmi_write_infoframe ||
!bridge->funcs->hdmi_clear_infoframe) return ERR_PTR(-EINVAL);
bridge_connector->bridge_hdmi = bridge;
if (bridge->supported_formats)
supported_formats = bridge->supported_formats; if (bridge->max_bpc)
max_bpc = bridge->max_bpc;
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) { if (bridge_connector->bridge_hdmi_audio) return ERR_PTR(-EBUSY);
if (bridge_connector->bridge_dp_audio) return ERR_PTR(-EBUSY);
if (!bridge->hdmi_audio_max_i2s_playback_channels &&
!bridge->hdmi_audio_spdif_playback) return ERR_PTR(-EINVAL);
if (!bridge->funcs->hdmi_audio_prepare ||
!bridge->funcs->hdmi_audio_shutdown) return ERR_PTR(-EINVAL);
bridge_connector->bridge_hdmi_audio = bridge;
}
if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) { if (bridge_connector->bridge_dp_audio) return ERR_PTR(-EBUSY);
if (bridge_connector->bridge_hdmi_audio) return ERR_PTR(-EBUSY);
if (!bridge->hdmi_audio_max_i2s_playback_channels &&
!bridge->hdmi_audio_spdif_playback) return ERR_PTR(-EINVAL);
if (!bridge->funcs->dp_audio_prepare ||
!bridge->funcs->dp_audio_shutdown) return ERR_PTR(-EINVAL);
bridge_connector->bridge_dp_audio = bridge;
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { if (bridge_connector->bridge_hdmi_cec) return ERR_PTR(-EBUSY);
bridge_connector->bridge_hdmi_cec = bridge;
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { if (bridge_connector->bridge_hdmi_cec) return ERR_PTR(-EBUSY);
bridge_connector->bridge_hdmi_cec = bridge;
if (!bridge->funcs->hdmi_cec_enable ||
!bridge->funcs->hdmi_cec_log_addr ||
!bridge->funcs->hdmi_cec_transmit) return ERR_PTR(-EINVAL);
}
if (!drm_bridge_get_next_bridge(bridge))
connector_type = bridge->type;
#ifdef CONFIG_OF if (!drm_bridge_get_next_bridge(bridge) &&
bridge->of_node)
connector->fwnode = fwnode_handle_get(of_fwnode_handle(bridge->of_node)); #endif
if (bridge->ddc)
ddc = bridge->ddc;
if (drm_bridge_is_panel(bridge))
panel_bridge = bridge;
}
if (connector_type == DRM_MODE_CONNECTOR_Unknown) return ERR_PTR(-EINVAL);
if (bridge_connector->bridge_hdmi) { if (!connector->ycbcr_420_allowed)
supported_formats &= ~BIT(HDMI_COLORSPACE_YUV420);
bridge = bridge_connector->bridge_hdmi;
ret = drmm_connector_hdmi_init(drm, connector,
bridge_connector->bridge_hdmi->vendor,
bridge_connector->bridge_hdmi->product,
&drm_bridge_connector_funcs,
&drm_bridge_connector_hdmi_funcs,
connector_type, ddc,
supported_formats,
max_bpc); if (ret) return ERR_PTR(ret);
} else {
ret = drmm_connector_init(drm, connector,
&drm_bridge_connector_funcs,
connector_type, ddc); if (ret) return ERR_PTR(ret);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.