0001
0002
0003
0004
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
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
0101
0102
0103
0104 static const struct dw_hdmi_phy_config imx_phy_config[] = {
0105
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
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
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");