brintos

brintos / linux-shallow public Read only

0
0
Text · 11.8 KiB · 94f8e31 Raw
451 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/4 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>5 */6 7#include <drm/drm_atomic.h>8#include <drm/drm_atomic_helper.h>9#include <drm/drm_crtc.h>10#include <drm/drm_gem_dma_helper.h>11#include <drm/drm_vblank.h>12 13#include "tidss_crtc.h"14#include "tidss_dispc.h"15#include "tidss_drv.h"16#include "tidss_irq.h"17#include "tidss_plane.h"18 19/* Page flip and frame done IRQs */20 21static void tidss_crtc_finish_page_flip(struct tidss_crtc *tcrtc)22{23	struct drm_device *ddev = tcrtc->crtc.dev;24	struct tidss_device *tidss = to_tidss(ddev);25	struct drm_pending_vblank_event *event;26	unsigned long flags;27	bool busy;28 29	spin_lock_irqsave(&ddev->event_lock, flags);30 31	/*32	 * New settings are taken into use at VFP, and GO bit is cleared at33	 * the same time. This happens before the vertical blank interrupt.34	 * So there is a small change that the driver sets GO bit after VFP, but35	 * before vblank, and we have to check for that case here.36	 */37	busy = dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport);38	if (busy) {39		spin_unlock_irqrestore(&ddev->event_lock, flags);40		return;41	}42 43	event = tcrtc->event;44	tcrtc->event = NULL;45 46	if (!event) {47		spin_unlock_irqrestore(&ddev->event_lock, flags);48		return;49	}50 51	drm_crtc_send_vblank_event(&tcrtc->crtc, event);52 53	spin_unlock_irqrestore(&ddev->event_lock, flags);54 55	drm_crtc_vblank_put(&tcrtc->crtc);56}57 58void tidss_crtc_vblank_irq(struct drm_crtc *crtc)59{60	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);61 62	drm_crtc_handle_vblank(crtc);63 64	tidss_crtc_finish_page_flip(tcrtc);65}66 67void tidss_crtc_framedone_irq(struct drm_crtc *crtc)68{69	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);70 71	complete(&tcrtc->framedone_completion);72}73 74void tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus)75{76	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);77 78	dev_err_ratelimited(crtc->dev->dev, "CRTC%u SYNC LOST: (irq %llx)\n",79			    tcrtc->hw_videoport, irqstatus);80}81 82/* drm_crtc_helper_funcs */83 84static int tidss_crtc_atomic_check(struct drm_crtc *crtc,85				   struct drm_atomic_state *state)86{87	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,88									  crtc);89	struct drm_device *ddev = crtc->dev;90	struct tidss_device *tidss = to_tidss(ddev);91	struct dispc_device *dispc = tidss->dispc;92	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);93	u32 hw_videoport = tcrtc->hw_videoport;94	const struct drm_display_mode *mode;95	enum drm_mode_status ok;96 97	dev_dbg(ddev->dev, "%s\n", __func__);98 99	if (!crtc_state->enable)100		return 0;101 102	mode = &crtc_state->adjusted_mode;103 104	ok = dispc_vp_mode_valid(dispc, hw_videoport, mode);105	if (ok != MODE_OK) {106		dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n",107			__func__, mode->hdisplay, mode->vdisplay, mode->clock);108		return -EINVAL;109	}110 111	return dispc_vp_bus_check(dispc, hw_videoport, crtc_state);112}113 114/*115 * This needs all affected planes to be present in the atomic116 * state. The untouched planes are added to the state in117 * tidss_atomic_check().118 */119static void tidss_crtc_position_planes(struct tidss_device *tidss,120				       struct drm_crtc *crtc,121				       struct drm_crtc_state *old_state,122				       bool newmodeset)123{124	struct drm_atomic_state *ostate = old_state->state;125	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);126	struct drm_crtc_state *cstate = crtc->state;127	int layer;128 129	if (!newmodeset && !cstate->zpos_changed &&130	    !to_tidss_crtc_state(cstate)->plane_pos_changed)131		return;132 133	for (layer = 0; layer < tidss->feat->num_planes; layer++) {134		struct drm_plane_state *pstate;135		struct drm_plane *plane;136		bool layer_active = false;137		int i;138 139		for_each_new_plane_in_state(ostate, plane, pstate, i) {140			if (pstate->crtc != crtc || !pstate->visible)141				continue;142 143			if (pstate->normalized_zpos == layer) {144				layer_active = true;145				break;146			}147		}148 149		if (layer_active) {150			struct tidss_plane *tplane = to_tidss_plane(plane);151 152			dispc_ovr_set_plane(tidss->dispc, tplane->hw_plane_id,153					    tcrtc->hw_videoport,154					    pstate->crtc_x, pstate->crtc_y,155					    layer);156		}157		dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer,158				       layer_active);159	}160}161 162static void tidss_crtc_atomic_flush(struct drm_crtc *crtc,163				    struct drm_atomic_state *state)164{165	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,166									      crtc);167	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);168	struct drm_device *ddev = crtc->dev;169	struct tidss_device *tidss = to_tidss(ddev);170	unsigned long flags;171 172	dev_dbg(ddev->dev, "%s: %s is %sactive, %s modeset, event %p\n",173		__func__, crtc->name, crtc->state->active ? "" : "not ",174		drm_atomic_crtc_needs_modeset(crtc->state) ? "needs" : "doesn't need",175		crtc->state->event);176 177	/*178	 * Flush CRTC changes with go bit only if new modeset is not179	 * coming, so CRTC is enabled trough out the commit.180	 */181	if (drm_atomic_crtc_needs_modeset(crtc->state))182		return;183 184	/* If the GO bit is stuck we better quit here. */185	if (WARN_ON(dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport)))186		return;187 188	/* We should have event if CRTC is enabled through out this commit. */189	if (WARN_ON(!crtc->state->event))190		return;191 192	/* Write vp properties to HW if needed. */193	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, false);194 195	/* Update plane positions if needed. */196	tidss_crtc_position_planes(tidss, crtc, old_crtc_state, false);197 198	WARN_ON(drm_crtc_vblank_get(crtc) != 0);199 200	spin_lock_irqsave(&ddev->event_lock, flags);201	dispc_vp_go(tidss->dispc, tcrtc->hw_videoport);202 203	WARN_ON(tcrtc->event);204 205	tcrtc->event = crtc->state->event;206	crtc->state->event = NULL;207 208	spin_unlock_irqrestore(&ddev->event_lock, flags);209}210 211static void tidss_crtc_atomic_enable(struct drm_crtc *crtc,212				     struct drm_atomic_state *state)213{214	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,215									 crtc);216	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);217	struct drm_device *ddev = crtc->dev;218	struct tidss_device *tidss = to_tidss(ddev);219	const struct drm_display_mode *mode = &crtc->state->adjusted_mode;220	unsigned long flags;221	int r;222 223	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);224 225	tidss_runtime_get(tidss);226 227	r = dispc_vp_set_clk_rate(tidss->dispc, tcrtc->hw_videoport,228				  mode->clock * 1000);229	if (r != 0)230		return;231 232	r = dispc_vp_enable_clk(tidss->dispc, tcrtc->hw_videoport);233	if (r != 0)234		return;235 236	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, true);237	tidss_crtc_position_planes(tidss, crtc, old_state, true);238 239	/* Turn vertical blanking interrupt reporting on. */240	drm_crtc_vblank_on(crtc);241 242	dispc_vp_prepare(tidss->dispc, tcrtc->hw_videoport, crtc->state);243 244	dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport, crtc->state);245 246	spin_lock_irqsave(&ddev->event_lock, flags);247 248	if (crtc->state->event) {249		drm_crtc_send_vblank_event(crtc, crtc->state->event);250		crtc->state->event = NULL;251	}252 253	spin_unlock_irqrestore(&ddev->event_lock, flags);254}255 256static void tidss_crtc_atomic_disable(struct drm_crtc *crtc,257				      struct drm_atomic_state *state)258{259	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);260	struct drm_device *ddev = crtc->dev;261	struct tidss_device *tidss = to_tidss(ddev);262	unsigned long flags;263 264	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);265 266	reinit_completion(&tcrtc->framedone_completion);267 268	/*269	 * If a layer is left enabled when the videoport is disabled, and the270	 * vid pipeline that was used for the layer is taken into use on271	 * another videoport, the DSS will report sync lost issues. Disable all272	 * the layers here as a work-around.273	 */274	for (u32 layer = 0; layer < tidss->feat->num_planes; layer++)275		dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer,276				       false);277 278	dispc_vp_disable(tidss->dispc, tcrtc->hw_videoport);279 280	if (!wait_for_completion_timeout(&tcrtc->framedone_completion,281					 msecs_to_jiffies(500)))282		dev_err(tidss->dev, "Timeout waiting for framedone on crtc %d",283			tcrtc->hw_videoport);284 285	dispc_vp_unprepare(tidss->dispc, tcrtc->hw_videoport);286 287	spin_lock_irqsave(&ddev->event_lock, flags);288	if (crtc->state->event) {289		drm_crtc_send_vblank_event(crtc, crtc->state->event);290		crtc->state->event = NULL;291	}292	spin_unlock_irqrestore(&ddev->event_lock, flags);293 294	drm_crtc_vblank_off(crtc);295 296	dispc_vp_disable_clk(tidss->dispc, tcrtc->hw_videoport);297 298	tidss_runtime_put(tidss);299}300 301static302enum drm_mode_status tidss_crtc_mode_valid(struct drm_crtc *crtc,303					   const struct drm_display_mode *mode)304{305	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);306	struct drm_device *ddev = crtc->dev;307	struct tidss_device *tidss = to_tidss(ddev);308 309	return dispc_vp_mode_valid(tidss->dispc, tcrtc->hw_videoport, mode);310}311 312static const struct drm_crtc_helper_funcs tidss_crtc_helper_funcs = {313	.atomic_check = tidss_crtc_atomic_check,314	.atomic_flush = tidss_crtc_atomic_flush,315	.atomic_enable = tidss_crtc_atomic_enable,316	.atomic_disable = tidss_crtc_atomic_disable,317 318	.mode_valid = tidss_crtc_mode_valid,319};320 321/* drm_crtc_funcs */322 323static int tidss_crtc_enable_vblank(struct drm_crtc *crtc)324{325	struct drm_device *ddev = crtc->dev;326	struct tidss_device *tidss = to_tidss(ddev);327 328	dev_dbg(ddev->dev, "%s\n", __func__);329 330	tidss_runtime_get(tidss);331 332	tidss_irq_enable_vblank(crtc);333 334	return 0;335}336 337static void tidss_crtc_disable_vblank(struct drm_crtc *crtc)338{339	struct drm_device *ddev = crtc->dev;340	struct tidss_device *tidss = to_tidss(ddev);341 342	dev_dbg(ddev->dev, "%s\n", __func__);343 344	tidss_irq_disable_vblank(crtc);345 346	tidss_runtime_put(tidss);347}348 349static void tidss_crtc_reset(struct drm_crtc *crtc)350{351	struct tidss_crtc_state *tcrtc;352 353	if (crtc->state)354		__drm_atomic_helper_crtc_destroy_state(crtc->state);355 356	kfree(crtc->state);357 358	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);359	if (!tcrtc) {360		crtc->state = NULL;361		return;362	}363 364	__drm_atomic_helper_crtc_reset(crtc, &tcrtc->base);365}366 367static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)368{369	struct tidss_crtc_state *state, *current_state;370 371	if (WARN_ON(!crtc->state))372		return NULL;373 374	current_state = to_tidss_crtc_state(crtc->state);375 376	state = kmalloc(sizeof(*state), GFP_KERNEL);377	if (!state)378		return NULL;379 380	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);381 382	state->plane_pos_changed = false;383 384	state->bus_format = current_state->bus_format;385	state->bus_flags = current_state->bus_flags;386 387	return &state->base;388}389 390static void tidss_crtc_destroy(struct drm_crtc *crtc)391{392	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);393 394	drm_crtc_cleanup(crtc);395	kfree(tcrtc);396}397 398static const struct drm_crtc_funcs tidss_crtc_funcs = {399	.reset = tidss_crtc_reset,400	.destroy = tidss_crtc_destroy,401	.set_config = drm_atomic_helper_set_config,402	.page_flip = drm_atomic_helper_page_flip,403	.atomic_duplicate_state = tidss_crtc_duplicate_state,404	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,405	.enable_vblank = tidss_crtc_enable_vblank,406	.disable_vblank = tidss_crtc_disable_vblank,407};408 409struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss,410				     u32 hw_videoport,411				     struct drm_plane *primary)412{413	struct tidss_crtc *tcrtc;414	struct drm_crtc *crtc;415	unsigned int gamma_lut_size = 0;416	bool has_ctm = tidss->feat->vp_feat.color.has_ctm;417	int ret;418 419	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);420	if (!tcrtc)421		return ERR_PTR(-ENOMEM);422 423	tcrtc->hw_videoport = hw_videoport;424	init_completion(&tcrtc->framedone_completion);425 426	crtc =  &tcrtc->crtc;427 428	ret = drm_crtc_init_with_planes(&tidss->ddev, crtc, primary,429					NULL, &tidss_crtc_funcs, NULL);430	if (ret < 0) {431		kfree(tcrtc);432		return ERR_PTR(ret);433	}434 435	drm_crtc_helper_add(crtc, &tidss_crtc_helper_funcs);436 437	/*438	 * The dispc gamma functions adapt to what ever size we ask439	 * from it no matter what HW supports. X-server assumes 256440	 * element gamma tables so lets use that.441	 */442	if (tidss->feat->vp_feat.color.gamma_size)443		gamma_lut_size = 256;444 445	drm_crtc_enable_color_mgmt(crtc, 0, has_ctm, gamma_lut_size);446	if (gamma_lut_size)447		drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size);448 449	return tcrtc;450}451