xref: /openbmc/linux/drivers/gpu/drm/meson/meson_crtc.c (revision e3d786a3)
1 /*
2  * Copyright (C) 2016 BayLibre, SAS
3  * Author: Neil Armstrong <narmstrong@baylibre.com>
4  * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
5  * Copyright (C) 2014 Endless Mobile
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  * Written by:
21  *     Jasper St. Pierre <jstpierre@mecheye.net>
22  */
23 
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/mutex.h>
27 #include <linux/platform_device.h>
28 #include <drm/drmP.h>
29 #include <drm/drm_atomic.h>
30 #include <drm/drm_atomic_helper.h>
31 #include <drm/drm_flip_work.h>
32 #include <drm/drm_crtc_helper.h>
33 
34 #include "meson_crtc.h"
35 #include "meson_plane.h"
36 #include "meson_venc.h"
37 #include "meson_vpp.h"
38 #include "meson_viu.h"
39 #include "meson_canvas.h"
40 #include "meson_registers.h"
41 
42 /* CRTC definition */
43 
44 struct meson_crtc {
45 	struct drm_crtc base;
46 	struct drm_pending_vblank_event *event;
47 	struct meson_drm *priv;
48 };
49 #define to_meson_crtc(x) container_of(x, struct meson_crtc, base)
50 
51 /* CRTC */
52 
53 static int meson_crtc_enable_vblank(struct drm_crtc *crtc)
54 {
55 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
56 	struct meson_drm *priv = meson_crtc->priv;
57 
58 	meson_venc_enable_vsync(priv);
59 
60 	return 0;
61 }
62 
63 static void meson_crtc_disable_vblank(struct drm_crtc *crtc)
64 {
65 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
66 	struct meson_drm *priv = meson_crtc->priv;
67 
68 	meson_venc_disable_vsync(priv);
69 }
70 
71 static const struct drm_crtc_funcs meson_crtc_funcs = {
72 	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
73 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
74 	.destroy		= drm_crtc_cleanup,
75 	.page_flip		= drm_atomic_helper_page_flip,
76 	.reset			= drm_atomic_helper_crtc_reset,
77 	.set_config             = drm_atomic_helper_set_config,
78 	.enable_vblank		= meson_crtc_enable_vblank,
79 	.disable_vblank		= meson_crtc_disable_vblank,
80 
81 };
82 
83 static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
84 				     struct drm_crtc_state *old_state)
85 {
86 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
87 	struct drm_crtc_state *crtc_state = crtc->state;
88 	struct meson_drm *priv = meson_crtc->priv;
89 
90 	DRM_DEBUG_DRIVER("\n");
91 
92 	if (!crtc_state) {
93 		DRM_ERROR("Invalid crtc_state\n");
94 		return;
95 	}
96 
97 	/* Enable VPP Postblend */
98 	writel(crtc_state->mode.hdisplay,
99 	       priv->io_base + _REG(VPP_POSTBLEND_H_SIZE));
100 
101 	writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE,
102 			    priv->io_base + _REG(VPP_MISC));
103 
104 	priv->viu.osd1_enabled = true;
105 }
106 
107 static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
108 				      struct drm_crtc_state *old_state)
109 {
110 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
111 	struct meson_drm *priv = meson_crtc->priv;
112 
113 	priv->viu.osd1_enabled = false;
114 	priv->viu.osd1_commit = false;
115 
116 	/* Disable VPP Postblend */
117 	writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0,
118 			    priv->io_base + _REG(VPP_MISC));
119 
120 	if (crtc->state->event && !crtc->state->active) {
121 		spin_lock_irq(&crtc->dev->event_lock);
122 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
123 		spin_unlock_irq(&crtc->dev->event_lock);
124 
125 		crtc->state->event = NULL;
126 	}
127 }
128 
129 static void meson_crtc_atomic_begin(struct drm_crtc *crtc,
130 				    struct drm_crtc_state *state)
131 {
132 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
133 	unsigned long flags;
134 
135 	if (crtc->state->event) {
136 		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
137 
138 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
139 		meson_crtc->event = crtc->state->event;
140 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
141 		crtc->state->event = NULL;
142 	}
143 }
144 
145 static void meson_crtc_atomic_flush(struct drm_crtc *crtc,
146 				    struct drm_crtc_state *old_crtc_state)
147 {
148 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
149 	struct meson_drm *priv = meson_crtc->priv;
150 
151 	priv->viu.osd1_commit = true;
152 }
153 
154 static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = {
155 	.atomic_begin	= meson_crtc_atomic_begin,
156 	.atomic_flush	= meson_crtc_atomic_flush,
157 	.atomic_enable	= meson_crtc_atomic_enable,
158 	.atomic_disable	= meson_crtc_atomic_disable,
159 };
160 
161 void meson_crtc_irq(struct meson_drm *priv)
162 {
163 	struct meson_crtc *meson_crtc = to_meson_crtc(priv->crtc);
164 	unsigned long flags;
165 
166 	/* Update the OSD registers */
167 	if (priv->viu.osd1_enabled && priv->viu.osd1_commit) {
168 		writel_relaxed(priv->viu.osd1_ctrl_stat,
169 				priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
170 		writel_relaxed(priv->viu.osd1_blk0_cfg[0],
171 				priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0));
172 		writel_relaxed(priv->viu.osd1_blk0_cfg[1],
173 				priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W1));
174 		writel_relaxed(priv->viu.osd1_blk0_cfg[2],
175 				priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W2));
176 		writel_relaxed(priv->viu.osd1_blk0_cfg[3],
177 				priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3));
178 		writel_relaxed(priv->viu.osd1_blk0_cfg[4],
179 				priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4));
180 
181 		/* If output is interlace, make use of the Scaler */
182 		if (priv->viu.osd1_interlace) {
183 			struct drm_plane *plane = priv->primary_plane;
184 			struct drm_plane_state *state = plane->state;
185 			struct drm_rect dest = {
186 				.x1 = state->crtc_x,
187 				.y1 = state->crtc_y,
188 				.x2 = state->crtc_x + state->crtc_w,
189 				.y2 = state->crtc_y + state->crtc_h,
190 			};
191 
192 			meson_vpp_setup_interlace_vscaler_osd1(priv, &dest);
193 		} else
194 			meson_vpp_disable_interlace_vscaler_osd1(priv);
195 
196 		meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1,
197 			   priv->viu.osd1_addr, priv->viu.osd1_stride,
198 			   priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE,
199 			   MESON_CANVAS_BLKMODE_LINEAR);
200 
201 		/* Enable OSD1 */
202 		writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND,
203 				    priv->io_base + _REG(VPP_MISC));
204 
205 		priv->viu.osd1_commit = false;
206 	}
207 
208 	drm_crtc_handle_vblank(priv->crtc);
209 
210 	spin_lock_irqsave(&priv->drm->event_lock, flags);
211 	if (meson_crtc->event) {
212 		drm_crtc_send_vblank_event(priv->crtc, meson_crtc->event);
213 		drm_crtc_vblank_put(priv->crtc);
214 		meson_crtc->event = NULL;
215 	}
216 	spin_unlock_irqrestore(&priv->drm->event_lock, flags);
217 }
218 
219 int meson_crtc_create(struct meson_drm *priv)
220 {
221 	struct meson_crtc *meson_crtc;
222 	struct drm_crtc *crtc;
223 	int ret;
224 
225 	meson_crtc = devm_kzalloc(priv->drm->dev, sizeof(*meson_crtc),
226 				  GFP_KERNEL);
227 	if (!meson_crtc)
228 		return -ENOMEM;
229 
230 	meson_crtc->priv = priv;
231 	crtc = &meson_crtc->base;
232 	ret = drm_crtc_init_with_planes(priv->drm, crtc,
233 					priv->primary_plane, NULL,
234 					&meson_crtc_funcs, "meson_crtc");
235 	if (ret) {
236 		dev_err(priv->drm->dev, "Failed to init CRTC\n");
237 		return ret;
238 	}
239 
240 	drm_crtc_helper_add(crtc, &meson_crtc_helper_funcs);
241 
242 	priv->crtc = crtc;
243 
244 	return 0;
245 }
246