linux/drivers/gpu/drm/msm/disp/mdp5/mdp5_cmd_encoder.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
   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 version 2 and
   6 * only version 2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 */
  13
  14#include <drm/drm_crtc.h>
  15#include <drm/drm_crtc_helper.h>
  16
  17#include "mdp5_kms.h"
  18
  19static struct mdp5_kms *get_kms(struct drm_encoder *encoder)
  20{
  21        struct msm_drm_private *priv = encoder->dev->dev_private;
  22        return to_mdp5_kms(to_mdp_kms(priv->kms));
  23}
  24
  25#ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING
  26#include <mach/board.h>
  27#include <linux/msm-bus.h>
  28#include <linux/msm-bus-board.h>
  29
  30static void bs_set(struct mdp5_encoder *mdp5_cmd_enc, int idx)
  31{
  32        if (mdp5_cmd_enc->bsc) {
  33                DBG("set bus scaling: %d", idx);
  34                /* HACK: scaling down, and then immediately back up
  35                 * seems to leave things broken (underflow).. so
  36                 * never disable:
  37                 */
  38                idx = 1;
  39                msm_bus_scale_client_update_request(mdp5_cmd_enc->bsc, idx);
  40        }
  41}
  42#else
  43static void bs_set(struct mdp5_encoder *mdp5_cmd_enc, int idx) {}
  44#endif
  45
  46#define VSYNC_CLK_RATE 19200000
  47static int pingpong_tearcheck_setup(struct drm_encoder *encoder,
  48                                    struct drm_display_mode *mode)
  49{
  50        struct mdp5_kms *mdp5_kms = get_kms(encoder);
  51        struct device *dev = encoder->dev->dev;
  52        u32 total_lines_x100, vclks_line, cfg;
  53        long vsync_clk_speed;
  54        struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
  55        int pp_id = mixer->pp;
  56
  57        if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) {
  58                dev_err(dev, "vsync_clk is not initialized\n");
  59                return -EINVAL;
  60        }
  61
  62        total_lines_x100 = mode->vtotal * mode->vrefresh;
  63        if (!total_lines_x100) {
  64                dev_err(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n",
  65                                __func__, mode->vtotal, mode->vrefresh);
  66                return -EINVAL;
  67        }
  68
  69        vsync_clk_speed = clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE);
  70        if (vsync_clk_speed <= 0) {
  71                dev_err(dev, "vsync_clk round rate failed %ld\n",
  72                                                        vsync_clk_speed);
  73                return -EINVAL;
  74        }
  75        vclks_line = vsync_clk_speed * 100 / total_lines_x100;
  76
  77        cfg = MDP5_PP_SYNC_CONFIG_VSYNC_COUNTER_EN
  78                | MDP5_PP_SYNC_CONFIG_VSYNC_IN_EN;
  79        cfg |= MDP5_PP_SYNC_CONFIG_VSYNC_COUNT(vclks_line);
  80
  81        mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_VSYNC(pp_id), cfg);
  82        mdp5_write(mdp5_kms,
  83                REG_MDP5_PP_SYNC_CONFIG_HEIGHT(pp_id), 0xfff0);
  84        mdp5_write(mdp5_kms,
  85                REG_MDP5_PP_VSYNC_INIT_VAL(pp_id), mode->vdisplay);
  86        mdp5_write(mdp5_kms, REG_MDP5_PP_RD_PTR_IRQ(pp_id), mode->vdisplay + 1);
  87        mdp5_write(mdp5_kms, REG_MDP5_PP_START_POS(pp_id), mode->vdisplay);
  88        mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_THRESH(pp_id),
  89                        MDP5_PP_SYNC_THRESH_START(4) |
  90                        MDP5_PP_SYNC_THRESH_CONTINUE(4));
  91
  92        return 0;
  93}
  94
  95static int pingpong_tearcheck_enable(struct drm_encoder *encoder)
  96{
  97        struct mdp5_kms *mdp5_kms = get_kms(encoder);
  98        struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
  99        int pp_id = mixer->pp;
 100        int ret;
 101
 102        ret = clk_set_rate(mdp5_kms->vsync_clk,
 103                clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE));
 104        if (ret) {
 105                dev_err(encoder->dev->dev,
 106                        "vsync_clk clk_set_rate failed, %d\n", ret);
 107                return ret;
 108        }
 109        ret = clk_prepare_enable(mdp5_kms->vsync_clk);
 110        if (ret) {
 111                dev_err(encoder->dev->dev,
 112                        "vsync_clk clk_prepare_enable failed, %d\n", ret);
 113                return ret;
 114        }
 115
 116        mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1);
 117
 118        return 0;
 119}
 120
 121static void pingpong_tearcheck_disable(struct drm_encoder *encoder)
 122{
 123        struct mdp5_kms *mdp5_kms = get_kms(encoder);
 124        struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
 125        int pp_id = mixer->pp;
 126
 127        mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0);
 128        clk_disable_unprepare(mdp5_kms->vsync_clk);
 129}
 130
 131void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder,
 132                               struct drm_display_mode *mode,
 133                               struct drm_display_mode *adjusted_mode)
 134{
 135        mode = adjusted_mode;
 136
 137        DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
 138                        mode->base.id, mode->name,
 139                        mode->vrefresh, mode->clock,
 140                        mode->hdisplay, mode->hsync_start,
 141                        mode->hsync_end, mode->htotal,
 142                        mode->vdisplay, mode->vsync_start,
 143                        mode->vsync_end, mode->vtotal,
 144                        mode->type, mode->flags);
 145        pingpong_tearcheck_setup(encoder, mode);
 146        mdp5_crtc_set_pipeline(encoder->crtc);
 147}
 148
 149void mdp5_cmd_encoder_disable(struct drm_encoder *encoder)
 150{
 151        struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
 152        struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
 153        struct mdp5_interface *intf = mdp5_cmd_enc->intf;
 154        struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
 155
 156        if (WARN_ON(!mdp5_cmd_enc->enabled))
 157                return;
 158
 159        pingpong_tearcheck_disable(encoder);
 160
 161        mdp5_ctl_set_encoder_state(ctl, pipeline, false);
 162        mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true);
 163
 164        bs_set(mdp5_cmd_enc, 0);
 165
 166        mdp5_cmd_enc->enabled = false;
 167}
 168
 169void mdp5_cmd_encoder_enable(struct drm_encoder *encoder)
 170{
 171        struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
 172        struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
 173        struct mdp5_interface *intf = mdp5_cmd_enc->intf;
 174        struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
 175
 176        if (WARN_ON(mdp5_cmd_enc->enabled))
 177                return;
 178
 179        bs_set(mdp5_cmd_enc, 1);
 180        if (pingpong_tearcheck_enable(encoder))
 181                return;
 182
 183        mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true);
 184
 185        mdp5_ctl_set_encoder_state(ctl, pipeline, true);
 186
 187        mdp5_cmd_enc->enabled = true;
 188}
 189
 190int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
 191                                       struct drm_encoder *slave_encoder)
 192{
 193        struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
 194        struct mdp5_kms *mdp5_kms;
 195        struct device *dev;
 196        int intf_num;
 197        u32 data = 0;
 198
 199        if (!encoder || !slave_encoder)
 200                return -EINVAL;
 201
 202        mdp5_kms = get_kms(encoder);
 203        intf_num = mdp5_cmd_enc->intf->num;
 204
 205        /* Switch slave encoder's trigger MUX, to use the master's
 206         * start signal for the slave encoder
 207         */
 208        if (intf_num == 1)
 209                data |= MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX;
 210        else if (intf_num == 2)
 211                data |= MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX;
 212        else
 213                return -EINVAL;
 214
 215        /* Smart Panel, Sync mode */
 216        data |= MDP5_SPLIT_DPL_UPPER_SMART_PANEL;
 217
 218        dev = &mdp5_kms->pdev->dev;
 219
 220        /* Make sure clocks are on when connectors calling this function. */
 221        pm_runtime_get_sync(dev);
 222        mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, data);
 223
 224        mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER,
 225                   MDP5_SPLIT_DPL_LOWER_SMART_PANEL);
 226        mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1);
 227        pm_runtime_put_sync(dev);
 228
 229        return 0;
 230}
 231