From: Koji Matsuoka <koji.matsuoka.xm@xxxxxxxxxxx> The HDMI driver in the R-Car Gen3 uses dw_hdmi driver. Signed-off-by: Koji Matsuoka <koji.matsuoka.xm@xxxxxxxxxxx> [geert: Select DRM_DW_HDMI on non-r8a7795 to fix shmobile_defconfig build] Signed-off-by: Geert Uytterhoeven <geert+renesas@xxxxxxxxx> --- drivers/gpu/drm/rcar-du/Kconfig | 2 + drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 9 +- drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 6 +- drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c | 225 +++++++++++++++++++++++++++--- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 6 +- 5 files changed, 227 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 7fc3ca5..d7c7ffa 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -15,6 +15,8 @@ config DRM_RCAR_DU config DRM_RCAR_HDMI bool "R-Car DU HDMI Encoder Support" depends on DRM_RCAR_DU + depends on OF + select DRM_DW_HDMI help Enable support for external HDMI encoders. diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 1b16297..5d64893 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -1,7 +1,7 @@ /* * rcar_du_encoder.c -- R-Car Display Unit Encoder * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@xxxxxxxxxxxxxxxx) * @@ -118,7 +118,8 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_encoder_type type, enum rcar_du_output output, struct device_node *enc_node, - struct device_node *con_node) + struct device_node *con_node, + const char *device_name) { struct rcar_du_encoder *renc; struct drm_encoder *encoder; @@ -162,8 +163,12 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, break; } + renc->device_name = device_name; + if (type == RCAR_DU_ENCODER_HDMI) { ret = rcar_du_hdmienc_init(rcdu, renc, enc_node); + if (of_device_is_compatible(enc_node, "rockchip,rcar-dw-hdmi")) + goto done; if (ret < 0) goto done; } else { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index dde523a..51a4664 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -1,7 +1,7 @@ /* * rcar_du_encoder.h -- R-Car Display Unit Encoder * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@xxxxxxxxxxxxxxxx) * @@ -33,6 +33,7 @@ struct rcar_du_encoder { enum rcar_du_output output; struct rcar_du_hdmienc *hdmi; struct rcar_du_lvdsenc *lvds; + const char *device_name; }; #define to_rcar_encoder(e) \ @@ -55,6 +56,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_encoder_type type, enum rcar_du_output output, struct device_node *enc_node, - struct device_node *con_node); + struct device_node *con_node, + const char *device_name); #endif /* __RCAR_DU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index 15d553a..b8b89a0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -1,7 +1,7 @@ /* * R-Car Display Unit HDMI Encoder * - * Copyright (C) 2014 Renesas Electronics Corporation + * Copyright (C) 2014-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@xxxxxxxxxxxxxxxx) * @@ -13,10 +13,13 @@ #include <linux/slab.h> +#include <drm/bridge/dw_hdmi.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <linux/of_platform.h> + #include "rcar_du_drv.h" #include "rcar_du_encoder.h" #include "rcar_du_hdmienc.h" @@ -24,7 +27,9 @@ struct rcar_du_hdmienc { struct rcar_du_encoder *renc; + struct device *dev; bool enabled; + unsigned int index; }; #define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi) @@ -32,6 +37,10 @@ struct rcar_du_hdmienc { static void rcar_du_hdmienc_disable(struct drm_encoder *encoder) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); + const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs; + + if ((bfuncs) && (bfuncs->post_disable)) + bfuncs->post_disable(encoder->bridge); if (hdmienc->renc->lvds) rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, @@ -43,10 +52,13 @@ static void rcar_du_hdmienc_disable(struct drm_encoder *encoder) static void rcar_du_hdmienc_enable(struct drm_encoder *encoder) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); + const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs; if (hdmienc->renc->lvds) rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, true); + if ((bfuncs) && (bfuncs->enable)) + bfuncs->enable(encoder->bridge); hdmienc->enabled = true; } @@ -56,13 +68,19 @@ static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder, struct drm_connector_state *conn_state) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); + const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs; struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + const struct drm_display_mode *mode = &crtc_state->mode; + int ret = 0; if (hdmienc->renc->lvds) rcar_du_lvdsenc_atomic_check(hdmienc->renc->lvds, adjusted_mode); - return 0; + if ((bfuncs) && (bfuncs->mode_fixup)) + ret = bfuncs->mode_fixup(encoder->bridge, mode, + adjusted_mode) ? 0 : -EINVAL; + return ret; } static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, @@ -70,6 +88,10 @@ static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); + const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs; + + if ((bfuncs) && (bfuncs->mode_set)) + bfuncs->mode_set(encoder->bridge, mode, adjusted_mode); rcar_du_crtc_route_output(encoder->crtc, hdmienc->renc->output); } @@ -95,24 +117,187 @@ static const struct drm_encoder_funcs encoder_funcs = { .destroy = rcar_du_hdmienc_cleanup, }; +static const struct dw_hdmi_mpll_config rcar_du_hdmienc_mpll_cfg[] = { + { + 44900000, { + { 0x0003, 0x0000}, + { 0x0003, 0x0000}, + { 0x0003, 0x0000} + }, + }, { + 90000000, { + { 0x0002, 0x0000}, + { 0x0002, 0x0000}, + { 0x0002, 0x0000} + }, + }, { + 182750000, { + { 0x0001, 0x0000}, + { 0x0001, 0x0000}, + { 0x0001, 0x0000} + }, + }, { + 297000000, { + { 0x0000, 0x0000}, + { 0x0000, 0x0000}, + { 0x0000, 0x0000} + }, + }, { + ~0UL, { + { 0xFFFF, 0xFFFF }, + { 0xFFFF, 0xFFFF }, + { 0xFFFF, 0xFFFF }, + }, + } +}; +static const struct dw_hdmi_curr_ctrl rcar_du_hdmienc_cur_ctr[] = { + /* pixelclk bpp8 bpp10 bpp12 */ + { + 35500000, { 0x0344, 0x0000, 0x0000 }, + }, { + 44900000, { 0x0285, 0x0000, 0x0000 }, + }, { + 71000000, { 0x1184, 0x0000, 0x0000 }, + }, { + 90000000, { 0x1144, 0x0000, 0x0000 }, + }, { + 140250000, { 0x20c4, 0x0000, 0x0000 }, + }, { + 182750000, { 0x2084, 0x0000, 0x0000 }, + }, { + 297000000, { 0x0084, 0x0000, 0x0000 }, + }, { + ~0UL, { 0x0000, 0x0000, 0x0000 }, + } +}; + +static const struct dw_hdmi_multi_div rcar_du_hdmienc_multi_div[] = { + /* pixelclk bpp8 bpp10 bpp12 */ + { + 35500000, { 0x0328, 0x0000, 0x0000 }, + }, { + 44900000, { 0x0128, 0x0000, 0x0000 }, + }, { + 71000000, { 0x0314, 0x0000, 0x0000 }, + }, { + 90000000, { 0x0114, 0x0000, 0x0000 }, + }, { + 140250000, { 0x030a, 0x0000, 0x0000 }, + }, { + 182750000, { 0x010a, 0x0000, 0x0000 }, + }, { + 281250000, { 0x0305, 0x0000, 0x0000 }, + }, { + 297000000, { 0x0105, 0x0000, 0x0000 }, + }, { + ~0UL, { 0x0000, 0x0000, 0x0000 }, + } +}; + +static const struct dw_hdmi_phy_config rcar_du_hdmienc_phy_config[] = { + /*pixelclk symbol term vlev*/ + { 74250000, 0x8009, 0x0004, 0x0272}, + { 148500000, 0x802b, 0x0004, 0x028d}, + { 297000000, 0x8039, 0x0005, 0x028d}, + { ~0UL, 0x0000, 0x0000, 0x0000} +}; + +static enum drm_mode_status +rcar_du_hdmienc_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct dw_hdmi_plat_data rcar_du_hdmienc_hdmi0_drv_data = { + .mode_valid = rcar_du_hdmienc_mode_valid, + .mpll_cfg = rcar_du_hdmienc_mpll_cfg, + .cur_ctr = rcar_du_hdmienc_cur_ctr, + .multi_div = rcar_du_hdmienc_multi_div, + .phy_config = rcar_du_hdmienc_phy_config, + .dev_type = RCAR_HDMI, + .index = 0, +}; + +static const struct dw_hdmi_plat_data rcar_du_hdmienc_hdmi1_drv_data = { + .mode_valid = rcar_du_hdmienc_mode_valid, + .mpll_cfg = rcar_du_hdmienc_mpll_cfg, + .cur_ctr = rcar_du_hdmienc_cur_ctr, + .multi_div = rcar_du_hdmienc_multi_div, + .phy_config = rcar_du_hdmienc_phy_config, + .dev_type = RCAR_HDMI, + .index = 1, +}; + +static const struct of_device_id rcar_du_hdmienc_dt_ids[] = { + { + .data = &rcar_du_hdmienc_hdmi0_drv_data + }, + { + .data = &rcar_du_hdmienc_hdmi1_drv_data + }, + {}, +}; +MODULE_DEVICE_TABLE(of, rcar_du_hdmienc_dt_ids); + int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, struct rcar_du_encoder *renc, struct device_node *np) { struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc); - struct drm_bridge *bridge; struct rcar_du_hdmienc *hdmienc; - int ret; + struct resource *iores; + struct platform_device *pdev; + const struct dw_hdmi_plat_data *plat_data; + int ret, irq; + bool dw_hdmi_use = false; + struct drm_bridge *bridge = NULL; hdmienc = devm_kzalloc(rcdu->dev, sizeof(*hdmienc), GFP_KERNEL); if (hdmienc == NULL) return -ENOMEM; - /* Locate the DRM bridge from the HDMI encoder DT node. */ - bridge = of_drm_find_bridge(np); - if (!bridge) { - dev_dbg(rcdu->dev, "can't get bridge for %s, deferring probe\n", - of_node_full_name(np)); - return -EPROBE_DEFER; + if (strcmp(renc->device_name, "rockchip,rcar-dw-hdmi") == 0) + dw_hdmi_use = true; + + if (dw_hdmi_use) { + if (renc->output == RCAR_DU_OUTPUT_HDMI0) + hdmienc->index = 0; + else if (renc->output == RCAR_DU_OUTPUT_HDMI1) + hdmienc->index = 1; + else + return -EINVAL; + + np = of_parse_phandle(rcdu->dev->of_node, "hdmi", + hdmienc->index); + if (!np) { + dev_err(rcdu->dev, "hdmi node not found\n"); + return -ENXIO; + } + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return -ENXIO; + + plat_data = rcar_du_hdmienc_dt_ids[hdmienc->index].data; + hdmienc->dev = &pdev->dev; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -ENXIO; + + } else { + /* Locate the DRM bridge from the HDMI encoder DT node. */ + bridge = of_drm_find_bridge(np); + if (!bridge) { + dev_dbg(rcdu->dev, "can't get bridge for %s, deferring probe\n", + of_node_full_name(np)); + return -EPROBE_DEFER; + } } ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, @@ -126,13 +311,21 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, hdmienc->renc = renc; /* Link the bridge to the encoder. */ - bridge->encoder = encoder; - encoder->bridge = bridge; + if (bridge) { + bridge->encoder = encoder; + encoder->bridge = bridge; + } - ret = drm_bridge_attach(rcdu->ddev, bridge); - if (ret) { - drm_encoder_cleanup(encoder); - return ret; + if (dw_hdmi_use) + ret = dw_hdmi_bind(rcdu->dev, NULL, rcdu->ddev, encoder, + iores, irq, plat_data); + + if (bridge) { + ret = drm_bridge_attach(rcdu->ddev, bridge); + if (ret) { + drm_encoder_cleanup(encoder); + return ret; + } } return 0; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index e70a4f3..baac8c9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -362,6 +362,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, } encoders[] = { { "adi,adv7123", RCAR_DU_ENCODER_VGA }, { "adi,adv7511w", RCAR_DU_ENCODER_HDMI }, + { "rockchip,rcar-dw-hdmi", RCAR_DU_ENCODER_HDMI }, { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS }, }; @@ -372,6 +373,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, struct device_node *entity_ep_node; struct device_node *entity; int ret; + const char *enc_name = NULL; /* * Locate the connected entity and infer its type from the number of @@ -423,6 +425,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, if (of_device_is_compatible(encoder, encoders[i].compatible)) { enc_type = encoders[i].type; + enc_name = encoders[i].compatible; break; } } @@ -443,7 +446,8 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, connector = entity; } - ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector); + ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector, + enc_name); of_node_put(encoder); of_node_put(connector); -- 2.7.4 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel