1/*
2 * drivers/gpu/drm/omapdrm/omap_plane.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
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 "drm_flip_work.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
35struct callback {
36	void (*fxn)(void *);
37	void *arg;
38};
39
40#define to_omap_plane(x) container_of(x, struct omap_plane, base)
41
42struct omap_plane {
43	struct drm_plane base;
44	int id;  /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */
45	const char *name;
46	struct omap_overlay_info info;
47	struct omap_drm_apply apply;
48
49	/* position/orientation of scanout within the fb: */
50	struct omap_drm_window win;
51	bool enabled;
52
53	/* last fb that we pinned: */
54	struct drm_framebuffer *pinned_fb;
55
56	uint32_t nformats;
57	uint32_t formats[32];
58
59	struct omap_drm_irq error_irq;
60
61	/* for deferring bo unpin's until next post_apply(): */
62	struct drm_flip_work unpin_work;
63
64	// XXX maybe get rid of this and handle vblank in crtc too?
65	struct callback apply_done_cb;
66};
67
68static void omap_plane_unpin_worker(struct drm_flip_work *work, void *val)
69{
70	struct omap_plane *omap_plane =
71			container_of(work, struct omap_plane, unpin_work);
72	struct drm_device *dev = omap_plane->base.dev;
73
74	/*
75	 * omap_framebuffer_pin/unpin are always called from priv->wq,
76	 * so there's no need for locking here.
77	 */
78	omap_framebuffer_unpin(val);
79	mutex_lock(&dev->mode_config.mutex);
80	drm_framebuffer_unreference(val);
81	mutex_unlock(&dev->mode_config.mutex);
82}
83
84/* update which fb (if any) is pinned for scanout */
85static int omap_plane_update_pin(struct drm_plane *plane,
86				 struct drm_framebuffer *fb)
87{
88	struct omap_plane *omap_plane = to_omap_plane(plane);
89	struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
90
91	if (pinned_fb != fb) {
92		int ret = 0;
93
94		DBG("%p -> %p", pinned_fb, fb);
95
96		if (fb) {
97			drm_framebuffer_reference(fb);
98			ret = omap_framebuffer_pin(fb);
99		}
100
101		if (pinned_fb)
102			drm_flip_work_queue(&omap_plane->unpin_work, pinned_fb);
103
104		if (ret) {
105			dev_err(plane->dev->dev, "could not swap %p -> %p\n",
106					omap_plane->pinned_fb, fb);
107			drm_framebuffer_unreference(fb);
108			omap_plane->pinned_fb = NULL;
109			return ret;
110		}
111
112		omap_plane->pinned_fb = fb;
113	}
114
115	return 0;
116}
117
118static void omap_plane_pre_apply(struct omap_drm_apply *apply)
119{
120	struct omap_plane *omap_plane =
121			container_of(apply, struct omap_plane, apply);
122	struct omap_drm_window *win = &omap_plane->win;
123	struct drm_plane *plane = &omap_plane->base;
124	struct drm_device *dev = plane->dev;
125	struct omap_overlay_info *info = &omap_plane->info;
126	struct drm_crtc *crtc = plane->crtc;
127	enum omap_channel channel;
128	bool enabled = omap_plane->enabled && crtc;
129	int ret;
130
131	DBG("%s, enabled=%d", omap_plane->name, enabled);
132
133	/* if fb has changed, pin new fb: */
134	omap_plane_update_pin(plane, enabled ? plane->fb : NULL);
135
136	if (!enabled) {
137		dispc_ovl_enable(omap_plane->id, false);
138		return;
139	}
140
141	channel = omap_crtc_channel(crtc);
142
143	/* update scanout: */
144	omap_framebuffer_update_scanout(plane->fb, win, info);
145
146	DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
147			info->out_width, info->out_height,
148			info->screen_width);
149	DBG("%d,%d %pad %pad", info->pos_x, info->pos_y,
150			&info->paddr, &info->p_uv_addr);
151
152	dispc_ovl_set_channel_out(omap_plane->id, channel);
153
154	/* and finally, update omapdss: */
155	ret = dispc_ovl_setup(omap_plane->id, info, false,
156			      omap_crtc_timings(crtc), false);
157	if (ret) {
158		dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
159		return;
160	}
161
162	dispc_ovl_enable(omap_plane->id, true);
163}
164
165static void omap_plane_post_apply(struct omap_drm_apply *apply)
166{
167	struct omap_plane *omap_plane =
168			container_of(apply, struct omap_plane, apply);
169	struct drm_plane *plane = &omap_plane->base;
170	struct omap_drm_private *priv = plane->dev->dev_private;
171	struct callback cb;
172
173	cb = omap_plane->apply_done_cb;
174	omap_plane->apply_done_cb.fxn = NULL;
175
176	drm_flip_work_commit(&omap_plane->unpin_work, priv->wq);
177
178	if (cb.fxn)
179		cb.fxn(cb.arg);
180}
181
182static int omap_plane_apply(struct drm_plane *plane)
183{
184	if (plane->crtc) {
185		struct omap_plane *omap_plane = to_omap_plane(plane);
186		return omap_crtc_apply(plane->crtc, &omap_plane->apply);
187	}
188	return 0;
189}
190
191int omap_plane_mode_set(struct drm_plane *plane,
192			struct drm_crtc *crtc, struct drm_framebuffer *fb,
193			int crtc_x, int crtc_y,
194			unsigned int crtc_w, unsigned int crtc_h,
195			unsigned int src_x, unsigned int src_y,
196			unsigned int src_w, unsigned int src_h,
197			void (*fxn)(void *), void *arg)
198{
199	struct omap_plane *omap_plane = to_omap_plane(plane);
200	struct omap_drm_window *win = &omap_plane->win;
201
202	win->crtc_x = crtc_x;
203	win->crtc_y = crtc_y;
204	win->crtc_w = crtc_w;
205	win->crtc_h = crtc_h;
206
207	win->src_x = src_x;
208	win->src_y = src_y;
209	win->src_w = src_w;
210	win->src_h = src_h;
211
212	if (fxn) {
213		/* omap_crtc should ensure that a new page flip
214		 * isn't permitted while there is one pending:
215		 */
216		BUG_ON(omap_plane->apply_done_cb.fxn);
217
218		omap_plane->apply_done_cb.fxn = fxn;
219		omap_plane->apply_done_cb.arg = arg;
220	}
221
222	return omap_plane_apply(plane);
223}
224
225static int omap_plane_update(struct drm_plane *plane,
226		struct drm_crtc *crtc, struct drm_framebuffer *fb,
227		int crtc_x, int crtc_y,
228		unsigned int crtc_w, unsigned int crtc_h,
229		uint32_t src_x, uint32_t src_y,
230		uint32_t src_w, uint32_t src_h)
231{
232	struct omap_plane *omap_plane = to_omap_plane(plane);
233	omap_plane->enabled = true;
234
235	/* omap_plane_mode_set() takes adjusted src */
236	switch (omap_plane->win.rotation & 0xf) {
237	case BIT(DRM_ROTATE_90):
238	case BIT(DRM_ROTATE_270):
239		swap(src_w, src_h);
240		break;
241	}
242
243	/*
244	 * We don't need to take a reference to the framebuffer as the DRM core
245	 * has already done so for the purpose of setting plane->fb.
246	 */
247	plane->fb = fb;
248	plane->crtc = crtc;
249
250	/* src values are in Q16 fixed point, convert to integer: */
251	return omap_plane_mode_set(plane, crtc, fb,
252			crtc_x, crtc_y, crtc_w, crtc_h,
253			src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16,
254			NULL, NULL);
255}
256
257static int omap_plane_disable(struct drm_plane *plane)
258{
259	struct omap_plane *omap_plane = to_omap_plane(plane);
260
261	omap_plane->win.rotation = BIT(DRM_ROTATE_0);
262	omap_plane->info.zorder = plane->type == DRM_PLANE_TYPE_PRIMARY
263				? 0 : omap_plane->id;
264
265	return omap_plane_set_enable(plane, false);
266}
267
268static void omap_plane_destroy(struct drm_plane *plane)
269{
270	struct omap_plane *omap_plane = to_omap_plane(plane);
271
272	DBG("%s", omap_plane->name);
273
274	omap_irq_unregister(plane->dev, &omap_plane->error_irq);
275
276	drm_plane_cleanup(plane);
277
278	drm_flip_work_cleanup(&omap_plane->unpin_work);
279
280	kfree(omap_plane);
281}
282
283int omap_plane_set_enable(struct drm_plane *plane, bool enable)
284{
285	struct omap_plane *omap_plane = to_omap_plane(plane);
286
287	if (enable == omap_plane->enabled)
288		return 0;
289
290	omap_plane->enabled = enable;
291	return omap_plane_apply(plane);
292}
293
294/* helper to install properties which are common to planes and crtcs */
295void omap_plane_install_properties(struct drm_plane *plane,
296		struct drm_mode_object *obj)
297{
298	struct drm_device *dev = plane->dev;
299	struct omap_drm_private *priv = dev->dev_private;
300	struct drm_property *prop;
301
302	if (priv->has_dmm) {
303		prop = priv->rotation_prop;
304		if (!prop) {
305			prop = drm_mode_create_rotation_property(dev,
306								 BIT(DRM_ROTATE_0) |
307								 BIT(DRM_ROTATE_90) |
308								 BIT(DRM_ROTATE_180) |
309								 BIT(DRM_ROTATE_270) |
310								 BIT(DRM_REFLECT_X) |
311								 BIT(DRM_REFLECT_Y));
312			if (prop == NULL)
313				return;
314			priv->rotation_prop = prop;
315		}
316		drm_object_attach_property(obj, prop, 0);
317	}
318
319	prop = priv->zorder_prop;
320	if (!prop) {
321		prop = drm_property_create_range(dev, 0, "zorder", 0, 3);
322		if (prop == NULL)
323			return;
324		priv->zorder_prop = prop;
325	}
326	drm_object_attach_property(obj, prop, 0);
327}
328
329int omap_plane_set_property(struct drm_plane *plane,
330		struct drm_property *property, uint64_t val)
331{
332	struct omap_plane *omap_plane = to_omap_plane(plane);
333	struct omap_drm_private *priv = plane->dev->dev_private;
334	int ret = -EINVAL;
335
336	if (property == priv->rotation_prop) {
337		DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
338		omap_plane->win.rotation = val;
339		ret = omap_plane_apply(plane);
340	} else if (property == priv->zorder_prop) {
341		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
342		omap_plane->info.zorder = val;
343		ret = omap_plane_apply(plane);
344	}
345
346	return ret;
347}
348
349static const struct drm_plane_funcs omap_plane_funcs = {
350	.update_plane = omap_plane_update,
351	.disable_plane = omap_plane_disable,
352	.destroy = omap_plane_destroy,
353	.set_property = omap_plane_set_property,
354};
355
356static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
357{
358	struct omap_plane *omap_plane =
359			container_of(irq, struct omap_plane, error_irq);
360	DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name,
361		irqstatus);
362}
363
364static const char *plane_names[] = {
365	[OMAP_DSS_GFX] = "gfx",
366	[OMAP_DSS_VIDEO1] = "vid1",
367	[OMAP_DSS_VIDEO2] = "vid2",
368	[OMAP_DSS_VIDEO3] = "vid3",
369};
370
371static const uint32_t error_irqs[] = {
372	[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
373	[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
374	[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
375	[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
376};
377
378/* initialize plane */
379struct drm_plane *omap_plane_init(struct drm_device *dev,
380		int id, enum drm_plane_type type)
381{
382	struct omap_drm_private *priv = dev->dev_private;
383	struct drm_plane *plane;
384	struct omap_plane *omap_plane;
385	struct omap_overlay_info *info;
386	int ret;
387
388	DBG("%s: type=%d", plane_names[id], type);
389
390	omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
391	if (!omap_plane)
392		return ERR_PTR(-ENOMEM);
393
394	drm_flip_work_init(&omap_plane->unpin_work,
395			"unpin", omap_plane_unpin_worker);
396
397	omap_plane->nformats = omap_framebuffer_get_formats(
398			omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
399			dss_feat_get_supported_color_modes(id));
400	omap_plane->id = id;
401	omap_plane->name = plane_names[id];
402
403	plane = &omap_plane->base;
404
405	omap_plane->apply.pre_apply  = omap_plane_pre_apply;
406	omap_plane->apply.post_apply = omap_plane_post_apply;
407
408	omap_plane->error_irq.irqmask = error_irqs[id];
409	omap_plane->error_irq.irq = omap_plane_error_irq;
410	omap_irq_register(dev, &omap_plane->error_irq);
411
412	ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1,
413				       &omap_plane_funcs, omap_plane->formats,
414				       omap_plane->nformats, type);
415	if (ret < 0)
416		goto error;
417
418	omap_plane_install_properties(plane, &plane->base);
419
420	/* get our starting configuration, set defaults for parameters
421	 * we don't currently use, etc:
422	 */
423	info = &omap_plane->info;
424	info->rotation_type = OMAP_DSS_ROT_DMA;
425	info->rotation = OMAP_DSS_ROT_0;
426	info->global_alpha = 0xff;
427	info->mirror = 0;
428
429	/* Set defaults depending on whether we are a CRTC or overlay
430	 * layer.
431	 * TODO add ioctl to give userspace an API to change this.. this
432	 * will come in a subsequent patch.
433	 */
434	if (type == DRM_PLANE_TYPE_PRIMARY)
435		omap_plane->info.zorder = 0;
436	else
437		omap_plane->info.zorder = id;
438
439	return plane;
440
441error:
442	omap_irq_unregister(plane->dev, &omap_plane->error_irq);
443	kfree(omap_plane);
444	return NULL;
445}
446