linux/drivers/gpu/drm/msm/disp/mdp5/mdp5_cmd_encoder.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <drm/drm_crtc.h>
   7#include <drm/drm_probe_helper.h>
   8
   9#include "mdp5_kms.h"
  10
  11static struct mdp5_kms *get_kms(struct drm_encoder *encoder)
  12{
  13        struct msm_drm_private *priv = encoder->dev->dev_private;
  14        return to_mdp5_kms(to_mdp_kms(priv->kms));
  15}
  16
  17#define VSYNC_CLK_RATE 19200000
  18static int pingpong_tearcheck_setup(struct drm_encoder *encoder,
  19                                    struct drm_display_mode *mode)
  20{
  21        struct mdp5_kms *mdp5_kms = get_kms(encoder);
  22        struct device *dev = encoder->dev->dev;
  23        u32 total_lines_x100, vclks_line, cfg;
  24        long vsync_clk_speed;
  25        struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
  26        int pp_id = mixer->pp;
  27
  28        if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) {
  29                DRM_DEV_ERROR(dev, "vsync_clk is not initialized\n");
  30                return -EINVAL;
  31        }
  32
  33        total_lines_x100 = mode->vtotal * drm_mode_vrefresh(mode);
  34        if (!total_lines_x100) {
  35                DRM_DEV_ERROR(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n",
  36                              __func__, mode->vtotal, drm_mode_vrefresh(mode));
  37                return -EINVAL;
  38        }
  39
  40        vsync_clk_speed = clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE);
  41        if (vsync_clk_speed <= 0) {
  42                DRM_DEV_ERROR(dev, "vsync_clk round rate failed %ld\n",
  43                                                        vsync_clk_speed);
  44                return -EINVAL;
  45        }
  46        vclks_line = vsync_clk_speed * 100 / total_lines_x100;
  47
  48        cfg = MDP5_PP_SYNC_CONFIG_VSYNC_COUNTER_EN
  49                | MDP5_PP_SYNC_CONFIG_VSYNC_IN_EN;
  50        cfg |= MDP5_PP_SYNC_CONFIG_VSYNC_COUNT(vclks_line);
  51
  52        mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_VSYNC(pp_id), cfg);
  53        mdp5_write(mdp5_kms,
  54                REG_MDP5_PP_SYNC_CONFIG_HEIGHT(pp_id), 0xfff0);
  55        mdp5_write(mdp5_kms,
  56                REG_MDP5_PP_VSYNC_INIT_VAL(pp_id), mode->vdisplay);
  57        mdp5_write(mdp5_kms, REG_MDP5_PP_RD_PTR_IRQ(pp_id), mode->vdisplay + 1);
  58        mdp5_write(mdp5_kms, REG_MDP5_PP_START_POS(pp_id), mode->vdisplay);
  59        mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_THRESH(pp_id),
  60                        MDP5_PP_SYNC_THRESH_START(4) |
  61                        MDP5_PP_SYNC_THRESH_CONTINUE(4));
  62
  63        return 0;
  64}
  65
  66static int pingpong_tearcheck_enable(struct drm_encoder *encoder)
  67{
  68        struct mdp5_kms *mdp5_kms = get_kms(encoder);
  69        struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
  70        int pp_id = mixer->pp;
  71        int ret;
  72
  73        ret = clk_set_rate(mdp5_kms->vsync_clk,
  74                clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE));
  75        if (ret) {
  76                DRM_DEV_ERROR(encoder->dev->dev,
  77                        "vsync_clk clk_set_rate failed, %d\n", ret);
  78                return ret;
  79        }
  80        ret = clk_prepare_enable(mdp5_kms->vsync_clk);
  81        if (ret) {
  82                DRM_DEV_ERROR(encoder->dev->dev,
  83                        "vsync_clk clk_prepare_enable failed, %d\n", ret);
  84                return ret;
  85        }
  86
  87        mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1);
  88
  89        return 0;
  90}
  91
  92static void pingpong_tearcheck_disable(struct drm_encoder *encoder)
  93{
  94        struct mdp5_kms *mdp5_kms = get_kms(encoder);
  95        struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
  96        int pp_id = mixer->pp;
  97
  98        mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0);
  99        clk_disable_unprepare(mdp5_kms->vsync_clk);
 100}
 101
 102void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder,
 103                               struct drm_display_mode *mode,
 104                               struct drm_display_mode *adjusted_mode)
 105{
 106        mode = adjusted_mode;
 107
 108        DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
 109        pingpong_tearcheck_setup(encoder, mode);
 110        mdp5_crtc_set_pipeline(encoder->crtc);
 111}
 112
 113void mdp5_cmd_encoder_disable(struct drm_encoder *encoder)
 114{
 115        struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
 116        struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
 117        struct mdp5_interface *intf = mdp5_cmd_enc->intf;
 118        struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
 119
 120        if (WARN_ON(!mdp5_cmd_enc->enabled))
 121                return;
 122
 123        pingpong_tearcheck_disable(encoder);
 124
 125        mdp5_ctl_set_encoder_state(ctl, pipeline, false);
 126        mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true);
 127
 128        mdp5_cmd_enc->enabled = false;
 129}
 130
 131void mdp5_cmd_encoder_enable(struct drm_encoder *encoder)
 132{
 133        struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
 134        struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
 135        struct mdp5_interface *intf = mdp5_cmd_enc->intf;
 136        struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
 137
 138        if (WARN_ON(mdp5_cmd_enc->enabled))
 139                return;
 140
 141        if (pingpong_tearcheck_enable(encoder))
 142                return;
 143
 144        mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true);
 145
 146        mdp5_ctl_set_encoder_state(ctl, pipeline, true);
 147
 148        mdp5_cmd_enc->enabled = true;
 149}
 150
 151int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
 152                                       struct drm_encoder *slave_encoder)
 153{
 154        struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
 155        struct mdp5_kms *mdp5_kms;
 156        struct device *dev;
 157        int intf_num;
 158        u32 data = 0;
 159
 160        if (!encoder || !slave_encoder)
 161                return -EINVAL;
 162
 163        mdp5_kms = get_kms(encoder);
 164        intf_num = mdp5_cmd_enc->intf->num;
 165
 166        /* Switch slave encoder's trigger MUX, to use the master's
 167         * start signal for the slave encoder
 168         */
 169        if (intf_num == 1)
 170                data |= MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX;
 171        else if (intf_num == 2)
 172                data |= MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX;
 173        else
 174                return -EINVAL;
 175
 176        /* Smart Panel, Sync mode */
 177        data |= MDP5_SPLIT_DPL_UPPER_SMART_PANEL;
 178
 179        dev = &mdp5_kms->pdev->dev;
 180
 181        /* Make sure clocks are on when connectors calling this function. */
 182        pm_runtime_get_sync(dev);
 183        mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, data);
 184
 185        mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER,
 186                   MDP5_SPLIT_DPL_LOWER_SMART_PANEL);
 187        mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1);
 188        pm_runtime_put_sync(dev);
 189
 190        return 0;
 191}
 192