linux/drivers/gpu/drm/arm/hdlcd_crtc.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013-2015 ARM Limited
   3 * Author: Liviu Dudau <Liviu.Dudau@arm.com>
   4 *
   5 * This file is subject to the terms and conditions of the GNU General Public
   6 * License.  See the file COPYING in the main directory of this archive
   7 * for more details.
   8 *
   9 *  Implementation of a CRTC class for the HDLCD driver.
  10 */
  11
  12#include <drm/drmP.h>
  13#include <drm/drm_atomic_helper.h>
  14#include <drm/drm_crtc.h>
  15#include <drm/drm_crtc_helper.h>
  16#include <drm/drm_fb_helper.h>
  17#include <drm/drm_fb_cma_helper.h>
  18#include <drm/drm_gem_cma_helper.h>
  19#include <drm/drm_of.h>
  20#include <drm/drm_plane_helper.h>
  21#include <linux/clk.h>
  22#include <linux/of_graph.h>
  23#include <linux/platform_data/simplefb.h>
  24#include <video/videomode.h>
  25
  26#include "hdlcd_drv.h"
  27#include "hdlcd_regs.h"
  28
  29/*
  30 * The HDLCD controller is a dumb RGB streamer that gets connected to
  31 * a single HDMI transmitter or in the case of the ARM Models it gets
  32 * emulated by the software that does the actual rendering.
  33 *
  34 */
  35
  36static const struct drm_crtc_funcs hdlcd_crtc_funcs = {
  37        .destroy = drm_crtc_cleanup,
  38        .set_config = drm_atomic_helper_set_config,
  39        .page_flip = drm_atomic_helper_page_flip,
  40        .reset = drm_atomic_helper_crtc_reset,
  41        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
  42        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
  43};
  44
  45static struct simplefb_format supported_formats[] = SIMPLEFB_FORMATS;
  46
  47/*
  48 * Setup the HDLCD registers for decoding the pixels out of the framebuffer
  49 */
  50static int hdlcd_set_pxl_fmt(struct drm_crtc *crtc)
  51{
  52        unsigned int btpp;
  53        struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
  54        uint32_t pixel_format;
  55        struct simplefb_format *format = NULL;
  56        int i;
  57
  58        pixel_format = crtc->primary->state->fb->pixel_format;
  59
  60        for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
  61                if (supported_formats[i].fourcc == pixel_format)
  62                        format = &supported_formats[i];
  63        }
  64
  65        if (WARN_ON(!format))
  66                return 0;
  67
  68        /* HDLCD uses 'bytes per pixel', zero means 1 byte */
  69        btpp = (format->bits_per_pixel + 7) / 8;
  70        hdlcd_write(hdlcd, HDLCD_REG_PIXEL_FORMAT, (btpp - 1) << 3);
  71
  72        /*
  73         * The format of the HDLCD_REG_<color>_SELECT register is:
  74         *   - bits[23:16] - default value for that color component
  75         *   - bits[11:8]  - number of bits to extract for each color component
  76         *   - bits[4:0]   - index of the lowest bit to extract
  77         *
  78         * The default color value is used when bits[11:8] are zero, when the
  79         * pixel is outside the visible frame area or when there is a
  80         * buffer underrun.
  81         */
  82        hdlcd_write(hdlcd, HDLCD_REG_RED_SELECT, format->red.offset |
  83#ifdef CONFIG_DRM_HDLCD_SHOW_UNDERRUN
  84                    0x00ff0000 |        /* show underruns in red */
  85#endif
  86                    ((format->red.length & 0xf) << 8));
  87        hdlcd_write(hdlcd, HDLCD_REG_GREEN_SELECT, format->green.offset |
  88                    ((format->green.length & 0xf) << 8));
  89        hdlcd_write(hdlcd, HDLCD_REG_BLUE_SELECT, format->blue.offset |
  90                    ((format->blue.length & 0xf) << 8));
  91
  92        return 0;
  93}
  94
  95static void hdlcd_crtc_mode_set_nofb(struct drm_crtc *crtc)
  96{
  97        struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
  98        struct drm_display_mode *m = &crtc->state->adjusted_mode;
  99        struct videomode vm;
 100        unsigned int polarities, line_length, err;
 101
 102        vm.vfront_porch = m->crtc_vsync_start - m->crtc_vdisplay;
 103        vm.vback_porch = m->crtc_vtotal - m->crtc_vsync_end;
 104        vm.vsync_len = m->crtc_vsync_end - m->crtc_vsync_start;
 105        vm.hfront_porch = m->crtc_hsync_start - m->crtc_hdisplay;
 106        vm.hback_porch = m->crtc_htotal - m->crtc_hsync_end;
 107        vm.hsync_len = m->crtc_hsync_end - m->crtc_hsync_start;
 108
 109        polarities = HDLCD_POLARITY_DATAEN | HDLCD_POLARITY_DATA;
 110
 111        if (m->flags & DRM_MODE_FLAG_PHSYNC)
 112                polarities |= HDLCD_POLARITY_HSYNC;
 113        if (m->flags & DRM_MODE_FLAG_PVSYNC)
 114                polarities |= HDLCD_POLARITY_VSYNC;
 115
 116        line_length = crtc->primary->state->fb->pitches[0];
 117
 118        /* Allow max number of outstanding requests and largest burst size */
 119        hdlcd_write(hdlcd, HDLCD_REG_BUS_OPTIONS,
 120                    HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
 121
 122        hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_LENGTH, line_length);
 123        hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_PITCH, line_length);
 124        hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_COUNT, m->crtc_vdisplay - 1);
 125        hdlcd_write(hdlcd, HDLCD_REG_V_DATA, m->crtc_vdisplay - 1);
 126        hdlcd_write(hdlcd, HDLCD_REG_V_BACK_PORCH, vm.vback_porch - 1);
 127        hdlcd_write(hdlcd, HDLCD_REG_V_FRONT_PORCH, vm.vfront_porch - 1);
 128        hdlcd_write(hdlcd, HDLCD_REG_V_SYNC, vm.vsync_len - 1);
 129        hdlcd_write(hdlcd, HDLCD_REG_H_BACK_PORCH, vm.hback_porch - 1);
 130        hdlcd_write(hdlcd, HDLCD_REG_H_FRONT_PORCH, vm.hfront_porch - 1);
 131        hdlcd_write(hdlcd, HDLCD_REG_H_SYNC, vm.hsync_len - 1);
 132        hdlcd_write(hdlcd, HDLCD_REG_H_DATA, m->crtc_hdisplay - 1);
 133        hdlcd_write(hdlcd, HDLCD_REG_POLARITIES, polarities);
 134
 135        err = hdlcd_set_pxl_fmt(crtc);
 136        if (err)
 137                return;
 138
 139        clk_set_rate(hdlcd->clk, m->crtc_clock * 1000);
 140}
 141
 142static void hdlcd_crtc_enable(struct drm_crtc *crtc)
 143{
 144        struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
 145
 146        clk_prepare_enable(hdlcd->clk);
 147        hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 1);
 148        drm_crtc_vblank_on(crtc);
 149}
 150
 151static void hdlcd_crtc_disable(struct drm_crtc *crtc)
 152{
 153        struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
 154
 155        if (!crtc->primary->fb)
 156                return;
 157
 158        clk_disable_unprepare(hdlcd->clk);
 159        hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 0);
 160        drm_crtc_vblank_off(crtc);
 161}
 162
 163static int hdlcd_crtc_atomic_check(struct drm_crtc *crtc,
 164                                   struct drm_crtc_state *state)
 165{
 166        struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
 167        struct drm_display_mode *mode = &state->adjusted_mode;
 168        long rate, clk_rate = mode->clock * 1000;
 169
 170        rate = clk_round_rate(hdlcd->clk, clk_rate);
 171        if (rate != clk_rate) {
 172                /* clock required by mode not supported by hardware */
 173                return -EINVAL;
 174        }
 175
 176        return 0;
 177}
 178
 179static void hdlcd_crtc_atomic_begin(struct drm_crtc *crtc,
 180                                    struct drm_crtc_state *state)
 181{
 182        struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
 183        unsigned long flags;
 184
 185        if (crtc->state->event) {
 186                struct drm_pending_vblank_event *event = crtc->state->event;
 187
 188                crtc->state->event = NULL;
 189                event->pipe = drm_crtc_index(crtc);
 190
 191                WARN_ON(drm_crtc_vblank_get(crtc) != 0);
 192
 193                spin_lock_irqsave(&crtc->dev->event_lock, flags);
 194                list_add_tail(&event->base.link, &hdlcd->event_list);
 195                spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
 196        }
 197}
 198
 199static void hdlcd_crtc_atomic_flush(struct drm_crtc *crtc,
 200                                    struct drm_crtc_state *state)
 201{
 202}
 203
 204static bool hdlcd_crtc_mode_fixup(struct drm_crtc *crtc,
 205                        const struct drm_display_mode *mode,
 206                        struct drm_display_mode *adjusted_mode)
 207{
 208        return true;
 209}
 210
 211static const struct drm_crtc_helper_funcs hdlcd_crtc_helper_funcs = {
 212        .mode_fixup     = hdlcd_crtc_mode_fixup,
 213        .mode_set       = drm_helper_crtc_mode_set,
 214        .mode_set_base  = drm_helper_crtc_mode_set_base,
 215        .mode_set_nofb  = hdlcd_crtc_mode_set_nofb,
 216        .enable         = hdlcd_crtc_enable,
 217        .disable        = hdlcd_crtc_disable,
 218        .prepare        = hdlcd_crtc_disable,
 219        .commit         = hdlcd_crtc_enable,
 220        .atomic_check   = hdlcd_crtc_atomic_check,
 221        .atomic_begin   = hdlcd_crtc_atomic_begin,
 222        .atomic_flush   = hdlcd_crtc_atomic_flush,
 223};
 224
 225static int hdlcd_plane_atomic_check(struct drm_plane *plane,
 226                                    struct drm_plane_state *state)
 227{
 228        return 0;
 229}
 230
 231static void hdlcd_plane_atomic_update(struct drm_plane *plane,
 232                                      struct drm_plane_state *state)
 233{
 234        struct hdlcd_drm_private *hdlcd;
 235        struct drm_gem_cma_object *gem;
 236        dma_addr_t scanout_start;
 237
 238        if (!plane->state->crtc || !plane->state->fb)
 239                return;
 240
 241        hdlcd = crtc_to_hdlcd_priv(plane->state->crtc);
 242        gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
 243        scanout_start = gem->paddr;
 244        hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start);
 245}
 246
 247static const struct drm_plane_helper_funcs hdlcd_plane_helper_funcs = {
 248        .prepare_fb = NULL,
 249        .cleanup_fb = NULL,
 250        .atomic_check = hdlcd_plane_atomic_check,
 251        .atomic_update = hdlcd_plane_atomic_update,
 252};
 253
 254static void hdlcd_plane_destroy(struct drm_plane *plane)
 255{
 256        drm_plane_helper_disable(plane);
 257        drm_plane_cleanup(plane);
 258}
 259
 260static const struct drm_plane_funcs hdlcd_plane_funcs = {
 261        .update_plane           = drm_atomic_helper_update_plane,
 262        .disable_plane          = drm_atomic_helper_disable_plane,
 263        .destroy                = hdlcd_plane_destroy,
 264        .reset                  = drm_atomic_helper_plane_reset,
 265        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 266        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 267};
 268
 269static struct drm_plane *hdlcd_plane_init(struct drm_device *drm)
 270{
 271        struct hdlcd_drm_private *hdlcd = drm->dev_private;
 272        struct drm_plane *plane = NULL;
 273        u32 formats[ARRAY_SIZE(supported_formats)], i;
 274        int ret;
 275
 276        plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
 277        if (!plane)
 278                return ERR_PTR(-ENOMEM);
 279
 280        for (i = 0; i < ARRAY_SIZE(supported_formats); i++)
 281                formats[i] = supported_formats[i].fourcc;
 282
 283        ret = drm_universal_plane_init(drm, plane, 0xff, &hdlcd_plane_funcs,
 284                                       formats, ARRAY_SIZE(formats),
 285                                       DRM_PLANE_TYPE_PRIMARY, NULL);
 286        if (ret) {
 287                devm_kfree(drm->dev, plane);
 288                return ERR_PTR(ret);
 289        }
 290
 291        drm_plane_helper_add(plane, &hdlcd_plane_helper_funcs);
 292        hdlcd->plane = plane;
 293
 294        return plane;
 295}
 296
 297void hdlcd_crtc_suspend(struct drm_crtc *crtc)
 298{
 299        hdlcd_crtc_disable(crtc);
 300}
 301
 302void hdlcd_crtc_resume(struct drm_crtc *crtc)
 303{
 304        hdlcd_crtc_enable(crtc);
 305}
 306
 307int hdlcd_setup_crtc(struct drm_device *drm)
 308{
 309        struct hdlcd_drm_private *hdlcd = drm->dev_private;
 310        struct drm_plane *primary;
 311        int ret;
 312
 313        primary = hdlcd_plane_init(drm);
 314        if (IS_ERR(primary))
 315                return PTR_ERR(primary);
 316
 317        ret = drm_crtc_init_with_planes(drm, &hdlcd->crtc, primary, NULL,
 318                                        &hdlcd_crtc_funcs, NULL);
 319        if (ret) {
 320                hdlcd_plane_destroy(primary);
 321                devm_kfree(drm->dev, primary);
 322                return ret;
 323        }
 324
 325        drm_crtc_helper_add(&hdlcd->crtc, &hdlcd_crtc_helper_funcs);
 326        return 0;
 327}
 328