1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // Copyright 2018 IBM Corporation |
3 | |
4 | #include <linux/clk.h> |
5 | #include <linux/reset.h> |
6 | #include <linux/regmap.h> |
7 | |
8 | #include <drm/drm_device.h> |
9 | #include <drm/drm_fb_dma_helper.h> |
10 | #include <drm/drm_fourcc.h> |
11 | #include <drm/drm_framebuffer.h> |
12 | #include <drm/drm_gem_atomic_helper.h> |
13 | #include <drm/drm_gem_dma_helper.h> |
14 | #include <drm/drm_panel.h> |
15 | #include <drm/drm_simple_kms_helper.h> |
16 | #include <drm/drm_vblank.h> |
17 | |
18 | #include "aspeed_gfx.h" |
19 | |
20 | static struct aspeed_gfx * |
21 | drm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe) |
22 | { |
23 | return container_of(pipe, struct aspeed_gfx, pipe); |
24 | } |
25 | |
26 | static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp) |
27 | { |
28 | struct drm_crtc *crtc = &priv->pipe.crtc; |
29 | struct drm_device *drm = crtc->dev; |
30 | const u32 format = crtc->primary->state->fb->format->format; |
31 | u32 ctrl1; |
32 | |
33 | ctrl1 = readl(addr: priv->base + CRT_CTRL1); |
34 | ctrl1 &= ~CRT_CTRL_COLOR_MASK; |
35 | |
36 | switch (format) { |
37 | case DRM_FORMAT_RGB565: |
38 | dev_dbg(drm->dev, "Setting up RGB565 mode\n" ); |
39 | ctrl1 |= CRT_CTRL_COLOR_RGB565; |
40 | *bpp = 16; |
41 | break; |
42 | case DRM_FORMAT_XRGB8888: |
43 | dev_dbg(drm->dev, "Setting up XRGB8888 mode\n" ); |
44 | ctrl1 |= CRT_CTRL_COLOR_XRGB8888; |
45 | *bpp = 32; |
46 | break; |
47 | default: |
48 | dev_err(drm->dev, "Unhandled pixel format %08x\n" , format); |
49 | return -EINVAL; |
50 | } |
51 | |
52 | writel(val: ctrl1, addr: priv->base + CRT_CTRL1); |
53 | |
54 | return 0; |
55 | } |
56 | |
57 | static void aspeed_gfx_enable_controller(struct aspeed_gfx *priv) |
58 | { |
59 | u32 ctrl1 = readl(addr: priv->base + CRT_CTRL1); |
60 | u32 ctrl2 = readl(addr: priv->base + CRT_CTRL2); |
61 | |
62 | /* Set DAC source for display output to Graphics CRT (GFX) */ |
63 | regmap_update_bits(map: priv->scu, reg: priv->dac_reg, BIT(16), BIT(16)); |
64 | |
65 | writel(val: ctrl1 | CRT_CTRL_EN, addr: priv->base + CRT_CTRL1); |
66 | writel(val: ctrl2 | CRT_CTRL_DAC_EN, addr: priv->base + CRT_CTRL2); |
67 | } |
68 | |
69 | static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv) |
70 | { |
71 | u32 ctrl1 = readl(addr: priv->base + CRT_CTRL1); |
72 | u32 ctrl2 = readl(addr: priv->base + CRT_CTRL2); |
73 | |
74 | writel(val: ctrl1 & ~CRT_CTRL_EN, addr: priv->base + CRT_CTRL1); |
75 | writel(val: ctrl2 & ~CRT_CTRL_DAC_EN, addr: priv->base + CRT_CTRL2); |
76 | |
77 | regmap_update_bits(map: priv->scu, reg: priv->dac_reg, BIT(16), val: 0); |
78 | } |
79 | |
80 | static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv) |
81 | { |
82 | struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode; |
83 | u32 ctrl1, d_offset, t_count, bpp; |
84 | int err; |
85 | |
86 | err = aspeed_gfx_set_pixel_fmt(priv, bpp: &bpp); |
87 | if (err) |
88 | return; |
89 | |
90 | #if 0 |
91 | /* TODO: we have only been able to test with the 40MHz USB clock. The |
92 | * clock is fixed, so we cannot adjust it here. */ |
93 | clk_set_rate(priv->pixel_clk, m->crtc_clock * 1000); |
94 | #endif |
95 | |
96 | ctrl1 = readl(addr: priv->base + CRT_CTRL1); |
97 | ctrl1 &= ~(CRT_CTRL_INTERLACED | |
98 | CRT_CTRL_HSYNC_NEGATIVE | |
99 | CRT_CTRL_VSYNC_NEGATIVE); |
100 | |
101 | if (m->flags & DRM_MODE_FLAG_INTERLACE) |
102 | ctrl1 |= CRT_CTRL_INTERLACED; |
103 | |
104 | if (!(m->flags & DRM_MODE_FLAG_PHSYNC)) |
105 | ctrl1 |= CRT_CTRL_HSYNC_NEGATIVE; |
106 | |
107 | if (!(m->flags & DRM_MODE_FLAG_PVSYNC)) |
108 | ctrl1 |= CRT_CTRL_VSYNC_NEGATIVE; |
109 | |
110 | writel(val: ctrl1, addr: priv->base + CRT_CTRL1); |
111 | |
112 | /* Horizontal timing */ |
113 | writel(CRT_H_TOTAL(m->htotal - 1) | CRT_H_DE(m->hdisplay - 1), |
114 | addr: priv->base + CRT_HORIZ0); |
115 | writel(CRT_H_RS_START(m->hsync_start - 1) | CRT_H_RS_END(m->hsync_end), |
116 | addr: priv->base + CRT_HORIZ1); |
117 | |
118 | |
119 | /* Vertical timing */ |
120 | writel(CRT_V_TOTAL(m->vtotal - 1) | CRT_V_DE(m->vdisplay - 1), |
121 | addr: priv->base + CRT_VERT0); |
122 | writel(CRT_V_RS_START(m->vsync_start) | CRT_V_RS_END(m->vsync_end), |
123 | addr: priv->base + CRT_VERT1); |
124 | |
125 | /* |
126 | * Display Offset: address difference between consecutive scan lines |
127 | * Terminal Count: memory size of one scan line |
128 | */ |
129 | d_offset = m->hdisplay * bpp / 8; |
130 | t_count = DIV_ROUND_UP(m->hdisplay * bpp, priv->scan_line_max); |
131 | |
132 | writel(CRT_DISP_OFFSET(d_offset) | CRT_TERM_COUNT(t_count), |
133 | addr: priv->base + CRT_OFFSET); |
134 | |
135 | /* |
136 | * Threshold: FIFO thresholds of refill and stop (16 byte chunks |
137 | * per line, rounded up) |
138 | */ |
139 | writel(val: priv->throd_val, addr: priv->base + CRT_THROD); |
140 | } |
141 | |
142 | static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe, |
143 | struct drm_crtc_state *crtc_state, |
144 | struct drm_plane_state *plane_state) |
145 | { |
146 | struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); |
147 | struct drm_crtc *crtc = &pipe->crtc; |
148 | |
149 | aspeed_gfx_crtc_mode_set_nofb(priv); |
150 | aspeed_gfx_enable_controller(priv); |
151 | drm_crtc_vblank_on(crtc); |
152 | } |
153 | |
154 | static void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe) |
155 | { |
156 | struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); |
157 | struct drm_crtc *crtc = &pipe->crtc; |
158 | |
159 | drm_crtc_vblank_off(crtc); |
160 | aspeed_gfx_disable_controller(priv); |
161 | } |
162 | |
163 | static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe, |
164 | struct drm_plane_state *plane_state) |
165 | { |
166 | struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); |
167 | struct drm_crtc *crtc = &pipe->crtc; |
168 | struct drm_framebuffer *fb = pipe->plane.state->fb; |
169 | struct drm_pending_vblank_event *event; |
170 | struct drm_gem_dma_object *gem; |
171 | |
172 | spin_lock_irq(lock: &crtc->dev->event_lock); |
173 | event = crtc->state->event; |
174 | if (event) { |
175 | crtc->state->event = NULL; |
176 | |
177 | if (drm_crtc_vblank_get(crtc) == 0) |
178 | drm_crtc_arm_vblank_event(crtc, e: event); |
179 | else |
180 | drm_crtc_send_vblank_event(crtc, e: event); |
181 | } |
182 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
183 | |
184 | if (!fb) |
185 | return; |
186 | |
187 | gem = drm_fb_dma_get_gem_obj(fb, plane: 0); |
188 | if (!gem) |
189 | return; |
190 | writel(val: gem->dma_addr, addr: priv->base + CRT_ADDR); |
191 | } |
192 | |
193 | static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe) |
194 | { |
195 | struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); |
196 | u32 reg = readl(addr: priv->base + CRT_CTRL1); |
197 | |
198 | /* Clear pending VBLANK IRQ */ |
199 | writel(val: reg | CRT_CTRL_VERTICAL_INTR_STS, addr: priv->base + CRT_CTRL1); |
200 | |
201 | reg |= CRT_CTRL_VERTICAL_INTR_EN; |
202 | writel(val: reg, addr: priv->base + CRT_CTRL1); |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe) |
208 | { |
209 | struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); |
210 | u32 reg = readl(addr: priv->base + CRT_CTRL1); |
211 | |
212 | reg &= ~CRT_CTRL_VERTICAL_INTR_EN; |
213 | writel(val: reg, addr: priv->base + CRT_CTRL1); |
214 | |
215 | /* Clear pending VBLANK IRQ */ |
216 | writel(val: reg | CRT_CTRL_VERTICAL_INTR_STS, addr: priv->base + CRT_CTRL1); |
217 | } |
218 | |
219 | static const struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = { |
220 | .enable = aspeed_gfx_pipe_enable, |
221 | .disable = aspeed_gfx_pipe_disable, |
222 | .update = aspeed_gfx_pipe_update, |
223 | .enable_vblank = aspeed_gfx_enable_vblank, |
224 | .disable_vblank = aspeed_gfx_disable_vblank, |
225 | }; |
226 | |
227 | static const uint32_t aspeed_gfx_formats[] = { |
228 | DRM_FORMAT_XRGB8888, |
229 | DRM_FORMAT_RGB565, |
230 | }; |
231 | |
232 | int aspeed_gfx_create_pipe(struct drm_device *drm) |
233 | { |
234 | struct aspeed_gfx *priv = to_aspeed_gfx(drm); |
235 | |
236 | return drm_simple_display_pipe_init(dev: drm, pipe: &priv->pipe, funcs: &aspeed_gfx_funcs, |
237 | formats: aspeed_gfx_formats, |
238 | ARRAY_SIZE(aspeed_gfx_formats), |
239 | NULL, |
240 | connector: &priv->connector); |
241 | } |
242 | |