Change rk3066_hdmi.c to bridge driver mode. Signed-off-by: Johan Jonker <jbx6244@xxxxxxxxx> --- Apply after: [PATCH v10] drm/rockchip: rk3066_hdmi: add sound support --- drivers/gpu/drm/rockchip/rk3066_hdmi.c | 293 ++++++++++++++----------- 1 file changed, 161 insertions(+), 132 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index e3b8faf89ae2..905610a10549 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -5,12 +5,15 @@ */ #include <drm/drm_atomic.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <linux/clk.h> +#include <linux/media-bus-format.h> #include <linux/mfd/syscon.h> #include <linux/platform_device.h> #include <linux/regmap.h> @@ -53,7 +56,8 @@ struct rk3066_hdmi { struct clk *hclk; void __iomem *regs; - struct drm_connector connector; + struct drm_bridge bridge; + struct drm_connector *connector; struct rockchip_encoder encoder; struct rk3066_hdmi_i2c *i2c; @@ -75,11 +79,6 @@ static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder) return container_of(rkencoder, struct rk3066_hdmi, encoder); } -static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector) -{ - return container_of(connector, struct rk3066_hdmi, connector); -} - static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset) { return readl_relaxed(hdmi->regs + offset); @@ -208,8 +207,7 @@ static int rk3066_hdmi_config_avi(struct rk3066_hdmi *hdmi, union hdmi_infoframe frame; int rc; - rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, - &hdmi->connector, mode); + rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, hdmi->connector, mode); if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444) frame.avi.colorspace = HDMI_COLORSPACE_YUV444; @@ -347,7 +345,7 @@ static int rk3066_hdmi_audio_hw_params(struct device *dev, void *d, struct hdmi_codec_params *params) { struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); - struct drm_display_info *display = &hdmi->connector.display_info; + struct drm_display_info *display = &hdmi->connector->display_info; if (!display->has_audio) { DRM_DEV_ERROR(hdmi->dev, "no audio support\n"); @@ -380,7 +378,7 @@ static void rk3066_hdmi_audio_shutdown(struct device *dev, void *d) static int rk3066_hdmi_audio_mute_stream(struct device *dev, void *d, bool mute, int direction) { struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); - struct drm_display_info *display = &hdmi->connector.display_info; + struct drm_display_info *display = &hdmi->connector->display_info; if (!display->has_audio) { DRM_DEV_ERROR(hdmi->dev, "no audio support\n"); @@ -515,8 +513,7 @@ static int rk3066_hdmi_config_video_timing(struct rk3066_hdmi *hdmi, return 0; } -static void -rk3066_hdmi_phy_write(struct rk3066_hdmi *hdmi, u16 offset, u8 value) +static void rk3066_hdmi_phy_write(struct rk3066_hdmi *hdmi, u16 offset, u8 value) { hdmi_writeb(hdmi, offset, value); hdmi_modb(hdmi, HDMI_SYS_CTRL, @@ -572,7 +569,7 @@ static void rk3066_hdmi_config_phy(struct rk3066_hdmi *hdmi) static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi, struct drm_display_mode *mode) { - struct drm_display_info *display = &hdmi->connector.display_info; + struct drm_display_info *display = &hdmi->connector->display_info; hdmi->hdmi_data.vic = drm_match_cea_mode(mode); hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB; @@ -644,15 +641,16 @@ static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi, return 0; } -static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void rk3066_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) { - struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); + struct drm_atomic_state *state = bridge_state->base.state; + struct rk3066_hdmi *hdmi = bridge->driver_private; struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; int mux, val; - conn_state = drm_atomic_get_new_connector_state(state, &hdmi->connector); + conn_state = drm_atomic_get_new_connector_state(state, hdmi->connector); if (WARN_ON(!conn_state)) return; @@ -660,7 +658,7 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder, if (WARN_ON(!crtc_state)) return; - mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); + mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, bridge->encoder); if (mux) val = (HDMI_VIDEO_SEL << 16) | HDMI_VIDEO_SEL; else @@ -674,10 +672,10 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder, rk3066_hdmi_setup(hdmi, &crtc_state->adjusted_mode); } -static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void rk3066_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) { - struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); + struct rk3066_hdmi *hdmi = bridge->driver_private; DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n"); @@ -693,148 +691,146 @@ static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder, rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_A); } -static int -rk3066_hdmi_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +static enum drm_connector_status rk3066_hdmi_bridge_detect(struct drm_bridge *bridge) { - struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); - - s->output_mode = ROCKCHIP_OUT_MODE_P888; - s->output_type = DRM_MODE_CONNECTOR_HDMIA; + struct rk3066_hdmi *hdmi = bridge->driver_private; - return 0; + return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ? + connector_status_connected : connector_status_disconnected; } -static int rk3066_hdmi_encoder_late_register(struct drm_encoder *encoder) +static enum drm_mode_status rk3066_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) { - struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); + if ((mode->flags & DRM_MODE_FLAG_DBLCLK) || + (mode->flags & DRM_MODE_FLAG_INTERLACE)) + return MODE_BAD; - return rk3066_hdmi_audio_codec_init(hdmi); -} + if (mode->clock < 25000) + return MODE_CLOCK_LOW; -static void rk3066_hdmi_encoder_early_unregister(struct drm_encoder *encoder) -{ - struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); + if (mode->clock > 148500) + return MODE_CLOCK_HIGH; - platform_device_unregister(hdmi->audio_pdev); + return MODE_OK; } -static const struct drm_encoder_funcs rk3066_hdmi_encoder_funcs = { - .destroy = drm_encoder_cleanup, - .late_register = rk3066_hdmi_encoder_late_register, - .early_unregister = rk3066_hdmi_encoder_early_unregister, -}; - -static const -struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = { - .atomic_check = rk3066_hdmi_encoder_atomic_check, - .atomic_enable = rk3066_hdmi_encoder_enable, - .atomic_disable = rk3066_hdmi_encoder_disable, -}; - -static enum drm_connector_status -rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force) +static int rk3066_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 rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct rk3066_hdmi *hdmi = bridge->driver_private; + struct drm_display_info *display = &hdmi->connector->display_info; - return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ? - connector_status_connected : connector_status_disconnected; + s->output_mode = ROCKCHIP_OUT_MODE_P888; + s->output_type = DRM_MODE_CONNECTOR_HDMIA; + + return rk3066_hdmi_bridge_mode_valid(bridge, display, + &crtc_state->adjusted_mode) == MODE_OK ? 0 : -EINVAL; } -static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector) +static const struct drm_edid *rk3066_hdmi_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) { - struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); + struct rk3066_hdmi *hdmi = bridge->driver_private; const struct drm_edid *drm_edid; - int ret = 0; if (!hdmi->ddc) - return 0; + return NULL; drm_edid = drm_edid_read_ddc(connector, hdmi->ddc); - drm_edid_connector_update(connector, drm_edid); - ret = drm_edid_connector_add_modes(connector); - drm_edid_free(drm_edid); + if (!drm_edid) { + dev_dbg(hdmi->dev, "failed to get edid\n"); + return NULL; + } - return ret; + return drm_edid; } -static enum drm_mode_status -rk3066_hdmi_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) +#define MAX_INPUT_SEL_FORMATS 1 + +static u32 *rk3066_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) { - u32 vic = drm_match_cea_mode(mode); + u32 *input_fmts; - if (vic > 1) - return MODE_OK; - else - return MODE_BAD; -} + *num_input_fmts = 0; -static struct drm_encoder * -rk3066_hdmi_connector_best_encoder(struct drm_connector *connector) -{ - struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + *num_input_fmts = 1; - return &hdmi->encoder.encoder; + return input_fmts; } -static int -rk3066_hdmi_probe_single_connector_modes(struct drm_connector *connector, - uint32_t maxX, uint32_t maxY) +#define MAX_OUTPUT_SEL_FORMATS 1 + +static u32 *rk3066_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) { - if (maxX > 1920) - maxX = 1920; - if (maxY > 1080) - maxY = 1080; + u32 *output_fmts; - return drm_helper_probe_single_connector_modes(connector, maxX, maxY); -} + *num_output_fmts = 0; -static void rk3066_hdmi_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts), + GFP_KERNEL); + if (!output_fmts) + return NULL; -static const struct drm_connector_funcs rk3066_hdmi_connector_funcs = { - .fill_modes = rk3066_hdmi_probe_single_connector_modes, - .detect = rk3066_hdmi_connector_detect, - .destroy = rk3066_hdmi_connector_destroy, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; + output_fmts[0] = MEDIA_BUS_FMT_FIXED; + *num_output_fmts = 1; + + return output_fmts; +} -static const -struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = { - .get_modes = rk3066_hdmi_connector_get_modes, - .mode_valid = rk3066_hdmi_connector_mode_valid, - .best_encoder = rk3066_hdmi_connector_best_encoder, +static const struct drm_bridge_funcs rk3066_hdmi_bridge_funcs = { + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_check = rk3066_hdmi_bridge_atomic_check, + .atomic_enable = rk3066_hdmi_bridge_atomic_enable, + .atomic_disable = rk3066_hdmi_bridge_atomic_disable, + .atomic_get_input_bus_fmts = rk3066_hdmi_bridge_atomic_get_input_bus_fmts, + .atomic_get_output_bus_fmts = rk3066_hdmi_bridge_atomic_get_output_bus_fmts, + .mode_valid = rk3066_hdmi_bridge_mode_valid, + .detect = rk3066_hdmi_bridge_detect, + .edid_read = rk3066_hdmi_bridge_edid_read, }; -static int rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi) +static int rk3066_hdmi_encoder_late_register(struct drm_encoder *encoder) { - struct drm_encoder *encoder = &hdmi->encoder.encoder; - - drm_encoder_helper_add(encoder, &rk3066_hdmi_encoder_helper_funcs); - drm_encoder_init(drm, encoder, &rk3066_hdmi_encoder_funcs, - DRM_MODE_ENCODER_TMDS, NULL); - - hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; + struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); - drm_connector_helper_add(&hdmi->connector, - &rk3066_hdmi_connector_helper_funcs); - drm_connector_init_with_ddc(drm, &hdmi->connector, - &rk3066_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA, - hdmi->ddc); + return rk3066_hdmi_audio_codec_init(hdmi); +} - drm_connector_attach_encoder(&hdmi->connector, encoder); +static void rk3066_hdmi_encoder_early_unregister(struct drm_encoder *encoder) +{ + struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); - return 0; + platform_device_unregister(hdmi->audio_pdev); } +static const struct drm_encoder_funcs rk3066_hdmi_encoder_funcs = { + .destroy = drm_encoder_cleanup, + .late_register = rk3066_hdmi_encoder_late_register, + .early_unregister = rk3066_hdmi_encoder_early_unregister, +}; + static irqreturn_t rk3066_hdmi_hardirq(int irq, void *dev_id) { struct rk3066_hdmi *hdmi = dev_id; @@ -863,7 +859,8 @@ static irqreturn_t rk3066_hdmi_irq(int irq, void *dev_id) { struct rk3066_hdmi *hdmi = dev_id; - drm_helper_hpd_irq_event(hdmi->connector.dev); + if (hdmi->bridge.dev) + drm_helper_hpd_irq_event(hdmi->bridge.dev); return IRQ_HANDLED; } @@ -1074,9 +1071,8 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master, hdmi_writeb(hdmi, HDMI_INTR_MASK4, 0); rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_A); - ret = rk3066_hdmi_register(drm, hdmi); - if (ret) - goto err_disable_i2c; + drm_encoder_init(drm, encoder, &rk3066_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); dev_set_drvdata(dev, hdmi); @@ -1085,15 +1081,47 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master, dev_name(dev), hdmi); if (ret) { DRM_DEV_ERROR(dev, "failed to request hdmi irq: %d\n", ret); - goto err_cleanup_hdmi; + goto err_free_encoder; + } + + hdmi->bridge.driver_private = hdmi; + hdmi->bridge.funcs = &rk3066_hdmi_bridge_funcs; + hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID + | DRM_BRIDGE_OP_HPD; + hdmi->bridge.interlace_allowed = false; + hdmi->bridge.ddc = hdmi->ddc; + hdmi->bridge.of_node = pdev->dev.of_node; + hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + + drm_bridge_add(&hdmi->bridge); + + ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + DRM_DEV_ERROR(dev, "failed to attach bridge: %d\n", ret); + goto err_remove_bridge; + } + + hdmi->connector = drm_bridge_connector_init(drm, encoder); + if (IS_ERR(hdmi->connector)) { + DRM_DEV_ERROR(dev, "failed to initialize bridge connector: %pe\n", hdmi->connector); + ret = PTR_ERR(hdmi->connector); + goto err_remove_bridge; + } + + ret = drm_connector_attach_encoder(hdmi->connector, encoder); + if (ret < 0) { + DRM_DEV_ERROR(dev, "failed to attach connector: %d\n", ret); + goto err_free_connector; } return 0; -err_cleanup_hdmi: - hdmi->connector.funcs->destroy(&hdmi->connector); - hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); -err_disable_i2c: +err_free_connector: + drm_connector_cleanup(hdmi->connector); +err_remove_bridge: + drm_bridge_remove(&hdmi->bridge); +err_free_encoder: + drm_encoder_cleanup(encoder); i2c_put_adapter(hdmi->ddc); err_disable_hclk: clk_disable_unprepare(hdmi->hclk); @@ -1106,8 +1134,9 @@ static void rk3066_hdmi_unbind(struct device *dev, struct device *master, { struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); - hdmi->connector.funcs->destroy(&hdmi->connector); - hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); + drm_connector_cleanup(hdmi->connector); + drm_bridge_remove(&hdmi->bridge); + drm_encoder_cleanup(&hdmi->encoder.encoder); i2c_put_adapter(hdmi->ddc); clk_disable_unprepare(hdmi->hclk); -- 2.39.2