brintos

brintos / linux-shallow public Read only

0
0
Text · 26.9 KiB · 03958b7 Raw
1026 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2023 Loongson Technology Corporation Limited4 */5 6#include <linux/debugfs.h>7#include <linux/delay.h>8 9#include <drm/drm_atomic.h>10#include <drm/drm_atomic_helper.h>11#include <drm/drm_debugfs.h>12#include <drm/drm_vblank.h>13 14#include "lsdc_drv.h"15 16/*17 * After the CRTC soft reset, the vblank counter would be reset to zero.18 * But the address and other settings in the CRTC register remain the same19 * as before.20 */21 22static void lsdc_crtc0_soft_reset(struct lsdc_crtc *lcrtc)23{24	struct lsdc_device *ldev = lcrtc->ldev;25	u32 val;26 27	val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);28 29	val &= CFG_VALID_BITS_MASK;30 31	/* Soft reset bit, active low */32	val &= ~CFG_RESET_N;33 34	val &= ~CFG_PIX_FMT_MASK;35 36	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);37 38	udelay(1);39 40	val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE;41 42	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);43 44	/* Wait about a vblank time */45	mdelay(20);46}47 48static void lsdc_crtc1_soft_reset(struct lsdc_crtc *lcrtc)49{50	struct lsdc_device *ldev = lcrtc->ldev;51	u32 val;52 53	val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);54 55	val &= CFG_VALID_BITS_MASK;56 57	/* Soft reset bit, active low */58	val &= ~CFG_RESET_N;59 60	val &= ~CFG_PIX_FMT_MASK;61 62	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);63 64	udelay(1);65 66	val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE;67 68	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);69 70	/* Wait about a vblank time */71	msleep(20);72}73 74static void lsdc_crtc0_enable(struct lsdc_crtc *lcrtc)75{76	struct lsdc_device *ldev = lcrtc->ldev;77	u32 val;78 79	val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);80 81	/*82	 * This may happen in extremely rare cases, but a soft reset can83	 * bring it back to normal. We add a warning here, hoping to catch84	 * something if it happens.85	 */86	if (val & CRTC_ANCHORED) {87		drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name);88		return lsdc_crtc0_soft_reset(lcrtc);89	}90 91	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val | CFG_OUTPUT_ENABLE);92}93 94static void lsdc_crtc0_disable(struct lsdc_crtc *lcrtc)95{96	struct lsdc_device *ldev = lcrtc->ldev;97 98	lsdc_ureg32_clr(ldev, LSDC_CRTC0_CFG_REG, CFG_OUTPUT_ENABLE);99 100	udelay(9);101}102 103static void lsdc_crtc1_enable(struct lsdc_crtc *lcrtc)104{105	struct lsdc_device *ldev = lcrtc->ldev;106	u32 val;107 108	/*109	 * This may happen in extremely rare cases, but a soft reset can110	 * bring it back to normal. We add a warning here, hoping to catch111	 * something if it happens.112	 */113	val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);114	if (val & CRTC_ANCHORED) {115		drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name);116		return lsdc_crtc1_soft_reset(lcrtc);117	}118 119	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val | CFG_OUTPUT_ENABLE);120}121 122static void lsdc_crtc1_disable(struct lsdc_crtc *lcrtc)123{124	struct lsdc_device *ldev = lcrtc->ldev;125 126	lsdc_ureg32_clr(ldev, LSDC_CRTC1_CFG_REG, CFG_OUTPUT_ENABLE);127 128	udelay(9);129}130 131/* All Loongson display controllers have hardware scanout position recoders */132 133static void lsdc_crtc0_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos)134{135	struct lsdc_device *ldev = lcrtc->ldev;136	u32 val;137 138	val = lsdc_rreg32(ldev, LSDC_CRTC0_SCAN_POS_REG);139 140	*hpos = val >> 16;141	*vpos = val & 0xffff;142}143 144static void lsdc_crtc1_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos)145{146	struct lsdc_device *ldev = lcrtc->ldev;147	u32 val;148 149	val = lsdc_rreg32(ldev, LSDC_CRTC1_SCAN_POS_REG);150 151	*hpos = val >> 16;152	*vpos = val & 0xffff;153}154 155static void lsdc_crtc0_enable_vblank(struct lsdc_crtc *lcrtc)156{157	struct lsdc_device *ldev = lcrtc->ldev;158 159	lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN);160}161 162static void lsdc_crtc0_disable_vblank(struct lsdc_crtc *lcrtc)163{164	struct lsdc_device *ldev = lcrtc->ldev;165 166	lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN);167}168 169static void lsdc_crtc1_enable_vblank(struct lsdc_crtc *lcrtc)170{171	struct lsdc_device *ldev = lcrtc->ldev;172 173	lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN);174}175 176static void lsdc_crtc1_disable_vblank(struct lsdc_crtc *lcrtc)177{178	struct lsdc_device *ldev = lcrtc->ldev;179 180	lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN);181}182 183static void lsdc_crtc0_flip(struct lsdc_crtc *lcrtc)184{185	struct lsdc_device *ldev = lcrtc->ldev;186 187	lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_PAGE_FLIP);188}189 190static void lsdc_crtc1_flip(struct lsdc_crtc *lcrtc)191{192	struct lsdc_device *ldev = lcrtc->ldev;193 194	lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_PAGE_FLIP);195}196 197/*198 * CRTC0 clone from CRTC1 or CRTC1 clone from CRTC0 using hardware logic199 * This may be useful for custom cloning (TWIN) applications. Saving the200 * bandwidth compared with the clone (mirroring) display mode provided by201 * drm core.202 */203 204static void lsdc_crtc0_clone(struct lsdc_crtc *lcrtc)205{206	struct lsdc_device *ldev = lcrtc->ldev;207 208	lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_HW_CLONE);209}210 211static void lsdc_crtc1_clone(struct lsdc_crtc *lcrtc)212{213	struct lsdc_device *ldev = lcrtc->ldev;214 215	lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_HW_CLONE);216}217 218static void lsdc_crtc0_set_mode(struct lsdc_crtc *lcrtc,219				const struct drm_display_mode *mode)220{221	struct lsdc_device *ldev = lcrtc->ldev;222 223	lsdc_wreg32(ldev, LSDC_CRTC0_HDISPLAY_REG,224		    (mode->crtc_htotal << 16) | mode->crtc_hdisplay);225 226	lsdc_wreg32(ldev, LSDC_CRTC0_VDISPLAY_REG,227		    (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);228 229	lsdc_wreg32(ldev, LSDC_CRTC0_HSYNC_REG,230		    (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN);231 232	lsdc_wreg32(ldev, LSDC_CRTC0_VSYNC_REG,233		    (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN);234}235 236static void lsdc_crtc1_set_mode(struct lsdc_crtc *lcrtc,237				const struct drm_display_mode *mode)238{239	struct lsdc_device *ldev = lcrtc->ldev;240 241	lsdc_wreg32(ldev, LSDC_CRTC1_HDISPLAY_REG,242		    (mode->crtc_htotal << 16) | mode->crtc_hdisplay);243 244	lsdc_wreg32(ldev, LSDC_CRTC1_VDISPLAY_REG,245		    (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);246 247	lsdc_wreg32(ldev, LSDC_CRTC1_HSYNC_REG,248		    (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN);249 250	lsdc_wreg32(ldev, LSDC_CRTC1_VSYNC_REG,251		    (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN);252}253 254/*255 * This is required for S3 support.256 * After resuming from suspend, LSDC_CRTCx_CFG_REG (x = 0 or 1) is filled257 * with garbage value, which causes the CRTC hang there.258 *259 * This function provides minimal settings for the affected registers.260 * This overrides the firmware's settings on startup, making the CRTC work261 * on our own, similar to the functional of GPU POST (Power On Self Test).262 * Only touch CRTC hardware-related parts.263 */264 265static void lsdc_crtc0_reset(struct lsdc_crtc *lcrtc)266{267	struct lsdc_device *ldev = lcrtc->ldev;268 269	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888);270}271 272static void lsdc_crtc1_reset(struct lsdc_crtc *lcrtc)273{274	struct lsdc_device *ldev = lcrtc->ldev;275 276	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888);277}278 279static const struct lsdc_crtc_hw_ops ls7a1000_crtc_hw_ops[2] = {280	{281		.enable = lsdc_crtc0_enable,282		.disable = lsdc_crtc0_disable,283		.enable_vblank = lsdc_crtc0_enable_vblank,284		.disable_vblank = lsdc_crtc0_disable_vblank,285		.flip = lsdc_crtc0_flip,286		.clone = lsdc_crtc0_clone,287		.set_mode = lsdc_crtc0_set_mode,288		.get_scan_pos = lsdc_crtc0_scan_pos,289		.soft_reset = lsdc_crtc0_soft_reset,290		.reset = lsdc_crtc0_reset,291	},292	{293		.enable = lsdc_crtc1_enable,294		.disable = lsdc_crtc1_disable,295		.enable_vblank = lsdc_crtc1_enable_vblank,296		.disable_vblank = lsdc_crtc1_disable_vblank,297		.flip = lsdc_crtc1_flip,298		.clone = lsdc_crtc1_clone,299		.set_mode = lsdc_crtc1_set_mode,300		.get_scan_pos = lsdc_crtc1_scan_pos,301		.soft_reset = lsdc_crtc1_soft_reset,302		.reset = lsdc_crtc1_reset,303	},304};305 306/*307 * The 32-bit hardware vblank counter has been available since LS7A2000308 * and LS2K2000. The counter increases even though the CRTC is disabled,309 * it will be reset only if the CRTC is being soft reset.310 * Those registers are also readable for ls7a1000, but its value does not311 * change.312 */313 314static u32 lsdc_crtc0_get_vblank_count(struct lsdc_crtc *lcrtc)315{316	struct lsdc_device *ldev = lcrtc->ldev;317 318	return lsdc_rreg32(ldev, LSDC_CRTC0_VSYNC_COUNTER_REG);319}320 321static u32 lsdc_crtc1_get_vblank_count(struct lsdc_crtc *lcrtc)322{323	struct lsdc_device *ldev = lcrtc->ldev;324 325	return lsdc_rreg32(ldev, LSDC_CRTC1_VSYNC_COUNTER_REG);326}327 328/*329 * The DMA step bit fields are available since LS7A2000/LS2K2000, for330 * supporting odd resolutions. But a large DMA step save the bandwidth.331 * The larger, the better. Behavior of writing those bits on LS7A1000332 * or LS2K1000 is underfined.333 */334 335static void lsdc_crtc0_set_dma_step(struct lsdc_crtc *lcrtc,336				    enum lsdc_dma_steps dma_step)337{338	struct lsdc_device *ldev = lcrtc->ldev;339	u32 val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);340 341	val &= ~CFG_DMA_STEP_MASK;342	val |= dma_step << CFG_DMA_STEP_SHIFT;343 344	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);345}346 347static void lsdc_crtc1_set_dma_step(struct lsdc_crtc *lcrtc,348				    enum lsdc_dma_steps dma_step)349{350	struct lsdc_device *ldev = lcrtc->ldev;351	u32 val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);352 353	val &= ~CFG_DMA_STEP_MASK;354	val |= dma_step << CFG_DMA_STEP_SHIFT;355 356	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);357}358 359static const struct lsdc_crtc_hw_ops ls7a2000_crtc_hw_ops[2] = {360	{361		.enable = lsdc_crtc0_enable,362		.disable = lsdc_crtc0_disable,363		.enable_vblank = lsdc_crtc0_enable_vblank,364		.disable_vblank = lsdc_crtc0_disable_vblank,365		.flip = lsdc_crtc0_flip,366		.clone = lsdc_crtc0_clone,367		.set_mode = lsdc_crtc0_set_mode,368		.soft_reset = lsdc_crtc0_soft_reset,369		.get_scan_pos = lsdc_crtc0_scan_pos,370		.set_dma_step = lsdc_crtc0_set_dma_step,371		.get_vblank_counter = lsdc_crtc0_get_vblank_count,372		.reset = lsdc_crtc0_reset,373	},374	{375		.enable = lsdc_crtc1_enable,376		.disable = lsdc_crtc1_disable,377		.enable_vblank = lsdc_crtc1_enable_vblank,378		.disable_vblank = lsdc_crtc1_disable_vblank,379		.flip = lsdc_crtc1_flip,380		.clone = lsdc_crtc1_clone,381		.set_mode = lsdc_crtc1_set_mode,382		.get_scan_pos = lsdc_crtc1_scan_pos,383		.soft_reset = lsdc_crtc1_soft_reset,384		.set_dma_step = lsdc_crtc1_set_dma_step,385		.get_vblank_counter = lsdc_crtc1_get_vblank_count,386		.reset = lsdc_crtc1_reset,387	},388};389 390static void lsdc_crtc_reset(struct drm_crtc *crtc)391{392	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);393	const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;394	struct lsdc_crtc_state *priv_crtc_state;395 396	if (crtc->state)397		crtc->funcs->atomic_destroy_state(crtc, crtc->state);398 399	priv_crtc_state = kzalloc(sizeof(*priv_crtc_state), GFP_KERNEL);400 401	if (!priv_crtc_state)402		__drm_atomic_helper_crtc_reset(crtc, NULL);403	else404		__drm_atomic_helper_crtc_reset(crtc, &priv_crtc_state->base);405 406	/* Reset the CRTC hardware, this is required for S3 support */407	ops->reset(lcrtc);408}409 410static void lsdc_crtc_atomic_destroy_state(struct drm_crtc *crtc,411					   struct drm_crtc_state *state)412{413	struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);414 415	__drm_atomic_helper_crtc_destroy_state(&priv_state->base);416 417	kfree(priv_state);418}419 420static struct drm_crtc_state *421lsdc_crtc_atomic_duplicate_state(struct drm_crtc *crtc)422{423	struct lsdc_crtc_state *new_priv_state;424	struct lsdc_crtc_state *old_priv_state;425 426	new_priv_state = kzalloc(sizeof(*new_priv_state), GFP_KERNEL);427	if (!new_priv_state)428		return NULL;429 430	__drm_atomic_helper_crtc_duplicate_state(crtc, &new_priv_state->base);431 432	old_priv_state = to_lsdc_crtc_state(crtc->state);433 434	memcpy(&new_priv_state->pparms, &old_priv_state->pparms,435	       sizeof(new_priv_state->pparms));436 437	return &new_priv_state->base;438}439 440static u32 lsdc_crtc_get_vblank_counter(struct drm_crtc *crtc)441{442	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);443 444	/* 32-bit hardware vblank counter */445	return lcrtc->hw_ops->get_vblank_counter(lcrtc);446}447 448static int lsdc_crtc_enable_vblank(struct drm_crtc *crtc)449{450	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);451 452	if (!lcrtc->has_vblank)453		return -EINVAL;454 455	lcrtc->hw_ops->enable_vblank(lcrtc);456 457	return 0;458}459 460static void lsdc_crtc_disable_vblank(struct drm_crtc *crtc)461{462	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);463 464	if (!lcrtc->has_vblank)465		return;466 467	lcrtc->hw_ops->disable_vblank(lcrtc);468}469 470/*471 * CRTC related debugfs472 * Primary planes and cursor planes belong to the CRTC as well.473 * For the sake of convenience, plane-related registers are also add here.474 */475 476#define REG_DEF(reg) { \477	.name = __stringify_1(LSDC_##reg##_REG), \478	.offset = LSDC_##reg##_REG, \479}480 481static const struct lsdc_reg32 lsdc_crtc_regs_array[2][21] = {482	[0] = {483		REG_DEF(CRTC0_CFG),484		REG_DEF(CRTC0_FB_ORIGIN),485		REG_DEF(CRTC0_DVO_CONF),486		REG_DEF(CRTC0_HDISPLAY),487		REG_DEF(CRTC0_HSYNC),488		REG_DEF(CRTC0_VDISPLAY),489		REG_DEF(CRTC0_VSYNC),490		REG_DEF(CRTC0_GAMMA_INDEX),491		REG_DEF(CRTC0_GAMMA_DATA),492		REG_DEF(CRTC0_SYNC_DEVIATION),493		REG_DEF(CRTC0_VSYNC_COUNTER),494		REG_DEF(CRTC0_SCAN_POS),495		REG_DEF(CRTC0_STRIDE),496		REG_DEF(CRTC0_FB1_ADDR_HI),497		REG_DEF(CRTC0_FB1_ADDR_LO),498		REG_DEF(CRTC0_FB0_ADDR_HI),499		REG_DEF(CRTC0_FB0_ADDR_LO),500		REG_DEF(CURSOR0_CFG),501		REG_DEF(CURSOR0_POSITION),502		REG_DEF(CURSOR0_BG_COLOR),503		REG_DEF(CURSOR0_FG_COLOR),504	},505	[1] = {506		REG_DEF(CRTC1_CFG),507		REG_DEF(CRTC1_FB_ORIGIN),508		REG_DEF(CRTC1_DVO_CONF),509		REG_DEF(CRTC1_HDISPLAY),510		REG_DEF(CRTC1_HSYNC),511		REG_DEF(CRTC1_VDISPLAY),512		REG_DEF(CRTC1_VSYNC),513		REG_DEF(CRTC1_GAMMA_INDEX),514		REG_DEF(CRTC1_GAMMA_DATA),515		REG_DEF(CRTC1_SYNC_DEVIATION),516		REG_DEF(CRTC1_VSYNC_COUNTER),517		REG_DEF(CRTC1_SCAN_POS),518		REG_DEF(CRTC1_STRIDE),519		REG_DEF(CRTC1_FB1_ADDR_HI),520		REG_DEF(CRTC1_FB1_ADDR_LO),521		REG_DEF(CRTC1_FB0_ADDR_HI),522		REG_DEF(CRTC1_FB0_ADDR_LO),523		REG_DEF(CURSOR1_CFG),524		REG_DEF(CURSOR1_POSITION),525		REG_DEF(CURSOR1_BG_COLOR),526		REG_DEF(CURSOR1_FG_COLOR),527	},528};529 530static int lsdc_crtc_show_regs(struct seq_file *m, void *arg)531{532	struct drm_info_node *node = (struct drm_info_node *)m->private;533	struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;534	struct lsdc_device *ldev = lcrtc->ldev;535	unsigned int i;536 537	for (i = 0; i < lcrtc->nreg; i++) {538		const struct lsdc_reg32 *preg = &lcrtc->preg[i];539		u32 offset = preg->offset;540 541		seq_printf(m, "%s (0x%04x): 0x%08x\n",542			   preg->name, offset, lsdc_rreg32(ldev, offset));543	}544 545	return 0;546}547 548static int lsdc_crtc_show_scan_position(struct seq_file *m, void *arg)549{550	struct drm_info_node *node = (struct drm_info_node *)m->private;551	struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;552	int x, y;553 554	lcrtc->hw_ops->get_scan_pos(lcrtc, &x, &y);555	seq_printf(m, "Scanout position: x: %08u, y: %08u\n", x, y);556 557	return 0;558}559 560static int lsdc_crtc_show_vblank_counter(struct seq_file *m, void *arg)561{562	struct drm_info_node *node = (struct drm_info_node *)m->private;563	struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;564 565	if (lcrtc->hw_ops->get_vblank_counter)566		seq_printf(m, "%s vblank counter: %08u\n\n", lcrtc->base.name,567			   lcrtc->hw_ops->get_vblank_counter(lcrtc));568 569	return 0;570}571 572static int lsdc_pixpll_show_clock(struct seq_file *m, void *arg)573{574	struct drm_info_node *node = (struct drm_info_node *)m->private;575	struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;576	struct lsdc_pixpll *pixpll = &lcrtc->pixpll;577	const struct lsdc_pixpll_funcs *funcs = pixpll->funcs;578	struct drm_crtc *crtc = &lcrtc->base;579	struct drm_display_mode *mode = &crtc->state->mode;580	struct drm_printer printer = drm_seq_file_printer(m);581	unsigned int out_khz;582 583	out_khz = funcs->get_rate(pixpll);584 585	seq_printf(m, "%s: %dx%d@%d\n", crtc->name,586		   mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode));587 588	seq_printf(m, "Pixel clock required: %d kHz\n", mode->clock);589	seq_printf(m, "Actual frequency output: %u kHz\n", out_khz);590	seq_printf(m, "Diff: %d kHz\n", out_khz - mode->clock);591 592	funcs->print(pixpll, &printer);593 594	return 0;595}596 597static struct drm_info_list lsdc_crtc_debugfs_list[2][4] = {598	[0] = {599		{ "regs", lsdc_crtc_show_regs, 0, NULL },600		{ "pixclk", lsdc_pixpll_show_clock, 0, NULL },601		{ "scanpos", lsdc_crtc_show_scan_position, 0, NULL },602		{ "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL },603	},604	[1] = {605		{ "regs", lsdc_crtc_show_regs, 0, NULL },606		{ "pixclk", lsdc_pixpll_show_clock, 0, NULL },607		{ "scanpos", lsdc_crtc_show_scan_position, 0, NULL },608		{ "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL },609	},610};611 612/* operate manually */613 614static int lsdc_crtc_man_op_show(struct seq_file *m, void *data)615{616	seq_puts(m, "soft_reset: soft reset this CRTC\n");617	seq_puts(m, "enable: enable this CRTC\n");618	seq_puts(m, "disable: disable this CRTC\n");619	seq_puts(m, "flip: trigger the page flip\n");620	seq_puts(m, "clone: clone the another crtc with hardware logic\n");621 622	return 0;623}624 625static int lsdc_crtc_man_op_open(struct inode *inode, struct file *file)626{627	struct drm_crtc *crtc = inode->i_private;628 629	return single_open(file, lsdc_crtc_man_op_show, crtc);630}631 632static ssize_t lsdc_crtc_man_op_write(struct file *file,633				      const char __user *ubuf,634				      size_t len,635				      loff_t *offp)636{637	struct seq_file *m = file->private_data;638	struct lsdc_crtc *lcrtc = m->private;639	const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;640	char buf[16];641 642	if (len > sizeof(buf) - 1)643		return -EINVAL;644 645	if (copy_from_user(buf, ubuf, len))646		return -EFAULT;647 648	buf[len] = '\0';649 650	if (sysfs_streq(buf, "soft_reset"))651		ops->soft_reset(lcrtc);652	else if (sysfs_streq(buf, "enable"))653		ops->enable(lcrtc);654	else if (sysfs_streq(buf, "disable"))655		ops->disable(lcrtc);656	else if (sysfs_streq(buf, "flip"))657		ops->flip(lcrtc);658	else if (sysfs_streq(buf, "clone"))659		ops->clone(lcrtc);660 661	return len;662}663 664static const struct file_operations lsdc_crtc_man_op_fops = {665	.owner = THIS_MODULE,666	.open = lsdc_crtc_man_op_open,667	.read = seq_read,668	.llseek = seq_lseek,669	.release = single_release,670	.write = lsdc_crtc_man_op_write,671};672 673static int lsdc_crtc_late_register(struct drm_crtc *crtc)674{675	struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc);676	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);677	struct drm_minor *minor = crtc->dev->primary;678	unsigned int index = dispipe->index;679	unsigned int i;680 681	lcrtc->preg = lsdc_crtc_regs_array[index];682	lcrtc->nreg = ARRAY_SIZE(lsdc_crtc_regs_array[index]);683	lcrtc->p_info_list = lsdc_crtc_debugfs_list[index];684	lcrtc->n_info_list = ARRAY_SIZE(lsdc_crtc_debugfs_list[index]);685 686	for (i = 0; i < lcrtc->n_info_list; ++i)687		lcrtc->p_info_list[i].data = lcrtc;688 689	drm_debugfs_create_files(lcrtc->p_info_list, lcrtc->n_info_list,690				 crtc->debugfs_entry, minor);691 692	/* Manual operations supported */693	debugfs_create_file("ops", 0644, crtc->debugfs_entry, lcrtc,694			    &lsdc_crtc_man_op_fops);695 696	return 0;697}698 699static void lsdc_crtc_atomic_print_state(struct drm_printer *p,700					 const struct drm_crtc_state *state)701{702	const struct lsdc_crtc_state *priv_state;703	const struct lsdc_pixpll_parms *pparms;704 705	priv_state = container_of_const(state, struct lsdc_crtc_state, base);706	pparms = &priv_state->pparms;707 708	drm_printf(p, "\tInput clock divider = %u\n", pparms->div_ref);709	drm_printf(p, "\tMedium clock multiplier = %u\n", pparms->loopc);710	drm_printf(p, "\tOutput clock divider = %u\n", pparms->div_out);711}712 713static const struct drm_crtc_funcs ls7a1000_crtc_funcs = {714	.reset = lsdc_crtc_reset,715	.destroy = drm_crtc_cleanup,716	.set_config = drm_atomic_helper_set_config,717	.page_flip = drm_atomic_helper_page_flip,718	.atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state,719	.atomic_destroy_state = lsdc_crtc_atomic_destroy_state,720	.late_register = lsdc_crtc_late_register,721	.enable_vblank = lsdc_crtc_enable_vblank,722	.disable_vblank = lsdc_crtc_disable_vblank,723	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,724	.atomic_print_state = lsdc_crtc_atomic_print_state,725};726 727static const struct drm_crtc_funcs ls7a2000_crtc_funcs = {728	.reset = lsdc_crtc_reset,729	.destroy = drm_crtc_cleanup,730	.set_config = drm_atomic_helper_set_config,731	.page_flip = drm_atomic_helper_page_flip,732	.atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state,733	.atomic_destroy_state = lsdc_crtc_atomic_destroy_state,734	.late_register = lsdc_crtc_late_register,735	.get_vblank_counter = lsdc_crtc_get_vblank_counter,736	.enable_vblank = lsdc_crtc_enable_vblank,737	.disable_vblank = lsdc_crtc_disable_vblank,738	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,739	.atomic_print_state = lsdc_crtc_atomic_print_state,740};741 742static enum drm_mode_status743lsdc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)744{745	struct drm_device *ddev = crtc->dev;746	struct lsdc_device *ldev = to_lsdc(ddev);747	const struct lsdc_desc *descp = ldev->descp;748	unsigned int pitch;749 750	if (mode->hdisplay > descp->max_width)751		return MODE_BAD_HVALUE;752 753	if (mode->vdisplay > descp->max_height)754		return MODE_BAD_VVALUE;755 756	if (mode->clock > descp->max_pixel_clk) {757		drm_dbg_kms(ddev, "mode %dx%d, pixel clock=%d is too high\n",758			    mode->hdisplay, mode->vdisplay, mode->clock);759		return MODE_CLOCK_HIGH;760	}761 762	/* 4 for DRM_FORMAT_XRGB8888 */763	pitch = mode->hdisplay * 4;764 765	if (pitch % descp->pitch_align) {766		drm_dbg_kms(ddev, "align to %u bytes is required: %u\n",767			    descp->pitch_align, pitch);768		return MODE_BAD_WIDTH;769	}770 771	return MODE_OK;772}773 774static int lsdc_pixpll_atomic_check(struct drm_crtc *crtc,775				    struct drm_crtc_state *state)776{777	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);778	struct lsdc_pixpll *pixpll = &lcrtc->pixpll;779	const struct lsdc_pixpll_funcs *pfuncs = pixpll->funcs;780	struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);781	unsigned int clock = state->mode.clock;782	int ret;783 784	ret = pfuncs->compute(pixpll, clock, &priv_state->pparms);785	if (ret) {786		drm_warn(crtc->dev, "Failed to find PLL params for %ukHz\n",787			 clock);788		return -EINVAL;789	}790 791	return 0;792}793 794static int lsdc_crtc_helper_atomic_check(struct drm_crtc *crtc,795					 struct drm_atomic_state *state)796{797	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);798 799	if (!crtc_state->enable)800		return 0;801 802	return lsdc_pixpll_atomic_check(crtc, crtc_state);803}804 805static void lsdc_crtc_mode_set_nofb(struct drm_crtc *crtc)806{807	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);808	const struct lsdc_crtc_hw_ops *crtc_hw_ops = lcrtc->hw_ops;809	struct lsdc_pixpll *pixpll = &lcrtc->pixpll;810	const struct lsdc_pixpll_funcs *pixpll_funcs = pixpll->funcs;811	struct drm_crtc_state *state = crtc->state;812	struct drm_display_mode *mode = &state->mode;813	struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);814 815	pixpll_funcs->update(pixpll, &priv_state->pparms);816 817	if (crtc_hw_ops->set_dma_step) {818		unsigned int width_in_bytes = mode->hdisplay * 4;819		enum lsdc_dma_steps dma_step;820 821		/*822		 * Using DMA step as large as possible, for improving823		 * hardware DMA efficiency.824		 */825		if (width_in_bytes % 256 == 0)826			dma_step = LSDC_DMA_STEP_256_BYTES;827		else if (width_in_bytes % 128 == 0)828			dma_step = LSDC_DMA_STEP_128_BYTES;829		else if (width_in_bytes % 64 == 0)830			dma_step = LSDC_DMA_STEP_64_BYTES;831		else  /* width_in_bytes % 32 == 0 */832			dma_step = LSDC_DMA_STEP_32_BYTES;833 834		crtc_hw_ops->set_dma_step(lcrtc, dma_step);835	}836 837	crtc_hw_ops->set_mode(lcrtc, mode);838}839 840static void lsdc_crtc_send_vblank(struct drm_crtc *crtc)841{842	struct drm_device *ddev = crtc->dev;843	unsigned long flags;844 845	if (!crtc->state || !crtc->state->event)846		return;847 848	drm_dbg(ddev, "Send vblank manually\n");849 850	spin_lock_irqsave(&ddev->event_lock, flags);851	drm_crtc_send_vblank_event(crtc, crtc->state->event);852	crtc->state->event = NULL;853	spin_unlock_irqrestore(&ddev->event_lock, flags);854}855 856static void lsdc_crtc_atomic_enable(struct drm_crtc *crtc,857				    struct drm_atomic_state *state)858{859	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);860 861	if (lcrtc->has_vblank)862		drm_crtc_vblank_on(crtc);863 864	lcrtc->hw_ops->enable(lcrtc);865}866 867static void lsdc_crtc_atomic_disable(struct drm_crtc *crtc,868				     struct drm_atomic_state *state)869{870	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);871 872	if (lcrtc->has_vblank)873		drm_crtc_vblank_off(crtc);874 875	lcrtc->hw_ops->disable(lcrtc);876 877	/*878	 * Make sure we issue a vblank event after disabling the CRTC if879	 * someone was waiting it.880	 */881	lsdc_crtc_send_vblank(crtc);882}883 884static void lsdc_crtc_atomic_flush(struct drm_crtc *crtc,885				   struct drm_atomic_state *state)886{887	spin_lock_irq(&crtc->dev->event_lock);888	if (crtc->state->event) {889		if (drm_crtc_vblank_get(crtc) == 0)890			drm_crtc_arm_vblank_event(crtc, crtc->state->event);891		else892			drm_crtc_send_vblank_event(crtc, crtc->state->event);893		crtc->state->event = NULL;894	}895	spin_unlock_irq(&crtc->dev->event_lock);896}897 898static bool lsdc_crtc_get_scanout_position(struct drm_crtc *crtc,899					   bool in_vblank_irq,900					   int *vpos,901					   int *hpos,902					   ktime_t *stime,903					   ktime_t *etime,904					   const struct drm_display_mode *mode)905{906	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);907	const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;908	int vsw, vbp, vactive_start, vactive_end, vfp_end;909	int x, y;910 911	vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;912	vbp = mode->crtc_vtotal - mode->crtc_vsync_end;913 914	vactive_start = vsw + vbp + 1;915	vactive_end = vactive_start + mode->crtc_vdisplay;916 917	/* last scan line before VSYNC */918	vfp_end = mode->crtc_vtotal;919 920	if (stime)921		*stime = ktime_get();922 923	ops->get_scan_pos(lcrtc, &x, &y);924 925	if (y > vactive_end)926		y = y - vfp_end - vactive_start;927	else928		y -= vactive_start;929 930	*vpos = y;931	*hpos = 0;932 933	if (etime)934		*etime = ktime_get();935 936	return true;937}938 939static const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = {940	.mode_valid = lsdc_crtc_mode_valid,941	.mode_set_nofb = lsdc_crtc_mode_set_nofb,942	.atomic_enable = lsdc_crtc_atomic_enable,943	.atomic_disable = lsdc_crtc_atomic_disable,944	.atomic_check = lsdc_crtc_helper_atomic_check,945	.atomic_flush = lsdc_crtc_atomic_flush,946	.get_scanout_position = lsdc_crtc_get_scanout_position,947};948 949int ls7a1000_crtc_init(struct drm_device *ddev,950		       struct drm_crtc *crtc,951		       struct drm_plane *primary,952		       struct drm_plane *cursor,953		       unsigned int index,954		       bool has_vblank)955{956	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);957	int ret;958 959	ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index);960	if (ret) {961		drm_err(ddev, "pixel pll init failed: %d\n", ret);962		return ret;963	}964 965	lcrtc->ldev = to_lsdc(ddev);966	lcrtc->has_vblank = has_vblank;967	lcrtc->hw_ops = &ls7a1000_crtc_hw_ops[index];968 969	ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor,970					&ls7a1000_crtc_funcs,971					"LS-CRTC-%d", index);972	if (ret) {973		drm_err(ddev, "crtc init with planes failed: %d\n", ret);974		return ret;975	}976 977	drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs);978 979	ret = drm_mode_crtc_set_gamma_size(crtc, 256);980	if (ret)981		return ret;982 983	drm_crtc_enable_color_mgmt(crtc, 0, false, 256);984 985	return 0;986}987 988int ls7a2000_crtc_init(struct drm_device *ddev,989		       struct drm_crtc *crtc,990		       struct drm_plane *primary,991		       struct drm_plane *cursor,992		       unsigned int index,993		       bool has_vblank)994{995	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);996	int ret;997 998	ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index);999	if (ret) {1000		drm_err(ddev, "crtc init with pll failed: %d\n", ret);1001		return ret;1002	}1003 1004	lcrtc->ldev = to_lsdc(ddev);1005	lcrtc->has_vblank = has_vblank;1006	lcrtc->hw_ops = &ls7a2000_crtc_hw_ops[index];1007 1008	ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor,1009					&ls7a2000_crtc_funcs,1010					"LS-CRTC-%u", index);1011	if (ret) {1012		drm_err(ddev, "crtc init with planes failed: %d\n", ret);1013		return ret;1014	}1015 1016	drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs);1017 1018	ret = drm_mode_crtc_set_gamma_size(crtc, 256);1019	if (ret)1020		return ret;1021 1022	drm_crtc_enable_color_mgmt(crtc, 0, false, 256);1023 1024	return 0;1025}1026