linux/drivers/gpu/drm/imx/dw_hdmi-imx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* 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 Cfg
 101 * PREEMP config 0.00
 102 * TX/CK level 10
 103 */
 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_status
 139imx6q_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_status
 153imx6dl_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_data
 183        }, {
 184          .compatible = "fsl,imx6dl-hdmi",
 185          .data = &imx6dl_hdmi_drv_data
 186        },
 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
 226        hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
 227        if (!hdmi)
 228                return -ENOMEM;
 229
 230        platform_set_drvdata(pdev, hdmi);
 231        hdmi->dev = &pdev->dev;
 232
 233        hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
 234        if (IS_ERR(hdmi->regmap)) {
 235                dev_err(hdmi->dev, "Unable to get gpr\n");
 236                return PTR_ERR(hdmi->regmap);
 237        }
 238
 239        hdmi->hdmi = dw_hdmi_probe(pdev, match->data);
 240        if (IS_ERR(hdmi->hdmi))
 241                return PTR_ERR(hdmi->hdmi);
 242
 243        hdmi->bridge = of_drm_find_bridge(np);
 244        if (!hdmi->bridge) {
 245                dev_err(hdmi->dev, "Unable to find bridge\n");
 246                return -ENODEV;
 247        }
 248
 249        return component_add(&pdev->dev, &dw_hdmi_imx_ops);
 250}
 251
 252static int dw_hdmi_imx_remove(struct platform_device *pdev)
 253{
 254        struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
 255
 256        component_del(&pdev->dev, &dw_hdmi_imx_ops);
 257        dw_hdmi_remove(hdmi->hdmi);
 258
 259        return 0;
 260}
 261
 262static struct platform_driver dw_hdmi_imx_platform_driver = {
 263        .probe  = dw_hdmi_imx_probe,
 264        .remove = dw_hdmi_imx_remove,
 265        .driver = {
 266                .name = "dwhdmi-imx",
 267                .of_match_table = dw_hdmi_imx_dt_ids,
 268        },
 269};
 270
 271module_platform_driver(dw_hdmi_imx_platform_driver);
 272
 273MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
 274MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
 275MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
 276MODULE_LICENSE("GPL");
 277MODULE_ALIAS("platform:dwhdmi-imx");
 278