linux/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
   4 */
   5
   6#include <linux/clk.h>
   7#include <linux/mfd/syscon.h>
   8#include <linux/module.h>
   9#include <linux/platform_device.h>
  10#include <linux/phy/phy.h>
  11#include <linux/regmap.h>
  12
  13#include <drm/drm_of.h>
  14#include <drm/drmP.h>
  15#include <drm/drm_edid.h>
  16#include <drm/drm_probe_helper.h>
  17#include <drm/bridge/dw_hdmi.h>
  18
  19#include "rockchip_drm_drv.h"
  20#include "rockchip_drm_vop.h"
  21
  22#define RK3288_GRF_SOC_CON6             0x025C
  23#define RK3288_HDMI_LCDC_SEL            BIT(4)
  24#define RK3328_GRF_SOC_CON2             0x0408
  25
  26#define RK3328_HDMI_SDAIN_MSK           BIT(11)
  27#define RK3328_HDMI_SCLIN_MSK           BIT(10)
  28#define RK3328_HDMI_HPD_IOE             BIT(2)
  29#define RK3328_GRF_SOC_CON3             0x040c
  30/* need to be unset if hdmi or i2c should control voltage */
  31#define RK3328_HDMI_SDA5V_GRF           BIT(15)
  32#define RK3328_HDMI_SCL5V_GRF           BIT(14)
  33#define RK3328_HDMI_HPD5V_GRF           BIT(13)
  34#define RK3328_HDMI_CEC5V_GRF           BIT(12)
  35#define RK3328_GRF_SOC_CON4             0x0410
  36#define RK3328_HDMI_HPD_SARADC          BIT(13)
  37#define RK3328_HDMI_CEC_5V              BIT(11)
  38#define RK3328_HDMI_SDA_5V              BIT(10)
  39#define RK3328_HDMI_SCL_5V              BIT(9)
  40#define RK3328_HDMI_HPD_5V              BIT(8)
  41
  42#define RK3399_GRF_SOC_CON20            0x6250
  43#define RK3399_HDMI_LCDC_SEL            BIT(6)
  44
  45#define HIWORD_UPDATE(val, mask)        (val | (mask) << 16)
  46
  47/**
  48 * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips
  49 * @lcdsel_grf_reg: grf register offset of lcdc select
  50 * @lcdsel_big: reg value of selecting vop big for HDMI
  51 * @lcdsel_lit: reg value of selecting vop little for HDMI
  52 */
  53struct rockchip_hdmi_chip_data {
  54        int     lcdsel_grf_reg;
  55        u32     lcdsel_big;
  56        u32     lcdsel_lit;
  57};
  58
  59struct rockchip_hdmi {
  60        struct device *dev;
  61        struct regmap *regmap;
  62        struct drm_encoder encoder;
  63        const struct rockchip_hdmi_chip_data *chip_data;
  64        struct clk *vpll_clk;
  65        struct clk *grf_clk;
  66        struct dw_hdmi *hdmi;
  67        struct phy *phy;
  68};
  69
  70#define to_rockchip_hdmi(x)     container_of(x, struct rockchip_hdmi, x)
  71
  72static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
  73        {
  74                27000000, {
  75                        { 0x00b3, 0x0000},
  76                        { 0x2153, 0x0000},
  77                        { 0x40f3, 0x0000}
  78                },
  79        }, {
  80                36000000, {
  81                        { 0x00b3, 0x0000},
  82                        { 0x2153, 0x0000},
  83                        { 0x40f3, 0x0000}
  84                },
  85        }, {
  86                40000000, {
  87                        { 0x00b3, 0x0000},
  88                        { 0x2153, 0x0000},
  89                        { 0x40f3, 0x0000}
  90                },
  91        }, {
  92                54000000, {
  93                        { 0x0072, 0x0001},
  94                        { 0x2142, 0x0001},
  95                        { 0x40a2, 0x0001},
  96                },
  97        }, {
  98                65000000, {
  99                        { 0x0072, 0x0001},
 100                        { 0x2142, 0x0001},
 101                        { 0x40a2, 0x0001},
 102                },
 103        }, {
 104                66000000, {
 105                        { 0x013e, 0x0003},
 106                        { 0x217e, 0x0002},
 107                        { 0x4061, 0x0002}
 108                },
 109        }, {
 110                74250000, {
 111                        { 0x0072, 0x0001},
 112                        { 0x2145, 0x0002},
 113                        { 0x4061, 0x0002}
 114                },
 115        }, {
 116                83500000, {
 117                        { 0x0072, 0x0001},
 118                },
 119        }, {
 120                108000000, {
 121                        { 0x0051, 0x0002},
 122                        { 0x2145, 0x0002},
 123                        { 0x4061, 0x0002}
 124                },
 125        }, {
 126                106500000, {
 127                        { 0x0051, 0x0002},
 128                        { 0x2145, 0x0002},
 129                        { 0x4061, 0x0002}
 130                },
 131        }, {
 132                146250000, {
 133                        { 0x0051, 0x0002},
 134                        { 0x2145, 0x0002},
 135                        { 0x4061, 0x0002}
 136                },
 137        }, {
 138                148500000, {
 139                        { 0x0051, 0x0003},
 140                        { 0x214c, 0x0003},
 141                        { 0x4064, 0x0003}
 142                },
 143        }, {
 144                ~0UL, {
 145                        { 0x00a0, 0x000a },
 146                        { 0x2001, 0x000f },
 147                        { 0x4002, 0x000f },
 148                },
 149        }
 150};
 151
 152static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 153        /*      pixelclk    bpp8    bpp10   bpp12 */
 154        {
 155                40000000,  { 0x0018, 0x0018, 0x0018 },
 156        }, {
 157                65000000,  { 0x0028, 0x0028, 0x0028 },
 158        }, {
 159                66000000,  { 0x0038, 0x0038, 0x0038 },
 160        }, {
 161                74250000,  { 0x0028, 0x0038, 0x0038 },
 162        }, {
 163                83500000,  { 0x0028, 0x0038, 0x0038 },
 164        }, {
 165                146250000, { 0x0038, 0x0038, 0x0038 },
 166        }, {
 167                148500000, { 0x0000, 0x0038, 0x0038 },
 168        }, {
 169                ~0UL,      { 0x0000, 0x0000, 0x0000},
 170        }
 171};
 172
 173static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 174        /*pixelclk   symbol   term   vlev*/
 175        { 74250000,  0x8009, 0x0004, 0x0272},
 176        { 148500000, 0x802b, 0x0004, 0x028d},
 177        { 297000000, 0x8039, 0x0005, 0x028d},
 178        { ~0UL,      0x0000, 0x0000, 0x0000}
 179};
 180
 181static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 182{
 183        struct device_node *np = hdmi->dev->of_node;
 184
 185        hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 186        if (IS_ERR(hdmi->regmap)) {
 187                DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
 188                return PTR_ERR(hdmi->regmap);
 189        }
 190
 191        hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
 192        if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
 193                hdmi->vpll_clk = NULL;
 194        } else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
 195                return -EPROBE_DEFER;
 196        } else if (IS_ERR(hdmi->vpll_clk)) {
 197                DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
 198                return PTR_ERR(hdmi->vpll_clk);
 199        }
 200
 201        hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
 202        if (PTR_ERR(hdmi->grf_clk) == -ENOENT) {
 203                hdmi->grf_clk = NULL;
 204        } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
 205                return -EPROBE_DEFER;
 206        } else if (IS_ERR(hdmi->grf_clk)) {
 207                DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
 208                return PTR_ERR(hdmi->grf_clk);
 209        }
 210
 211        return 0;
 212}
 213
 214static enum drm_mode_status
 215dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
 216                            const struct drm_display_mode *mode)
 217{
 218        const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
 219        int pclk = mode->clock * 1000;
 220        bool valid = false;
 221        int i;
 222
 223        for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
 224                if (pclk == mpll_cfg[i].mpixelclock) {
 225                        valid = true;
 226                        break;
 227                }
 228        }
 229
 230        return (valid) ? MODE_OK : MODE_BAD;
 231}
 232
 233static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = {
 234        .destroy = drm_encoder_cleanup,
 235};
 236
 237static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
 238{
 239}
 240
 241static bool
 242dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
 243                                    const struct drm_display_mode *mode,
 244                                    struct drm_display_mode *adj_mode)
 245{
 246        return true;
 247}
 248
 249static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 250                                              struct drm_display_mode *mode,
 251                                              struct drm_display_mode *adj_mode)
 252{
 253        struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 254
 255        clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
 256}
 257
 258static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 259{
 260        struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 261        u32 val;
 262        int ret;
 263
 264        if (hdmi->chip_data->lcdsel_grf_reg < 0)
 265                return;
 266
 267        ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
 268        if (ret)
 269                val = hdmi->chip_data->lcdsel_lit;
 270        else
 271                val = hdmi->chip_data->lcdsel_big;
 272
 273        ret = clk_prepare_enable(hdmi->grf_clk);
 274        if (ret < 0) {
 275                DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret);
 276                return;
 277        }
 278
 279        ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
 280        if (ret != 0)
 281                DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
 282
 283        clk_disable_unprepare(hdmi->grf_clk);
 284        DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n",
 285                      ret ? "LIT" : "BIG");
 286}
 287
 288static int
 289dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
 290                                      struct drm_crtc_state *crtc_state,
 291                                      struct drm_connector_state *conn_state)
 292{
 293        struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 294
 295        s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 296        s->output_type = DRM_MODE_CONNECTOR_HDMIA;
 297
 298        return 0;
 299}
 300
 301static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
 302        .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
 303        .mode_set   = dw_hdmi_rockchip_encoder_mode_set,
 304        .enable     = dw_hdmi_rockchip_encoder_enable,
 305        .disable    = dw_hdmi_rockchip_encoder_disable,
 306        .atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
 307};
 308
 309static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
 310                             struct drm_display_mode *mode)
 311{
 312        struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 313
 314        return phy_power_on(hdmi->phy);
 315}
 316
 317static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data)
 318{
 319        struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 320
 321        phy_power_off(hdmi->phy);
 322}
 323
 324static enum drm_connector_status
 325dw_hdmi_rk3328_read_hpd(struct dw_hdmi *dw_hdmi, void *data)
 326{
 327        struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 328        enum drm_connector_status status;
 329
 330        status = dw_hdmi_phy_read_hpd(dw_hdmi, data);
 331
 332        if (status == connector_status_connected)
 333                regmap_write(hdmi->regmap,
 334                        RK3328_GRF_SOC_CON4,
 335                        HIWORD_UPDATE(RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V,
 336                                      RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V));
 337        else
 338                regmap_write(hdmi->regmap,
 339                        RK3328_GRF_SOC_CON4,
 340                        HIWORD_UPDATE(0, RK3328_HDMI_SDA_5V |
 341                                         RK3328_HDMI_SCL_5V));
 342        return status;
 343}
 344
 345static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
 346{
 347        struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 348
 349        dw_hdmi_phy_setup_hpd(dw_hdmi, data);
 350
 351        /* Enable and map pins to 3V grf-controlled io-voltage */
 352        regmap_write(hdmi->regmap,
 353                RK3328_GRF_SOC_CON4,
 354                HIWORD_UPDATE(0, RK3328_HDMI_HPD_SARADC | RK3328_HDMI_CEC_5V |
 355                                 RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V |
 356                                 RK3328_HDMI_HPD_5V));
 357        regmap_write(hdmi->regmap,
 358                RK3328_GRF_SOC_CON3,
 359                HIWORD_UPDATE(0, RK3328_HDMI_SDA5V_GRF | RK3328_HDMI_SCL5V_GRF |
 360                                 RK3328_HDMI_HPD5V_GRF |
 361                                 RK3328_HDMI_CEC5V_GRF));
 362        regmap_write(hdmi->regmap,
 363                RK3328_GRF_SOC_CON2,
 364                HIWORD_UPDATE(RK3328_HDMI_SDAIN_MSK | RK3328_HDMI_SCLIN_MSK,
 365                              RK3328_HDMI_SDAIN_MSK | RK3328_HDMI_SCLIN_MSK |
 366                              RK3328_HDMI_HPD_IOE));
 367}
 368
 369static struct rockchip_hdmi_chip_data rk3288_chip_data = {
 370        .lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
 371        .lcdsel_big = HIWORD_UPDATE(0, RK3288_HDMI_LCDC_SEL),
 372        .lcdsel_lit = HIWORD_UPDATE(RK3288_HDMI_LCDC_SEL, RK3288_HDMI_LCDC_SEL),
 373};
 374
 375static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
 376        .mode_valid = dw_hdmi_rockchip_mode_valid,
 377        .mpll_cfg   = rockchip_mpll_cfg,
 378        .cur_ctr    = rockchip_cur_ctr,
 379        .phy_config = rockchip_phy_config,
 380        .phy_data = &rk3288_chip_data,
 381};
 382
 383static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = {
 384        .init           = dw_hdmi_rockchip_genphy_init,
 385        .disable        = dw_hdmi_rockchip_genphy_disable,
 386        .read_hpd       = dw_hdmi_rk3328_read_hpd,
 387        .update_hpd     = dw_hdmi_phy_update_hpd,
 388        .setup_hpd      = dw_hdmi_rk3328_setup_hpd,
 389};
 390
 391static struct rockchip_hdmi_chip_data rk3328_chip_data = {
 392        .lcdsel_grf_reg = -1,
 393};
 394
 395static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
 396        .mode_valid = dw_hdmi_rockchip_mode_valid,
 397        .mpll_cfg = rockchip_mpll_cfg,
 398        .cur_ctr = rockchip_cur_ctr,
 399        .phy_config = rockchip_phy_config,
 400        .phy_data = &rk3328_chip_data,
 401        .phy_ops = &rk3328_hdmi_phy_ops,
 402        .phy_name = "inno_dw_hdmi_phy2",
 403        .phy_force_vendor = true,
 404};
 405
 406static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 407        .lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
 408        .lcdsel_big = HIWORD_UPDATE(0, RK3399_HDMI_LCDC_SEL),
 409        .lcdsel_lit = HIWORD_UPDATE(RK3399_HDMI_LCDC_SEL, RK3399_HDMI_LCDC_SEL),
 410};
 411
 412static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
 413        .mode_valid = dw_hdmi_rockchip_mode_valid,
 414        .mpll_cfg   = rockchip_mpll_cfg,
 415        .cur_ctr    = rockchip_cur_ctr,
 416        .phy_config = rockchip_phy_config,
 417        .phy_data = &rk3399_chip_data,
 418};
 419
 420static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 421        { .compatible = "rockchip,rk3288-dw-hdmi",
 422          .data = &rk3288_hdmi_drv_data
 423        },
 424        { .compatible = "rockchip,rk3328-dw-hdmi",
 425          .data = &rk3328_hdmi_drv_data
 426        },
 427        { .compatible = "rockchip,rk3399-dw-hdmi",
 428          .data = &rk3399_hdmi_drv_data
 429        },
 430        {},
 431};
 432MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
 433
 434static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 435                                 void *data)
 436{
 437        struct platform_device *pdev = to_platform_device(dev);
 438        struct dw_hdmi_plat_data *plat_data;
 439        const struct of_device_id *match;
 440        struct drm_device *drm = data;
 441        struct drm_encoder *encoder;
 442        struct rockchip_hdmi *hdmi;
 443        int ret;
 444
 445        if (!pdev->dev.of_node)
 446                return -ENODEV;
 447
 448        hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
 449        if (!hdmi)
 450                return -ENOMEM;
 451
 452        match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
 453        plat_data = devm_kmemdup(&pdev->dev, match->data,
 454                                             sizeof(*plat_data), GFP_KERNEL);
 455        if (!plat_data)
 456                return -ENOMEM;
 457
 458        hdmi->dev = &pdev->dev;
 459        hdmi->chip_data = plat_data->phy_data;
 460        plat_data->phy_data = hdmi;
 461        encoder = &hdmi->encoder;
 462
 463        encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 464        /*
 465         * If we failed to find the CRTC(s) which this encoder is
 466         * supposed to be connected to, it's because the CRTC has
 467         * not been registered yet.  Defer probing, and hope that
 468         * the required CRTC is added later.
 469         */
 470        if (encoder->possible_crtcs == 0)
 471                return -EPROBE_DEFER;
 472
 473        ret = rockchip_hdmi_parse_dt(hdmi);
 474        if (ret) {
 475                DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n");
 476                return ret;
 477        }
 478
 479        ret = clk_prepare_enable(hdmi->vpll_clk);
 480        if (ret) {
 481                DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
 482                              ret);
 483                return ret;
 484        }
 485
 486        hdmi->phy = devm_phy_optional_get(dev, "hdmi");
 487        if (IS_ERR(hdmi->phy)) {
 488                ret = PTR_ERR(hdmi->phy);
 489                if (ret != -EPROBE_DEFER)
 490                        DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n");
 491                return ret;
 492        }
 493
 494        drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
 495        drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
 496                         DRM_MODE_ENCODER_TMDS, NULL);
 497
 498        platform_set_drvdata(pdev, hdmi);
 499
 500        hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
 501
 502        /*
 503         * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
 504         * which would have called the encoder cleanup.  Do it manually.
 505         */
 506        if (IS_ERR(hdmi->hdmi)) {
 507                ret = PTR_ERR(hdmi->hdmi);
 508                drm_encoder_cleanup(encoder);
 509                clk_disable_unprepare(hdmi->vpll_clk);
 510        }
 511
 512        return ret;
 513}
 514
 515static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 516                                    void *data)
 517{
 518        struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 519
 520        dw_hdmi_unbind(hdmi->hdmi);
 521        clk_disable_unprepare(hdmi->vpll_clk);
 522}
 523
 524static const struct component_ops dw_hdmi_rockchip_ops = {
 525        .bind   = dw_hdmi_rockchip_bind,
 526        .unbind = dw_hdmi_rockchip_unbind,
 527};
 528
 529static int dw_hdmi_rockchip_probe(struct platform_device *pdev)
 530{
 531        return component_add(&pdev->dev, &dw_hdmi_rockchip_ops);
 532}
 533
 534static int dw_hdmi_rockchip_remove(struct platform_device *pdev)
 535{
 536        component_del(&pdev->dev, &dw_hdmi_rockchip_ops);
 537
 538        return 0;
 539}
 540
 541struct platform_driver dw_hdmi_rockchip_pltfm_driver = {
 542        .probe  = dw_hdmi_rockchip_probe,
 543        .remove = dw_hdmi_rockchip_remove,
 544        .driver = {
 545                .name = "dwhdmi-rockchip",
 546                .of_match_table = dw_hdmi_rockchip_dt_ids,
 547        },
 548};
 549