Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  rzg2l_du_crtc.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * RZ/G2L Display Unit CRTCs
 *
 * Copyright (C) 2023 Renesas Electronics Corporation
 *
 * Based on rcar_du_crtc.c
 */


#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/reset.h>

#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_vblank.h>

#include "rzg2l_du_crtc.h"
#include "rzg2l_du_drv.h"
#include "rzg2l_du_encoder.h"
#include "rzg2l_du_kms.h"
#include "rzg2l_du_vsp.h"

#define DU_MCR0   0x00
#define DU_MCR0_DI_EN  BIT(8)

#define DU_DITR0  0x10
#define DU_DITR0_DEMD_HIGH (BIT(8) | BIT(9))
#define DU_DITR0_VSPOL  BIT(16)
#define DU_DITR0_HSPOL  BIT(17)

#define DU_DITR1  0x14
#define DU_DITR1_VSA(x)  ((x) << 0)
#define DU_DITR1_VACTIVE(x) ((x) << 16)

#define DU_DITR2  0x18
#define DU_DITR2_VBP(x)  ((x) << 0)
#define DU_DITR2_VFP(x)  ((x) << 16)

#define DU_DITR3  0x1c
#define DU_DITR3_HSA(x)  ((x) << 0)
#define DU_DITR3_HACTIVE(x) ((x) << 16)

#define DU_DITR4  0x20
#define DU_DITR4_HBP(x)  ((x) << 0)
#define DU_DITR4_HFP(x)  ((x) << 16)

#define DU_MCR1   0x40
#define DU_MCR1_PB_AUTOCLR BIT(16)

#define DU_PBCR0  0x4c
#define DU_PBCR0_PB_DEP(x) ((x) << 0)

/* -----------------------------------------------------------------------------
 * Hardware Setup
 */


static void rzg2l_du_crtc_set_display_timing(struct rzg2l_du_crtc *rcrtc)
{
 const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
 unsigned long mode_clock = mode->clock * 1000;
 u32 ditr0, ditr1, ditr2, ditr3, ditr4, pbcr0;
 struct rzg2l_du_device *rcdu = rcrtc->dev;

 clk_prepare_enable(rcrtc->rzg2l_clocks.dclk);
 clk_set_rate(rcrtc->rzg2l_clocks.dclk, mode_clock);

 ditr0 = (DU_DITR0_DEMD_HIGH
       | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DU_DITR0_VSPOL : 0)
       | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DU_DITR0_HSPOL : 0));

 ditr1 = DU_DITR1_VSA(mode->vsync_end - mode->vsync_start)
       | DU_DITR1_VACTIVE(mode->vdisplay);

 ditr2 = DU_DITR2_VBP(mode->vtotal - mode->vsync_end)
       | DU_DITR2_VFP(mode->vsync_start - mode->vdisplay);

 ditr3 = DU_DITR3_HSA(mode->hsync_end - mode->hsync_start)
       | DU_DITR3_HACTIVE(mode->hdisplay);

 ditr4 = DU_DITR4_HBP(mode->htotal - mode->hsync_end)
       | DU_DITR4_HFP(mode->hsync_start - mode->hdisplay);

 pbcr0 = DU_PBCR0_PB_DEP(0x1f);

 writel(ditr0, rcdu->mmio + DU_DITR0);
 writel(ditr1, rcdu->mmio + DU_DITR1);
 writel(ditr2, rcdu->mmio + DU_DITR2);
 writel(ditr3, rcdu->mmio + DU_DITR3);
 writel(ditr4, rcdu->mmio + DU_DITR4);
 writel(pbcr0, rcdu->mmio + DU_PBCR0);

 /* Enable auto clear */
 writel(DU_MCR1_PB_AUTOCLR, rcdu->mmio + DU_MCR1);
}

/* -----------------------------------------------------------------------------
 * Page Flip
 */


void rzg2l_du_crtc_finish_page_flip(struct rzg2l_du_crtc *rcrtc)
{
 struct drm_pending_vblank_event *event;
 struct drm_device *dev = rcrtc->crtc.dev;
 unsigned long flags;

 spin_lock_irqsave(&dev->event_lock, flags);
 event = rcrtc->event;
 rcrtc->event = NULL;
 spin_unlock_irqrestore(&dev->event_lock, flags);

 if (!event)
  return;

 spin_lock_irqsave(&dev->event_lock, flags);
 drm_crtc_send_vblank_event(&rcrtc->crtc, event);
 wake_up(&rcrtc->flip_wait);
 spin_unlock_irqrestore(&dev->event_lock, flags);

 drm_crtc_vblank_put(&rcrtc->crtc);
}

static bool rzg2l_du_crtc_page_flip_pending(struct rzg2l_du_crtc *rcrtc)
{
 struct drm_device *dev = rcrtc->crtc.dev;
 unsigned long flags;
 bool pending;

 spin_lock_irqsave(&dev->event_lock, flags);
 pending = rcrtc->event;
 spin_unlock_irqrestore(&dev->event_lock, flags);

 return pending;
}

