Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
0003  *
0004  * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now)
0005  */
0006 
0007 #include <linux/component.h>
0008 #include <linux/mfd/syscon.h>
0009 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/regmap.h>
0013 
0014 #include <video/imx-ipu-v3.h>
0015 
0016 #include <drm/bridge/dw_hdmi.h>
0017 #include <drm/drm_atomic_helper.h>
0018 #include <drm/drm_bridge.h>
0019 #include <drm/drm_edid.h>
0020 #include <drm/drm_encoder.h>
0021 #include <drm/drm_managed.h>
0022 #include <drm/drm_of.h>
0023 #include <drm/drm_simple_kms_helper.h>
0024 
0025 #include "imx-drm.h"
0026 
0027 struct imx_hdmi;
0028 
0029 struct imx_hdmi_encoder {
0030     struct drm_encoder encoder;
0031     struct imx_hdmi *hdmi;
0032 };
0033 
0034 struct imx_hdmi {
0035     struct device *dev;
0036     struct drm_bridge *bridge;
0037     struct dw_hdmi *hdmi;
0038     struct regmap *regmap;
0039 };
0040 
0041 static inline struct imx_hdmi *enc_to_imx_hdmi(struct drm_encoder *e)
0042 {
0043     return container_of(e, struct imx_hdmi_encoder, encoder)->hdmi;
0044 }
0045 
0046 static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {
0047     {
0048         45250000, {
0049             { 0x01e0, 0x0000 },
0050             { 0x21e1, 0x0000 },
0051             { 0x41e2, 0x0000 }
0052         },
0053     }, {
0054         92500000, {
0055             { 0x0140, 0x0005 },
0056             { 0x2141, 0x0005 },
0057             { 0x4142, 0x0005 },
0058     },
0059     }, {
0060         148500000, {
0061             { 0x00a0, 0x000a },
0062             { 0x20a1, 0x000a },
0063             { 0x40a2, 0x000a },
0064         },
0065     }, {
0066         216000000, {
0067             { 0x00a0, 0x000a },
0068             { 0x2001, 0x000f },
0069             { 0x4002, 0x000f },
0070         },
0071     }, {
0072         ~0UL, {
0073             { 0x0000, 0x0000 },
0074             { 0x0000, 0x0000 },
0075             { 0x0000, 0x0000 },
0076         },
0077     }
0078 };
0079 
0080 static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = {
0081     /*      pixelclk     bpp8    bpp10   bpp12 */
0082     {
0083         54000000, { 0x091c, 0x091c, 0x06dc },
0084     }, {
0085         58400000, { 0x091c, 0x06dc, 0x06dc },
0086     }, {
0087         72000000, { 0x06dc, 0x06dc, 0x091c },
0088     }, {
0089         74250000, { 0x06dc, 0x0b5c, 0x091c },
0090     }, {
0091         118800000, { 0x091c, 0x091c, 0x06dc },
0092     }, {
0093         216000000, { 0x06dc, 0x0b5c, 0x091c },
0094     }, {
0095         ~0UL, { 0x0000, 0x0000, 0x0000 },
0096     },
0097 };
0098 
0099 /*
0100  * Resistance term 133Ohm Cfg
0101  * PREEMP config 0.00
0102  * TX/CK level 10
0103  */
0104 static const struct dw_hdmi_phy_config imx_phy_config[] = {
0105     /*pixelclk   symbol   term   vlev */
0106     { 216000000, 0x800d, 0x0005, 0x01ad},
0107     { ~0UL,      0x0000, 0x0000, 0x0000}
0108 };
0109 
0110 static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder)
0111 {
0112     struct imx_hdmi *hdmi = enc_to_imx_hdmi(encoder);
0113     int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder);
0114 
0115     regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
0116                IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
0117                mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
0118 }
0119 
0120 static int dw_hdmi_imx_atomic_check(struct drm_encoder *encoder,
0121                     struct drm_crtc_state *crtc_state,
0122                     struct drm_connector_state *conn_state)
0123 {
0124     struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
0125 
0126     imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
0127     imx_crtc_state->di_hsync_pin = 2;
0128     imx_crtc_state->di_vsync_pin = 3;
0129 
0130     return 0;
0131 }
0132 
0133 static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {
0134     .enable     = dw_hdmi_imx_encoder_enable,
0135     .atomic_check = dw_hdmi_imx_atomic_check,
0136 };
0137 
0138 static enum drm_mode_status
0139 imx6q_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
0140               const struct drm_display_info *info,
0141               const struct drm_display_mode *mode)
0142 {
0143     if (mode->clock < 13500)
0144         return MODE_CLOCK_LOW;
0145     /* FIXME: Hardware is capable of 266MHz, but setup data is missing. */
0146     if (mode->clock > 216000)
0147         return MODE_CLOCK_HIGH;
0148 
0149     return MODE_OK;
0150 }
0151 
0152 static enum drm_mode_status
0153 imx6dl_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
0154                const struct drm_display_info *info,
0155                const struct drm_display_mode *mode)
0156 {
0157     if (mode->clock < 13500)
0158         return MODE_CLOCK_LOW;
0159     /* FIXME: Hardware is capable of 270MHz, but setup data is missing. */
0160     if (mode->clock > 216000)
0161         return MODE_CLOCK_HIGH;
0162 
0163     return MODE_OK;
0164 }
0165 
0166 static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = {
0167     .mpll_cfg   = imx_mpll_cfg,
0168     .cur_ctr    = imx_cur_ctr,
0169     .phy_config = imx_phy_config,
0170     .mode_valid = imx6q_hdmi_mode_valid,
0171 };
0172 
0173 static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = {
0174     .mpll_cfg = imx_mpll_cfg,
0175     .cur_ctr  = imx_cur_ctr,
0176     .phy_config = imx_phy_config,
0177     .mode_valid = imx6dl_hdmi_mode_valid,
0178 };
0179 
0180 static const struct of_device_id dw_hdmi_imx_dt_ids[] = {
0181     { .compatible = "fsl,imx6q-hdmi",
0182       .data = &imx6q_hdmi_drv_data
0183     }, {
0184       .compatible = "fsl,imx6dl-hdmi",
0185       .data = &imx6dl_hdmi_drv_data
0186     },
0187     {},
0188 };
0189 MODULE_DEVICE_TABLE(of, dw_hdmi_imx_dt_ids);
0190 
0191 static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
0192                 void *data)
0193 {
0194     struct drm_device *drm = data;
0195     struct imx_hdmi_encoder *hdmi_encoder;
0196     struct drm_encoder *encoder;
0197     int ret;
0198 
0199     hdmi_encoder = drmm_simple_encoder_alloc(drm, struct imx_hdmi_encoder,
0200                          encoder, DRM_MODE_ENCODER_TMDS);
0201     if (IS_ERR(hdmi_encoder))
0202         return PTR_ERR(hdmi_encoder);
0203 
0204     hdmi_encoder->hdmi = dev_get_drvdata(dev);
0205     encoder = &hdmi_encoder->encoder;
0206 
0207     ret = imx_drm_encoder_parse_of(drm, encoder, dev->of_node);
0208     if (ret)
0209         return ret;
0210 
0211     drm_encoder_helper_add(encoder, &dw_hdmi_imx_encoder_helper_funcs);
0212 
0213     return drm_bridge_attach(encoder, hdmi_encoder->hdmi->bridge, NULL, 0);
0214 }
0215 
0216 static const struct component_ops dw_hdmi_imx_ops = {
0217     .bind   = dw_hdmi_imx_bind,
0218 };
0219 
0220 static int dw_hdmi_imx_probe(struct platform_device *pdev)
0221 {
0222     struct device_node *np = pdev->dev.of_node;
0223     const struct of_device_id *match = of_match_node(dw_hdmi_imx_dt_ids, np);
0224     struct imx_hdmi *hdmi;
0225     int ret;
0226 
0227     hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
0228     if (!hdmi)
0229         return -ENOMEM;
0230 
0231     platform_set_drvdata(pdev, hdmi);
0232     hdmi->dev = &pdev->dev;
0233 
0234     hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
0235     if (IS_ERR(hdmi->regmap)) {
0236         dev_err(hdmi->dev, "Unable to get gpr\n");
0237         return PTR_ERR(hdmi->regmap);
0238     }
0239 
0240     hdmi->hdmi = dw_hdmi_probe(pdev, match->data);
0241     if (IS_ERR(hdmi->hdmi))
0242         return PTR_ERR(hdmi->hdmi);
0243 
0244     hdmi->bridge = of_drm_find_bridge(np);
0245     if (!hdmi->bridge) {
0246         dev_err(hdmi->dev, "Unable to find bridge\n");
0247         dw_hdmi_remove(hdmi->hdmi);
0248         return -ENODEV;
0249     }
0250 
0251     ret = component_add(&pdev->dev, &dw_hdmi_imx_ops);
0252     if (ret)
0253         dw_hdmi_remove(hdmi->hdmi);
0254 
0255     return ret;
0256 }
0257 
0258 static int dw_hdmi_imx_remove(struct platform_device *pdev)
0259 {
0260     struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
0261 
0262     component_del(&pdev->dev, &dw_hdmi_imx_ops);
0263     dw_hdmi_remove(hdmi->hdmi);
0264 
0265     return 0;
0266 }
0267 
0268 static struct platform_driver dw_hdmi_imx_platform_driver = {
0269     .probe  = dw_hdmi_imx_probe,
0270     .remove = dw_hdmi_imx_remove,
0271     .driver = {
0272         .name = "dwhdmi-imx",
0273         .of_match_table = dw_hdmi_imx_dt_ids,
0274     },
0275 };
0276 
0277 module_platform_driver(dw_hdmi_imx_platform_driver);
0278 
0279 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
0280 MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
0281 MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
0282 MODULE_LICENSE("GPL");
0283 MODULE_ALIAS("platform:dwhdmi-imx");