1/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors: Joonyoung Shim <jy0922.shim@samsung.com>
4 *
5 * This program is free software; you can redistribute  it and/or modify it
6 * under  the terms of  the GNU General  Public License as published by the
7 * Free Software Foundation;  either version 2 of the  License, or (at your
8 * option) any later version.
9 *
10 */
11
12#include <drm/drmP.h>
13
14#include <drm/exynos_drm.h>
15#include <drm/drm_plane_helper.h>
16#include "exynos_drm_drv.h"
17#include "exynos_drm_crtc.h"
18#include "exynos_drm_fb.h"
19#include "exynos_drm_gem.h"
20#include "exynos_drm_plane.h"
21
22static const uint32_t formats[] = {
23	DRM_FORMAT_XRGB8888,
24	DRM_FORMAT_ARGB8888,
25	DRM_FORMAT_NV12,
26};
27
28/*
29 * This function is to get X or Y size shown via screen. This needs length and
30 * start position of CRTC.
31 *
32 *      <--- length --->
33 * CRTC ----------------
34 *      ^ start        ^ end
35 *
36 * There are six cases from a to f.
37 *
38 *             <----- SCREEN ----->
39 *             0                 last
40 *   ----------|------------------|----------
41 * CRTCs
42 * a -------
43 *        b -------
44 *        c --------------------------
45 *                 d --------
46 *                           e -------
47 *                                  f -------
48 */
49static int exynos_plane_get_size(int start, unsigned length, unsigned last)
50{
51	int end = start + length;
52	int size = 0;
53
54	if (start <= 0) {
55		if (end > 0)
56			size = min_t(unsigned, end, last);
57	} else if (start <= last) {
58		size = min_t(unsigned, last - start, length);
59	}
60
61	return size;
62}
63
64int exynos_check_plane(struct drm_plane *plane, struct drm_framebuffer *fb)
65{
66	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
67	int nr;
68	int i;
69
70	nr = exynos_drm_fb_get_buf_cnt(fb);
71	for (i = 0; i < nr; i++) {
72		struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
73
74		if (!buffer) {
75			DRM_DEBUG_KMS("buffer is null\n");
76			return -EFAULT;
77		}
78
79		exynos_plane->dma_addr[i] = buffer->dma_addr + fb->offsets[i];
80
81		DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
82				i, (unsigned long)exynos_plane->dma_addr[i]);
83	}
84
85	return 0;
86}
87
88void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
89			  struct drm_framebuffer *fb, int crtc_x, int crtc_y,
90			  unsigned int crtc_w, unsigned int crtc_h,
91			  uint32_t src_x, uint32_t src_y,
92			  uint32_t src_w, uint32_t src_h)
93{
94	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
95	unsigned int actual_w;
96	unsigned int actual_h;
97
98	actual_w = exynos_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay);
99	actual_h = exynos_plane_get_size(crtc_y, crtc_h, crtc->mode.vdisplay);
100
101	if (crtc_x < 0) {
102		if (actual_w)
103			src_x -= crtc_x;
104		crtc_x = 0;
105	}
106
107	if (crtc_y < 0) {
108		if (actual_h)
109			src_y -= crtc_y;
110		crtc_y = 0;
111	}
112
113	/* set ratio */
114	exynos_plane->h_ratio = (src_w << 16) / crtc_w;
115	exynos_plane->v_ratio = (src_h << 16) / crtc_h;
116
117	/* set drm framebuffer data. */
118	exynos_plane->src_x = src_x;
119	exynos_plane->src_y = src_y;
120	exynos_plane->src_width = (actual_w * exynos_plane->h_ratio) >> 16;
121	exynos_plane->src_height = (actual_h * exynos_plane->v_ratio) >> 16;
122	exynos_plane->fb_width = fb->width;
123	exynos_plane->fb_height = fb->height;
124	exynos_plane->bpp = fb->bits_per_pixel;
125	exynos_plane->pitch = fb->pitches[0];
126	exynos_plane->pixel_format = fb->pixel_format;
127
128	/* set plane range to be displayed. */
129	exynos_plane->crtc_x = crtc_x;
130	exynos_plane->crtc_y = crtc_y;
131	exynos_plane->crtc_width = actual_w;
132	exynos_plane->crtc_height = actual_h;
133
134	/* set drm mode data. */
135	exynos_plane->mode_width = crtc->mode.hdisplay;
136	exynos_plane->mode_height = crtc->mode.vdisplay;
137	exynos_plane->refresh = crtc->mode.vrefresh;
138	exynos_plane->scan_flag = crtc->mode.flags;
139
140	DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
141			exynos_plane->crtc_x, exynos_plane->crtc_y,
142			exynos_plane->crtc_width, exynos_plane->crtc_height);
143
144	plane->crtc = crtc;
145}
146
147int
148exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
149		     struct drm_framebuffer *fb, int crtc_x, int crtc_y,
150		     unsigned int crtc_w, unsigned int crtc_h,
151		     uint32_t src_x, uint32_t src_y,
152		     uint32_t src_w, uint32_t src_h)
153{
154
155	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
156	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
157	int ret;
158
159	ret = exynos_check_plane(plane, fb);
160	if (ret < 0)
161		return ret;
162
163	exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
164			      crtc_w, crtc_h, src_x >> 16, src_y >> 16,
165			      src_w >> 16, src_h >> 16);
166
167	if (exynos_crtc->ops->win_commit)
168		exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
169
170	return 0;
171}
172
173static int exynos_disable_plane(struct drm_plane *plane)
174{
175	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
176	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(plane->crtc);
177
178	if (exynos_crtc && exynos_crtc->ops->win_disable)
179		exynos_crtc->ops->win_disable(exynos_crtc,
180					      exynos_plane->zpos);
181
182	return 0;
183}
184
185static struct drm_plane_funcs exynos_plane_funcs = {
186	.update_plane	= exynos_update_plane,
187	.disable_plane	= exynos_disable_plane,
188	.destroy	= drm_plane_cleanup,
189};
190
191static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
192					      unsigned int zpos)
193{
194	struct drm_device *dev = plane->dev;
195	struct exynos_drm_private *dev_priv = dev->dev_private;
196	struct drm_property *prop;
197
198	prop = dev_priv->plane_zpos_property;
199	if (!prop) {
200		prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
201						 "zpos", 0, MAX_PLANE - 1);
202		if (!prop)
203			return;
204
205		dev_priv->plane_zpos_property = prop;
206	}
207
208	drm_object_attach_property(&plane->base, prop, zpos);
209}
210
211int exynos_plane_init(struct drm_device *dev,
212		      struct exynos_drm_plane *exynos_plane,
213		      unsigned long possible_crtcs, enum drm_plane_type type,
214		      unsigned int zpos)
215{
216	int err;
217
218	err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs,
219				       &exynos_plane_funcs, formats,
220				       ARRAY_SIZE(formats), type);
221	if (err) {
222		DRM_ERROR("failed to initialize plane\n");
223		return err;
224	}
225
226	exynos_plane->zpos = zpos;
227
228	if (type == DRM_PLANE_TYPE_OVERLAY)
229		exynos_plane_attach_zpos_property(&exynos_plane->base, zpos);
230
231	return 0;
232}
233