brintos

brintos / linux-shallow public Read only

0
0
Text · 10.8 KiB · c4c1474 Raw
429 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * RZ/G2L Display Unit CRTCs4 *5 * Copyright (C) 2023 Renesas Electronics Corporation6 *7 * Based on rcar_du_crtc.c8 */9 10#include <linux/clk.h>11#include <linux/mutex.h>12#include <linux/platform_device.h>13#include <linux/reset.h>14 15#include <drm/drm_atomic.h>16#include <drm/drm_atomic_helper.h>17#include <drm/drm_bridge.h>18#include <drm/drm_crtc.h>19#include <drm/drm_device.h>20#include <drm/drm_framebuffer.h>21#include <drm/drm_gem_dma_helper.h>22#include <drm/drm_vblank.h>23 24#include "rzg2l_du_crtc.h"25#include "rzg2l_du_drv.h"26#include "rzg2l_du_encoder.h"27#include "rzg2l_du_kms.h"28#include "rzg2l_du_vsp.h"29 30#define DU_MCR0			0x0031#define DU_MCR0_DPI_OE		BIT(0)32#define DU_MCR0_DI_EN		BIT(8)33 34#define DU_DITR0		0x1035#define DU_DITR0_DEMD_HIGH	(BIT(8) | BIT(9))36#define DU_DITR0_VSPOL		BIT(16)37#define DU_DITR0_HSPOL		BIT(17)38 39#define DU_DITR1		0x1440#define DU_DITR1_VSA(x)		((x) << 0)41#define DU_DITR1_VACTIVE(x)	((x) << 16)42 43#define DU_DITR2		0x1844#define DU_DITR2_VBP(x)		((x) << 0)45#define DU_DITR2_VFP(x)		((x) << 16)46 47#define DU_DITR3		0x1c48#define DU_DITR3_HSA(x)		((x) << 0)49#define DU_DITR3_HACTIVE(x)	((x) << 16)50 51#define DU_DITR4		0x2052#define DU_DITR4_HBP(x)		((x) << 0)53#define DU_DITR4_HFP(x)		((x) << 16)54 55#define DU_MCR1			0x4056#define DU_MCR1_PB_AUTOCLR	BIT(16)57 58#define DU_PBCR0		0x4c59#define DU_PBCR0_PB_DEP(x)	((x) << 0)60 61/* -----------------------------------------------------------------------------62 * Hardware Setup63 */64 65static void rzg2l_du_crtc_set_display_timing(struct rzg2l_du_crtc *rcrtc)66{67	const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;68	unsigned long mode_clock = mode->clock * 1000;69	u32 ditr0, ditr1, ditr2, ditr3, ditr4, pbcr0;70	struct rzg2l_du_device *rcdu = rcrtc->dev;71 72	clk_prepare_enable(rcrtc->rzg2l_clocks.dclk);73	clk_set_rate(rcrtc->rzg2l_clocks.dclk, mode_clock);74 75	ditr0 = (DU_DITR0_DEMD_HIGH76	      | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DU_DITR0_VSPOL : 0)77	      | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DU_DITR0_HSPOL : 0));78 79	ditr1 = DU_DITR1_VSA(mode->vsync_end - mode->vsync_start)80	      | DU_DITR1_VACTIVE(mode->vdisplay);81 82	ditr2 = DU_DITR2_VBP(mode->vtotal - mode->vsync_end)83	      | DU_DITR2_VFP(mode->vsync_start - mode->vdisplay);84 85	ditr3 = DU_DITR3_HSA(mode->hsync_end - mode->hsync_start)86	      | DU_DITR3_HACTIVE(mode->hdisplay);87 88	ditr4 = DU_DITR4_HBP(mode->htotal - mode->hsync_end)89	      | DU_DITR4_HFP(mode->hsync_start - mode->hdisplay);90 91	pbcr0 = DU_PBCR0_PB_DEP(0x1f);92 93	writel(ditr0, rcdu->mmio + DU_DITR0);94	writel(ditr1, rcdu->mmio + DU_DITR1);95	writel(ditr2, rcdu->mmio + DU_DITR2);96	writel(ditr3, rcdu->mmio + DU_DITR3);97	writel(ditr4, rcdu->mmio + DU_DITR4);98	writel(pbcr0, rcdu->mmio + DU_PBCR0);99 100	/* Enable auto clear */101	writel(DU_MCR1_PB_AUTOCLR, rcdu->mmio + DU_MCR1);102}103 104/* -----------------------------------------------------------------------------105 * Page Flip106 */107 108void rzg2l_du_crtc_finish_page_flip(struct rzg2l_du_crtc *rcrtc)109{110	struct drm_pending_vblank_event *event;111	struct drm_device *dev = rcrtc->crtc.dev;112	unsigned long flags;113 114	spin_lock_irqsave(&dev->event_lock, flags);115	event = rcrtc->event;116	rcrtc->event = NULL;117	spin_unlock_irqrestore(&dev->event_lock, flags);118 119	if (!event)120		return;121 122	spin_lock_irqsave(&dev->event_lock, flags);123	drm_crtc_send_vblank_event(&rcrtc->crtc, event);124	wake_up(&rcrtc->flip_wait);125	spin_unlock_irqrestore(&dev->event_lock, flags);126 127	drm_crtc_vblank_put(&rcrtc->crtc);128}129 130static bool rzg2l_du_crtc_page_flip_pending(struct rzg2l_du_crtc *rcrtc)131{132	struct drm_device *dev = rcrtc->crtc.dev;133	unsigned long flags;134	bool pending;135 136	spin_lock_irqsave(&dev->event_lock, flags);137	pending = rcrtc->event;138	spin_unlock_irqrestore(&dev->event_lock, flags);139 140	return pending;141}142 143static void rzg2l_du_crtc_wait_page_flip(struct rzg2l_du_crtc *rcrtc)144{145	struct rzg2l_du_device *rcdu = rcrtc->dev;146 147	if (wait_event_timeout(rcrtc->flip_wait,148			       !rzg2l_du_crtc_page_flip_pending(rcrtc),149			       msecs_to_jiffies(50)))150		return;151 152	dev_warn(rcdu->dev, "page flip timeout\n");153 154	rzg2l_du_crtc_finish_page_flip(rcrtc);155}156 157/* -----------------------------------------------------------------------------158 * Start/Stop and Suspend/Resume159 */160 161static void rzg2l_du_crtc_setup(struct rzg2l_du_crtc *rcrtc)162{163	/* Configure display timings and output routing */164	rzg2l_du_crtc_set_display_timing(rcrtc);165 166	/* Enable the VSP compositor. */167	rzg2l_du_vsp_enable(rcrtc);168 169	/* Turn vertical blanking interrupt reporting on. */170	drm_crtc_vblank_on(&rcrtc->crtc);171}172 173static int rzg2l_du_crtc_get(struct rzg2l_du_crtc *rcrtc)174{175	int ret;176 177	/*178	 * Guard against double-get, as the function is called from both the179	 * .atomic_enable() and .atomic_flush() handlers.180	 */181	if (rcrtc->initialized)182		return 0;183 184	ret = clk_prepare_enable(rcrtc->rzg2l_clocks.aclk);185	if (ret < 0)186		return ret;187 188	ret = clk_prepare_enable(rcrtc->rzg2l_clocks.pclk);189	if (ret < 0)190		goto error_bus_clock;191 192	ret = reset_control_deassert(rcrtc->rstc);193	if (ret < 0)194		goto error_peri_clock;195 196	rzg2l_du_crtc_setup(rcrtc);197	rcrtc->initialized = true;198 199	return 0;200 201error_peri_clock:202	clk_disable_unprepare(rcrtc->rzg2l_clocks.pclk);203error_bus_clock:204	clk_disable_unprepare(rcrtc->rzg2l_clocks.aclk);205	return ret;206}207 208static void rzg2l_du_crtc_put(struct rzg2l_du_crtc *rcrtc)209{210	clk_disable_unprepare(rcrtc->rzg2l_clocks.dclk);211	reset_control_assert(rcrtc->rstc);212	clk_disable_unprepare(rcrtc->rzg2l_clocks.pclk);213	clk_disable_unprepare(rcrtc->rzg2l_clocks.aclk);214 215	rcrtc->initialized = false;216}217 218static void rzg2l_du_start_stop(struct rzg2l_du_crtc *rcrtc, bool start)219{220	struct rzg2l_du_crtc_state *rstate = to_rzg2l_crtc_state(rcrtc->crtc.state);221	struct rzg2l_du_device *rcdu = rcrtc->dev;222	u32 val = DU_MCR0_DI_EN;223 224	if (rstate->outputs & BIT(RZG2L_DU_OUTPUT_DPAD0))225		val |= DU_MCR0_DPI_OE;226 227	writel(start ? val : 0, rcdu->mmio + DU_MCR0);228}229 230static void rzg2l_du_crtc_start(struct rzg2l_du_crtc *rcrtc)231{232	rzg2l_du_start_stop(rcrtc, true);233}234 235static void rzg2l_du_crtc_stop(struct rzg2l_du_crtc *rcrtc)236{237	struct drm_crtc *crtc = &rcrtc->crtc;238 239	/*240	 * Disable vertical blanking interrupt reporting. We first need to wait241	 * for page flip completion before stopping the CRTC as userspace242	 * expects page flips to eventually complete.243	 */244	rzg2l_du_crtc_wait_page_flip(rcrtc);245	drm_crtc_vblank_off(crtc);246 247	/* Disable the VSP compositor. */248	rzg2l_du_vsp_disable(rcrtc);249 250	rzg2l_du_start_stop(rcrtc, false);251}252 253/* -----------------------------------------------------------------------------254 * CRTC Functions255 */256 257static void rzg2l_du_crtc_atomic_enable(struct drm_crtc *crtc,258					struct drm_atomic_state *state)259{260	struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);261 262	rzg2l_du_crtc_get(rcrtc);263 264	rzg2l_du_crtc_start(rcrtc);265}266 267static void rzg2l_du_crtc_atomic_disable(struct drm_crtc *crtc,268					 struct drm_atomic_state *state)269{270	struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);271 272	rzg2l_du_crtc_stop(rcrtc);273	rzg2l_du_crtc_put(rcrtc);274 275	spin_lock_irq(&crtc->dev->event_lock);276	if (crtc->state->event) {277		drm_crtc_send_vblank_event(crtc, crtc->state->event);278		crtc->state->event = NULL;279	}280	spin_unlock_irq(&crtc->dev->event_lock);281}282 283static void rzg2l_du_crtc_atomic_flush(struct drm_crtc *crtc,284				       struct drm_atomic_state *state)285{286	struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);287	struct drm_device *dev = rcrtc->crtc.dev;288	unsigned long flags;289 290	WARN_ON(!crtc->state->enable);291 292	if (crtc->state->event) {293		WARN_ON(drm_crtc_vblank_get(crtc) != 0);294 295		spin_lock_irqsave(&dev->event_lock, flags);296		rcrtc->event = crtc->state->event;297		crtc->state->event = NULL;298		spin_unlock_irqrestore(&dev->event_lock, flags);299	}300 301	rzg2l_du_vsp_atomic_flush(rcrtc);302}303 304static const struct drm_crtc_helper_funcs crtc_helper_funcs = {305	.atomic_flush = rzg2l_du_crtc_atomic_flush,306	.atomic_enable = rzg2l_du_crtc_atomic_enable,307	.atomic_disable = rzg2l_du_crtc_atomic_disable,308};309 310static struct drm_crtc_state *311rzg2l_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc)312{313	struct rzg2l_du_crtc_state *state;314	struct rzg2l_du_crtc_state *copy;315 316	if (WARN_ON(!crtc->state))317		return NULL;318 319	state = to_rzg2l_crtc_state(crtc->state);320	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);321	if (!copy)322		return NULL;323 324	__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->state);325 326	return &copy->state;327}328 329static void rzg2l_du_crtc_atomic_destroy_state(struct drm_crtc *crtc,330					       struct drm_crtc_state *state)331{332	__drm_atomic_helper_crtc_destroy_state(state);333	kfree(to_rzg2l_crtc_state(state));334}335 336static void rzg2l_du_crtc_reset(struct drm_crtc *crtc)337{338	struct rzg2l_du_crtc_state *state;339 340	if (crtc->state) {341		rzg2l_du_crtc_atomic_destroy_state(crtc, crtc->state);342		crtc->state = NULL;343	}344 345	state = kzalloc(sizeof(*state), GFP_KERNEL);346	if (!state)347		return;348 349	__drm_atomic_helper_crtc_reset(crtc, &state->state);350}351 352static int rzg2l_du_crtc_enable_vblank(struct drm_crtc *crtc)353{354	struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);355 356	rcrtc->vblank_enable = true;357 358	return 0;359}360 361static void rzg2l_du_crtc_disable_vblank(struct drm_crtc *crtc)362{363	struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);364 365	rcrtc->vblank_enable = false;366}367 368static const struct drm_crtc_funcs crtc_funcs_rz = {369	.reset = rzg2l_du_crtc_reset,370	.set_config = drm_atomic_helper_set_config,371	.page_flip = drm_atomic_helper_page_flip,372	.atomic_duplicate_state = rzg2l_du_crtc_atomic_duplicate_state,373	.atomic_destroy_state = rzg2l_du_crtc_atomic_destroy_state,374	.enable_vblank = rzg2l_du_crtc_enable_vblank,375	.disable_vblank = rzg2l_du_crtc_disable_vblank,376};377 378/* -----------------------------------------------------------------------------379 * Initialization380 */381 382int rzg2l_du_crtc_create(struct rzg2l_du_device *rcdu)383{384	struct rzg2l_du_crtc *rcrtc = &rcdu->crtcs[0];385	struct drm_crtc *crtc = &rcrtc->crtc;386	struct drm_plane *primary;387	int ret;388 389	rcrtc->rstc = devm_reset_control_get_shared(rcdu->dev, NULL);390	if (IS_ERR(rcrtc->rstc)) {391		dev_err(rcdu->dev, "can't get cpg reset\n");392		return PTR_ERR(rcrtc->rstc);393	}394 395	rcrtc->rzg2l_clocks.aclk = devm_clk_get(rcdu->dev, "aclk");396	if (IS_ERR(rcrtc->rzg2l_clocks.aclk)) {397		dev_err(rcdu->dev, "no axi clock for DU\n");398		return PTR_ERR(rcrtc->rzg2l_clocks.aclk);399	}400 401	rcrtc->rzg2l_clocks.pclk = devm_clk_get(rcdu->dev, "pclk");402	if (IS_ERR(rcrtc->rzg2l_clocks.pclk)) {403		dev_err(rcdu->dev, "no peripheral clock for DU\n");404		return PTR_ERR(rcrtc->rzg2l_clocks.pclk);405	}406 407	rcrtc->rzg2l_clocks.dclk = devm_clk_get(rcdu->dev, "vclk");408	if (IS_ERR(rcrtc->rzg2l_clocks.dclk)) {409		dev_err(rcdu->dev, "no video clock for DU\n");410		return PTR_ERR(rcrtc->rzg2l_clocks.dclk);411	}412 413	init_waitqueue_head(&rcrtc->flip_wait);414	rcrtc->dev = rcdu;415 416	primary = rzg2l_du_vsp_get_drm_plane(rcrtc, rcrtc->vsp_pipe);417	if (IS_ERR(primary))418		return PTR_ERR(primary);419 420	ret = drmm_crtc_init_with_planes(&rcdu->ddev, crtc, primary, NULL,421					 &crtc_funcs_rz, NULL);422	if (ret < 0)423		return ret;424 425	drm_crtc_helper_add(crtc, &crtc_helper_funcs);426 427	return 0;428}429