]>
Commit | Line | Data |
---|---|---|
bbbe775e NA |
1 | /* |
2 | * Copyright (C) 2016 BayLibre, SAS | |
3 | * Author: Neil Armstrong <[email protected]> | |
4 | * Copyright (C) 2015 Amlogic, Inc. All rights reserved. | |
5 | * Copyright (C) 2014 Endless Mobile | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 of the | |
10 | * License, or (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
19 | * | |
20 | * Written by: | |
21 | * Jasper St. Pierre <[email protected]> | |
22 | */ | |
23 | ||
24 | #include <linux/kernel.h> | |
25 | #include <linux/module.h> | |
26 | #include <linux/mutex.h> | |
27 | #include <linux/platform_device.h> | |
28 | #include <drm/drmP.h> | |
29 | #include <drm/drm_atomic.h> | |
30 | #include <drm/drm_atomic_helper.h> | |
31 | #include <drm/drm_flip_work.h> | |
32 | #include <drm/drm_crtc_helper.h> | |
33 | ||
34 | #include "meson_crtc.h" | |
35 | #include "meson_plane.h" | |
65686873 | 36 | #include "meson_venc.h" |
bbbe775e NA |
37 | #include "meson_vpp.h" |
38 | #include "meson_viu.h" | |
e88230a3 | 39 | #include "meson_canvas.h" |
bbbe775e NA |
40 | #include "meson_registers.h" |
41 | ||
42 | /* CRTC definition */ | |
43 | ||
44 | struct meson_crtc { | |
45 | struct drm_crtc base; | |
46 | struct drm_pending_vblank_event *event; | |
47 | struct meson_drm *priv; | |
2bcd3eca | 48 | bool enabled; |
bbbe775e NA |
49 | }; |
50 | #define to_meson_crtc(x) container_of(x, struct meson_crtc, base) | |
51 | ||
52 | /* CRTC */ | |
53 | ||
65686873 SG |
54 | static int meson_crtc_enable_vblank(struct drm_crtc *crtc) |
55 | { | |
56 | struct meson_crtc *meson_crtc = to_meson_crtc(crtc); | |
57 | struct meson_drm *priv = meson_crtc->priv; | |
58 | ||
59 | meson_venc_enable_vsync(priv); | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | static void meson_crtc_disable_vblank(struct drm_crtc *crtc) | |
65 | { | |
66 | struct meson_crtc *meson_crtc = to_meson_crtc(crtc); | |
67 | struct meson_drm *priv = meson_crtc->priv; | |
68 | ||
69 | meson_venc_disable_vsync(priv); | |
70 | } | |
71 | ||
bbbe775e NA |
72 | static const struct drm_crtc_funcs meson_crtc_funcs = { |
73 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
74 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | |
75 | .destroy = drm_crtc_cleanup, | |
76 | .page_flip = drm_atomic_helper_page_flip, | |
77 | .reset = drm_atomic_helper_crtc_reset, | |
78 | .set_config = drm_atomic_helper_set_config, | |
65686873 SG |
79 | .enable_vblank = meson_crtc_enable_vblank, |
80 | .disable_vblank = meson_crtc_disable_vblank, | |
81 | ||
bbbe775e NA |
82 | }; |
83 | ||
2bcd3eca | 84 | static void meson_crtc_enable(struct drm_crtc *crtc) |
bbbe775e NA |
85 | { |
86 | struct meson_crtc *meson_crtc = to_meson_crtc(crtc); | |
cc0c43a0 | 87 | struct drm_crtc_state *crtc_state = crtc->state; |
bbbe775e NA |
88 | struct meson_drm *priv = meson_crtc->priv; |
89 | ||
cc0c43a0 NA |
90 | DRM_DEBUG_DRIVER("\n"); |
91 | ||
92 | if (!crtc_state) { | |
93 | DRM_ERROR("Invalid crtc_state\n"); | |
94 | return; | |
95 | } | |
96 | ||
bbbe775e | 97 | /* Enable VPP Postblend */ |
cc0c43a0 | 98 | writel(crtc_state->mode.hdisplay, |
bbbe775e NA |
99 | priv->io_base + _REG(VPP_POSTBLEND_H_SIZE)); |
100 | ||
101 | writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE, | |
102 | priv->io_base + _REG(VPP_MISC)); | |
103 | ||
2bcd3eca NA |
104 | drm_crtc_vblank_on(crtc); |
105 | ||
106 | meson_crtc->enabled = true; | |
107 | } | |
108 | ||
109 | static void meson_crtc_atomic_enable(struct drm_crtc *crtc, | |
110 | struct drm_crtc_state *old_state) | |
111 | { | |
112 | struct meson_crtc *meson_crtc = to_meson_crtc(crtc); | |
113 | struct meson_drm *priv = meson_crtc->priv; | |
114 | ||
115 | DRM_DEBUG_DRIVER("\n"); | |
116 | ||
117 | if (!meson_crtc->enabled) | |
118 | meson_crtc_enable(crtc); | |
119 | ||
bbbe775e NA |
120 | priv->viu.osd1_enabled = true; |
121 | } | |
122 | ||
64581714 LP |
123 | static void meson_crtc_atomic_disable(struct drm_crtc *crtc, |
124 | struct drm_crtc_state *old_state) | |
bbbe775e NA |
125 | { |
126 | struct meson_crtc *meson_crtc = to_meson_crtc(crtc); | |
127 | struct meson_drm *priv = meson_crtc->priv; | |
128 | ||
2bcd3eca NA |
129 | drm_crtc_vblank_off(crtc); |
130 | ||
bbbe775e | 131 | priv->viu.osd1_enabled = false; |
cc0c43a0 | 132 | priv->viu.osd1_commit = false; |
bbbe775e NA |
133 | |
134 | /* Disable VPP Postblend */ | |
135 | writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0, | |
136 | priv->io_base + _REG(VPP_MISC)); | |
137 | ||
138 | if (crtc->state->event && !crtc->state->active) { | |
139 | spin_lock_irq(&crtc->dev->event_lock); | |
140 | drm_crtc_send_vblank_event(crtc, crtc->state->event); | |
141 | spin_unlock_irq(&crtc->dev->event_lock); | |
142 | ||
143 | crtc->state->event = NULL; | |
144 | } | |
2bcd3eca NA |
145 | |
146 | meson_crtc->enabled = false; | |
bbbe775e NA |
147 | } |
148 | ||
149 | static void meson_crtc_atomic_begin(struct drm_crtc *crtc, | |
150 | struct drm_crtc_state *state) | |
151 | { | |
152 | struct meson_crtc *meson_crtc = to_meson_crtc(crtc); | |
153 | unsigned long flags; | |
154 | ||
2bcd3eca NA |
155 | if (crtc->state->enable && !meson_crtc->enabled) |
156 | meson_crtc_enable(crtc); | |
157 | ||
bbbe775e NA |
158 | if (crtc->state->event) { |
159 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | |
160 | ||
161 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | |
162 | meson_crtc->event = crtc->state->event; | |
163 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | |
164 | crtc->state->event = NULL; | |
165 | } | |
166 | } | |
167 | ||
168 | static void meson_crtc_atomic_flush(struct drm_crtc *crtc, | |
169 | struct drm_crtc_state *old_crtc_state) | |
170 | { | |
171 | struct meson_crtc *meson_crtc = to_meson_crtc(crtc); | |
172 | struct meson_drm *priv = meson_crtc->priv; | |
173 | ||
cc0c43a0 | 174 | priv->viu.osd1_commit = true; |
bbbe775e NA |
175 | } |
176 | ||
177 | static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = { | |
bbbe775e NA |
178 | .atomic_begin = meson_crtc_atomic_begin, |
179 | .atomic_flush = meson_crtc_atomic_flush, | |
0b20a0f8 | 180 | .atomic_enable = meson_crtc_atomic_enable, |
64581714 | 181 | .atomic_disable = meson_crtc_atomic_disable, |
bbbe775e NA |
182 | }; |
183 | ||
184 | void meson_crtc_irq(struct meson_drm *priv) | |
185 | { | |
186 | struct meson_crtc *meson_crtc = to_meson_crtc(priv->crtc); | |
187 | unsigned long flags; | |
188 | ||
189 | /* Update the OSD registers */ | |
190 | if (priv->viu.osd1_enabled && priv->viu.osd1_commit) { | |
191 | writel_relaxed(priv->viu.osd1_ctrl_stat, | |
192 | priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); | |
193 | writel_relaxed(priv->viu.osd1_blk0_cfg[0], | |
194 | priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0)); | |
195 | writel_relaxed(priv->viu.osd1_blk0_cfg[1], | |
196 | priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W1)); | |
197 | writel_relaxed(priv->viu.osd1_blk0_cfg[2], | |
198 | priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W2)); | |
199 | writel_relaxed(priv->viu.osd1_blk0_cfg[3], | |
200 | priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3)); | |
201 | writel_relaxed(priv->viu.osd1_blk0_cfg[4], | |
202 | priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4)); | |
203 | ||
204 | /* If output is interlace, make use of the Scaler */ | |
205 | if (priv->viu.osd1_interlace) { | |
206 | struct drm_plane *plane = priv->primary_plane; | |
207 | struct drm_plane_state *state = plane->state; | |
208 | struct drm_rect dest = { | |
209 | .x1 = state->crtc_x, | |
210 | .y1 = state->crtc_y, | |
211 | .x2 = state->crtc_x + state->crtc_w, | |
212 | .y2 = state->crtc_y + state->crtc_h, | |
213 | }; | |
214 | ||
215 | meson_vpp_setup_interlace_vscaler_osd1(priv, &dest); | |
216 | } else | |
217 | meson_vpp_disable_interlace_vscaler_osd1(priv); | |
218 | ||
e88230a3 NA |
219 | meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1, |
220 | priv->viu.osd1_addr, priv->viu.osd1_stride, | |
221 | priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE, | |
222 | MESON_CANVAS_BLKMODE_LINEAR); | |
223 | ||
bbbe775e NA |
224 | /* Enable OSD1 */ |
225 | writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND, | |
226 | priv->io_base + _REG(VPP_MISC)); | |
227 | ||
228 | priv->viu.osd1_commit = false; | |
229 | } | |
230 | ||
231 | drm_crtc_handle_vblank(priv->crtc); | |
232 | ||
233 | spin_lock_irqsave(&priv->drm->event_lock, flags); | |
234 | if (meson_crtc->event) { | |
235 | drm_crtc_send_vblank_event(priv->crtc, meson_crtc->event); | |
236 | drm_crtc_vblank_put(priv->crtc); | |
237 | meson_crtc->event = NULL; | |
238 | } | |
239 | spin_unlock_irqrestore(&priv->drm->event_lock, flags); | |
240 | } | |
241 | ||
242 | int meson_crtc_create(struct meson_drm *priv) | |
243 | { | |
244 | struct meson_crtc *meson_crtc; | |
245 | struct drm_crtc *crtc; | |
246 | int ret; | |
247 | ||
248 | meson_crtc = devm_kzalloc(priv->drm->dev, sizeof(*meson_crtc), | |
249 | GFP_KERNEL); | |
250 | if (!meson_crtc) | |
251 | return -ENOMEM; | |
252 | ||
253 | meson_crtc->priv = priv; | |
254 | crtc = &meson_crtc->base; | |
255 | ret = drm_crtc_init_with_planes(priv->drm, crtc, | |
256 | priv->primary_plane, NULL, | |
257 | &meson_crtc_funcs, "meson_crtc"); | |
258 | if (ret) { | |
259 | dev_err(priv->drm->dev, "Failed to init CRTC\n"); | |
260 | return ret; | |
261 | } | |
262 | ||
263 | drm_crtc_helper_add(crtc, &meson_crtc_helper_funcs); | |
264 | ||
265 | priv->crtc = crtc; | |
266 | ||
267 | return 0; | |
268 | } |