282 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.3 *4 * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now)5 */6 7#include <linux/component.h>8#include <linux/mfd/syscon.h>9#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>10#include <linux/module.h>11#include <linux/platform_device.h>12#include <linux/regmap.h>13 14#include <video/imx-ipu-v3.h>15 16#include <drm/bridge/dw_hdmi.h>17#include <drm/drm_atomic_helper.h>18#include <drm/drm_bridge.h>19#include <drm/drm_edid.h>20#include <drm/drm_encoder.h>21#include <drm/drm_managed.h>22#include <drm/drm_of.h>23#include <drm/drm_simple_kms_helper.h>24 25#include "imx-drm.h"26 27struct imx_hdmi;28 29struct imx_hdmi_encoder {30 struct drm_encoder encoder;31 struct imx_hdmi *hdmi;32};33 34struct imx_hdmi {35 struct device *dev;36 struct drm_bridge *bridge;37 struct dw_hdmi *hdmi;38 struct regmap *regmap;39};40 41static inline struct imx_hdmi *enc_to_imx_hdmi(struct drm_encoder *e)42{43 return container_of(e, struct imx_hdmi_encoder, encoder)->hdmi;44}45 46static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {47 {48 45250000, {49 { 0x01e0, 0x0000 },50 { 0x21e1, 0x0000 },51 { 0x41e2, 0x0000 }52 },53 }, {54 92500000, {55 { 0x0140, 0x0005 },56 { 0x2141, 0x0005 },57 { 0x4142, 0x0005 },58 },59 }, {60 148500000, {61 { 0x00a0, 0x000a },62 { 0x20a1, 0x000a },63 { 0x40a2, 0x000a },64 },65 }, {66 216000000, {67 { 0x00a0, 0x000a },68 { 0x2001, 0x000f },69 { 0x4002, 0x000f },70 },71 }, {72 ~0UL, {73 { 0x0000, 0x0000 },74 { 0x0000, 0x0000 },75 { 0x0000, 0x0000 },76 },77 }78};79 80static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = {81 /* pixelclk bpp8 bpp10 bpp12 */82 {83 54000000, { 0x091c, 0x091c, 0x06dc },84 }, {85 58400000, { 0x091c, 0x06dc, 0x06dc },86 }, {87 72000000, { 0x06dc, 0x06dc, 0x091c },88 }, {89 74250000, { 0x06dc, 0x0b5c, 0x091c },90 }, {91 118800000, { 0x091c, 0x091c, 0x06dc },92 }, {93 216000000, { 0x06dc, 0x0b5c, 0x091c },94 }, {95 ~0UL, { 0x0000, 0x0000, 0x0000 },96 },97};98 99/*100 * Resistance term 133Ohm Cfg101 * PREEMP config 0.00102 * TX/CK level 10103 */104static const struct dw_hdmi_phy_config imx_phy_config[] = {105 /*pixelclk symbol term vlev */106 { 216000000, 0x800d, 0x0005, 0x01ad},107 { ~0UL, 0x0000, 0x0000, 0x0000}108};109 110static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder)111{112 struct imx_hdmi *hdmi = enc_to_imx_hdmi(encoder);113 int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder);114 115 regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,116 IMX6Q_GPR3_HDMI_MUX_CTL_MASK,117 mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);118}119 120static int dw_hdmi_imx_atomic_check(struct drm_encoder *encoder,121 struct drm_crtc_state *crtc_state,122 struct drm_connector_state *conn_state)123{124 struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);125 126 imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;127 imx_crtc_state->di_hsync_pin = 2;128 imx_crtc_state->di_vsync_pin = 3;129 130 return 0;131}132 133static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {134 .enable = dw_hdmi_imx_encoder_enable,135 .atomic_check = dw_hdmi_imx_atomic_check,136};137 138static enum drm_mode_status139imx6q_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,140 const struct drm_display_info *info,141 const struct drm_display_mode *mode)142{143 if (mode->clock < 13500)144 return MODE_CLOCK_LOW;145 /* FIXME: Hardware is capable of 266MHz, but setup data is missing. */146 if (mode->clock > 216000)147 return MODE_CLOCK_HIGH;148 149 return MODE_OK;150}151 152static enum drm_mode_status153imx6dl_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,154 const struct drm_display_info *info,155 const struct drm_display_mode *mode)156{157 if (mode->clock < 13500)158 return MODE_CLOCK_LOW;159 /* FIXME: Hardware is capable of 270MHz, but setup data is missing. */160 if (mode->clock > 216000)161 return MODE_CLOCK_HIGH;162 163 return MODE_OK;164}165 166static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = {167 .mpll_cfg = imx_mpll_cfg,168 .cur_ctr = imx_cur_ctr,169 .phy_config = imx_phy_config,170 .mode_valid = imx6q_hdmi_mode_valid,171};172 173static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = {174 .mpll_cfg = imx_mpll_cfg,175 .cur_ctr = imx_cur_ctr,176 .phy_config = imx_phy_config,177 .mode_valid = imx6dl_hdmi_mode_valid,178};179 180static const struct of_device_id dw_hdmi_imx_dt_ids[] = {181 { .compatible = "fsl,imx6q-hdmi",182 .data = &imx6q_hdmi_drv_data183 }, {184 .compatible = "fsl,imx6dl-hdmi",185 .data = &imx6dl_hdmi_drv_data186 },187 {},188};189MODULE_DEVICE_TABLE(of, dw_hdmi_imx_dt_ids);190 191static int dw_hdmi_imx_bind(struct device *dev, struct device *master,192 void *data)193{194 struct drm_device *drm = data;195 struct imx_hdmi_encoder *hdmi_encoder;196 struct drm_encoder *encoder;197 int ret;198 199 hdmi_encoder = drmm_simple_encoder_alloc(drm, struct imx_hdmi_encoder,200 encoder, DRM_MODE_ENCODER_TMDS);201 if (IS_ERR(hdmi_encoder))202 return PTR_ERR(hdmi_encoder);203 204 hdmi_encoder->hdmi = dev_get_drvdata(dev);205 encoder = &hdmi_encoder->encoder;206 207 ret = imx_drm_encoder_parse_of(drm, encoder, dev->of_node);208 if (ret)209 return ret;210 211 drm_encoder_helper_add(encoder, &dw_hdmi_imx_encoder_helper_funcs);212 213 return drm_bridge_attach(encoder, hdmi_encoder->hdmi->bridge, NULL, 0);214}215 216static const struct component_ops dw_hdmi_imx_ops = {217 .bind = dw_hdmi_imx_bind,218};219 220static int dw_hdmi_imx_probe(struct platform_device *pdev)221{222 struct device_node *np = pdev->dev.of_node;223 const struct of_device_id *match = of_match_node(dw_hdmi_imx_dt_ids, np);224 struct imx_hdmi *hdmi;225 int ret;226 227 hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);228 if (!hdmi)229 return -ENOMEM;230 231 platform_set_drvdata(pdev, hdmi);232 hdmi->dev = &pdev->dev;233 234 hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");235 if (IS_ERR(hdmi->regmap)) {236 dev_err(hdmi->dev, "Unable to get gpr\n");237 return PTR_ERR(hdmi->regmap);238 }239 240 hdmi->hdmi = dw_hdmi_probe(pdev, match->data);241 if (IS_ERR(hdmi->hdmi))242 return PTR_ERR(hdmi->hdmi);243 244 hdmi->bridge = of_drm_find_bridge(np);245 if (!hdmi->bridge) {246 dev_err(hdmi->dev, "Unable to find bridge\n");247 dw_hdmi_remove(hdmi->hdmi);248 return -ENODEV;249 }250 251 ret = component_add(&pdev->dev, &dw_hdmi_imx_ops);252 if (ret)253 dw_hdmi_remove(hdmi->hdmi);254 255 return ret;256}257 258static void dw_hdmi_imx_remove(struct platform_device *pdev)259{260 struct imx_hdmi *hdmi = platform_get_drvdata(pdev);261 262 component_del(&pdev->dev, &dw_hdmi_imx_ops);263 dw_hdmi_remove(hdmi->hdmi);264}265 266static struct platform_driver dw_hdmi_imx_platform_driver = {267 .probe = dw_hdmi_imx_probe,268 .remove_new = dw_hdmi_imx_remove,269 .driver = {270 .name = "dwhdmi-imx",271 .of_match_table = dw_hdmi_imx_dt_ids,272 },273};274 275module_platform_driver(dw_hdmi_imx_platform_driver);276 277MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");278MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");279MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");280MODULE_LICENSE("GPL");281MODULE_ALIAS("platform:dwhdmi-imx");282