Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/msm/disp/mdp4/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 13 kB image not shown  

Quelle  mdp4_lcdc_encoder.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2014 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 * Author: Vinay Simha <vinaysimha@inforcecomputing.com>
 */


#include <linux/delay.h>

#include <drm/drm_crtc.h>
#include <drm/drm_probe_helper.h>

#include "mdp4_kms.h"

struct mdp4_lcdc_encoder {
 struct drm_encoder base;
 struct drm_panel *panel;
 struct clk *lcdc_clk;
 unsigned long int pixclock;
 struct regulator_bulk_data regs[3];
 bool enabled;
 uint32_t bsc;
};
#define to_mdp4_lcdc_encoder(x) container_of(x, struct mdp4_lcdc_encoder, base)

static struct mdp4_kms *get_kms(struct drm_encoder *encoder)
{
 struct msm_drm_private *priv = encoder->dev->dev_private;
 return to_mdp4_kms(to_mdp_kms(priv->kms));
}

/* this should probably be a helper: */
static struct drm_connector *get_connector(struct drm_encoder *encoder)
{
 struct drm_device *dev = encoder->dev;
 struct drm_connector *connector;

 list_for_each_entry(connector, &dev->mode_config.connector_list, head)
  if (connector->encoder == encoder)
   return connector;

 return NULL;
}

static void setup_phy(struct drm_encoder *encoder)
{
 struct drm_device *dev = encoder->dev;
 struct drm_connector *connector = get_connector(encoder);
 struct mdp4_kms *mdp4_kms = get_kms(encoder);
 uint32_t lvds_intf = 0, lvds_phy_cfg0 = 0;
 int bpp, nchan, swap;

 if (!connector)
  return;

 bpp = 3 * connector->display_info.bpc;

 if (!bpp)
  bpp = 18;

 /* TODO, these should come from panel somehow: */
 nchan = 1;
 swap = 0;

 switch (bpp) {
 case 24:
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0),
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x08) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x05) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x04) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x03));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0),
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x02) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x01) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x00));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1),
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x11) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x10) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0d) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0c));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1),
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0b) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0a) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x09));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2),
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x15));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2),
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x14) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x13) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x12));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(3),
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1b) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x17) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x16) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0f));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(3),
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0e) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x07) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x06));
  if (nchan == 2) {
   lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE3_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
  } else {
   lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
  }
  break;

 case 18:
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0),
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x0a) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x07) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x06) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x05));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0),
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x04) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x03) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x02));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1),
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x13) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x12) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0f) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0e));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1),
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0d) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0c) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x0b));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2),
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) |
    MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x17));
  mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2),
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x16) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x15) |
    MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x14));
  if (nchan == 2) {
   lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
  } else {
   lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
     MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
  }
  lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_RGB_OUT;
  break;

 default:
  DRM_DEV_ERROR(dev->dev, "unknown bpp: %d\n", bpp);
  return;
 }

 switch (nchan) {
 case 1:
  lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0;
  lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN |
    MDP4_LCDC_LVDS_INTF_CTL_MODE_SEL;
  break;
 case 2:
  lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0 |
    MDP4_LVDS_PHY_CFG0_CHANNEL1;
  lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_CLK_LANE_EN |
    MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN;
  break;
 default:
  DRM_DEV_ERROR(dev->dev, "unknown # of channels: %d\n", nchan);
  return;
 }

 if (swap)
  lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH_SWAP;

 lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_ENABLE;

 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_INTF_CTL, lvds_intf);
 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG2, 0x30);

 mb();
 udelay(1);
 lvds_phy_cfg0 |= MDP4_LVDS_PHY_CFG0_SERIALIZATION_ENBLE;
 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0);
}

