mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	drm/meson: encoder_hdmi: switch to bridge DRM_BRIDGE_ATTACH_NO_CONNECTOR
This implements the necessary change to no more use the embedded connector in dw-hdmi and use the dedicated bridge connector driver by passing DRM_BRIDGE_ATTACH_NO_CONNECTOR to the bridge attach call. The necessary connector properties are added to handle the same functionalities as the embedded dw-hdmi connector, i.e. the HDR metadata, the CEC notifier & other flags. The dw-hdmi output_port is set to 1 in order to look for a connector next bridge in order to get DRM_BRIDGE_ATTACH_NO_CONNECTOR working. Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Acked-by: Sam Ravnborg <sam@ravnborg.org> Acked-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Link: https://patchwork.freedesktop.org/patch/msgid/20211020123947.2585572-5-narmstrong@baylibre.com
This commit is contained in:
		
							parent
							
								
									e67f6037ae
								
							
						
					
					
						commit
						0af5e0b411
					
				
					 3 changed files with 82 additions and 2 deletions
				
			
		| 
						 | 
					@ -6,9 +6,11 @@ config DRM_MESON
 | 
				
			||||||
	select DRM_KMS_HELPER
 | 
						select DRM_KMS_HELPER
 | 
				
			||||||
	select DRM_KMS_CMA_HELPER
 | 
						select DRM_KMS_CMA_HELPER
 | 
				
			||||||
	select DRM_GEM_CMA_HELPER
 | 
						select DRM_GEM_CMA_HELPER
 | 
				
			||||||
 | 
						select DRM_DISPLAY_CONNECTOR
 | 
				
			||||||
	select VIDEOMODE_HELPERS
 | 
						select VIDEOMODE_HELPERS
 | 
				
			||||||
	select REGMAP_MMIO
 | 
						select REGMAP_MMIO
 | 
				
			||||||
	select MESON_CANVAS
 | 
						select MESON_CANVAS
 | 
				
			||||||
 | 
						select CEC_CORE if CEC_NOTIFIER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config DRM_MESON_DW_HDMI
 | 
					config DRM_MESON_DW_HDMI
 | 
				
			||||||
	tristate "HDMI Synopsys Controller support for Amlogic Meson Display"
 | 
						tristate "HDMI Synopsys Controller support for Amlogic Meson Display"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -803,6 +803,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
 | 
				
			||||||
	dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
 | 
						dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
 | 
				
			||||||
	dw_plat_data->ycbcr_420_allowed = true;
 | 
						dw_plat_data->ycbcr_420_allowed = true;
 | 
				
			||||||
	dw_plat_data->disable_cec = true;
 | 
						dw_plat_data->disable_cec = true;
 | 
				
			||||||
 | 
						dw_plat_data->output_port = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
 | 
						if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
 | 
				
			||||||
	    dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
 | 
						    dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,8 +14,11 @@
 | 
				
			||||||
#include <linux/regulator/consumer.h>
 | 
					#include <linux/regulator/consumer.h>
 | 
				
			||||||
#include <linux/reset.h>
 | 
					#include <linux/reset.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <media/cec-notifier.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <drm/drm_atomic_helper.h>
 | 
					#include <drm/drm_atomic_helper.h>
 | 
				
			||||||
#include <drm/drm_bridge.h>
 | 
					#include <drm/drm_bridge.h>
 | 
				
			||||||
 | 
					#include <drm/drm_bridge_connector.h>
 | 
				
			||||||
#include <drm/drm_device.h>
 | 
					#include <drm/drm_device.h>
 | 
				
			||||||
#include <drm/drm_edid.h>
 | 
					#include <drm/drm_edid.h>
 | 
				
			||||||
#include <drm/drm_probe_helper.h>
 | 
					#include <drm/drm_probe_helper.h>
 | 
				
			||||||
