Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 44struct 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
 53static 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
 63static 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
 71static 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
 83static 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
107static 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
129static 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
145static 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
154static 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
161void 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
219int 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}