linux/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
<<
>>
Prefs
   1/*
   2 * Copyright 2015 Freescale Semiconductor, Inc.
   3 *
   4 * Freescale DCU drm device driver
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 */
  11
  12#include <linux/clk.h>
  13#include <linux/regmap.h>
  14
  15#include <drm/drmP.h>
  16#include <drm/drm_atomic.h>
  17#include <drm/drm_atomic_helper.h>
  18#include <drm/drm_crtc.h>
  19#include <drm/drm_crtc_helper.h>
  20
  21#include "fsl_dcu_drm_crtc.h"
  22#include "fsl_dcu_drm_drv.h"
  23#include "fsl_dcu_drm_plane.h"
  24
  25static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc,
  26                                          struct drm_crtc_state *old_crtc_state)
  27{
  28        struct drm_device *dev = crtc->dev;
  29        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
  30        struct drm_pending_vblank_event *event = crtc->state->event;
  31
  32        regmap_write(fsl_dev->regmap,
  33                     DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG);
  34
  35        if (event) {
  36                crtc->state->event = NULL;
  37
  38                spin_lock_irq(&crtc->dev->event_lock);
  39                if (drm_crtc_vblank_get(crtc) == 0)
  40                        drm_crtc_arm_vblank_event(crtc, event);
  41                else
  42                        drm_crtc_send_vblank_event(crtc, event);
  43                spin_unlock_irq(&crtc->dev->event_lock);
  44        }
  45}
  46
  47static void fsl_dcu_drm_crtc_atomic_disable(struct drm_crtc *crtc,
  48                                        struct drm_crtc_state *old_crtc_state)
  49{
  50        struct drm_device *dev = crtc->dev;
  51        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
  52
  53        /* always disable planes on the CRTC */
  54        drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, true);
  55
  56        drm_crtc_vblank_off(crtc);
  57
  58        regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
  59                           DCU_MODE_DCU_MODE_MASK,
  60                           DCU_MODE_DCU_MODE(DCU_MODE_OFF));
  61        regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
  62                     DCU_UPDATE_MODE_READREG);
  63        clk_disable_unprepare(fsl_dev->pix_clk);
  64}
  65
  66static void fsl_dcu_drm_crtc_atomic_enable(struct drm_crtc *crtc,
  67                                           struct drm_crtc_state *old_state)
  68{
  69        struct drm_device *dev = crtc->dev;
  70        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
  71
  72        clk_prepare_enable(fsl_dev->pix_clk);
  73        regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
  74                           DCU_MODE_DCU_MODE_MASK,
  75                           DCU_MODE_DCU_MODE(DCU_MODE_NORMAL));
  76        regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
  77                     DCU_UPDATE_MODE_READREG);
  78
  79        drm_crtc_vblank_on(crtc);
  80}
  81
  82static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
  83{
  84        struct drm_device *dev = crtc->dev;
  85        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
  86        struct drm_connector *con = &fsl_dev->connector.base;
  87        struct drm_display_mode *mode = &crtc->state->mode;
  88        unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0;
  89
  90        index = drm_crtc_index(crtc);
  91        clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000);
  92
  93        /* Configure timings: */
  94        hbp = mode->htotal - mode->hsync_end;
  95        hfp = mode->hsync_start - mode->hdisplay;
  96        hsw = mode->hsync_end - mode->hsync_start;
  97        vbp = mode->vtotal - mode->vsync_end;
  98        vfp = mode->vsync_start - mode->vdisplay;
  99        vsw = mode->vsync_end - mode->vsync_start;
 100
 101        /* INV_PXCK as default (most display sample data on rising edge) */
 102        if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE))
 103                pol |= DCU_SYN_POL_INV_PXCK;
 104
 105        if (mode->flags & DRM_MODE_FLAG_NHSYNC)
 106                pol |= DCU_SYN_POL_INV_HS_LOW;
 107
 108        if (mode->flags & DRM_MODE_FLAG_NVSYNC)
 109                pol |= DCU_SYN_POL_INV_VS_LOW;
 110
 111        regmap_write(fsl_dev->regmap, DCU_HSYN_PARA,
 112                     DCU_HSYN_PARA_BP(hbp) |
 113                     DCU_HSYN_PARA_PW(hsw) |
 114                     DCU_HSYN_PARA_FP(hfp));
 115        regmap_write(fsl_dev->regmap, DCU_VSYN_PARA,
 116                     DCU_VSYN_PARA_BP(vbp) |
 117                     DCU_VSYN_PARA_PW(vsw) |
 118                     DCU_VSYN_PARA_FP(vfp));
 119        regmap_write(fsl_dev->regmap, DCU_DISP_SIZE,
 120                     DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) |
 121                     DCU_DISP_SIZE_DELTA_X(mode->hdisplay));
 122        regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol);
 123        regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) |
 124                     DCU_BGND_G(0) | DCU_BGND_B(0));
 125        regmap_write(fsl_dev->regmap, DCU_DCU_MODE,
 126                     DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN);
 127        regmap_write(fsl_dev->regmap, DCU_THRESHOLD,
 128                     DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) |
 129                     DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) |
 130                     DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL));
 131        return;
 132}
 133
 134static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = {
 135        .atomic_disable = fsl_dcu_drm_crtc_atomic_disable,
 136        .atomic_flush = fsl_dcu_drm_crtc_atomic_flush,
 137        .atomic_enable = fsl_dcu_drm_crtc_atomic_enable,
 138        .mode_set_nofb = fsl_dcu_drm_crtc_mode_set_nofb,
 139};
 140
 141static int fsl_dcu_drm_crtc_enable_vblank(struct drm_crtc *crtc)
 142{
 143        struct drm_device *dev = crtc->dev;
 144        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 145        unsigned int value;
 146
 147        regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
 148        value &= ~DCU_INT_MASK_VBLANK;
 149        regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
 150
 151        return 0;
 152}
 153
 154static void fsl_dcu_drm_crtc_disable_vblank(struct drm_crtc *crtc)
 155{
 156        struct drm_device *dev = crtc->dev;
 157        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 158        unsigned int value;
 159
 160        regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
 161        value |= DCU_INT_MASK_VBLANK;
 162        regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
 163}
 164
 165static const struct drm_crtc_funcs fsl_dcu_drm_crtc_funcs = {
 166        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
 167        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 168        .destroy = drm_crtc_cleanup,
 169        .page_flip = drm_atomic_helper_page_flip,
 170        .reset = drm_atomic_helper_crtc_reset,
 171        .set_config = drm_atomic_helper_set_config,
 172        .enable_vblank = fsl_dcu_drm_crtc_enable_vblank,
 173        .disable_vblank = fsl_dcu_drm_crtc_disable_vblank,
 174};
 175
 176int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev)
 177{
 178        struct drm_plane *primary;
 179        struct drm_crtc *crtc = &fsl_dev->crtc;
 180        int ret;
 181
 182        fsl_dcu_drm_init_planes(fsl_dev->drm);
 183
 184        primary = fsl_dcu_drm_primary_create_plane(fsl_dev->drm);
 185        if (!primary)
 186                return -ENOMEM;
 187
 188        ret = drm_crtc_init_with_planes(fsl_dev->drm, crtc, primary, NULL,
 189                                        &fsl_dcu_drm_crtc_funcs, NULL);
 190        if (ret) {
 191                primary->funcs->destroy(primary);
 192                return ret;
 193        }
 194
 195        drm_crtc_helper_add(crtc, &fsl_dcu_drm_crtc_helper_funcs);
 196
 197        return 0;
 198}
 199