static void mdp4_lcdc_encoder_mode_set(struct drm_encoder *encoder,
  struct drm_display_mode *mode,
  struct drm_display_mode *adjusted_mode)
{
 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
   to_mdp4_lcdc_encoder(encoder);
 struct mdp4_kms *mdp4_kms = get_kms(encoder);
 uint32_t lcdc_hsync_skew, vsync_period, vsync_len, ctrl_pol;
 uint32_t display_v_start, display_v_end;
 uint32_t hsync_start_x, hsync_end_x;

 mode = adjusted_mode;

 DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));

 mdp4_lcdc_encoder->pixclock = mode->clock * 1000;

 DBG("pixclock=%lu", mdp4_lcdc_encoder->pixclock);

 ctrl_pol = 0;
 if (mode->flags & DRM_MODE_FLAG_NHSYNC)
  ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_HSYNC_LOW;
 if (mode->flags & DRM_MODE_FLAG_NVSYNC)
  ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_VSYNC_LOW;
 /* probably need to get DATA_EN polarity from panel.. */

 lcdc_hsync_skew = 0;  /* get this from panel? */

 hsync_start_x = (mode->htotal - mode->hsync_start);
 hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1;

 vsync_period = mode->vtotal * mode->htotal;
 vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal;
 display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + lcdc_hsync_skew;
 display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + lcdc_hsync_skew - 1;

 mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_CTRL,
   MDP4_LCDC_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) |
   MDP4_LCDC_HSYNC_CTRL_PERIOD(mode->htotal));
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_PERIOD, vsync_period);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_LEN, vsync_len);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_HCTRL,
   MDP4_LCDC_DISPLAY_HCTRL_START(hsync_start_x) |
   MDP4_LCDC_DISPLAY_HCTRL_END(hsync_end_x));
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VSTART, display_v_start);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VEND, display_v_end);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_BORDER_CLR, 0);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_UNDERFLOW_CLR,
   MDP4_LCDC_UNDERFLOW_CLR_ENABLE_RECOVERY |
   MDP4_LCDC_UNDERFLOW_CLR_COLOR(0xff));
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_SKEW, lcdc_hsync_skew);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_CTRL_POLARITY, ctrl_pol);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_HCTL,
   MDP4_LCDC_ACTIVE_HCTL_START(0) |
   MDP4_LCDC_ACTIVE_HCTL_END(0));
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VSTART, 0);
 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VEND, 0);
}

static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder)
{
 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
   to_mdp4_lcdc_encoder(encoder);
 struct mdp4_kms *mdp4_kms = get_kms(encoder);

 if (WARN_ON(!mdp4_lcdc_encoder->enabled))
  return;

 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);

 /*
 * Wait for a vsync so we know the ENABLE=0 latched before
 * the (connector) source of the vsync's gets disabled,
 * otherwise we end up in a funny state if we re-enable
 * before the disable latches, which results that some of
 * the settings changes for the new modeset (like new
 * scanout buffer) don't latch properly..
 */

 mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_PRIMARY_VSYNC);

 clk_disable_unprepare(mdp4_lcdc_encoder->lcdc_clk);

 regulator_bulk_disable(ARRAY_SIZE(mdp4_lcdc_encoder->regs),
          mdp4_lcdc_encoder->regs);

 mdp4_lcdc_encoder->enabled = false;
}