static void rzg2l_du_crtc_wait_page_flip(struct rzg2l_du_crtc *rcrtc)
{
 struct rzg2l_du_device *rcdu = rcrtc->dev;

 if (wait_event_timeout(rcrtc->flip_wait,
          !rzg2l_du_crtc_page_flip_pending(rcrtc),
          msecs_to_jiffies(50)))
  return;

 dev_warn(rcdu->dev, "page flip timeout\n");

 rzg2l_du_crtc_finish_page_flip(rcrtc);
}

/* -----------------------------------------------------------------------------
 * Start/Stop and Suspend/Resume
 */


static void rzg2l_du_crtc_setup(struct rzg2l_du_crtc *rcrtc)
{
 /* Configure display timings and output routing */
 rzg2l_du_crtc_set_display_timing(rcrtc);

 /* Enable the VSP compositor. */
 rzg2l_du_vsp_enable(rcrtc);

 /* Turn vertical blanking interrupt reporting on. */
 drm_crtc_vblank_on(&rcrtc->crtc);
}

static int rzg2l_du_crtc_get(struct rzg2l_du_crtc *rcrtc)
{
 int ret;

 /*
 * Guard against double-get, as the function is called from both the
 * .atomic_enable() and .atomic_flush() handlers.
 */

 if (rcrtc->initialized)
  return 0;

 ret = clk_prepare_enable(rcrtc->rzg2l_clocks.aclk);
 if (ret < 0)
  return ret;

 ret = clk_prepare_enable(rcrtc->rzg2l_clocks.pclk);
 if (ret < 0)
  goto error_bus_clock;

 ret = reset_control_deassert(rcrtc->rstc);
 if (ret < 0)
  goto error_peri_clock;

 rzg2l_du_crtc_setup(rcrtc);
 rcrtc->initialized = true;

 return 0;

error_peri_clock:
 clk_disable_unprepare(rcrtc->rzg2l_clocks.pclk);
error_bus_clock:
 clk_disable_unprepare(rcrtc->rzg2l_clocks.aclk);
 return ret;
}

static void rzg2l_du_crtc_put(struct rzg2l_du_crtc *rcrtc)
{
 clk_disable_unprepare(rcrtc->rzg2l_clocks.dclk);
 reset_control_assert(rcrtc->rstc);
 clk_disable_unprepare(rcrtc->rzg2l_clocks.pclk);
 clk_disable_unprepare(rcrtc->rzg2l_clocks.aclk);

 rcrtc->initialized = false;
}

static void rzg2l_du_start_stop(struct rzg2l_du_crtc *rcrtc, bool start)
{
 struct rzg2l_du_device *rcdu = rcrtc->dev;

 writel(start ? DU_MCR0_DI_EN : 0, rcdu->mmio + DU_MCR0);
}

static void rzg2l_du_crtc_start(struct rzg2l_du_crtc *rcrtc)
{
 rzg2l_du_start_stop(rcrtc, true);
}

static void rzg2l_du_crtc_stop(struct rzg2l_du_crtc *rcrtc)
{
 struct drm_crtc *crtc = &rcrtc->crtc;

 /*
 * Disable vertical blanking interrupt reporting. We first need to wait
 * for page flip completion before stopping the CRTC as userspace
 * expects page flips to eventually complete.
 */

 rzg2l_du_crtc_wait_page_flip(rcrtc);
 drm_crtc_vblank_off(crtc);

 /* Disable the VSP compositor. */
 rzg2l_du_vsp_disable(rcrtc);

 rzg2l_du_start_stop(rcrtc, false);
}

/* -----------------------------------------------------------------------------
 * CRTC Functions
 */


static void rzg2l_du_crtc_atomic_enable(struct drm_crtc *crtc,
     struct drm_atomic_state *state)
{
 struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);

 rzg2l_du_crtc_get(rcrtc);

 rzg2l_du_crtc_start(rcrtc);
}

static void rzg2l_du_crtc_atomic_disable(struct drm_crtc *crtc,
      struct drm_atomic_state *state)
{
 struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);

 rzg2l_du_crtc_stop(rcrtc);
 rzg2l_du_crtc_put(rcrtc);

 spin_lock_irq(&crtc->dev->event_lock);
 if (crtc->state->event) {
  drm_crtc_send_vblank_event(crtc, crtc->state->event);
  crtc->state->event = NULL;
 }
 spin_unlock_irq(&crtc->dev->event_lock);
}

static void rzg2l_du_crtc_atomic_flush(struct drm_crtc *crtc,
           struct drm_atomic_state *state)
{
 struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);
 struct drm_device *dev = rcrtc->crtc.dev;
 unsigned long flags;

 WARN_ON(!crtc->state->enable);

 if (crtc->state->event) {
  WARN_ON(drm_crtc_vblank_get(crtc) != 0);

  spin_lock_irqsave(&dev->event_lock, flags);
  rcrtc->event = crtc->state->event;
  crtc->state->event = NULL;
  spin_unlock_irqrestore(&dev->event_lock, flags);
 }

 rzg2l_du_vsp_atomic_flush(rcrtc);
}

