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/clk.h>
  11#include <linux/mfd/syscon.h>
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/regmap.h>
  15
  16#include <drm/drm_of.h>
  17#include <drm/drmP.h>
  18#include <drm/drm_crtc_helper.h>
  19#include <drm/drm_edid.h>
  20#include <drm/bridge/dw_hdmi.h>
  21
  22#include "rockchip_drm_drv.h"
  23#include "rockchip_drm_vop.h"
  24
  25#define RK3288_GRF_SOC_CON6             0x025C
  26#define RK3288_HDMI_LCDC_SEL            BIT(4)
  27#define RK3399_GRF_SOC_CON20            0x6250
  28#define RK3399_HDMI_LCDC_SEL            BIT(6)
  29
  30#define HIWORD_UPDATE(val, mask)        (val | (mask) << 16)
  31
  32/**
  33 * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips
  34 * @lcdsel_grf_reg: grf register offset of lcdc select
  35 * @lcdsel_big: reg value of selecting vop big for HDMI
  36 * @lcdsel_lit: reg value of selecting vop little for HDMI
  37 */
  38struct rockchip_hdmi_chip_data {
  39        u32     lcdsel_grf_reg;
  40        u32     lcdsel_big;
  41        u32     lcdsel_lit;
  42};
  43
  44struct rockchip_hdmi {
  45        struct device *dev;
  46        struct regmap *regmap;
  47        struct drm_encoder encoder;
  48        const struct rockchip_hdmi_chip_data *chip_data;
  49        struct clk *vpll_clk;
  50        struct clk *grf_clk;
  51};
  52
  53#define to_rockchip_hdmi(x)     container_of(x, struct rockchip_hdmi, x)
  54
  55static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
  56        {
  57                27000000, {
  58                        { 0x00b3, 0x0000},
  59                        { 0x2153, 0x0000},
  60                        { 0x40f3, 0x0000}
  61                },
  62        }, {
  63                36000000, {
  64                        { 0x00b3, 0x0000},
  65                        { 0x2153, 0x0000},
  66                        { 0x40f3, 0x0000}
  67                },
  68        }, {
  69                40000000, {
  70                        { 0x00b3, 0x0000},
  71                        { 0x2153, 0x0000},
  72                        { 0x40f3, 0x0000}
  73                },
  74        }, {
  75                54000000, {
  76                        { 0x0072, 0x0001},
  77                        { 0x2142, 0x0001},
  78                        { 0x40a2, 0x0001},
  79                },
  80        }, {
  81                65000000, {
  82                        { 0x0072, 0x0001},
  83                        { 0x2142, 0x0001},
  84                        { 0x40a2, 0x0001},
  85                },
  86        }, {
  87                66000000, {
  88                        { 0x013e, 0x0003},
  89                        { 0x217e, 0x0002},
  90                        { 0x4061, 0x0002}
  91                },
  92        }, {
  93                74250000, {
  94                        { 0x0072, 0x0001},
  95                        { 0x2145, 0x0002},
  96                        { 0x4061, 0x0002}
  97                },
  98        }, {
  99                83500000, {
 100                        { 0x0072, 0x0001},
 101                },
 102        }, {
 103                108000000, {
 104                        { 0x0051, 0x0002},
 105                        { 0x2145, 0x0002},
 106                        { 0x4061, 0x0002}
 107                },
 108        }, {
 109                106500000, {
 110                        { 0x0051, 0x0002},
 111                        { 0x2145, 0x0002},
 112                        { 0x4061, 0x0002}
 113                },
 114        }, {
 115                146250000, {
 116                        { 0x0051, 0x0002},
 117                        { 0x2145, 0x0002},
 118                        { 0x4061, 0x0002}
 119                },
 120        }, {
 121                148500000, {
 122                        { 0x0051, 0x0003},
 123                        { 0x214c, 0x0003},
 124                        { 0x4064, 0x0003}
 125                },
 126        }, {
 127                ~0UL, {
 128                        { 0x00a0, 0x000a },
 129                        { 0x2001, 0x000f },
 130                        { 0x4002, 0x000f },
 131                },
 132        }
 133};
 134
 135static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 136        /*      pixelclk    bpp8    bpp10   bpp12 */
 137        {
 138                40000000,  { 0x0018, 0x0018, 0x0018 },
 139        }, {
 140                65000000,  { 0x0028, 0x0028, 0x0028 },
 141        }, {
 142                66000000,  { 0x0038, 0x0038, 0x0038 },
 143        }, {
 144                74250000,  { 0x0028, 0x0038, 0x0038 },
 145        }, {
 146                83500000,  { 0x0028, 0x0038, 0x0038 },
 147        }, {
 148                146250000, { 0x0038, 0x0038, 0x0038 },
 149        }, {
 150                148500000, { 0x0000, 0x0038, 0x0038 },
 151        }, {
 152                ~0UL,      { 0x0000, 0x0000, 0x0000},
 153        }
 154};
 155
 156static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 157        /*pixelclk   symbol   term   vlev*/
 158        { 74250000,  0x8009, 0x0004, 0x0272},
 159        { 148500000, 0x802b, 0x0004, 0x028d},
 160        { 297000000, 0x8039, 0x0005, 0x028d},
 161        { ~0UL,      0x0000, 0x0000, 0x0000}
 162};
 163
 164static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 165{
 166        struct device_node *np = hdmi->dev->of_node;
 167        int ret;
 168
 169        hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 170        if (IS_ERR(hdmi->regmap)) {
 171                dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
 172                return PTR_ERR(hdmi->regmap);
 173        }
 174
 175        hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
 176        if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
 177                hdmi->vpll_clk = NULL;
 178        } else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
 179                return -EPROBE_DEFER;
 180        } else if (IS_ERR(hdmi->vpll_clk)) {
 181                dev_err(hdmi->dev, "failed to get grf clock\n");
 182                return PTR_ERR(hdmi->vpll_clk);
 183        }
 184
 185        hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
 186        if (PTR_ERR(hdmi->grf_clk) == -ENOENT) {
 187                hdmi->grf_clk = NULL;
 188        } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
 189                return -EPROBE_DEFER;
 190        } else if (IS_ERR(hdmi->grf_clk)) {
 191                dev_err(hdmi->dev, "failed to get grf clock\n");
 192                return PTR_ERR(hdmi->grf_clk);
 193        }
 194
 195        ret = clk_prepare_enable(hdmi->vpll_clk);
 196        if (ret) {
 197                dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
 198                return ret;
 199        }
 200
 201        return 0;
 202}
 203
 204static enum drm_mode_status
 205dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
 206                            const struct drm_display_mode *mode)
 207{
 208        const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
 209        int pclk = mode->clock * 1000;
 210        bool valid = false;
 211        int i;
 212
 213        for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
 214                if (pclk == mpll_cfg[i].mpixelclock) {
 215                        valid = true;
 216                        break;
 217                }
 218        }
 219
 220        return (valid) ? MODE_OK : MODE_BAD;
 221}
 222
 223static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = {
 224        .destroy = drm_encoder_cleanup,
 225};
 226
 227static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
 228{
 229}
 230
 231static bool
 232dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
 233                                    const struct drm_display_mode *mode,
 234                                    struct drm_display_mode *adj_mode)
 235{
 236        return true;
 237}
 238
 239static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 240                                              struct drm_display_mode *mode,
 241                                              struct drm_display_mode *adj_mode)
 242{
 243        struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 244
 245        clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
 246}
 247
 248static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 249{
 250        struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 251        u32 val;
 252        int ret;
 253
 254        ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
 255        if (ret)
 256                val = hdmi->chip_data->lcdsel_lit;
 257        else
 258                val = hdmi->chip_data->lcdsel_big;
 259
 260        ret = clk_prepare_enable(hdmi->grf_clk);
 261        if (ret < 0) {
 262                dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret);
 263                return;
 264        }
 265
 266        ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
 267        if (ret != 0)
 268                dev_err(hdmi->dev, "Could not write to GRF: %d\n", ret);
 269
 270        clk_disable_unprepare(hdmi->grf_clk);
 271        dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
 272                ret ? "LIT" : "BIG");
 273}
 274
 275static int
 276dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
 277                                      struct drm_crtc_state *crtc_state,
 278                                      struct drm_connector_state *conn_state)
 279{
 280        struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 281
 282        s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 283        s->output_type = DRM_MODE_CONNECTOR_HDMIA;
 284
 285        return 0;
 286}
 287
 288static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
 289        .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
 290        .mode_set   = dw_hdmi_rockchip_encoder_mode_set,
 291        .enable     = dw_hdmi_rockchip_encoder_enable,
 292        .disable    = dw_hdmi_rockchip_encoder_disable,
 293        .atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
 294};
 295
 296static struct rockchip_hdmi_chip_data rk3288_chip_data = {
 297        .lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
 298        .lcdsel_big = HIWORD_UPDATE(0, RK3288_HDMI_LCDC_SEL),
 299        .lcdsel_lit = HIWORD_UPDATE(RK3288_HDMI_LCDC_SEL, RK3288_HDMI_LCDC_SEL),
 300};
 301
 302static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
 303        .mode_valid = dw_hdmi_rockchip_mode_valid,
 304        .mpll_cfg   = rockchip_mpll_cfg,
 305        .cur_ctr    = rockchip_cur_ctr,
 306        .phy_config = rockchip_phy_config,
 307        .phy_data = &rk3288_chip_data,
 308};
 309
 310static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 311        .lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
 312        .lcdsel_big = HIWORD_UPDATE(0, RK3399_HDMI_LCDC_SEL),
 313        .lcdsel_lit = HIWORD_UPDATE(RK3399_HDMI_LCDC_SEL, RK3399_HDMI_LCDC_SEL),
 314};
 315
 316static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
 317        .mode_valid = dw_hdmi_rockchip_mode_valid,
 318        .mpll_cfg   = rockchip_mpll_cfg,
 319        .cur_ctr    = rockchip_cur_ctr,
 320        .phy_config = rockchip_phy_config,
 321        .phy_data = &rk3399_chip_data,
 322};
 323
 324static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 325        { .compatible = "rockchip,rk3288-dw-hdmi",
 326          .data = &rk3288_hdmi_drv_data
 327        },
 328        { .compatible = "rockchip,rk3399-dw-hdmi",
 329          .data = &rk3399_hdmi_drv_data
 330        },
 331        {},
 332};
 333MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
 334
 335static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 336                                 void *data)
 337{
 338        struct platform_device *pdev = to_platform_device(dev);
 339        const struct dw_hdmi_plat_data *plat_data;
 340        const struct of_device_id *match;
 341        struct drm_device *drm = data;
 342        struct drm_encoder *encoder;
 343        struct rockchip_hdmi *hdmi;
 344        int ret;
 345
 346        if (!pdev->dev.of_node)
 347                return -ENODEV;
 348
 349        hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
 350        if (!hdmi)
 351                return -ENOMEM;
 352
 353        match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
 354        plat_data = match->data;
 355        hdmi->dev = &pdev->dev;
 356        hdmi->chip_data = plat_data->phy_data;
 357        encoder = &hdmi->encoder;
 358
 359        encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 360        /*
 361         * If we failed to find the CRTC(s) which this encoder is
 362         * supposed to be connected to, it's because the CRTC has
 363         * not been registered yet.  Defer probing, and hope that
 364         * the required CRTC is added later.
 365         */
 366        if (encoder->possible_crtcs == 0)
 367                return -EPROBE_DEFER;
 368
 369        ret = rockchip_hdmi_parse_dt(hdmi);
 370        if (ret) {
 371                dev_err(hdmi->dev, "Unable to parse OF data\n");
 372                return ret;
 373        }
 374
 375        drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
 376        drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
 377                         DRM_MODE_ENCODER_TMDS, NULL);
 378
 379        ret = dw_hdmi_bind(pdev, encoder, plat_data);
 380
 381        /*
 382         * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
 383         * which would have called the encoder cleanup.  Do it manually.
 384         */
 385        if (ret)
 386                drm_encoder_cleanup(encoder);
 387
 388        return ret;
 389}
 390
 391static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 392                                    void *data)
 393{
 394        return dw_hdmi_unbind(dev);
 395}
 396
 397static const struct component_ops dw_hdmi_rockchip_ops = {
 398        .bind   = dw_hdmi_rockchip_bind,
 399        .unbind = dw_hdmi_rockchip_unbind,
 400};
 401
 402static int dw_hdmi_rockchip_probe(struct platform_device *pdev)
 403{
 404        return component_add(&pdev->dev, &dw_hdmi_rockchip_ops);
 405}
 406
 407static int dw_hdmi_rockchip_remove(struct platform_device *pdev)
 408{
 409        component_del(&pdev->dev, &dw_hdmi_rockchip_ops);
 410
 411        return 0;
 412}
 413
 414struct platform_driver dw_hdmi_rockchip_pltfm_driver = {
 415        .probe  = dw_hdmi_rockchip_probe,
 416        .remove = dw_hdmi_rockchip_remove,
 417        .driver = {
 418                .name = "dwhdmi-rockchip",
 419                .of_match_table = dw_hdmi_rockchip_dt_ids,
 420        },
 421};
 422