| 
						 | 
					@ -34,8 +37,10 @@ struct meson_encoder_hdmi {
 | 
				
			||||||
	struct drm_encoder encoder;
 | 
						struct drm_encoder encoder;
 | 
				
			||||||
	struct drm_bridge bridge;
 | 
						struct drm_bridge bridge;
 | 
				
			||||||
	struct drm_bridge *next_bridge;
 | 
						struct drm_bridge *next_bridge;
 | 
				
			||||||
 | 
						struct drm_connector *connector;
 | 
				
			||||||
	struct meson_drm *priv;
 | 
						struct meson_drm *priv;
 | 
				
			||||||
	unsigned long output_bus_fmt;
 | 
						unsigned long output_bus_fmt;
 | 
				
			||||||
 | 
						struct cec_notifier *cec_notifier;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define bridge_to_meson_encoder_hdmi(x) \
 | 
					#define bridge_to_meson_encoder_hdmi(x) \
 | 
				
			||||||
| 
						 | 
					@ -50,6 +55,14 @@ static int meson_encoder_hdmi_attach(struct drm_bridge *bridge,
 | 
				
			||||||
				 &encoder_hdmi->bridge, flags);
 | 
									 &encoder_hdmi->bridge, flags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void meson_encoder_hdmi_detach(struct drm_bridge *bridge)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cec_notifier_conn_unregister(encoder_hdmi->cec_notifier);
 | 
				
			||||||
 | 
						encoder_hdmi->cec_notifier = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi,
 | 
					static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi,
 | 
				
			||||||
					const struct drm_display_mode *mode)
 | 
										const struct drm_display_mode *mode)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -298,9 +311,30 @@ static int meson_encoder_hdmi_atomic_check(struct drm_bridge *bridge,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
										  enum drm_connector_status status)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
 | 
				
			||||||
 | 
						struct edid *edid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!encoder_hdmi->cec_notifier)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (status == connector_status_connected) {
 | 
				
			||||||
 | 
							edid = drm_bridge_get_edid(encoder_hdmi->next_bridge, encoder_hdmi->connector);
 | 
				
			||||||
 | 
							if (!edid)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cec_notifier_set_phys_addr_from_edid(encoder_hdmi->cec_notifier, edid);
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							cec_notifier_phys_addr_invalidate(encoder_hdmi->cec_notifier);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
 | 
					static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
 | 
				
			||||||
	.attach = meson_encoder_hdmi_attach,
 | 
						.attach = meson_encoder_hdmi_attach,
 | 
				
			||||||
 | 
						.detach = meson_encoder_hdmi_detach,
 | 
				
			||||||
	.mode_valid = meson_encoder_hdmi_mode_valid,
 | 
						.mode_valid = meson_encoder_hdmi_mode_valid,
 | 
				
			||||||
 | 
						.hpd_notify = meson_encoder_hdmi_hpd_notify,
 | 
				
			||||||
	.atomic_enable = meson_encoder_hdmi_atomic_enable,
 | 
						.atomic_enable = meson_encoder_hdmi_atomic_enable,
 | 
				
			||||||
	.atomic_disable = meson_encoder_hdmi_atomic_disable,
 | 
						.atomic_disable = meson_encoder_hdmi_atomic_disable,
 | 
				
			||||||
	.atomic_get_input_bus_fmts = meson_encoder_hdmi_get_inp_bus_fmts,
 | 
						.atomic_get_input_bus_fmts = meson_encoder_hdmi_get_inp_bus_fmts,
 | 
				
			||||||
| 
						 | 
					@ -313,6 +347,7 @@ static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
 | 
				
			||||||
int meson_encoder_hdmi_init(struct meson_drm *priv)
 | 
					int meson_encoder_hdmi_init(struct meson_drm *priv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct meson_encoder_hdmi *meson_encoder_hdmi;
 | 
						struct meson_encoder_hdmi *meson_encoder_hdmi;
 | 
				
			||||||
 | 
						struct platform_device *pdev;
 | 
				
			||||||
	struct device_node *remote;
 | 
						struct device_node *remote;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -337,6 +372,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
 | 
				
			||||||
	meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs;
 | 
						meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs;
 | 
				
			||||||
	meson_encoder_hdmi->bridge.of_node = priv->dev->of_node;
 | 
						meson_encoder_hdmi->bridge.of_node = priv->dev->of_node;
 | 
				
			||||||
	meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
 | 
						meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
 | 
				
			||||||
 | 
						meson_encoder_hdmi->bridge.interlace_allowed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	drm_bridge_add(&meson_encoder_hdmi->bridge);
 | 
						drm_bridge_add(&meson_encoder_hdmi->bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -353,17 +389,58 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
 | 
				
			||||||
	meson_encoder_hdmi->encoder.possible_crtcs = BIT(0);
 | 
						meson_encoder_hdmi->encoder.possible_crtcs = BIT(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Attach HDMI Encoder Bridge to Encoder */
 | 
						/* Attach HDMI Encoder Bridge to Encoder */
 | 
				
			||||||
	ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL, 0);
 | 
						ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL,
 | 
				
			||||||
 | 
									DRM_BRIDGE_ATTACH_NO_CONNECTOR);
 | 
				
			||||||
	if (ret) {
 | 
						if (ret) {
 | 
				
			||||||
		dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
 | 
							dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Initialize & attach Bridge Connector */
 | 
				
			||||||
 | 
						meson_encoder_hdmi->connector = drm_bridge_connector_init(priv->drm,
 | 
				
			||||||
 | 
												&meson_encoder_hdmi->encoder);
 | 
				
			||||||
 | 
						if (IS_ERR(meson_encoder_hdmi->connector)) {
 | 
				
			||||||
 | 
							dev_err(priv->dev, "Unable to create HDMI bridge connector\n");
 | 
				
			||||||
 | 
							return PTR_ERR(meson_encoder_hdmi->connector);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						drm_connector_attach_encoder(meson_encoder_hdmi->connector,
 | 
				
			||||||
 | 
									     &meson_encoder_hdmi->encoder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * We should have now in place:
 | 
						 * We should have now in place:
 | 
				
			||||||
	 * encoder->[hdmi encoder bridge]->[dw-hdmi bridge]->[dw-hdmi connector]
 | 
						 * encoder->[hdmi encoder bridge]->[dw-hdmi bridge]->[display connector bridge]->[display connector]
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * drm_connector_attach_max_bpc_property() requires the
 | 
				
			||||||
 | 
						 * connector to have a state.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						drm_atomic_helper_connector_reset(meson_encoder_hdmi->connector);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) ||
 | 
				
			||||||
 | 
						    meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
 | 
				
			||||||
 | 
						    meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
 | 
				
			||||||
 | 
							drm_connector_attach_hdr_output_metadata_property(meson_encoder_hdmi->connector);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drm_connector_attach_max_bpc_property(meson_encoder_hdmi->connector, 8, 8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Handle this here until handled by drm_bridge_connector_init() */
 | 
				
			||||||
 | 
						meson_encoder_hdmi->connector->ycbcr_420_allowed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pdev = of_find_device_by_node(remote);
 | 
				
			||||||
 | 
						if (pdev) {
 | 
				
			||||||
 | 
							struct cec_connector_info conn_info;
 | 
				
			||||||
 | 
							struct cec_notifier *notifier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cec_fill_conn_info_from_drm(&conn_info, meson_encoder_hdmi->connector);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							notifier = cec_notifier_conn_register(&pdev->dev, NULL, &conn_info);
 | 
				
			||||||
 | 
							if (!notifier)
 | 
				
			||||||
 | 
								return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							meson_encoder_hdmi->cec_notifier = notifier;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_dbg(priv->dev, "HDMI encoder initialized\n");
 | 
						dev_dbg(priv->dev, "HDMI encoder initialized\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue