Commit | Line | Data |
---|---|---|
cd5351f4 RC |
1 | /* |
2 | * drivers/staging/omapdrm/omap_crtc.c | |
3 | * | |
4 | * Copyright (C) 2011 Texas Instruments | |
5 | * Author: Rob Clark <rob@ti.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 as published by | |
9 | * the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "omap_drv.h" | |
21 | ||
22 | #include "drm_mode.h" | |
23 | #include "drm_crtc.h" | |
24 | #include "drm_crtc_helper.h" | |
25 | ||
26 | #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) | |
27 | ||
28 | struct omap_crtc { | |
29 | struct drm_crtc base; | |
30 | struct omap_overlay *ovl; | |
31 | struct omap_overlay_info info; | |
32 | int id; | |
33 | ||
34 | /* if there is a pending flip, this will be non-null: */ | |
35 | struct drm_pending_vblank_event *event; | |
36 | }; | |
37 | ||
38 | /* push changes down to dss2 */ | |
39 | static int commit(struct drm_crtc *crtc) | |
40 | { | |
41 | struct drm_device *dev = crtc->dev; | |
42 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
43 | struct omap_overlay *ovl = omap_crtc->ovl; | |
44 | struct omap_overlay_info *info = &omap_crtc->info; | |
45 | int ret; | |
46 | ||
47 | DBG("%s", omap_crtc->ovl->name); | |
48 | DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, | |
49 | info->out_height, info->screen_width); | |
50 | DBG("%d,%d %08x", info->pos_x, info->pos_y, info->paddr); | |
51 | ||
52 | /* NOTE: do we want to do this at all here, or just wait | |
53 | * for dpms(ON) since other CRTC's may not have their mode | |
54 | * set yet, so fb dimensions may still change.. | |
55 | */ | |
56 | ret = ovl->set_overlay_info(ovl, info); | |
57 | if (ret) { | |
58 | dev_err(dev->dev, "could not set overlay info\n"); | |
59 | return ret; | |
60 | } | |
61 | ||
62 | /* our encoder doesn't necessarily get a commit() after this, in | |
63 | * particular in the dpms() and mode_set_base() cases, so force the | |
64 | * manager to update: | |
65 | * | |
66 | * could this be in the encoder somehow? | |
67 | */ | |
68 | if (ovl->manager) { | |
69 | ret = ovl->manager->apply(ovl->manager); | |
70 | if (ret) { | |
71 | dev_err(dev->dev, "could not apply settings\n"); | |
72 | return ret; | |
73 | } | |
74 | } | |
75 | ||
76 | if (info->enabled) { | |
77 | omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y, | |
78 | crtc->fb->width, crtc->fb->height); | |
79 | } | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | /* update parameters that are dependent on the framebuffer dimensions and | |
85 | * position within the fb that this crtc scans out from. This is called | |
86 | * when framebuffer dimensions or x,y base may have changed, either due | |
87 | * to our mode, or a change in another crtc that is scanning out of the | |
88 | * same fb. | |
89 | */ | |
90 | static void update_scanout(struct drm_crtc *crtc) | |
91 | { | |
92 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
93 | dma_addr_t paddr; | |
94 | unsigned int screen_width; | |
95 | ||
96 | omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y, | |
97 | NULL, &paddr, &screen_width); | |
98 | ||
99 | DBG("%s: %d,%d: %08x (%d)", omap_crtc->ovl->name, | |
100 | crtc->x, crtc->y, (u32)paddr, screen_width); | |
101 | ||
102 | omap_crtc->info.paddr = paddr; | |
103 | omap_crtc->info.screen_width = screen_width; | |
104 | } | |
105 | ||
106 | static void omap_crtc_gamma_set(struct drm_crtc *crtc, | |
107 | u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size) | |
108 | { | |
109 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
110 | DBG("%s", omap_crtc->ovl->name); | |
111 | } | |
112 | ||
113 | static void omap_crtc_destroy(struct drm_crtc *crtc) | |
114 | { | |
115 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
116 | DBG("%s", omap_crtc->ovl->name); | |
117 | drm_crtc_cleanup(crtc); | |
118 | kfree(omap_crtc); | |
119 | } | |
120 | ||
121 | static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) | |
122 | { | |
123 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
124 | ||
125 | DBG("%s: %d", omap_crtc->ovl->name, mode); | |
126 | ||
127 | if (mode == DRM_MODE_DPMS_ON) { | |
128 | update_scanout(crtc); | |
129 | omap_crtc->info.enabled = true; | |
130 | } else { | |
131 | omap_crtc->info.enabled = false; | |
132 | } | |
133 | ||
134 | WARN_ON(commit(crtc)); | |
135 | } | |
136 | ||
137 | static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, | |
138 | struct drm_display_mode *mode, | |
139 | struct drm_display_mode *adjusted_mode) | |
140 | { | |
141 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
142 | DBG("%s", omap_crtc->ovl->name); | |
143 | return true; | |
144 | } | |
145 | ||
146 | static int omap_crtc_mode_set(struct drm_crtc *crtc, | |
147 | struct drm_display_mode *mode, | |
148 | struct drm_display_mode *adjusted_mode, | |
149 | int x, int y, | |
150 | struct drm_framebuffer *old_fb) | |
151 | { | |
152 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
153 | ||
154 | DBG("%s: %d,%d: %dx%d", omap_crtc->ovl->name, x, y, | |
155 | mode->hdisplay, mode->vdisplay); | |
156 | ||
157 | /* just use adjusted mode */ | |
158 | mode = adjusted_mode; | |
159 | ||
160 | omap_crtc->info.width = mode->hdisplay; | |
161 | omap_crtc->info.height = mode->vdisplay; | |
162 | omap_crtc->info.out_width = mode->hdisplay; | |
163 | omap_crtc->info.out_height = mode->vdisplay; | |
164 | omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U; | |
165 | omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA; | |
166 | omap_crtc->info.rotation = OMAP_DSS_ROT_0; | |
167 | omap_crtc->info.global_alpha = 0xff; | |
168 | omap_crtc->info.mirror = 0; | |
169 | omap_crtc->info.mirror = 0; | |
170 | omap_crtc->info.pos_x = 0; | |
171 | omap_crtc->info.pos_y = 0; | |
172 | #if 0 /* re-enable when these are available in DSS2 driver */ | |
173 | omap_crtc->info.zorder = 3; /* GUI in the front, video behind */ | |
174 | omap_crtc->info.min_x_decim = 1; | |
175 | omap_crtc->info.max_x_decim = 1; | |
176 | omap_crtc->info.min_y_decim = 1; | |
177 | omap_crtc->info.max_y_decim = 1; | |
178 | #endif | |
179 | ||
180 | update_scanout(crtc); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static void omap_crtc_prepare(struct drm_crtc *crtc) | |
186 | { | |
187 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
188 | struct omap_overlay *ovl = omap_crtc->ovl; | |
189 | ||
190 | DBG("%s", omap_crtc->ovl->name); | |
191 | ||
192 | ovl->get_overlay_info(ovl, &omap_crtc->info); | |
193 | ||
194 | omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | |
195 | } | |
196 | ||
197 | static void omap_crtc_commit(struct drm_crtc *crtc) | |
198 | { | |
199 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
200 | DBG("%s", omap_crtc->ovl->name); | |
201 | omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | |
202 | } | |
203 | ||
204 | static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | |
205 | struct drm_framebuffer *old_fb) | |
206 | { | |
207 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
208 | ||
209 | DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb); | |
210 | ||
211 | update_scanout(crtc); | |
212 | ||
213 | return commit(crtc); | |
214 | } | |
215 | ||
216 | static void omap_crtc_load_lut(struct drm_crtc *crtc) | |
217 | { | |
218 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
219 | DBG("%s", omap_crtc->ovl->name); | |
220 | } | |
221 | ||
222 | static void page_flip_cb(void *arg) | |
223 | { | |
224 | struct drm_crtc *crtc = arg; | |
225 | struct drm_device *dev = crtc->dev; | |
226 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
227 | struct drm_pending_vblank_event *event = omap_crtc->event; | |
228 | struct timeval now; | |
229 | unsigned long flags; | |
230 | ||
231 | WARN_ON(!event); | |
232 | ||
233 | omap_crtc->event = NULL; | |
234 | ||
235 | update_scanout(crtc); | |
236 | WARN_ON(commit(crtc)); | |
237 | ||
238 | /* wakeup userspace */ | |
239 | /* TODO: this should happen *after* flip in vsync IRQ handler */ | |
240 | if (event) { | |
241 | spin_lock_irqsave(&dev->event_lock, flags); | |
242 | event->event.sequence = drm_vblank_count_and_time( | |
243 | dev, omap_crtc->id, &now); | |
244 | event->event.tv_sec = now.tv_sec; | |
245 | event->event.tv_usec = now.tv_usec; | |
246 | list_add_tail(&event->base.link, | |
247 | &event->base.file_priv->event_list); | |
248 | wake_up_interruptible(&event->base.file_priv->event_wait); | |
249 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
250 | } | |
251 | } | |
252 | ||
253 | static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, | |
254 | struct drm_framebuffer *fb, | |
255 | struct drm_pending_vblank_event *event) | |
256 | { | |
257 | struct drm_device *dev = crtc->dev; | |
258 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
259 | ||
260 | DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id); | |
261 | ||
262 | if (omap_crtc->event) { | |
263 | dev_err(dev->dev, "already a pending flip\n"); | |
264 | return -EINVAL; | |
265 | } | |
266 | ||
267 | crtc->fb = fb; | |
268 | omap_crtc->event = event; | |
269 | ||
270 | omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ, | |
271 | page_flip_cb, crtc); | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
276 | static const struct drm_crtc_funcs omap_crtc_funcs = { | |
277 | .gamma_set = omap_crtc_gamma_set, | |
278 | .set_config = drm_crtc_helper_set_config, | |
279 | .destroy = omap_crtc_destroy, | |
280 | .page_flip = omap_crtc_page_flip_locked, | |
281 | }; | |
282 | ||
283 | static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { | |
284 | .dpms = omap_crtc_dpms, | |
285 | .mode_fixup = omap_crtc_mode_fixup, | |
286 | .mode_set = omap_crtc_mode_set, | |
287 | .prepare = omap_crtc_prepare, | |
288 | .commit = omap_crtc_commit, | |
289 | .mode_set_base = omap_crtc_mode_set_base, | |
290 | .load_lut = omap_crtc_load_lut, | |
291 | }; | |
292 | ||
293 | struct omap_overlay *omap_crtc_get_overlay(struct drm_crtc *crtc) | |
294 | { | |
295 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
296 | return omap_crtc->ovl; | |
297 | } | |
298 | ||
299 | /* initialize crtc */ | |
300 | struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |
301 | struct omap_overlay *ovl, int id) | |
302 | { | |
303 | struct drm_crtc *crtc = NULL; | |
304 | struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); | |
305 | ||
306 | DBG("%s", ovl->name); | |
307 | ||
308 | if (!omap_crtc) { | |
309 | dev_err(dev->dev, "could not allocate CRTC\n"); | |
310 | goto fail; | |
311 | } | |
312 | ||
313 | omap_crtc->ovl = ovl; | |
314 | omap_crtc->id = id; | |
315 | crtc = &omap_crtc->base; | |
316 | drm_crtc_init(dev, crtc, &omap_crtc_funcs); | |
317 | drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); | |
318 | ||
319 | return crtc; | |
320 | ||
321 | fail: | |
322 | if (crtc) { | |
323 | drm_crtc_cleanup(crtc); | |
324 | kfree(omap_crtc); | |
325 | } | |
326 | return NULL; | |
327 | } |