static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
 .atomic_flush = rzg2l_du_crtc_atomic_flush,
 .atomic_enable = rzg2l_du_crtc_atomic_enable,
 .atomic_disable = rzg2l_du_crtc_atomic_disable,
};

static struct drm_crtc_state *
rzg2l_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
 struct rzg2l_du_crtc_state *state;
 struct rzg2l_du_crtc_state *copy;

 if (WARN_ON(!crtc->state))
  return NULL;

 state = to_rzg2l_crtc_state(crtc->state);
 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
 if (!copy)
  return NULL;

 __drm_atomic_helper_crtc_duplicate_state(crtc, ©->state);

 return ©->state;
}

static void rzg2l_du_crtc_atomic_destroy_state(struct drm_crtc *crtc,
            struct drm_crtc_state *state)
{
 __drm_atomic_helper_crtc_destroy_state(state);
 kfree(to_rzg2l_crtc_state(state));
}

static void rzg2l_du_crtc_reset(struct drm_crtc *crtc)
{
 struct rzg2l_du_crtc_state *state;

 if (crtc->state) {
  rzg2l_du_crtc_atomic_destroy_state(crtc, crtc->state);
  crtc->state = NULL;
 }

 state = kzalloc(sizeof(*state), GFP_KERNEL);
 if (!state)
  return;

 __drm_atomic_helper_crtc_reset(crtc, &state->state);
}

static int rzg2l_du_crtc_enable_vblank(struct drm_crtc *crtc)
{
 struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);

 rcrtc->vblank_enable = true;

 return 0;
}

static void rzg2l_du_crtc_disable_vblank(struct drm_crtc *crtc)
{
 struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);

 rcrtc->vblank_enable = false;
}

static const struct drm_crtc_funcs crtc_funcs_rz = {
 .reset = rzg2l_du_crtc_reset,
 .set_config = drm_atomic_helper_set_config,
 .page_flip = drm_atomic_helper_page_flip,
 .atomic_duplicate_state = rzg2l_du_crtc_atomic_duplicate_state,
 .atomic_destroy_state = rzg2l_du_crtc_atomic_destroy_state,
 .enable_vblank = rzg2l_du_crtc_enable_vblank,
 .disable_vblank = rzg2l_du_crtc_disable_vblank,
};

/* -----------------------------------------------------------------------------
 * Initialization
 */


int rzg2l_du_crtc_create(struct rzg2l_du_device *rcdu)
{
 struct rzg2l_du_crtc *rcrtc = &rcdu->crtcs[0];
 struct drm_crtc *crtc = &rcrtc->crtc;
 struct drm_plane *primary;
 int ret;

 rcrtc->rstc = devm_reset_control_get_shared(rcdu->dev, NULL);
 if (IS_ERR(rcrtc->rstc)) {
  dev_err(rcdu->dev, "can't get cpg reset\n");
  return PTR_ERR(rcrtc->rstc);
 }

 rcrtc->rzg2l_clocks.aclk = devm_clk_get(rcdu->dev, "aclk");
 if (IS_ERR(rcrtc->rzg2l_clocks.aclk)) {
  dev_err(rcdu->dev, "no axi clock for DU\n");
  return PTR_ERR(rcrtc->rzg2l_clocks.aclk);
 }

 rcrtc->rzg2l_clocks.pclk = devm_clk_get(rcdu->dev, "pclk");
 if (IS_ERR(rcrtc->rzg2l_clocks.pclk)) {
  dev_err(rcdu->dev, "no peripheral clock for DU\n");
  return PTR_ERR(rcrtc->rzg2l_clocks.pclk);
 }

 rcrtc->rzg2l_clocks.dclk = devm_clk_get(rcdu->dev, "vclk");
 if (IS_ERR(rcrtc->rzg2l_clocks.dclk)) {
  dev_err(rcdu->dev, "no video clock for DU\n");
  return PTR_ERR(rcrtc->rzg2l_clocks.dclk);
 }

 init_waitqueue_head(&rcrtc->flip_wait);
 rcrtc->dev = rcdu;

 primary = rzg2l_du_vsp_get_drm_plane(rcrtc, rcrtc->vsp_pipe);
 if (IS_ERR(primary))
  return PTR_ERR(primary);

 ret = drmm_crtc_init_with_planes(&rcdu->ddev, crtc, primary, NULL,
      &crtc_funcs_rz, NULL);
 if (ret < 0)
  return ret;

 drm_crtc_helper_add(crtc, &crtc_helper_funcs);

 return 0;
}

Messung V0.5
C=97 H=98 G=97

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge