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