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