2 * drivers/staging/omapdrm/omap_crtc.c
4 * Copyright (C) 2011 Texas Instruments
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.
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
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/>.
24 #include "drm_crtc_helper.h"
26 #define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
30 struct omap_overlay *ovl;
31 struct omap_overlay_info info;
34 /* if there is a pending flip, this will be non-null: */
35 struct drm_pending_vblank_event *event;
38 /* push changes down to dss2 */
39 static int commit(struct drm_crtc *crtc)
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;
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);
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..
56 ret = ovl->set_overlay_info(ovl, info);
58 dev_err(dev->dev, "could not set overlay info\n");
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
66 * could this be in the encoder somehow?
69 ret = ovl->manager->apply(ovl->manager);
71 dev_err(dev->dev, "could not apply settings\n");
77 omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y,
78 crtc->fb->width, crtc->fb->height);
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
90 static void update_scanout(struct drm_crtc *crtc)
92 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
94 unsigned int screen_width;
96 omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y,
97 NULL, &paddr, &screen_width);
99 DBG("%s: %d,%d: %08x (%d)", omap_crtc->ovl->name,
100 crtc->x, crtc->y, (u32)paddr, screen_width);
102 omap_crtc->info.paddr = paddr;
103 omap_crtc->info.screen_width = screen_width;
106 static void omap_crtc_gamma_set(struct drm_crtc *crtc,
107 u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size)
109 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
110 DBG("%s", omap_crtc->ovl->name);
113 static void omap_crtc_destroy(struct drm_crtc *crtc)
115 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
116 DBG("%s", omap_crtc->ovl->name);
117 drm_crtc_cleanup(crtc);
121 static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
123 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
125 DBG("%s: %d", omap_crtc->ovl->name, mode);
127 if (mode == DRM_MODE_DPMS_ON) {
128 update_scanout(crtc);
129 omap_crtc->info.enabled = true;
131 omap_crtc->info.enabled = false;
134 WARN_ON(commit(crtc));
137 static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
138 struct drm_display_mode *mode,
139 struct drm_display_mode *adjusted_mode)
141 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
142 DBG("%s", omap_crtc->ovl->name);
146 static int omap_crtc_mode_set(struct drm_crtc *crtc,
147 struct drm_display_mode *mode,
148 struct drm_display_mode *adjusted_mode,
150 struct drm_framebuffer *old_fb)
152 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
154 DBG("%s: %d,%d: %dx%d", omap_crtc->ovl->name, x, y,
155 mode->hdisplay, mode->vdisplay);
157 /* just use adjusted mode */
158 mode = adjusted_mode;
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;
180 update_scanout(crtc);
185 static void omap_crtc_prepare(struct drm_crtc *crtc)
187 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
188 struct omap_overlay *ovl = omap_crtc->ovl;
190 DBG("%s", omap_crtc->ovl->name);
192 ovl->get_overlay_info(ovl, &omap_crtc->info);
194 omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
197 static void omap_crtc_commit(struct drm_crtc *crtc)
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);
204 static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
205 struct drm_framebuffer *old_fb)
207 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
209 DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb);
211 update_scanout(crtc);
216 static void omap_crtc_load_lut(struct drm_crtc *crtc)
218 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
219 DBG("%s", omap_crtc->ovl->name);
222 static void page_flip_cb(void *arg)
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;
233 omap_crtc->event = NULL;
235 update_scanout(crtc);
236 WARN_ON(commit(crtc));
238 /* wakeup userspace */
239 /* TODO: this should happen *after* flip in vsync IRQ handler */
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);
253 static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
254 struct drm_framebuffer *fb,
255 struct drm_pending_vblank_event *event)
257 struct drm_device *dev = crtc->dev;
258 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
260 DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
262 if (omap_crtc->event) {
263 dev_err(dev->dev, "already a pending flip\n");
268 omap_crtc->event = event;
270 omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ,
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,
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,
293 struct omap_overlay *omap_crtc_get_overlay(struct drm_crtc *crtc)
295 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
296 return omap_crtc->ovl;
299 /* initialize crtc */
300 struct drm_crtc *omap_crtc_init(struct drm_device *dev,
301 struct omap_overlay *ovl, int id)
303 struct drm_crtc *crtc = NULL;
304 struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
306 DBG("%s", ovl->name);
309 dev_err(dev->dev, "could not allocate CRTC\n");
313 omap_crtc->ovl = ovl;
315 crtc = &omap_crtc->base;
316 drm_crtc_init(dev, crtc, &omap_crtc_funcs);
317 drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
323 omap_crtc_destroy(crtc);