Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 // Copyright 2018 IBM Corporation
0003 
0004 #include <linux/clk.h>
0005 #include <linux/reset.h>
0006 #include <linux/regmap.h>
0007 
0008 #include <drm/drm_crtc_helper.h>
0009 #include <drm/drm_device.h>
0010 #include <drm/drm_fb_cma_helper.h>
0011 #include <drm/drm_fourcc.h>
0012 #include <drm/drm_framebuffer.h>
0013 #include <drm/drm_gem_atomic_helper.h>
0014 #include <drm/drm_gem_cma_helper.h>
0015 #include <drm/drm_panel.h>
0016 #include <drm/drm_simple_kms_helper.h>
0017 #include <drm/drm_vblank.h>
0018 
0019 #include "aspeed_gfx.h"
0020 
0021 static struct aspeed_gfx *
0022 drm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe)
0023 {
0024     return container_of(pipe, struct aspeed_gfx, pipe);
0025 }
0026 
0027 static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp)
0028 {
0029     struct drm_crtc *crtc = &priv->pipe.crtc;
0030     struct drm_device *drm = crtc->dev;
0031     const u32 format = crtc->primary->state->fb->format->format;
0032     u32 ctrl1;
0033 
0034     ctrl1 = readl(priv->base + CRT_CTRL1);
0035     ctrl1 &= ~CRT_CTRL_COLOR_MASK;
0036 
0037     switch (format) {
0038     case DRM_FORMAT_RGB565:
0039         dev_dbg(drm->dev, "Setting up RGB565 mode\n");
0040         ctrl1 |= CRT_CTRL_COLOR_RGB565;
0041         *bpp = 16;
0042         break;
0043     case DRM_FORMAT_XRGB8888:
0044         dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
0045         ctrl1 |= CRT_CTRL_COLOR_XRGB8888;
0046         *bpp = 32;
0047         break;
0048     default:
0049         dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
0050         return -EINVAL;
0051     }
0052 
0053     writel(ctrl1, priv->base + CRT_CTRL1);
0054 
0055     return 0;
0056 }
0057 
0058 static void aspeed_gfx_enable_controller(struct aspeed_gfx *priv)
0059 {
0060     u32 ctrl1 = readl(priv->base + CRT_CTRL1);
0061     u32 ctrl2 = readl(priv->base + CRT_CTRL2);
0062 
0063     /* Set DAC source for display output to Graphics CRT (GFX) */
0064     regmap_update_bits(priv->scu, priv->dac_reg, BIT(16), BIT(16));
0065 
0066     writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1);
0067     writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
0068 }
0069 
0070 static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv)
0071 {
0072     u32 ctrl1 = readl(priv->base + CRT_CTRL1);
0073     u32 ctrl2 = readl(priv->base + CRT_CTRL2);
0074 
0075     writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1);
0076     writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
0077 
0078     regmap_update_bits(priv->scu, priv->dac_reg, BIT(16), 0);
0079 }
0080 
0081 static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
0082 {
0083     struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode;
0084     u32 ctrl1, d_offset, t_count, bpp;
0085     int err;
0086 
0087     err = aspeed_gfx_set_pixel_fmt(priv, &bpp);
0088     if (err)
0089         return;
0090 
0091 #if 0
0092     /* TODO: we have only been able to test with the 40MHz USB clock. The
0093      * clock is fixed, so we cannot adjust it here. */
0094     clk_set_rate(priv->pixel_clk, m->crtc_clock * 1000);
0095 #endif
0096 
0097     ctrl1 = readl(priv->base + CRT_CTRL1);
0098     ctrl1 &= ~(CRT_CTRL_INTERLACED |
0099             CRT_CTRL_HSYNC_NEGATIVE |
0100             CRT_CTRL_VSYNC_NEGATIVE);
0101 
0102     if (m->flags & DRM_MODE_FLAG_INTERLACE)
0103         ctrl1 |= CRT_CTRL_INTERLACED;
0104 
0105     if (!(m->flags & DRM_MODE_FLAG_PHSYNC))
0106         ctrl1 |= CRT_CTRL_HSYNC_NEGATIVE;
0107 
0108     if (!(m->flags & DRM_MODE_FLAG_PVSYNC))
0109         ctrl1 |= CRT_CTRL_VSYNC_NEGATIVE;
0110 
0111     writel(ctrl1, priv->base + CRT_CTRL1);
0112 
0113     /* Horizontal timing */
0114     writel(CRT_H_TOTAL(m->htotal - 1) | CRT_H_DE(m->hdisplay - 1),
0115             priv->base + CRT_HORIZ0);
0116     writel(CRT_H_RS_START(m->hsync_start - 1) | CRT_H_RS_END(m->hsync_end),
0117             priv->base + CRT_HORIZ1);
0118 
0119 
0120     /* Vertical timing */
0121     writel(CRT_V_TOTAL(m->vtotal - 1) | CRT_V_DE(m->vdisplay - 1),
0122             priv->base + CRT_VERT0);
0123     writel(CRT_V_RS_START(m->vsync_start) | CRT_V_RS_END(m->vsync_end),
0124             priv->base + CRT_VERT1);
0125 
0126     /*
0127      * Display Offset: address difference between consecutive scan lines
0128      * Terminal Count: memory size of one scan line
0129      */
0130     d_offset = m->hdisplay * bpp / 8;
0131     t_count = DIV_ROUND_UP(m->hdisplay * bpp, priv->scan_line_max);
0132 
0133     writel(CRT_DISP_OFFSET(d_offset) | CRT_TERM_COUNT(t_count),
0134             priv->base + CRT_OFFSET);
0135 
0136     /*
0137      * Threshold: FIFO thresholds of refill and stop (16 byte chunks
0138      * per line, rounded up)
0139      */
0140     writel(priv->throd_val, priv->base + CRT_THROD);
0141 }
0142 
0143 static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe,
0144                   struct drm_crtc_state *crtc_state,
0145                   struct drm_plane_state *plane_state)
0146 {
0147     struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
0148     struct drm_crtc *crtc = &pipe->crtc;
0149 
0150     aspeed_gfx_crtc_mode_set_nofb(priv);
0151     aspeed_gfx_enable_controller(priv);
0152     drm_crtc_vblank_on(crtc);
0153 }
0154 
0155 static void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe)
0156 {
0157     struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
0158     struct drm_crtc *crtc = &pipe->crtc;
0159 
0160     drm_crtc_vblank_off(crtc);
0161     aspeed_gfx_disable_controller(priv);
0162 }
0163 
0164 static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
0165                    struct drm_plane_state *plane_state)
0166 {
0167     struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
0168     struct drm_crtc *crtc = &pipe->crtc;
0169     struct drm_framebuffer *fb = pipe->plane.state->fb;
0170     struct drm_pending_vblank_event *event;
0171     struct drm_gem_cma_object *gem;
0172 
0173     spin_lock_irq(&crtc->dev->event_lock);
0174     event = crtc->state->event;
0175     if (event) {
0176         crtc->state->event = NULL;
0177 
0178         if (drm_crtc_vblank_get(crtc) == 0)
0179             drm_crtc_arm_vblank_event(crtc, event);
0180         else
0181             drm_crtc_send_vblank_event(crtc, event);
0182     }
0183     spin_unlock_irq(&crtc->dev->event_lock);
0184 
0185     if (!fb)
0186         return;
0187 
0188     gem = drm_fb_cma_get_gem_obj(fb, 0);
0189     if (!gem)
0190         return;
0191     writel(gem->paddr, priv->base + CRT_ADDR);
0192 }
0193 
0194 static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe)
0195 {
0196     struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
0197     u32 reg = readl(priv->base + CRT_CTRL1);
0198 
0199     /* Clear pending VBLANK IRQ */
0200     writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
0201 
0202     reg |= CRT_CTRL_VERTICAL_INTR_EN;
0203     writel(reg, priv->base + CRT_CTRL1);
0204 
0205     return 0;
0206 }
0207 
0208 static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe)
0209 {
0210     struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
0211     u32 reg = readl(priv->base + CRT_CTRL1);
0212 
0213     reg &= ~CRT_CTRL_VERTICAL_INTR_EN;
0214     writel(reg, priv->base + CRT_CTRL1);
0215 
0216     /* Clear pending VBLANK IRQ */
0217     writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
0218 }
0219 
0220 static const struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = {
0221     .enable     = aspeed_gfx_pipe_enable,
0222     .disable    = aspeed_gfx_pipe_disable,
0223     .update     = aspeed_gfx_pipe_update,
0224     .enable_vblank  = aspeed_gfx_enable_vblank,
0225     .disable_vblank = aspeed_gfx_disable_vblank,
0226 };
0227 
0228 static const uint32_t aspeed_gfx_formats[] = {
0229     DRM_FORMAT_XRGB8888,
0230     DRM_FORMAT_RGB565,
0231 };
0232 
0233 int aspeed_gfx_create_pipe(struct drm_device *drm)
0234 {
0235     struct aspeed_gfx *priv = to_aspeed_gfx(drm);
0236 
0237     return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs,
0238                         aspeed_gfx_formats,
0239                         ARRAY_SIZE(aspeed_gfx_formats),
0240                         NULL,
0241                         &priv->connector);
0242 }