static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder)
{
 struct drm_device *dev = encoder->dev;
 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
   to_mdp4_lcdc_encoder(encoder);
 unsigned long pc = mdp4_lcdc_encoder->pixclock;
 struct mdp4_kms *mdp4_kms = get_kms(encoder);
 uint32_t config;
 int ret;

 if (WARN_ON(mdp4_lcdc_encoder->enabled))
  return;

 /* TODO: hard-coded for 18bpp: */
 config =
  MDP4_DMA_CONFIG_R_BPC(BPC6) |
  MDP4_DMA_CONFIG_G_BPC(BPC6) |
  MDP4_DMA_CONFIG_B_BPC(BPC6) |
  MDP4_DMA_CONFIG_PACK(0x21) |
  MDP4_DMA_CONFIG_DEFLKR_EN |
  MDP4_DMA_CONFIG_DITHER_EN;

 if (!of_property_read_bool(dev->dev->of_node, "qcom,lcdc-align-lsb"))
  config |= MDP4_DMA_CONFIG_PACK_ALIGN_MSB;

 mdp4_crtc_set_config(encoder->crtc, config);
 mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 0);

 ret = regulator_bulk_enable(ARRAY_SIZE(mdp4_lcdc_encoder->regs),
        mdp4_lcdc_encoder->regs);
 if (ret)
  DRM_DEV_ERROR(dev->dev, "failed to enable regulators: %d\n", ret);

 DBG("setting lcdc_clk=%lu", pc);
 ret = clk_set_rate(mdp4_lcdc_encoder->lcdc_clk, pc);
 if (ret)
  DRM_DEV_ERROR(dev->dev, "failed to configure lcdc_clk: %d\n", ret);
 ret = clk_prepare_enable(mdp4_lcdc_encoder->lcdc_clk);
 if (ret)
  DRM_DEV_ERROR(dev->dev, "failed to enable lcdc_clk: %d\n", ret);

 setup_phy(encoder);

 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 1);

 mdp4_lcdc_encoder->enabled = true;
}

static enum drm_mode_status
mdp4_lcdc_encoder_mode_valid(struct drm_encoder *encoder,
  const struct drm_display_mode *mode)
{
 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
   to_mdp4_lcdc_encoder(encoder);
 long actual, requested;

 requested = 1000 * mode->clock;
 actual = clk_round_rate(mdp4_lcdc_encoder->lcdc_clk, requested);

 DBG("requested=%ld, actual=%ld", requested, actual);

 if (actual != requested)
  return MODE_CLOCK_RANGE;

 return MODE_OK;
}

static const struct drm_encoder_helper_funcs mdp4_lcdc_encoder_helper_funcs = {
 .mode_set = mdp4_lcdc_encoder_mode_set,
 .disable = mdp4_lcdc_encoder_disable,
 .enable = mdp4_lcdc_encoder_enable,
 .mode_valid = mdp4_lcdc_encoder_mode_valid,
};

/* initialize encoder */
struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev)
{
 struct drm_encoder *encoder;
 struct mdp4_lcdc_encoder *mdp4_lcdc_encoder;
 int ret;

 mdp4_lcdc_encoder = drmm_encoder_alloc(dev, struct mdp4_lcdc_encoder, base,
            NULL, DRM_MODE_ENCODER_LVDS, NULL);
 if (IS_ERR(mdp4_lcdc_encoder))
  return ERR_CAST(mdp4_lcdc_encoder);

 encoder = &mdp4_lcdc_encoder->base;

 drm_encoder_helper_add(encoder, &mdp4_lcdc_encoder_helper_funcs);

 mdp4_lcdc_encoder->lcdc_clk = mpd4_get_lcdc_clock(dev);
 if (IS_ERR(mdp4_lcdc_encoder->lcdc_clk)) {
  DRM_DEV_ERROR(dev->dev, "failed to get lvds_clk\n");
  return ERR_CAST(mdp4_lcdc_encoder->lcdc_clk);
 }

 /* TODO: different regulators in other cases? */
 mdp4_lcdc_encoder->regs[0].supply = "lvds-vccs-3p3v";
 mdp4_lcdc_encoder->regs[1].supply = "lvds-pll-vdda";
 mdp4_lcdc_encoder->regs[2].supply = "lvds-vdda";

 ret = devm_regulator_bulk_get(dev->dev,
          ARRAY_SIZE(mdp4_lcdc_encoder->regs),
          mdp4_lcdc_encoder->regs);
 if (ret)
  return ERR_PTR(ret);

 return encoder;
}

Messung V0.5
C=96 H=91 G=93

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.