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