linux/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Rockchip SoC DP (Display Port) interface driver.
   4 *
   5 * Copyright (C) Fuzhou Rockchip Electronics Co., Ltd.
   6 * Author: Andy Yan <andy.yan@rock-chips.com>
   7 *         Yakir Yang <ykk@rock-chips.com>
   8 *         Jeff Chen <jeff.chen@rock-chips.com>
   9 */
  10
  11#include <linux/component.h>
  12#include <linux/mfd/syscon.h>
  13#include <linux/of_device.h>
  14#include <linux/of_graph.h>
  15#include <linux/regmap.h>
  16#include <linux/reset.h>
  17#include <linux/clk.h>
  18
  19#include <video/of_videomode.h>
  20#include <video/videomode.h>
  21
  22#include <drm/drm_atomic.h>
  23#include <drm/drm_atomic_helper.h>
  24#include <drm/bridge/analogix_dp.h>
  25#include <drm/drm_dp_helper.h>
  26#include <drm/drm_of.h>
  27#include <drm/drm_panel.h>
  28#include <drm/drm_probe_helper.h>
  29#include <drm/drm_simple_kms_helper.h>
  30
  31#include "rockchip_drm_drv.h"
  32#include "rockchip_drm_vop.h"
  33
  34#define RK3288_GRF_SOC_CON6             0x25c
  35#define RK3288_EDP_LCDC_SEL             BIT(5)
  36#define RK3399_GRF_SOC_CON20            0x6250
  37#define RK3399_EDP_LCDC_SEL             BIT(5)
  38
  39#define HIWORD_UPDATE(val, mask)        (val | (mask) << 16)
  40
  41#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS   100
  42
  43#define to_dp(nm)       container_of(nm, struct rockchip_dp_device, nm)
  44
  45/**
  46 * struct rockchip_dp_chip_data - splite the grf setting of kind of chips
  47 * @lcdsel_grf_reg: grf register offset of lcdc select
  48 * @lcdsel_big: reg value of selecting vop big for eDP
  49 * @lcdsel_lit: reg value of selecting vop little for eDP
  50 * @chip_type: specific chip type
  51 */
  52struct rockchip_dp_chip_data {
  53        u32     lcdsel_grf_reg;
  54        u32     lcdsel_big;
  55        u32     lcdsel_lit;
  56        u32     chip_type;
  57};
  58
  59struct rockchip_dp_device {
  60        struct drm_device        *drm_dev;
  61        struct device            *dev;
  62        struct drm_encoder       encoder;
  63        struct drm_display_mode  mode;
  64
  65        struct clk               *pclk;
  66        struct clk               *grfclk;
  67        struct regmap            *grf;
  68        struct reset_control     *rst;
  69
  70        const struct rockchip_dp_chip_data *data;
  71
  72        struct analogix_dp_device *adp;
  73        struct analogix_dp_plat_data plat_data;
  74};
  75
  76static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
  77{
  78        reset_control_assert(dp->rst);
  79        usleep_range(10, 20);
  80        reset_control_deassert(dp->rst);
  81
  82        return 0;
  83}
  84
  85static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
  86{
  87        struct rockchip_dp_device *dp = to_dp(plat_data);
  88        int ret;
  89
  90        ret = clk_prepare_enable(dp->pclk);
  91        if (ret < 0) {
  92                DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret);
  93                return ret;
  94        }
  95
  96        ret = rockchip_dp_pre_init(dp);
  97        if (ret < 0) {
  98                DRM_DEV_ERROR(dp->dev, "failed to dp pre init %d\n", ret);
  99                clk_disable_unprepare(dp->pclk);
 100                return ret;
 101        }
 102
 103        return ret;
 104}
 105
 106static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
 107{
 108        struct rockchip_dp_device *dp = to_dp(plat_data);
 109
 110        clk_disable_unprepare(dp->pclk);
 111
 112        return 0;
 113}
 114
 115static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data,
 116                                 struct drm_connector *connector)
 117{
 118        struct drm_display_info *di = &connector->display_info;
 119        /* VOP couldn't output YUV video format for eDP rightly */
 120        u32 mask = DRM_COLOR_FORMAT_YCRCB444 | DRM_COLOR_FORMAT_YCRCB422;
 121
 122        if ((di->color_formats & mask)) {
 123                DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n");
 124                di->color_formats &= ~mask;
 125                di->color_formats |= DRM_COLOR_FORMAT_RGB444;
 126                di->bpc = 8;
 127        }
 128
 129        return 0;
 130}
 131
 132static bool
 133rockchip_dp_drm_encoder_mode_fixup(struct drm_encoder *encoder,
 134                                   const struct drm_display_mode *mode,
 135                                   struct drm_display_mode *adjusted_mode)
 136{
 137        /* do nothing */
 138        return true;
 139}
 140
 141static void rockchip_dp_drm_encoder_mode_set(struct drm_encoder *encoder,
 142                                             struct drm_display_mode *mode,
 143                                             struct drm_display_mode *adjusted)
 144{
 145        /* do nothing */
 146}
 147
 148static
 149struct drm_crtc *rockchip_dp_drm_get_new_crtc(struct drm_encoder *encoder,
 150                                              struct drm_atomic_state *state)
 151{
 152        struct drm_connector *connector;
 153        struct drm_connector_state *conn_state;
 154
 155        connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
 156        if (!connector)
 157                return NULL;
 158
 159        conn_state = drm_atomic_get_new_connector_state(state, connector);
 160        if (!conn_state)
 161                return NULL;
 162
 163        return conn_state->crtc;
 164}
 165
 166static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 167                                           struct drm_atomic_state *state)
 168{
 169        struct rockchip_dp_device *dp = to_dp(encoder);
 170        struct drm_crtc *crtc;
 171        struct drm_crtc_state *old_crtc_state;
 172        int ret;
 173        u32 val;
 174
 175        crtc = rockchip_dp_drm_get_new_crtc(encoder, state);
 176        if (!crtc)
 177                return;
 178
 179        old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
 180        /* Coming back from self refresh, nothing to do */
 181        if (old_crtc_state && old_crtc_state->self_refresh_active)
 182                return;
 183
 184        ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
 185        if (ret < 0)
 186                return;
 187
 188        if (ret)
 189                val = dp->data->lcdsel_lit;
 190        else
 191                val = dp->data->lcdsel_big;
 192
 193        DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
 194
 195        ret = clk_prepare_enable(dp->grfclk);
 196        if (ret < 0) {
 197                DRM_DEV_ERROR(dp->dev, "failed to enable grfclk %d\n", ret);
 198                return;
 199        }
 200
 201        ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val);
 202        if (ret != 0)
 203                DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
 204
 205        clk_disable_unprepare(dp->grfclk);
 206}
 207
 208static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder,
 209                                            struct drm_atomic_state *state)
 210{
 211        struct rockchip_dp_device *dp = to_dp(encoder);
 212        struct drm_crtc *crtc;
 213        struct drm_crtc_state *new_crtc_state = NULL;
 214        int ret;
 215
 216        crtc = rockchip_dp_drm_get_new_crtc(encoder, state);
 217        /* No crtc means we're doing a full shutdown */
 218        if (!crtc)
 219                return;
 220
 221        new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
 222        /* If we're not entering self-refresh, no need to wait for vact */
 223        if (!new_crtc_state || !new_crtc_state->self_refresh_active)
 224                return;
 225
 226        ret = rockchip_drm_wait_vact_end(crtc, PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
 227        if (ret)
 228                DRM_DEV_ERROR(dp->dev, "line flag irq timed out\n");
 229}
 230
 231static int
 232rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
 233                                      struct drm_crtc_state *crtc_state,
 234                                      struct drm_connector_state *conn_state)
 235{
 236        struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 237        struct drm_display_info *di = &conn_state->connector->display_info;
 238
 239        /*
 240         * The hardware IC designed that VOP must output the RGB10 video
 241         * format to eDP controller, and if eDP panel only support RGB8,
 242         * then eDP controller should cut down the video data, not via VOP
 243         * controller, that's why we need to hardcode the VOP output mode
 244         * to RGA10 here.
 245         */
 246
 247        s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 248        s->output_type = DRM_MODE_CONNECTOR_eDP;
 249        s->output_bpc = di->bpc;
 250
 251        return 0;
 252}
 253
 254static struct drm_encoder_helper_funcs rockchip_dp_encoder_helper_funcs = {
 255        .mode_fixup = rockchip_dp_drm_encoder_mode_fixup,
 256        .mode_set = rockchip_dp_drm_encoder_mode_set,
 257        .atomic_enable = rockchip_dp_drm_encoder_enable,
 258        .atomic_disable = rockchip_dp_drm_encoder_disable,
 259        .atomic_check = rockchip_dp_drm_encoder_atomic_check,
 260};
 261
 262static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
 263{
 264        struct device *dev = dp->dev;
 265        struct device_node *np = dev->of_node;
 266
 267        dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 268        if (IS_ERR(dp->grf)) {
 269                DRM_DEV_ERROR(dev, "failed to get rockchip,grf property\n");
 270                return PTR_ERR(dp->grf);
 271        }
 272
 273        dp->grfclk = devm_clk_get(dev, "grf");
 274        if (PTR_ERR(dp->grfclk) == -ENOENT) {
 275                dp->grfclk = NULL;
 276        } else if (PTR_ERR(dp->grfclk) == -EPROBE_DEFER) {
 277                return -EPROBE_DEFER;
 278        } else if (IS_ERR(dp->grfclk)) {
 279                DRM_DEV_ERROR(dev, "failed to get grf clock\n");
 280                return PTR_ERR(dp->grfclk);
 281        }
 282
 283        dp->pclk = devm_clk_get(dev, "pclk");
 284        if (IS_ERR(dp->pclk)) {
 285                DRM_DEV_ERROR(dev, "failed to get pclk property\n");
 286                return PTR_ERR(dp->pclk);
 287        }
 288
 289        dp->rst = devm_reset_control_get(dev, "dp");
 290        if (IS_ERR(dp->rst)) {
 291                DRM_DEV_ERROR(dev, "failed to get dp reset control\n");
 292                return PTR_ERR(dp->rst);
 293        }
 294
 295        return 0;
 296}
 297
 298static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
 299{
 300        struct drm_encoder *encoder = &dp->encoder;
 301        struct drm_device *drm_dev = dp->drm_dev;
 302        struct device *dev = dp->dev;
 303        int ret;
 304
 305        encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 306                                                             dev->of_node);
 307        DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 308
 309        ret = drm_simple_encoder_init(drm_dev, encoder,
 310                                      DRM_MODE_ENCODER_TMDS);
 311        if (ret) {
 312                DRM_ERROR("failed to initialize encoder with drm\n");
 313                return ret;
 314        }
 315
 316        drm_encoder_helper_add(encoder, &rockchip_dp_encoder_helper_funcs);
 317
 318        return 0;
 319}
 320
 321static int rockchip_dp_bind(struct device *dev, struct device *master,
 322                            void *data)
 323{
 324        struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 325        struct drm_device *drm_dev = data;
 326        int ret;
 327
 328        dp->drm_dev = drm_dev;
 329
 330        ret = rockchip_dp_drm_create_encoder(dp);
 331        if (ret) {
 332                DRM_ERROR("failed to create drm encoder\n");
 333                return ret;
 334        }
 335
 336        dp->plat_data.encoder = &dp->encoder;
 337
 338        ret = analogix_dp_bind(dp->adp, drm_dev);
 339        if (ret)
 340                goto err_cleanup_encoder;
 341
 342        return 0;
 343err_cleanup_encoder:
 344        dp->encoder.funcs->destroy(&dp->encoder);
 345        return ret;
 346}
 347
 348static void rockchip_dp_unbind(struct device *dev, struct device *master,
 349                               void *data)
 350{
 351        struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 352
 353        analogix_dp_unbind(dp->adp);
 354        dp->encoder.funcs->destroy(&dp->encoder);
 355}
 356
 357static const struct component_ops rockchip_dp_component_ops = {
 358        .bind = rockchip_dp_bind,
 359        .unbind = rockchip_dp_unbind,
 360};
 361
 362static int rockchip_dp_probe(struct platform_device *pdev)
 363{
 364        struct device *dev = &pdev->dev;
 365        const struct rockchip_dp_chip_data *dp_data;
 366        struct drm_panel *panel = NULL;
 367        struct rockchip_dp_device *dp;
 368        int ret;
 369
 370        dp_data = of_device_get_match_data(dev);
 371        if (!dp_data)
 372                return -ENODEV;
 373
 374        ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
 375        if (ret < 0)
 376                return ret;
 377
 378        dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
 379        if (!dp)
 380                return -ENOMEM;
 381
 382        dp->dev = dev;
 383        dp->adp = ERR_PTR(-ENODEV);
 384        dp->data = dp_data;
 385        dp->plat_data.panel = panel;
 386        dp->plat_data.dev_type = dp->data->chip_type;
 387        dp->plat_data.power_on_start = rockchip_dp_poweron_start;
 388        dp->plat_data.power_off = rockchip_dp_powerdown;
 389        dp->plat_data.get_modes = rockchip_dp_get_modes;
 390
 391        ret = rockchip_dp_of_probe(dp);
 392        if (ret < 0)
 393                return ret;
 394
 395        platform_set_drvdata(pdev, dp);
 396
 397        dp->adp = analogix_dp_probe(dev, &dp->plat_data);
 398        if (IS_ERR(dp->adp))
 399                return PTR_ERR(dp->adp);
 400
 401        return component_add(dev, &rockchip_dp_component_ops);
 402}
 403
 404static int rockchip_dp_remove(struct platform_device *pdev)
 405{
 406        struct rockchip_dp_device *dp = platform_get_drvdata(pdev);
 407
 408        component_del(&pdev->dev, &rockchip_dp_component_ops);
 409        analogix_dp_remove(dp->adp);
 410
 411        return 0;
 412}
 413
 414#ifdef CONFIG_PM_SLEEP
 415static int rockchip_dp_suspend(struct device *dev)
 416{
 417        struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 418
 419        if (IS_ERR(dp->adp))
 420                return 0;
 421
 422        return analogix_dp_suspend(dp->adp);
 423}
 424
 425static int rockchip_dp_resume(struct device *dev)
 426{
 427        struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 428
 429        if (IS_ERR(dp->adp))
 430                return 0;
 431
 432        return analogix_dp_resume(dp->adp);
 433}
 434#endif
 435
 436static const struct dev_pm_ops rockchip_dp_pm_ops = {
 437#ifdef CONFIG_PM_SLEEP
 438        .suspend_late = rockchip_dp_suspend,
 439        .resume_early = rockchip_dp_resume,
 440#endif
 441};
 442
 443static const struct rockchip_dp_chip_data rk3399_edp = {
 444        .lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
 445        .lcdsel_big = HIWORD_UPDATE(0, RK3399_EDP_LCDC_SEL),
 446        .lcdsel_lit = HIWORD_UPDATE(RK3399_EDP_LCDC_SEL, RK3399_EDP_LCDC_SEL),
 447        .chip_type = RK3399_EDP,
 448};
 449
 450static const struct rockchip_dp_chip_data rk3288_dp = {
 451        .lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
 452        .lcdsel_big = HIWORD_UPDATE(0, RK3288_EDP_LCDC_SEL),
 453        .lcdsel_lit = HIWORD_UPDATE(RK3288_EDP_LCDC_SEL, RK3288_EDP_LCDC_SEL),
 454        .chip_type = RK3288_DP,
 455};
 456
 457static const struct of_device_id rockchip_dp_dt_ids[] = {
 458        {.compatible = "rockchip,rk3288-dp", .data = &rk3288_dp },
 459        {.compatible = "rockchip,rk3399-edp", .data = &rk3399_edp },
 460        {}
 461};
 462MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids);
 463
 464struct platform_driver rockchip_dp_driver = {
 465        .probe = rockchip_dp_probe,
 466        .remove = rockchip_dp_remove,
 467        .driver = {
 468                   .name = "rockchip-dp",
 469                   .pm = &rockchip_dp_pm_ops,
 470                   .of_match_table = of_match_ptr(rockchip_dp_dt_ids),
 471        },
 472};
 473