Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
omap_plane.c
Go to the documentation of this file.
1 /*
2  * drivers/staging/omapdrm/omap_plane.c
3  *
4  * Copyright (C) 2011 Texas Instruments
5  * Author: Rob Clark <[email protected]>
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 <linux/kfifo.h>
21 
22 #include "omap_drv.h"
23 #include "omap_dmm_tiler.h"
24 
25 /* some hackery because omapdss has an 'enum omap_plane' (which would be
26  * better named omap_plane_id).. and compiler seems unhappy about having
27  * both a 'struct omap_plane' and 'enum omap_plane'
28  */
29 #define omap_plane _omap_plane
30 
31 /*
32  * plane funcs
33  */
34 
35 struct callback {
36  void (*fxn)(void *);
37  void *arg;
38 };
39 
40 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
41 
42 struct omap_plane {
43  struct drm_plane base;
44  struct omap_overlay *ovl;
46 
47  /* position/orientation of scanout within the fb: */
49 
50 
51  /* last fb that we pinned: */
53 
56 
57  /* for synchronizing access to unpins fifo */
59 
60  /* set of bo's pending unpin until next END_WIN irq */
61  DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *);
63 
64  /* for deferred unpin when we need to wait for scanout complete irq */
65  struct work_struct work;
66 
67  /* callback on next endwin irq */
68  struct callback endwin;
69 };
70 
71 /* map from ovl->id to the irq we are interested in for scanout-done */
72 static const uint32_t id2irq[] = {
77 };
78 
79 static void dispc_isr(void *arg, uint32_t mask)
80 {
81  struct drm_plane *plane = arg;
82  struct omap_plane *omap_plane = to_omap_plane(plane);
83  struct omap_drm_private *priv = plane->dev->dev_private;
84 
85  omap_dispc_unregister_isr(dispc_isr, plane,
86  id2irq[omap_plane->ovl->id]);
87 
88  queue_work(priv->wq, &omap_plane->work);
89 }
90 
91 static void unpin_worker(struct work_struct *work)
92 {
93  struct omap_plane *omap_plane =
94  container_of(work, struct omap_plane, work);
95  struct callback endwin;
96 
97  mutex_lock(&omap_plane->unpin_mutex);
98  DBG("unpinning %d of %d", omap_plane->num_unpins,
99  omap_plane->num_unpins + omap_plane->pending_num_unpins);
100  while (omap_plane->num_unpins > 0) {
101  struct drm_gem_object *bo = NULL;
102  int ret = kfifo_get(&omap_plane->unpin_fifo, &bo);
103  WARN_ON(!ret);
104  omap_gem_put_paddr(bo);
105  drm_gem_object_unreference_unlocked(bo);
106  omap_plane->num_unpins--;
107  }
108  endwin = omap_plane->endwin;
109  omap_plane->endwin.fxn = NULL;
110  mutex_unlock(&omap_plane->unpin_mutex);
111 
112  if (endwin.fxn)
113  endwin.fxn(endwin.arg);
114 }
115 
116 static void install_irq(struct drm_plane *plane)
117 {
118  struct omap_plane *omap_plane = to_omap_plane(plane);
119  struct omap_overlay *ovl = omap_plane->ovl;
120  int ret;
121 
122  ret = omap_dispc_register_isr(dispc_isr, plane, id2irq[ovl->id]);
123 
124  /*
125  * omapdss has upper limit on # of registered irq handlers,
126  * which we shouldn't hit.. but if we do the limit should
127  * be raised or bad things happen:
128  */
129  WARN_ON(ret == -EBUSY);
130 }
131 
132 /* push changes down to dss2 */
133 static int commit(struct drm_plane *plane)
134 {
135  struct drm_device *dev = plane->dev;
136  struct omap_plane *omap_plane = to_omap_plane(plane);
137  struct omap_overlay *ovl = omap_plane->ovl;
138  struct omap_overlay_info *info = &omap_plane->info;
139  int ret;
140 
141  DBG("%s", ovl->name);
142  DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width,
143  info->out_height, info->screen_width);
144  DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
145  info->paddr, info->p_uv_addr);
146 
147  /* NOTE: do we want to do this at all here, or just wait
148  * for dpms(ON) since other CRTC's may not have their mode
149  * set yet, so fb dimensions may still change..
150  */
151  ret = ovl->set_overlay_info(ovl, info);
152  if (ret) {
153  dev_err(dev->dev, "could not set overlay info\n");
154  return ret;
155  }
156 
157  mutex_lock(&omap_plane->unpin_mutex);
158  omap_plane->num_unpins += omap_plane->pending_num_unpins;
159  omap_plane->pending_num_unpins = 0;
160  mutex_unlock(&omap_plane->unpin_mutex);
161 
162  /* our encoder doesn't necessarily get a commit() after this, in
163  * particular in the dpms() and mode_set_base() cases, so force the
164  * manager to update:
165  *
166  * could this be in the encoder somehow?
167  */
168  if (ovl->manager) {
169  ret = ovl->manager->apply(ovl->manager);
170  if (ret) {
171  dev_err(dev->dev, "could not apply settings\n");
172  return ret;
173  }
174 
175  /*
176  * NOTE: really this should be atomic w/ mgr->apply() but
177  * omapdss does not expose such an API
178  */
179  if (omap_plane->num_unpins > 0)
180  install_irq(plane);
181 
182  } else {
183  struct omap_drm_private *priv = dev->dev_private;
184  queue_work(priv->wq, &omap_plane->work);
185  }
186 
187 
188  if (ovl->is_enabled(ovl)) {
189  omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
190  info->out_width, info->out_height);
191  }
192 
193  return 0;
194 }
195 
196 /* when CRTC that we are attached to has potentially changed, this checks
197  * if we are attached to proper manager, and if necessary updates.
198  */
199 static void update_manager(struct drm_plane *plane)
200 {
201  struct omap_drm_private *priv = plane->dev->dev_private;
202  struct omap_plane *omap_plane = to_omap_plane(plane);
203  struct omap_overlay *ovl = omap_plane->ovl;
204  struct omap_overlay_manager *mgr = NULL;
205  int i;
206 
207  if (plane->crtc) {
208  for (i = 0; i < priv->num_encoders; i++) {
209  struct drm_encoder *encoder = priv->encoders[i];
210  if (encoder->crtc == plane->crtc) {
211  mgr = omap_encoder_get_manager(encoder);
212  break;
213  }
214  }
215  }
216 
217  if (ovl->manager != mgr) {
218  bool enabled = ovl->is_enabled(ovl);
219 
220  /* don't switch things around with enabled overlays: */
221  if (enabled)
223 
224  if (ovl->manager) {
225  DBG("disconnecting %s from %s", ovl->name,
226  ovl->manager->name);
227  ovl->unset_manager(ovl);
228  }
229 
230  if (mgr) {
231  DBG("connecting %s to %s", ovl->name, mgr->name);
232  ovl->set_manager(ovl, mgr);
233  }
234 
235  if (enabled && mgr)
237  }
238 }
239 
240 static void unpin(void *arg, struct drm_gem_object *bo)
241 {
242  struct drm_plane *plane = arg;
243  struct omap_plane *omap_plane = to_omap_plane(plane);
244 
245  if (kfifo_put(&omap_plane->unpin_fifo,
246  (const struct drm_gem_object **)&bo)) {
247  omap_plane->pending_num_unpins++;
248  /* also hold a ref so it isn't free'd while pinned */
249  drm_gem_object_reference(bo);
250  } else {
251  dev_err(plane->dev->dev, "unpin fifo full!\n");
252  omap_gem_put_paddr(bo);
253  }
254 }
255 
256 /* update which fb (if any) is pinned for scanout */
257 static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
258 {
259  struct omap_plane *omap_plane = to_omap_plane(plane);
260  struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
261 
262  if (pinned_fb != fb) {
263  int ret;
264 
265  DBG("%p -> %p", pinned_fb, fb);
266 
267  mutex_lock(&omap_plane->unpin_mutex);
268  ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin);
269  mutex_unlock(&omap_plane->unpin_mutex);
270 
271  if (ret) {
272  dev_err(plane->dev->dev, "could not swap %p -> %p\n",
273  omap_plane->pinned_fb, fb);
274  omap_plane->pinned_fb = NULL;
275  return ret;
276  }
277 
278  omap_plane->pinned_fb = fb;
279  }
280 
281  return 0;
282 }
283 
284 /* update parameters that are dependent on the framebuffer dimensions and
285  * position within the fb that this plane scans out from. This is called
286  * when framebuffer or x,y base may have changed.
287  */
288 static void update_scanout(struct drm_plane *plane)
289 {
290  struct omap_plane *omap_plane = to_omap_plane(plane);
291  struct omap_overlay_info *info = &omap_plane->info;
292  struct omap_drm_window *win = &omap_plane->win;
293  int ret;
294 
295  ret = update_pin(plane, plane->fb);
296  if (ret) {
297  dev_err(plane->dev->dev,
298  "could not pin fb: %d\n", ret);
300  return;
301  }
302 
303  omap_framebuffer_update_scanout(plane->fb, win, info);
304 
305  DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name,
306  win->src_x, win->src_y,
307  (u32)info->paddr, (u32)info->p_uv_addr,
308  info->screen_width);
309 }
310 
311 int omap_plane_mode_set(struct drm_plane *plane,
312  struct drm_crtc *crtc, struct drm_framebuffer *fb,
313  int crtc_x, int crtc_y,
314  unsigned int crtc_w, unsigned int crtc_h,
317 {
318  struct omap_plane *omap_plane = to_omap_plane(plane);
319  struct omap_drm_window *win = &omap_plane->win;
320 
321  win->crtc_x = crtc_x;
322  win->crtc_y = crtc_y;
323  win->crtc_w = crtc_w;
324  win->crtc_h = crtc_h;
325 
326  /* src values are in Q16 fixed point, convert to integer: */
327  win->src_x = src_x >> 16;
328  win->src_y = src_y >> 16;
329  win->src_w = src_w >> 16;
330  win->src_h = src_h >> 16;
331 
332  /* note: this is done after this fxn returns.. but if we need
333  * to do a commit/update_scanout, etc before this returns we
334  * need the current value.
335  */
336  plane->fb = fb;
337  plane->crtc = crtc;
338 
339  update_scanout(plane);
340  update_manager(plane);
341 
342  return 0;
343 }
344 
345 static int omap_plane_update(struct drm_plane *plane,
346  struct drm_crtc *crtc, struct drm_framebuffer *fb,
347  int crtc_x, int crtc_y,
348  unsigned int crtc_w, unsigned int crtc_h,
351 {
352  omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h,
353  src_x, src_y, src_w, src_h);
354  return omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
355 }
356 
357 static int omap_plane_disable(struct drm_plane *plane)
358 {
359  struct omap_plane *omap_plane = to_omap_plane(plane);
360  omap_plane->win.rotation = BIT(DRM_ROTATE_0);
361  return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
362 }
363 
364 static void omap_plane_destroy(struct drm_plane *plane)
365 {
366  struct omap_plane *omap_plane = to_omap_plane(plane);
367  DBG("%s", omap_plane->ovl->name);
368  omap_plane_disable(plane);
369  drm_plane_cleanup(plane);
370  WARN_ON(omap_plane->pending_num_unpins + omap_plane->num_unpins > 0);
371  kfifo_free(&omap_plane->unpin_fifo);
372  kfree(omap_plane);
373 }
374 
375 int omap_plane_dpms(struct drm_plane *plane, int mode)
376 {
377  struct omap_plane *omap_plane = to_omap_plane(plane);
378  struct omap_overlay *ovl = omap_plane->ovl;
379  int r;
380 
381  DBG("%s: %d", omap_plane->ovl->name, mode);
382 
383  if (mode == DRM_MODE_DPMS_ON) {
384  update_scanout(plane);
385  r = commit(plane);
386  if (!r)
387  r = ovl->enable(ovl);
388  } else {
389  struct omap_drm_private *priv = plane->dev->dev_private;
390  r = ovl->disable(ovl);
391  update_pin(plane, NULL);
392  queue_work(priv->wq, &omap_plane->work);
393  }
394 
395  return r;
396 }
397 
398 void omap_plane_on_endwin(struct drm_plane *plane,
399  void (*fxn)(void *), void *arg)
400 {
401  struct omap_plane *omap_plane = to_omap_plane(plane);
402 
403  mutex_lock(&omap_plane->unpin_mutex);
404  omap_plane->endwin.fxn = fxn;
405  omap_plane->endwin.arg = arg;
406  mutex_unlock(&omap_plane->unpin_mutex);
407 
408  install_irq(plane);
409 }
410 
411 /* helper to install properties which are common to planes and crtcs */
413  struct drm_mode_object *obj)
414 {
415  struct drm_device *dev = plane->dev;
416  struct omap_drm_private *priv = dev->dev_private;
417  struct drm_property *prop;
418 
419  prop = priv->rotation_prop;
420  if (!prop) {
421  const struct drm_prop_enum_list props[] = {
422  { DRM_ROTATE_0, "rotate-0" },
423  { DRM_ROTATE_90, "rotate-90" },
424  { DRM_ROTATE_180, "rotate-180" },
425  { DRM_ROTATE_270, "rotate-270" },
426  { DRM_REFLECT_X, "reflect-x" },
427  { DRM_REFLECT_Y, "reflect-y" },
428  };
429  prop = drm_property_create_bitmask(dev, 0, "rotation",
430  props, ARRAY_SIZE(props));
431  if (prop == NULL)
432  return;
433  priv->rotation_prop = prop;
434  }
435  drm_object_attach_property(obj, prop, 0);
436 
437  prop = priv->zorder_prop;
438  if (!prop) {
439  prop = drm_property_create_range(dev, 0, "zorder", 0, 3);
440  if (prop == NULL)
441  return;
442  priv->zorder_prop = prop;
443  }
444  drm_object_attach_property(obj, prop, 0);
445 }
446 
449 {
450  struct omap_plane *omap_plane = to_omap_plane(plane);
451  struct omap_drm_private *priv = plane->dev->dev_private;
452  int ret = -EINVAL;
453 
454  if (property == priv->rotation_prop) {
455  struct omap_overlay *ovl = omap_plane->ovl;
456 
457  DBG("%s: rotation: %02x", ovl->name, (uint32_t)val);
458  omap_plane->win.rotation = val;
459 
460  if (ovl->is_enabled(ovl))
461  ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
462  else
463  ret = 0;
464  } else if (property == priv->zorder_prop) {
465  struct omap_overlay *ovl = omap_plane->ovl;
466 
467  DBG("%s: zorder: %d", ovl->name, (uint32_t)val);
468  omap_plane->info.zorder = val;
469 
470  if (ovl->is_enabled(ovl))
471  ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
472  else
473  ret = 0;
474  }
475 
476  return ret;
477 }
478 
479 static const struct drm_plane_funcs omap_plane_funcs = {
480  .update_plane = omap_plane_update,
481  .disable_plane = omap_plane_disable,
482  .destroy = omap_plane_destroy,
483  .set_property = omap_plane_set_property,
484 };
485 
486 /* initialize plane */
488  struct omap_overlay *ovl, unsigned int possible_crtcs,
489  bool priv)
490 {
491  struct drm_plane *plane = NULL;
492  struct omap_plane *omap_plane;
493  int ret;
494 
495  DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name,
496  possible_crtcs, priv);
497 
498  /* friendly reminder to update table for future hw: */
499  WARN_ON(ovl->id >= ARRAY_SIZE(id2irq));
500 
501  omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
502  if (!omap_plane) {
503  dev_err(dev->dev, "could not allocate plane\n");
504  goto fail;
505  }
506 
507  mutex_init(&omap_plane->unpin_mutex);
508 
509  ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
510  if (ret) {
511  dev_err(dev->dev, "could not allocate unpin FIFO\n");
512  goto fail;
513  }
514 
515  INIT_WORK(&omap_plane->work, unpin_worker);
516 
518  omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
519  ovl->supported_modes);
520  omap_plane->ovl = ovl;
521  plane = &omap_plane->base;
522 
523  drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs,
524  omap_plane->formats, omap_plane->nformats, priv);
525 
526  omap_plane_install_properties(plane, &plane->base);
527 
528  /* get our starting configuration, set defaults for parameters
529  * we don't currently use, etc:
530  */
531  ovl->get_overlay_info(ovl, &omap_plane->info);
532  omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA;
533  omap_plane->info.rotation = OMAP_DSS_ROT_0;
534  omap_plane->info.global_alpha = 0xff;
535  omap_plane->info.mirror = 0;
536 
537  /* Set defaults depending on whether we are a CRTC or overlay
538  * layer.
539  * TODO add ioctl to give userspace an API to change this.. this
540  * will come in a subsequent patch.
541  */
542  if (priv)
543  omap_plane->info.zorder = 0;
544  else
545  omap_plane->info.zorder = ovl->id;
546 
547  update_manager(plane);
548 
549  return plane;
550 
551 fail:
552  if (plane) {
553  omap_plane_destroy(plane);
554  }
555  return NULL;
556 }