linux/drivers/gpu/drm/arc/arcpgu_crtc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ARC PGU DRM driver.
   4 *
   5 * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
   6 */
   7
   8#include <drm/drm_atomic_helper.h>
   9#include <drm/drm_device.h>
  10#include <drm/drm_fb_cma_helper.h>
  11#include <drm/drm_gem_cma_helper.h>
  12#include <drm/drm_plane_helper.h>
  13#include <drm/drm_probe_helper.h>
  14#include <linux/clk.h>
  15#include <linux/platform_data/simplefb.h>
  16
  17#include "arcpgu.h"
  18#include "arcpgu_regs.h"
  19
  20#define ENCODE_PGU_XY(x, y)     ((((x) - 1) << 16) | ((y) - 1))
  21
  22static const u32 arc_pgu_supported_formats[] = {
  23        DRM_FORMAT_RGB565,
  24        DRM_FORMAT_XRGB8888,
  25        DRM_FORMAT_ARGB8888,
  26};
  27
  28static void arc_pgu_set_pxl_fmt(struct drm_crtc *crtc)
  29{
  30        struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
  31        const struct drm_framebuffer *fb = crtc->primary->state->fb;
  32        uint32_t pixel_format = fb->format->format;
  33        u32 format = DRM_FORMAT_INVALID;
  34        int i;
  35        u32 reg_ctrl;
  36
  37        for (i = 0; i < ARRAY_SIZE(arc_pgu_supported_formats); i++) {
  38                if (arc_pgu_supported_formats[i] == pixel_format)
  39                        format = arc_pgu_supported_formats[i];
  40        }
  41
  42        if (WARN_ON(format == DRM_FORMAT_INVALID))
  43                return;
  44
  45        reg_ctrl = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL);
  46        if (format == DRM_FORMAT_RGB565)
  47                reg_ctrl &= ~ARCPGU_MODE_XRGB8888;
  48        else
  49                reg_ctrl |= ARCPGU_MODE_XRGB8888;
  50        arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, reg_ctrl);
  51}
  52
  53static const struct drm_crtc_funcs arc_pgu_crtc_funcs = {
  54        .destroy = drm_crtc_cleanup,
  55        .set_config = drm_atomic_helper_set_config,
  56        .page_flip = drm_atomic_helper_page_flip,
  57        .reset = drm_atomic_helper_crtc_reset,
  58        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
  59        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
  60};
  61
  62static enum drm_mode_status arc_pgu_crtc_mode_valid(struct drm_crtc *crtc,
  63                                                    const struct drm_display_mode *mode)
  64{
  65        struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
  66        long rate, clk_rate = mode->clock * 1000;
  67        long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */
  68
  69        rate = clk_round_rate(arcpgu->clk, clk_rate);
  70        if ((max(rate, clk_rate) - min(rate, clk_rate) < diff) && (rate > 0))
  71                return MODE_OK;
  72
  73        return MODE_NOCLOCK;
  74}
  75
  76static void arc_pgu_crtc_mode_set_nofb(struct drm_crtc *crtc)
  77{
  78        struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
  79        struct drm_display_mode *m = &crtc->state->adjusted_mode;
  80        u32 val;
  81
  82        arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
  83                      ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal));
  84
  85        arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC,
  86                      ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay,
  87                                    m->crtc_hsync_end - m->crtc_hdisplay));
  88
  89        arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC,
  90                      ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay,
  91                                    m->crtc_vsync_end - m->crtc_vdisplay));
  92
  93        arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE,
  94                      ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start,
  95                                    m->crtc_vblank_end - m->crtc_vblank_start));
  96
  97        val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL);
  98
  99        if (m->flags & DRM_MODE_FLAG_PVSYNC)
 100                val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST;
 101        else
 102                val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST);
 103
 104        if (m->flags & DRM_MODE_FLAG_PHSYNC)
 105                val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST;
 106        else
 107                val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST);
 108
 109        arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, val);
 110        arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, 0);
 111        arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, 1);
 112
 113        arc_pgu_set_pxl_fmt(crtc);
 114
 115        clk_set_rate(arcpgu->clk, m->crtc_clock * 1000);
 116}
 117
 118static void arc_pgu_crtc_atomic_enable(struct drm_crtc *crtc,
 119                                       struct drm_crtc_state *old_state)
 120{
 121        struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
 122
 123        clk_prepare_enable(arcpgu->clk);
 124        arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
 125                      arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) |
 126                      ARCPGU_CTRL_ENABLE_MASK);
 127}
 128
 129static void arc_pgu_crtc_atomic_disable(struct drm_crtc *crtc,
 130                                        struct drm_crtc_state *old_state)
 131{
 132        struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
 133
 134        clk_disable_unprepare(arcpgu->clk);
 135        arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
 136                              arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) &
 137                              ~ARCPGU_CTRL_ENABLE_MASK);
 138}
 139
 140static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = {
 141        .mode_valid     = arc_pgu_crtc_mode_valid,
 142        .mode_set_nofb  = arc_pgu_crtc_mode_set_nofb,
 143        .atomic_enable  = arc_pgu_crtc_atomic_enable,
 144        .atomic_disable = arc_pgu_crtc_atomic_disable,
 145};
 146
 147static void arc_pgu_plane_atomic_update(struct drm_plane *plane,
 148                                        struct drm_plane_state *state)
 149{
 150        struct arcpgu_drm_private *arcpgu;
 151        struct drm_gem_cma_object *gem;
 152
 153        if (!plane->state->crtc || !plane->state->fb)
 154                return;
 155
 156        arcpgu = crtc_to_arcpgu_priv(plane->state->crtc);
 157        gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
 158        arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr);
 159}
 160
 161static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = {
 162        .atomic_update = arc_pgu_plane_atomic_update,
 163};
 164
 165static void arc_pgu_plane_destroy(struct drm_plane *plane)
 166{
 167        drm_plane_cleanup(plane);
 168}
 169
 170static const struct drm_plane_funcs arc_pgu_plane_funcs = {
 171        .update_plane           = drm_atomic_helper_update_plane,
 172        .disable_plane          = drm_atomic_helper_disable_plane,
 173        .destroy                = arc_pgu_plane_destroy,
 174        .reset                  = drm_atomic_helper_plane_reset,
 175        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 176        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 177};
 178
 179static struct drm_plane *arc_pgu_plane_init(struct drm_device *drm)
 180{
 181        struct arcpgu_drm_private *arcpgu = drm->dev_private;
 182        struct drm_plane *plane = NULL;
 183        int ret;
 184
 185        plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
 186        if (!plane)
 187                return ERR_PTR(-ENOMEM);
 188
 189        ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs,
 190                                       arc_pgu_supported_formats,
 191                                       ARRAY_SIZE(arc_pgu_supported_formats),
 192                                       NULL,
 193                                       DRM_PLANE_TYPE_PRIMARY, NULL);
 194        if (ret)
 195                return ERR_PTR(ret);
 196
 197        drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs);
 198        arcpgu->plane = plane;
 199
 200        return plane;
 201}
 202
 203int arc_pgu_setup_crtc(struct drm_device *drm)
 204{
 205        struct arcpgu_drm_private *arcpgu = drm->dev_private;
 206        struct drm_plane *primary;
 207        int ret;
 208
 209        primary = arc_pgu_plane_init(drm);
 210        if (IS_ERR(primary))
 211                return PTR_ERR(primary);
 212
 213        ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL,
 214                                        &arc_pgu_crtc_funcs, NULL);
 215        if (ret) {
 216                arc_pgu_plane_destroy(primary);
 217                return ret;
 218        }
 219
 220        drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs);
 221        return 0;
 222}
 223