linux/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <linux/mfd/syscon.h>
  13#include <linux/regmap.h>
  14#include <drm/drm_of.h>
  15#include <drm/drmP.h>
  16#include <drm/drm_crtc_helper.h>
  17#include <drm/drm_edid.h>
  18#include <drm/drm_encoder_slave.h>
  19#include <drm/bridge/dw_hdmi.h>
  20
  21#include "rockchip_drm_drv.h"
  22#include "rockchip_drm_vop.h"
  23
  24#define GRF_SOC_CON6                    0x025c
  25#define HDMI_SEL_VOP_LIT                (1 << 4)
  26
  27struct rockchip_hdmi {
  28        struct device *dev;
  29        struct regmap *regmap;
  30        struct drm_encoder encoder;
  31};
  32
  33#define to_rockchip_hdmi(x)     container_of(x, struct rockchip_hdmi, x)
  34
  35static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
  36        {
  37                27000000, {
  38                        { 0x00b3, 0x0000},
  39                        { 0x2153, 0x0000},
  40                        { 0x40f3, 0x0000}
  41                },
  42        }, {
  43                36000000, {
  44                        { 0x00b3, 0x0000},
  45                        { 0x2153, 0x0000},
  46                        { 0x40f3, 0x0000}
  47                },
  48        }, {
  49                40000000, {
  50                        { 0x00b3, 0x0000},
  51                        { 0x2153, 0x0000},
  52                        { 0x40f3, 0x0000}
  53                },
  54        }, {
  55                54000000, {
  56                        { 0x0072, 0x0001},
  57                        { 0x2142, 0x0001},
  58                        { 0x40a2, 0x0001},
  59                },
  60        }, {
  61                65000000, {
  62                        { 0x0072, 0x0001},
  63                        { 0x2142, 0x0001},
  64                        { 0x40a2, 0x0001},
  65                },
  66        }, {
  67                66000000, {
  68                        { 0x013e, 0x0003},
  69                        { 0x217e, 0x0002},
  70                        { 0x4061, 0x0002}
  71                },
  72        }, {
  73                74250000, {
  74                        { 0x0072, 0x0001},
  75                        { 0x2145, 0x0002},
  76                        { 0x4061, 0x0002}
  77                },
  78        }, {
  79                83500000, {
  80                        { 0x0072, 0x0001},
  81                },
  82        }, {
  83                108000000, {
  84                        { 0x0051, 0x0002},
  85                        { 0x2145, 0x0002},
  86                        { 0x4061, 0x0002}
  87                },
  88        }, {
  89                106500000, {
  90                        { 0x0051, 0x0002},
  91                        { 0x2145, 0x0002},
  92                        { 0x4061, 0x0002}
  93                },
  94        }, {
  95                146250000, {
  96                        { 0x0051, 0x0002},
  97                        { 0x2145, 0x0002},
  98                        { 0x4061, 0x0002}
  99                },
 100        }, {
 101                148500000, {
 102                        { 0x0051, 0x0003},
 103                        { 0x214c, 0x0003},
 104                        { 0x4064, 0x0003}
 105                },
 106        }, {
 107                ~0UL, {
 108                        { 0x00a0, 0x000a },
 109                        { 0x2001, 0x000f },
 110                        { 0x4002, 0x000f },
 111                },
 112        }
 113};
 114
 115static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 116        /*      pixelclk    bpp8    bpp10   bpp12 */
 117        {
 118                40000000,  { 0x0018, 0x0018, 0x0018 },
 119        }, {
 120                65000000,  { 0x0028, 0x0028, 0x0028 },
 121        }, {
 122                66000000,  { 0x0038, 0x0038, 0x0038 },
 123        }, {
 124                74250000,  { 0x0028, 0x0038, 0x0038 },
 125        }, {
 126                83500000,  { 0x0028, 0x0038, 0x0038 },
 127        }, {
 128                146250000, { 0x0038, 0x0038, 0x0038 },
 129        }, {
 130                148500000, { 0x0000, 0x0038, 0x0038 },
 131        }, {
 132                ~0UL,      { 0x0000, 0x0000, 0x0000},
 133        }
 134};
 135
 136static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 137        /*pixelclk   symbol   term   vlev*/
 138        { 74250000,  0x8009, 0x0004, 0x0272},
 139        { 148500000, 0x802b, 0x0004, 0x028d},
 140        { 297000000, 0x8039, 0x0005, 0x028d},
 141        { ~0UL,      0x0000, 0x0000, 0x0000}
 142};
 143
 144static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 145{
 146        struct device_node *np = hdmi->dev->of_node;
 147
 148        hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 149        if (IS_ERR(hdmi->regmap)) {
 150                dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
 151                return PTR_ERR(hdmi->regmap);
 152        }
 153
 154        return 0;
 155}
 156
 157static enum drm_mode_status
 158dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
 159                            struct drm_display_mode *mode)
 160{
 161        const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
 162        int pclk = mode->clock * 1000;
 163        bool valid = false;
 164        int i;
 165
 166        for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
 167                if (pclk == mpll_cfg[i].mpixelclock) {
 168                        valid = true;
 169                        break;
 170                }
 171        }
 172
 173        return (valid) ? MODE_OK : MODE_BAD;
 174}
 175
 176static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = {
 177        .destroy = drm_encoder_cleanup,
 178};
 179
 180static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
 181{
 182}
 183
 184static bool
 185dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
 186                                    const struct drm_display_mode *mode,
 187                                    struct drm_display_mode *adj_mode)
 188{
 189        return true;
 190}
 191
 192static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 193                                              struct drm_display_mode *mode,
 194                                              struct drm_display_mode *adj_mode)
 195{
 196}
 197
 198static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 199{
 200        struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 201        u32 val;
 202        int mux;
 203
 204        rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
 205                                      ROCKCHIP_OUT_MODE_AAAA);
 206
 207        mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
 208        if (mux)
 209                val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
 210        else
 211                val = HDMI_SEL_VOP_LIT << 16;
 212
 213        regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
 214        dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
 215                (mux) ? "LIT" : "BIG");
 216}
 217
 218static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
 219        .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
 220        .mode_set   = dw_hdmi_rockchip_encoder_mode_set,
 221        .enable     = dw_hdmi_rockchip_encoder_enable,
 222        .disable    = dw_hdmi_rockchip_encoder_disable,
 223};
 224
 225static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
 226        .mode_valid = dw_hdmi_rockchip_mode_valid,
 227        .mpll_cfg   = rockchip_mpll_cfg,
 228        .cur_ctr    = rockchip_cur_ctr,
 229        .phy_config = rockchip_phy_config,
 230        .dev_type   = RK3288_HDMI,
 231};
 232
 233static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 234        { .compatible = "rockchip,rk3288-dw-hdmi",
 235          .data = &rockchip_hdmi_drv_data
 236        },
 237        {},
 238};
 239MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
 240
 241static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 242                                 void *data)
 243{
 244        struct platform_device *pdev = to_platform_device(dev);
 245        const struct dw_hdmi_plat_data *plat_data;
 246        const struct of_device_id *match;
 247        struct drm_device *drm = data;
 248        struct drm_encoder *encoder;
 249        struct rockchip_hdmi *hdmi;
 250        struct resource *iores;
 251        int irq;
 252        int ret;
 253
 254        if (!pdev->dev.of_node)
 255                return -ENODEV;
 256
 257        hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
 258        if (!hdmi)
 259                return -ENOMEM;
 260
 261        match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
 262        plat_data = match->data;
 263        hdmi->dev = &pdev->dev;
 264        encoder = &hdmi->encoder;
 265
 266        irq = platform_get_irq(pdev, 0);
 267        if (irq < 0)
 268                return irq;
 269
 270        iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 271        if (!iores)
 272                return -ENXIO;
 273
 274        encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 275        /*
 276         * If we failed to find the CRTC(s) which this encoder is
 277         * supposed to be connected to, it's because the CRTC has
 278         * not been registered yet.  Defer probing, and hope that
 279         * the required CRTC is added later.
 280         */
 281        if (encoder->possible_crtcs == 0)
 282                return -EPROBE_DEFER;
 283
 284        ret = rockchip_hdmi_parse_dt(hdmi);
 285        if (ret) {
 286                dev_err(hdmi->dev, "Unable to parse OF data\n");
 287                return ret;
 288        }
 289
 290        drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
 291        drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
 292                         DRM_MODE_ENCODER_TMDS, NULL);
 293
 294        ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
 295
 296        /*
 297         * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
 298         * which would have called the encoder cleanup.  Do it manually.
 299         */
 300        if (ret)
 301                drm_encoder_cleanup(encoder);
 302
 303        return ret;
 304}
 305
 306static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 307                                    void *data)
 308{
 309        return dw_hdmi_unbind(dev, master, data);
 310}
 311
 312static const struct component_ops dw_hdmi_rockchip_ops = {
 313        .bind   = dw_hdmi_rockchip_bind,
 314        .unbind = dw_hdmi_rockchip_unbind,
 315};
 316
 317static int dw_hdmi_rockchip_probe(struct platform_device *pdev)
 318{
 319        return component_add(&pdev->dev, &dw_hdmi_rockchip_ops);
 320}
 321
 322static int dw_hdmi_rockchip_remove(struct platform_device *pdev)
 323{
 324        component_del(&pdev->dev, &dw_hdmi_rockchip_ops);
 325
 326        return 0;
 327}
 328
 329static struct platform_driver dw_hdmi_rockchip_pltfm_driver = {
 330        .probe  = dw_hdmi_rockchip_probe,
 331        .remove = dw_hdmi_rockchip_remove,
 332        .driver = {
 333                .name = "dwhdmi-rockchip",
 334                .of_match_table = dw_hdmi_rockchip_dt_ids,
 335        },
 336};
 337
 338module_platform_driver(dw_hdmi_rockchip_pltfm_driver);
 339
 340MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
 341MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
 342MODULE_DESCRIPTION("Rockchip Specific DW-HDMI Driver Extension");
 343MODULE_LICENSE("GPL");
 344MODULE_ALIAS("platform:dwhdmi-rockchip");
 345