162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// Copyright 2018 IBM Corporation 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/clk.h> 562306a36Sopenharmony_ci#include <linux/reset.h> 662306a36Sopenharmony_ci#include <linux/regmap.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <drm/drm_device.h> 962306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h> 1062306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 1162306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 1262306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 1362306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 1462306a36Sopenharmony_ci#include <drm/drm_panel.h> 1562306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 1662306a36Sopenharmony_ci#include <drm/drm_vblank.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "aspeed_gfx.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic struct aspeed_gfx * 2162306a36Sopenharmony_cidrm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci return container_of(pipe, struct aspeed_gfx, pipe); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct drm_crtc *crtc = &priv->pipe.crtc; 2962306a36Sopenharmony_ci struct drm_device *drm = crtc->dev; 3062306a36Sopenharmony_ci const u32 format = crtc->primary->state->fb->format->format; 3162306a36Sopenharmony_ci u32 ctrl1; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci ctrl1 = readl(priv->base + CRT_CTRL1); 3462306a36Sopenharmony_ci ctrl1 &= ~CRT_CTRL_COLOR_MASK; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci switch (format) { 3762306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 3862306a36Sopenharmony_ci dev_dbg(drm->dev, "Setting up RGB565 mode\n"); 3962306a36Sopenharmony_ci ctrl1 |= CRT_CTRL_COLOR_RGB565; 4062306a36Sopenharmony_ci *bpp = 16; 4162306a36Sopenharmony_ci break; 4262306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 4362306a36Sopenharmony_ci dev_dbg(drm->dev, "Setting up XRGB8888 mode\n"); 4462306a36Sopenharmony_ci ctrl1 |= CRT_CTRL_COLOR_XRGB8888; 4562306a36Sopenharmony_ci *bpp = 32; 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci default: 4862306a36Sopenharmony_ci dev_err(drm->dev, "Unhandled pixel format %08x\n", format); 4962306a36Sopenharmony_ci return -EINVAL; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci writel(ctrl1, priv->base + CRT_CTRL1); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void aspeed_gfx_enable_controller(struct aspeed_gfx *priv) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci u32 ctrl1 = readl(priv->base + CRT_CTRL1); 6062306a36Sopenharmony_ci u32 ctrl2 = readl(priv->base + CRT_CTRL2); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* Set DAC source for display output to Graphics CRT (GFX) */ 6362306a36Sopenharmony_ci regmap_update_bits(priv->scu, priv->dac_reg, BIT(16), BIT(16)); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1); 6662306a36Sopenharmony_ci writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void aspeed_gfx_disable_controller(struct aspeed_gfx *priv) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci u32 ctrl1 = readl(priv->base + CRT_CTRL1); 7262306a36Sopenharmony_ci u32 ctrl2 = readl(priv->base + CRT_CTRL2); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1); 7562306a36Sopenharmony_ci writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci regmap_update_bits(priv->scu, priv->dac_reg, BIT(16), 0); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode; 8362306a36Sopenharmony_ci u32 ctrl1, d_offset, t_count, bpp; 8462306a36Sopenharmony_ci int err; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci err = aspeed_gfx_set_pixel_fmt(priv, &bpp); 8762306a36Sopenharmony_ci if (err) 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#if 0 9162306a36Sopenharmony_ci /* TODO: we have only been able to test with the 40MHz USB clock. The 9262306a36Sopenharmony_ci * clock is fixed, so we cannot adjust it here. */ 9362306a36Sopenharmony_ci clk_set_rate(priv->pixel_clk, m->crtc_clock * 1000); 9462306a36Sopenharmony_ci#endif 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ctrl1 = readl(priv->base + CRT_CTRL1); 9762306a36Sopenharmony_ci ctrl1 &= ~(CRT_CTRL_INTERLACED | 9862306a36Sopenharmony_ci CRT_CTRL_HSYNC_NEGATIVE | 9962306a36Sopenharmony_ci CRT_CTRL_VSYNC_NEGATIVE); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (m->flags & DRM_MODE_FLAG_INTERLACE) 10262306a36Sopenharmony_ci ctrl1 |= CRT_CTRL_INTERLACED; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!(m->flags & DRM_MODE_FLAG_PHSYNC)) 10562306a36Sopenharmony_ci ctrl1 |= CRT_CTRL_HSYNC_NEGATIVE; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!(m->flags & DRM_MODE_FLAG_PVSYNC)) 10862306a36Sopenharmony_ci ctrl1 |= CRT_CTRL_VSYNC_NEGATIVE; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci writel(ctrl1, priv->base + CRT_CTRL1); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Horizontal timing */ 11362306a36Sopenharmony_ci writel(CRT_H_TOTAL(m->htotal - 1) | CRT_H_DE(m->hdisplay - 1), 11462306a36Sopenharmony_ci priv->base + CRT_HORIZ0); 11562306a36Sopenharmony_ci writel(CRT_H_RS_START(m->hsync_start - 1) | CRT_H_RS_END(m->hsync_end), 11662306a36Sopenharmony_ci priv->base + CRT_HORIZ1); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Vertical timing */ 12062306a36Sopenharmony_ci writel(CRT_V_TOTAL(m->vtotal - 1) | CRT_V_DE(m->vdisplay - 1), 12162306a36Sopenharmony_ci priv->base + CRT_VERT0); 12262306a36Sopenharmony_ci writel(CRT_V_RS_START(m->vsync_start) | CRT_V_RS_END(m->vsync_end), 12362306a36Sopenharmony_ci priv->base + CRT_VERT1); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* 12662306a36Sopenharmony_ci * Display Offset: address difference between consecutive scan lines 12762306a36Sopenharmony_ci * Terminal Count: memory size of one scan line 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci d_offset = m->hdisplay * bpp / 8; 13062306a36Sopenharmony_ci t_count = DIV_ROUND_UP(m->hdisplay * bpp, priv->scan_line_max); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci writel(CRT_DISP_OFFSET(d_offset) | CRT_TERM_COUNT(t_count), 13362306a36Sopenharmony_ci priv->base + CRT_OFFSET); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* 13662306a36Sopenharmony_ci * Threshold: FIFO thresholds of refill and stop (16 byte chunks 13762306a36Sopenharmony_ci * per line, rounded up) 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci writel(priv->throd_val, priv->base + CRT_THROD); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe, 14362306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 14462306a36Sopenharmony_ci struct drm_plane_state *plane_state) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); 14762306a36Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci aspeed_gfx_crtc_mode_set_nofb(priv); 15062306a36Sopenharmony_ci aspeed_gfx_enable_controller(priv); 15162306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); 15762306a36Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 16062306a36Sopenharmony_ci aspeed_gfx_disable_controller(priv); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe, 16462306a36Sopenharmony_ci struct drm_plane_state *plane_state) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); 16762306a36Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 16862306a36Sopenharmony_ci struct drm_framebuffer *fb = pipe->plane.state->fb; 16962306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 17062306a36Sopenharmony_ci struct drm_gem_dma_object *gem; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 17362306a36Sopenharmony_ci event = crtc->state->event; 17462306a36Sopenharmony_ci if (event) { 17562306a36Sopenharmony_ci crtc->state->event = NULL; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (drm_crtc_vblank_get(crtc) == 0) 17862306a36Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, event); 17962306a36Sopenharmony_ci else 18062306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, event); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!fb) 18562306a36Sopenharmony_ci return; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci gem = drm_fb_dma_get_gem_obj(fb, 0); 18862306a36Sopenharmony_ci if (!gem) 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci writel(gem->dma_addr, priv->base + CRT_ADDR); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); 19662306a36Sopenharmony_ci u32 reg = readl(priv->base + CRT_CTRL1); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Clear pending VBLANK IRQ */ 19962306a36Sopenharmony_ci writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci reg |= CRT_CTRL_VERTICAL_INTR_EN; 20262306a36Sopenharmony_ci writel(reg, priv->base + CRT_CTRL1); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); 21062306a36Sopenharmony_ci u32 reg = readl(priv->base + CRT_CTRL1); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci reg &= ~CRT_CTRL_VERTICAL_INTR_EN; 21362306a36Sopenharmony_ci writel(reg, priv->base + CRT_CTRL1); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Clear pending VBLANK IRQ */ 21662306a36Sopenharmony_ci writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic const struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = { 22062306a36Sopenharmony_ci .enable = aspeed_gfx_pipe_enable, 22162306a36Sopenharmony_ci .disable = aspeed_gfx_pipe_disable, 22262306a36Sopenharmony_ci .update = aspeed_gfx_pipe_update, 22362306a36Sopenharmony_ci .enable_vblank = aspeed_gfx_enable_vblank, 22462306a36Sopenharmony_ci .disable_vblank = aspeed_gfx_disable_vblank, 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic const uint32_t aspeed_gfx_formats[] = { 22862306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 22962306a36Sopenharmony_ci DRM_FORMAT_RGB565, 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ciint aspeed_gfx_create_pipe(struct drm_device *drm) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct aspeed_gfx *priv = to_aspeed_gfx(drm); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs, 23762306a36Sopenharmony_ci aspeed_gfx_formats, 23862306a36Sopenharmony_ci ARRAY_SIZE(aspeed_gfx_formats), 23962306a36Sopenharmony_ci NULL, 24062306a36Sopenharmony_ci &priv->connector); 24162306a36Sopenharmony_ci} 242