Converting the HDMI controller to a bridge seems like the preferred way to support an YUV output, so let's do this. Signed-off-by: Maxime Ripard <maxime@xxxxxxxxxx> --- drivers/gpu/drm/vc4/vc4_crtc.c | 37 ++++++----- drivers/gpu/drm/vc4/vc4_drv.c | 15 +++-- drivers/gpu/drm/vc4/vc4_drv.h | 27 +++++--- drivers/gpu/drm/vc4/vc4_hdmi.c | 111 +++++++++++++++++++++------------ drivers/gpu/drm/vc4/vc4_hdmi.h | 8 +++ 5 files changed, 131 insertions(+), 67 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index e2607e1f2520..8c13d31827bc 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -283,14 +283,19 @@ static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) drm_connector_list_iter_begin(crtc->dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { struct drm_encoder *encoder; - struct vc4_encoder *vc4_encoder; + struct drm_bridge *bridge; + struct vc4_bridge *vc4_bridge; encoder = vc4_get_connector_encoder(connector); if (!encoder) continue; - vc4_encoder = to_vc4_encoder(encoder); - if (vc4_encoder->crtc == crtc) { + bridge = drm_bridge_chain_get_first_bridge(encoder); + if (!bridge) + continue; + + vc4_bridge = to_vc4_bridge(bridge); + if (vc4_bridge->crtc == crtc) { drm_connector_list_iter_end(&conn_iter); return encoder; } @@ -429,7 +434,8 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, unsigned int channel) { struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); - struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); + struct vc4_bridge *vc4_bridge = to_vc4_bridge(bridge); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_device *dev = crtc->dev; int ret; @@ -457,14 +463,14 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, */ mdelay(20); - if (vc4_encoder && vc4_encoder->post_crtc_disable) - vc4_encoder->post_crtc_disable(encoder, state); + if (vc4_bridge && vc4_bridge->post_crtc_disable) + vc4_bridge->post_crtc_disable(bridge, state); vc4_crtc_pixelvalve_reset(crtc); vc4_hvs_stop_channel(dev, channel); - if (vc4_encoder && vc4_encoder->post_crtc_powerdown) - vc4_encoder->post_crtc_powerdown(encoder, state); + if (vc4_bridge && vc4_bridge->post_crtc_powerdown) + vc4_bridge->post_crtc_powerdown(bridge, state); return 0; } @@ -529,7 +535,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); - struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); + struct vc4_bridge *vc4_bridge = to_vc4_bridge(bridge); require_hvs_enabled(dev); @@ -540,15 +547,15 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, vc4_hvs_atomic_enable(crtc, state); - if (vc4_encoder->pre_crtc_configure) - vc4_encoder->pre_crtc_configure(encoder, state); + if (vc4_bridge->pre_crtc_configure) + vc4_bridge->pre_crtc_configure(bridge, state); vc4_crtc_config_pv(crtc); CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN); - if (vc4_encoder->pre_crtc_enable) - vc4_encoder->pre_crtc_enable(encoder, state); + if (vc4_bridge->pre_crtc_enable) + vc4_bridge->pre_crtc_enable(bridge, state); /* When feeding the transposer block the pixelvalve is unneeded and * should not be enabled. @@ -556,8 +563,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, CRTC_WRITE(PV_V_CONTROL, CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); - if (vc4_encoder->post_crtc_enable) - vc4_encoder->post_crtc_enable(encoder, state); + if (vc4_bridge->post_crtc_enable) + vc4_bridge->post_crtc_enable(bridge, state); } static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index cd1fb75c66a7..cee54f3b64e9 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -217,20 +217,25 @@ static struct drm_crtc *vc4_drv_find_crtc(struct drm_device *drm, return NULL; } -static void vc4_drv_set_encoder_data(struct drm_device *drm) +static void vc4_drv_set_bridge_data(struct drm_device *drm) { struct drm_encoder *encoder; drm_for_each_encoder(encoder, drm) { - struct vc4_encoder *vc4_encoder; + struct vc4_bridge *vc4_bridge; + struct drm_bridge *bridge; struct drm_crtc *crtc; crtc = vc4_drv_find_crtc(drm, encoder); if (WARN_ON(!crtc)) return; - vc4_encoder = to_vc4_encoder(encoder); - vc4_encoder->crtc = crtc; + bridge = drm_bridge_chain_get_first_bridge(encoder); + if (!bridge) + continue; + + vc4_bridge = to_vc4_bridge(bridge); + vc4_bridge->crtc = crtc; } } @@ -296,7 +301,7 @@ static int vc4_drm_bind(struct device *dev) ret = component_bind_all(dev, drm); if (ret) return ret; - vc4_drv_set_encoder_data(drm); + vc4_drv_set_bridge_data(drm); ret = vc4_plane_create_additional_planes(drm); if (ret) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 1b569dcc2154..a5721ffc6529 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -10,6 +10,7 @@ #include <linux/uaccess.h> #include <drm/drm_atomic.h> +#include <drm/drm_bridge.h> #include <drm/drm_debugfs.h> #include <drm/drm_device.h> #include <drm/drm_encoder.h> @@ -438,16 +439,8 @@ enum vc4_encoder_type { struct vc4_encoder { struct drm_encoder base; - struct drm_crtc *crtc; enum vc4_encoder_type type; u32 clock_select; - - void (*pre_crtc_configure)(struct drm_encoder *encoder, struct drm_atomic_state *state); - void (*pre_crtc_enable)(struct drm_encoder *encoder, struct drm_atomic_state *state); - void (*post_crtc_enable)(struct drm_encoder *encoder, struct drm_atomic_state *state); - - void (*post_crtc_disable)(struct drm_encoder *encoder, struct drm_atomic_state *state); - void (*post_crtc_powerdown)(struct drm_encoder *encoder, struct drm_atomic_state *state); }; static inline struct vc4_encoder * @@ -456,6 +449,24 @@ to_vc4_encoder(struct drm_encoder *encoder) return container_of(encoder, struct vc4_encoder, base); } +struct vc4_bridge { + struct drm_bridge base; + struct drm_crtc *crtc; + + void (*pre_crtc_configure)(struct drm_bridge *bridge, struct drm_atomic_state *state); + void (*pre_crtc_enable)(struct drm_bridge *bridge, struct drm_atomic_state *state); + void (*post_crtc_enable)(struct drm_bridge *bridge, struct drm_atomic_state *state); + + void (*post_crtc_disable)(struct drm_bridge *bridge, struct drm_atomic_state *state); + void (*post_crtc_powerdown)(struct drm_bridge *bridge, struct drm_atomic_state *state); +}; + +static inline struct vc4_bridge * +to_vc4_bridge(struct drm_bridge *bridge) +{ + return container_of(bridge, struct vc4_bridge, base); +} + struct vc4_crtc_data { /* Bitmask of channels (FIFOs) of the HVS that the output can source from */ unsigned int hvs_available_channels; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 8f0af246f18f..4ce0aea6ba17 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -454,10 +454,10 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) vc4_hdmi_set_audio_infoframe(encoder); } -static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_post_crtc_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); @@ -468,10 +468,10 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX); } -static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_post_crtc_powerdown(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); int ret; if (vc4_hdmi->variant->phy_disable) @@ -489,10 +489,6 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, DRM_ERROR("Failed to release power domain: %d\n", ret); } -static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) -{ -} - static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, const struct drm_display_mode *mode) { @@ -740,9 +736,10 @@ vc4_hdmi_encoder_get_connector_state(struct drm_encoder *encoder, return NULL; } -static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_pre_crtc_configure(struct drm_bridge *bridge, + struct drm_atomic_state *state) { + struct drm_encoder *encoder = bridge->encoder; struct drm_connector_state *conn_state = vc4_hdmi_encoder_get_connector_state(encoder, state); struct vc4_hdmi_connector_state *vc4_conn_state = @@ -836,9 +833,10 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode); } -static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_pre_crtc_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { + struct drm_encoder *encoder = bridge->encoder; struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); @@ -848,9 +846,10 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder, HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); } -static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_post_crtc_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { + struct drm_encoder *encoder = bridge->encoder; struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); @@ -907,20 +906,17 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, vc4_hdmi_recenter_fifo(vc4_hdmi); } -static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) -{ -} - #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL -static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +static int vc4_hdmi_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state); struct drm_display_mode *mode = &crtc_state->adjusted_mode; - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); unsigned long long pixel_rate = mode->clock * 1000; unsigned long long tmds_rate; @@ -963,10 +959,11 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, } static enum drm_mode_status -vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, - const struct drm_display_mode *mode) +vc4_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); if (vc4_hdmi->variant->unsupported_odd_h_timings && ((mode->hdisplay % 2) || (mode->hsync_start % 2) || @@ -979,13 +976,49 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, return MODE_OK; } -static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { - .atomic_check = vc4_hdmi_encoder_atomic_check, - .mode_valid = vc4_hdmi_encoder_mode_valid, - .disable = vc4_hdmi_encoder_disable, - .enable = vc4_hdmi_encoder_enable, +static int vc4_hdmi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); + + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; + + return vc4_hdmi_connector_init(bridge->dev, vc4_hdmi); +} + +static const struct drm_bridge_funcs vc4_hdmi_bridge_funcs = { + .attach = vc4_hdmi_bridge_attach, + .atomic_check = vc4_hdmi_bridge_atomic_check, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .mode_valid = vc4_hdmi_bridge_mode_valid, }; +static int vc4_hdmi_bridge_init(struct drm_device *drm, + struct vc4_hdmi *vc4_hdmi) +{ + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_bridge *bridge = &vc4_hdmi->bridge.base; + struct device *dev = &vc4_hdmi->pdev->dev; + int ret; + + bridge->funcs = &vc4_hdmi_bridge_funcs; + bridge->of_node = dev->of_node; + bridge->type = DRM_MODE_CONNECTOR_HDMIA; + + drm_bridge_add(bridge); + + ret = drm_bridge_attach(encoder, bridge, NULL, 0); + if (ret) { + drm_bridge_remove(bridge); + return ret; + } + + return 0; +} + static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) { int i; @@ -1945,14 +1978,15 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, vc4_hdmi); encoder = &vc4_hdmi->encoder.base.base; vc4_hdmi->encoder.base.type = variant->encoder_type; - vc4_hdmi->encoder.base.pre_crtc_configure = vc4_hdmi_encoder_pre_crtc_configure; - vc4_hdmi->encoder.base.pre_crtc_enable = vc4_hdmi_encoder_pre_crtc_enable; - vc4_hdmi->encoder.base.post_crtc_enable = vc4_hdmi_encoder_post_crtc_enable; - vc4_hdmi->encoder.base.post_crtc_disable = vc4_hdmi_encoder_post_crtc_disable; - vc4_hdmi->encoder.base.post_crtc_powerdown = vc4_hdmi_encoder_post_crtc_powerdown; vc4_hdmi->pdev = pdev; vc4_hdmi->variant = variant; + vc4_hdmi->bridge.pre_crtc_configure = vc4_hdmi_bridge_pre_crtc_configure; + vc4_hdmi->bridge.pre_crtc_enable = vc4_hdmi_bridge_pre_crtc_enable; + vc4_hdmi->bridge.post_crtc_enable = vc4_hdmi_bridge_post_crtc_enable; + vc4_hdmi->bridge.post_crtc_disable = vc4_hdmi_bridge_post_crtc_disable; + vc4_hdmi->bridge.post_crtc_powerdown = vc4_hdmi_bridge_post_crtc_powerdown; + ret = variant->init_resources(vc4_hdmi); if (ret) return ret; @@ -1996,9 +2030,8 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) pm_runtime_enable(dev); drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); - drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs); - ret = vc4_hdmi_connector_init(drm, vc4_hdmi); + ret = vc4_hdmi_bridge_init(drm, vc4_hdmi); if (ret) goto err_destroy_encoder; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 8e42f9e7b3e2..d03c849d6ea0 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -1,6 +1,7 @@ #ifndef _VC4_HDMI_H_ #define _VC4_HDMI_H_ +#include <drm/drm_bridge.h> #include <drm/drm_connector.h> #include <media/cec.h> #include <sound/dmaengine_pcm.h> @@ -125,6 +126,7 @@ struct vc4_hdmi { struct vc4_hdmi_encoder encoder; struct drm_connector connector; + struct vc4_bridge bridge; struct i2c_adapter *ddc; void __iomem *hdmicore_regs; @@ -171,6 +173,12 @@ struct vc4_hdmi { struct debugfs_regset32 hd_regset; }; +static inline struct vc4_hdmi * +bridge_to_vc4_hdmi(struct drm_bridge *bridge) +{ + return container_of(bridge, struct vc4_hdmi, bridge.base); +} + static inline struct vc4_hdmi * connector_to_vc4_hdmi(struct drm_connector *connector) { -- 2.30.2 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel