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; | |
bb5c2d9a RC |
30 | struct drm_plane *plane; |
31 | const char *name; | |
cd5351f4 RC |
32 | int id; |
33 | ||
bb5c2d9a | 34 | /* if there is a pending flip, these will be non-null: */ |
cd5351f4 | 35 | struct drm_pending_vblank_event *event; |
bb5c2d9a | 36 | struct drm_framebuffer *old_fb; |
cd5351f4 RC |
37 | }; |
38 | ||
cd5351f4 RC |
39 | static void omap_crtc_destroy(struct drm_crtc *crtc) |
40 | { | |
41 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
bb5c2d9a | 42 | omap_crtc->plane->funcs->destroy(omap_crtc->plane); |
cd5351f4 RC |
43 | drm_crtc_cleanup(crtc); |
44 | kfree(omap_crtc); | |
45 | } | |
46 | ||
47 | static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) | |
48 | { | |
bb5c2d9a | 49 | struct omap_drm_private *priv = crtc->dev->dev_private; |
cd5351f4 | 50 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
bb5c2d9a | 51 | int i; |
cd5351f4 | 52 | |
bb5c2d9a | 53 | WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); |
cd5351f4 | 54 | |
bb5c2d9a RC |
55 | for (i = 0; i < priv->num_planes; i++) { |
56 | struct drm_plane *plane = priv->planes[i]; | |
57 | if (plane->crtc == crtc) | |
58 | WARN_ON(omap_plane_dpms(plane, mode)); | |
cd5351f4 | 59 | } |
cd5351f4 RC |
60 | } |
61 | ||
62 | static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, | |
bb5c2d9a RC |
63 | struct drm_display_mode *mode, |
64 | struct drm_display_mode *adjusted_mode) | |
cd5351f4 | 65 | { |
cd5351f4 RC |
66 | return true; |
67 | } | |
68 | ||
69 | static int omap_crtc_mode_set(struct drm_crtc *crtc, | |
bb5c2d9a RC |
70 | struct drm_display_mode *mode, |
71 | struct drm_display_mode *adjusted_mode, | |
72 | int x, int y, | |
73 | struct drm_framebuffer *old_fb) | |
cd5351f4 RC |
74 | { |
75 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
bb5c2d9a | 76 | struct drm_plane *plane = omap_crtc->plane; |
cd5351f4 | 77 | |
2f53700d | 78 | return omap_plane_mode_set(plane, crtc, crtc->fb, |
bb5c2d9a RC |
79 | 0, 0, mode->hdisplay, mode->vdisplay, |
80 | x << 16, y << 16, | |
81 | mode->hdisplay << 16, mode->vdisplay << 16); | |
cd5351f4 RC |
82 | } |
83 | ||
84 | static void omap_crtc_prepare(struct drm_crtc *crtc) | |
85 | { | |
86 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
bb5c2d9a | 87 | DBG("%s", omap_crtc->name); |
cd5351f4 RC |
88 | omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); |
89 | } | |
90 | ||
91 | static void omap_crtc_commit(struct drm_crtc *crtc) | |
92 | { | |
93 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
bb5c2d9a | 94 | DBG("%s", omap_crtc->name); |
cd5351f4 RC |
95 | omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); |
96 | } | |
97 | ||
98 | static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | |
bb5c2d9a | 99 | struct drm_framebuffer *old_fb) |
cd5351f4 RC |
100 | { |
101 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
bb5c2d9a RC |
102 | struct drm_plane *plane = omap_crtc->plane; |
103 | struct drm_display_mode *mode = &crtc->mode; | |
cd5351f4 | 104 | |
bb5c2d9a RC |
105 | return plane->funcs->update_plane(plane, crtc, crtc->fb, |
106 | 0, 0, mode->hdisplay, mode->vdisplay, | |
107 | x << 16, y << 16, | |
108 | mode->hdisplay << 16, mode->vdisplay << 16); | |
cd5351f4 RC |
109 | } |
110 | ||
111 | static void omap_crtc_load_lut(struct drm_crtc *crtc) | |
112 | { | |
cd5351f4 RC |
113 | } |
114 | ||
72d0c336 | 115 | static void vblank_cb(void *arg) |
cd5351f4 | 116 | { |
72d0c336 | 117 | static uint32_t sequence = 0; |
cd5351f4 RC |
118 | struct drm_crtc *crtc = arg; |
119 | struct drm_device *dev = crtc->dev; | |
120 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
121 | struct drm_pending_vblank_event *event = omap_crtc->event; | |
cd5351f4 | 122 | unsigned long flags; |
72d0c336 | 123 | struct timeval now; |
cd5351f4 RC |
124 | |
125 | WARN_ON(!event); | |
126 | ||
127 | omap_crtc->event = NULL; | |
cd5351f4 RC |
128 | |
129 | /* wakeup userspace */ | |
cd5351f4 | 130 | if (event) { |
7411f9cf RC |
131 | do_gettimeofday(&now); |
132 | ||
cd5351f4 | 133 | spin_lock_irqsave(&dev->event_lock, flags); |
7411f9cf RC |
134 | /* TODO: we can't yet use the vblank time accounting, |
135 | * because omapdss lower layer is the one that knows | |
136 | * the irq # and registers the handler, which more or | |
137 | * less defeats how drm_irq works.. for now just fake | |
138 | * the sequence number and use gettimeofday.. | |
139 | * | |
cd5351f4 RC |
140 | event->event.sequence = drm_vblank_count_and_time( |
141 | dev, omap_crtc->id, &now); | |
7411f9cf RC |
142 | */ |
143 | event->event.sequence = sequence++; | |
cd5351f4 RC |
144 | event->event.tv_sec = now.tv_sec; |
145 | event->event.tv_usec = now.tv_usec; | |
146 | list_add_tail(&event->base.link, | |
147 | &event->base.file_priv->event_list); | |
148 | wake_up_interruptible(&event->base.file_priv->event_wait); | |
149 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
150 | } | |
151 | } | |
152 | ||
72d0c336 RC |
153 | static void page_flip_cb(void *arg) |
154 | { | |
155 | struct drm_crtc *crtc = arg; | |
156 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
157 | struct drm_framebuffer *old_fb = omap_crtc->old_fb; | |
158 | ||
159 | omap_crtc->old_fb = NULL; | |
160 | ||
161 | omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); | |
162 | ||
163 | /* really we'd like to setup the callback atomically w/ setting the | |
164 | * new scanout buffer to avoid getting stuck waiting an extra vblank | |
165 | * cycle.. for now go for correctness and later figure out speed.. | |
166 | */ | |
167 | omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc); | |
168 | } | |
169 | ||
cd5351f4 RC |
170 | static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, |
171 | struct drm_framebuffer *fb, | |
172 | struct drm_pending_vblank_event *event) | |
173 | { | |
174 | struct drm_device *dev = crtc->dev; | |
175 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
176 | ||
177 | DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id); | |
178 | ||
179 | if (omap_crtc->event) { | |
180 | dev_err(dev->dev, "already a pending flip\n"); | |
181 | return -EINVAL; | |
182 | } | |
183 | ||
bb5c2d9a | 184 | omap_crtc->old_fb = crtc->fb; |
cd5351f4 | 185 | omap_crtc->event = event; |
bb5c2d9a | 186 | crtc->fb = fb; |
cd5351f4 | 187 | |
9a0774e0 | 188 | omap_gem_op_async(omap_framebuffer_bo(fb, 0), OMAP_GEM_READ, |
cd5351f4 RC |
189 | page_flip_cb, crtc); |
190 | ||
191 | return 0; | |
192 | } | |
193 | ||
194 | static const struct drm_crtc_funcs omap_crtc_funcs = { | |
cd5351f4 RC |
195 | .set_config = drm_crtc_helper_set_config, |
196 | .destroy = omap_crtc_destroy, | |
197 | .page_flip = omap_crtc_page_flip_locked, | |
198 | }; | |
199 | ||
200 | static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { | |
201 | .dpms = omap_crtc_dpms, | |
202 | .mode_fixup = omap_crtc_mode_fixup, | |
203 | .mode_set = omap_crtc_mode_set, | |
204 | .prepare = omap_crtc_prepare, | |
205 | .commit = omap_crtc_commit, | |
206 | .mode_set_base = omap_crtc_mode_set_base, | |
207 | .load_lut = omap_crtc_load_lut, | |
208 | }; | |
209 | ||
cd5351f4 RC |
210 | /* initialize crtc */ |
211 | struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |
212 | struct omap_overlay *ovl, int id) | |
213 | { | |
214 | struct drm_crtc *crtc = NULL; | |
215 | struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); | |
216 | ||
217 | DBG("%s", ovl->name); | |
218 | ||
219 | if (!omap_crtc) { | |
220 | dev_err(dev->dev, "could not allocate CRTC\n"); | |
221 | goto fail; | |
222 | } | |
223 | ||
cd5351f4 | 224 | crtc = &omap_crtc->base; |
bb5c2d9a RC |
225 | |
226 | omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true); | |
227 | omap_crtc->plane->crtc = crtc; | |
228 | omap_crtc->name = ovl->name; | |
229 | omap_crtc->id = id; | |
230 | ||
cd5351f4 RC |
231 | drm_crtc_init(dev, crtc, &omap_crtc_funcs); |
232 | drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); | |
233 | ||
234 | return crtc; | |
235 | ||
236 | fail: | |
237 | if (crtc) { | |
65b0bd06 | 238 | omap_crtc_destroy(crtc); |
cd5351f4 RC |
239 | } |
240 | return NULL; | |
241 | } |