162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2023 Loongson Technology Corporation Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <drm/drm_atomic.h>
962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1062306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
1162306a36Sopenharmony_ci#include <drm/drm_vblank.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "lsdc_drv.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * After the CRTC soft reset, the vblank counter would be reset to zero.
1762306a36Sopenharmony_ci * But the address and other settings in the CRTC register remain the same
1862306a36Sopenharmony_ci * as before.
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void lsdc_crtc0_soft_reset(struct lsdc_crtc *lcrtc)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
2462306a36Sopenharmony_ci	u32 val;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	val &= CFG_VALID_BITS_MASK;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	/* Soft reset bit, active low */
3162306a36Sopenharmony_ci	val &= ~CFG_RESET_N;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	val &= ~CFG_PIX_FMT_MASK;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	udelay(1);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Wait about a vblank time */
4462306a36Sopenharmony_ci	mdelay(20);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic void lsdc_crtc1_soft_reset(struct lsdc_crtc *lcrtc)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
5062306a36Sopenharmony_ci	u32 val;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	val &= CFG_VALID_BITS_MASK;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Soft reset bit, active low */
5762306a36Sopenharmony_ci	val &= ~CFG_RESET_N;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	val &= ~CFG_PIX_FMT_MASK;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	udelay(1);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* Wait about a vblank time */
7062306a36Sopenharmony_ci	msleep(20);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void lsdc_crtc0_enable(struct lsdc_crtc *lcrtc)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
7662306a36Sopenharmony_ci	u32 val;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci	 * This may happen in extremely rare cases, but a soft reset can
8262306a36Sopenharmony_ci	 * bring it back to normal. We add a warning here, hoping to catch
8362306a36Sopenharmony_ci	 * something if it happens.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	if (val & CRTC_ANCHORED) {
8662306a36Sopenharmony_ci		drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name);
8762306a36Sopenharmony_ci		return lsdc_crtc0_soft_reset(lcrtc);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val | CFG_OUTPUT_ENABLE);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void lsdc_crtc0_disable(struct lsdc_crtc *lcrtc)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	lsdc_ureg32_clr(ldev, LSDC_CRTC0_CFG_REG, CFG_OUTPUT_ENABLE);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	udelay(9);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void lsdc_crtc1_enable(struct lsdc_crtc *lcrtc)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
10562306a36Sopenharmony_ci	u32 val;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/*
10862306a36Sopenharmony_ci	 * This may happen in extremely rare cases, but a soft reset can
10962306a36Sopenharmony_ci	 * bring it back to normal. We add a warning here, hoping to catch
11062306a36Sopenharmony_ci	 * something if it happens.
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);
11362306a36Sopenharmony_ci	if (val & CRTC_ANCHORED) {
11462306a36Sopenharmony_ci		drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name);
11562306a36Sopenharmony_ci		return lsdc_crtc1_soft_reset(lcrtc);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val | CFG_OUTPUT_ENABLE);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void lsdc_crtc1_disable(struct lsdc_crtc *lcrtc)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	lsdc_ureg32_clr(ldev, LSDC_CRTC1_CFG_REG, CFG_OUTPUT_ENABLE);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	udelay(9);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* All Loongson display controllers have hardware scanout position recoders */
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void lsdc_crtc0_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
13562306a36Sopenharmony_ci	u32 val;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	val = lsdc_rreg32(ldev, LSDC_CRTC0_SCAN_POS_REG);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	*hpos = val >> 16;
14062306a36Sopenharmony_ci	*vpos = val & 0xffff;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void lsdc_crtc1_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
14662306a36Sopenharmony_ci	u32 val;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	val = lsdc_rreg32(ldev, LSDC_CRTC1_SCAN_POS_REG);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	*hpos = val >> 16;
15162306a36Sopenharmony_ci	*vpos = val & 0xffff;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void lsdc_crtc0_enable_vblank(struct lsdc_crtc *lcrtc)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void lsdc_crtc0_disable_vblank(struct lsdc_crtc *lcrtc)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void lsdc_crtc1_enable_vblank(struct lsdc_crtc *lcrtc)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void lsdc_crtc1_disable_vblank(struct lsdc_crtc *lcrtc)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void lsdc_crtc0_flip(struct lsdc_crtc *lcrtc)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_PAGE_FLIP);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic void lsdc_crtc1_flip(struct lsdc_crtc *lcrtc)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_PAGE_FLIP);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/*
19762306a36Sopenharmony_ci * CRTC0 clone from CRTC1 or CRTC1 clone from CRTC0 using hardware logic
19862306a36Sopenharmony_ci * This may be useful for custom cloning (TWIN) applications. Saving the
19962306a36Sopenharmony_ci * bandwidth compared with the clone (mirroring) display mode provided by
20062306a36Sopenharmony_ci * drm core.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void lsdc_crtc0_clone(struct lsdc_crtc *lcrtc)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_HW_CLONE);
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void lsdc_crtc1_clone(struct lsdc_crtc *lcrtc)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_HW_CLONE);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void lsdc_crtc0_set_mode(struct lsdc_crtc *lcrtc,
21862306a36Sopenharmony_ci				const struct drm_display_mode *mode)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_HDISPLAY_REG,
22362306a36Sopenharmony_ci		    (mode->crtc_htotal << 16) | mode->crtc_hdisplay);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_VDISPLAY_REG,
22662306a36Sopenharmony_ci		    (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_HSYNC_REG,
22962306a36Sopenharmony_ci		    (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_VSYNC_REG,
23262306a36Sopenharmony_ci		    (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void lsdc_crtc1_set_mode(struct lsdc_crtc *lcrtc,
23662306a36Sopenharmony_ci				const struct drm_display_mode *mode)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_HDISPLAY_REG,
24162306a36Sopenharmony_ci		    (mode->crtc_htotal << 16) | mode->crtc_hdisplay);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_VDISPLAY_REG,
24462306a36Sopenharmony_ci		    (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_HSYNC_REG,
24762306a36Sopenharmony_ci		    (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_VSYNC_REG,
25062306a36Sopenharmony_ci		    (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/*
25462306a36Sopenharmony_ci * This is required for S3 support.
25562306a36Sopenharmony_ci * After resuming from suspend, LSDC_CRTCx_CFG_REG (x = 0 or 1) is filled
25662306a36Sopenharmony_ci * with garbage value, which causes the CRTC hang there.
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci * This function provides minimal settings for the affected registers.
25962306a36Sopenharmony_ci * This overrides the firmware's settings on startup, making the CRTC work
26062306a36Sopenharmony_ci * on our own, similar to the functional of GPU POST (Power On Self Test).
26162306a36Sopenharmony_ci * Only touch CRTC hardware-related parts.
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void lsdc_crtc0_reset(struct lsdc_crtc *lcrtc)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void lsdc_crtc1_reset(struct lsdc_crtc *lcrtc)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic const struct lsdc_crtc_hw_ops ls7a1000_crtc_hw_ops[2] = {
27962306a36Sopenharmony_ci	{
28062306a36Sopenharmony_ci		.enable = lsdc_crtc0_enable,
28162306a36Sopenharmony_ci		.disable = lsdc_crtc0_disable,
28262306a36Sopenharmony_ci		.enable_vblank = lsdc_crtc0_enable_vblank,
28362306a36Sopenharmony_ci		.disable_vblank = lsdc_crtc0_disable_vblank,
28462306a36Sopenharmony_ci		.flip = lsdc_crtc0_flip,
28562306a36Sopenharmony_ci		.clone = lsdc_crtc0_clone,
28662306a36Sopenharmony_ci		.set_mode = lsdc_crtc0_set_mode,
28762306a36Sopenharmony_ci		.get_scan_pos = lsdc_crtc0_scan_pos,
28862306a36Sopenharmony_ci		.soft_reset = lsdc_crtc0_soft_reset,
28962306a36Sopenharmony_ci		.reset = lsdc_crtc0_reset,
29062306a36Sopenharmony_ci	},
29162306a36Sopenharmony_ci	{
29262306a36Sopenharmony_ci		.enable = lsdc_crtc1_enable,
29362306a36Sopenharmony_ci		.disable = lsdc_crtc1_disable,
29462306a36Sopenharmony_ci		.enable_vblank = lsdc_crtc1_enable_vblank,
29562306a36Sopenharmony_ci		.disable_vblank = lsdc_crtc1_disable_vblank,
29662306a36Sopenharmony_ci		.flip = lsdc_crtc1_flip,
29762306a36Sopenharmony_ci		.clone = lsdc_crtc1_clone,
29862306a36Sopenharmony_ci		.set_mode = lsdc_crtc1_set_mode,
29962306a36Sopenharmony_ci		.get_scan_pos = lsdc_crtc1_scan_pos,
30062306a36Sopenharmony_ci		.soft_reset = lsdc_crtc1_soft_reset,
30162306a36Sopenharmony_ci		.reset = lsdc_crtc1_reset,
30262306a36Sopenharmony_ci	},
30362306a36Sopenharmony_ci};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/*
30662306a36Sopenharmony_ci * The 32-bit hardware vblank counter has been available since LS7A2000
30762306a36Sopenharmony_ci * and LS2K2000. The counter increases even though the CRTC is disabled,
30862306a36Sopenharmony_ci * it will be reset only if the CRTC is being soft reset.
30962306a36Sopenharmony_ci * Those registers are also readable for ls7a1000, but its value does not
31062306a36Sopenharmony_ci * change.
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic u32 lsdc_crtc0_get_vblank_count(struct lsdc_crtc *lcrtc)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	return lsdc_rreg32(ldev, LSDC_CRTC0_VSYNC_COUNTER_REG);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic u32 lsdc_crtc1_get_vblank_count(struct lsdc_crtc *lcrtc)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return lsdc_rreg32(ldev, LSDC_CRTC1_VSYNC_COUNTER_REG);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/*
32862306a36Sopenharmony_ci * The DMA step bit fields are available since LS7A2000/LS2K2000, for
32962306a36Sopenharmony_ci * supporting odd resolutions. But a large DMA step save the bandwidth.
33062306a36Sopenharmony_ci * The larger, the better. Behavior of writing those bits on LS7A1000
33162306a36Sopenharmony_ci * or LS2K1000 is underfined.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void lsdc_crtc0_set_dma_step(struct lsdc_crtc *lcrtc,
33562306a36Sopenharmony_ci				    enum lsdc_dma_steps dma_step)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
33862306a36Sopenharmony_ci	u32 val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	val &= ~CFG_DMA_STEP_MASK;
34162306a36Sopenharmony_ci	val |= dma_step << CFG_DMA_STEP_SHIFT;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void lsdc_crtc1_set_dma_step(struct lsdc_crtc *lcrtc,
34762306a36Sopenharmony_ci				    enum lsdc_dma_steps dma_step)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
35062306a36Sopenharmony_ci	u32 val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	val &= ~CFG_DMA_STEP_MASK;
35362306a36Sopenharmony_ci	val |= dma_step << CFG_DMA_STEP_SHIFT;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic const struct lsdc_crtc_hw_ops ls7a2000_crtc_hw_ops[2] = {
35962306a36Sopenharmony_ci	{
36062306a36Sopenharmony_ci		.enable = lsdc_crtc0_enable,
36162306a36Sopenharmony_ci		.disable = lsdc_crtc0_disable,
36262306a36Sopenharmony_ci		.enable_vblank = lsdc_crtc0_enable_vblank,
36362306a36Sopenharmony_ci		.disable_vblank = lsdc_crtc0_disable_vblank,
36462306a36Sopenharmony_ci		.flip = lsdc_crtc0_flip,
36562306a36Sopenharmony_ci		.clone = lsdc_crtc0_clone,
36662306a36Sopenharmony_ci		.set_mode = lsdc_crtc0_set_mode,
36762306a36Sopenharmony_ci		.soft_reset = lsdc_crtc0_soft_reset,
36862306a36Sopenharmony_ci		.get_scan_pos = lsdc_crtc0_scan_pos,
36962306a36Sopenharmony_ci		.set_dma_step = lsdc_crtc0_set_dma_step,
37062306a36Sopenharmony_ci		.get_vblank_counter = lsdc_crtc0_get_vblank_count,
37162306a36Sopenharmony_ci		.reset = lsdc_crtc0_reset,
37262306a36Sopenharmony_ci	},
37362306a36Sopenharmony_ci	{
37462306a36Sopenharmony_ci		.enable = lsdc_crtc1_enable,
37562306a36Sopenharmony_ci		.disable = lsdc_crtc1_disable,
37662306a36Sopenharmony_ci		.enable_vblank = lsdc_crtc1_enable_vblank,
37762306a36Sopenharmony_ci		.disable_vblank = lsdc_crtc1_disable_vblank,
37862306a36Sopenharmony_ci		.flip = lsdc_crtc1_flip,
37962306a36Sopenharmony_ci		.clone = lsdc_crtc1_clone,
38062306a36Sopenharmony_ci		.set_mode = lsdc_crtc1_set_mode,
38162306a36Sopenharmony_ci		.get_scan_pos = lsdc_crtc1_scan_pos,
38262306a36Sopenharmony_ci		.soft_reset = lsdc_crtc1_soft_reset,
38362306a36Sopenharmony_ci		.set_dma_step = lsdc_crtc1_set_dma_step,
38462306a36Sopenharmony_ci		.get_vblank_counter = lsdc_crtc1_get_vblank_count,
38562306a36Sopenharmony_ci		.reset = lsdc_crtc1_reset,
38662306a36Sopenharmony_ci	},
38762306a36Sopenharmony_ci};
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic void lsdc_crtc_reset(struct drm_crtc *crtc)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
39262306a36Sopenharmony_ci	const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;
39362306a36Sopenharmony_ci	struct lsdc_crtc_state *priv_crtc_state;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (crtc->state)
39662306a36Sopenharmony_ci		crtc->funcs->atomic_destroy_state(crtc, crtc->state);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	priv_crtc_state = kzalloc(sizeof(*priv_crtc_state), GFP_KERNEL);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (!priv_crtc_state)
40162306a36Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, NULL);
40262306a36Sopenharmony_ci	else
40362306a36Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, &priv_crtc_state->base);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Reset the CRTC hardware, this is required for S3 support */
40662306a36Sopenharmony_ci	ops->reset(lcrtc);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic void lsdc_crtc_atomic_destroy_state(struct drm_crtc *crtc,
41062306a36Sopenharmony_ci					   struct drm_crtc_state *state)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	__drm_atomic_helper_crtc_destroy_state(&priv_state->base);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	kfree(priv_state);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic struct drm_crtc_state *
42062306a36Sopenharmony_cilsdc_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct lsdc_crtc_state *new_priv_state;
42362306a36Sopenharmony_ci	struct lsdc_crtc_state *old_priv_state;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	new_priv_state = kzalloc(sizeof(*new_priv_state), GFP_KERNEL);
42662306a36Sopenharmony_ci	if (!new_priv_state)
42762306a36Sopenharmony_ci		return NULL;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	__drm_atomic_helper_crtc_duplicate_state(crtc, &new_priv_state->base);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	old_priv_state = to_lsdc_crtc_state(crtc->state);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	memcpy(&new_priv_state->pparms, &old_priv_state->pparms,
43462306a36Sopenharmony_ci	       sizeof(new_priv_state->pparms));
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return &new_priv_state->base;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic u32 lsdc_crtc_get_vblank_counter(struct drm_crtc *crtc)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* 32-bit hardware vblank counter */
44462306a36Sopenharmony_ci	return lcrtc->hw_ops->get_vblank_counter(lcrtc);
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int lsdc_crtc_enable_vblank(struct drm_crtc *crtc)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (!lcrtc->has_vblank)
45262306a36Sopenharmony_ci		return -EINVAL;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	lcrtc->hw_ops->enable_vblank(lcrtc);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return 0;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic void lsdc_crtc_disable_vblank(struct drm_crtc *crtc)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (!lcrtc->has_vblank)
46462306a36Sopenharmony_ci		return;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	lcrtc->hw_ops->disable_vblank(lcrtc);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/*
47062306a36Sopenharmony_ci * CRTC related debugfs
47162306a36Sopenharmony_ci * Primary planes and cursor planes belong to the CRTC as well.
47262306a36Sopenharmony_ci * For the sake of convenience, plane-related registers are also add here.
47362306a36Sopenharmony_ci */
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci#define REG_DEF(reg) { \
47662306a36Sopenharmony_ci	.name = __stringify_1(LSDC_##reg##_REG), \
47762306a36Sopenharmony_ci	.offset = LSDC_##reg##_REG, \
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic const struct lsdc_reg32 lsdc_crtc_regs_array[2][21] = {
48162306a36Sopenharmony_ci	[0] = {
48262306a36Sopenharmony_ci		REG_DEF(CRTC0_CFG),
48362306a36Sopenharmony_ci		REG_DEF(CRTC0_FB_ORIGIN),
48462306a36Sopenharmony_ci		REG_DEF(CRTC0_DVO_CONF),
48562306a36Sopenharmony_ci		REG_DEF(CRTC0_HDISPLAY),
48662306a36Sopenharmony_ci		REG_DEF(CRTC0_HSYNC),
48762306a36Sopenharmony_ci		REG_DEF(CRTC0_VDISPLAY),
48862306a36Sopenharmony_ci		REG_DEF(CRTC0_VSYNC),
48962306a36Sopenharmony_ci		REG_DEF(CRTC0_GAMMA_INDEX),
49062306a36Sopenharmony_ci		REG_DEF(CRTC0_GAMMA_DATA),
49162306a36Sopenharmony_ci		REG_DEF(CRTC0_SYNC_DEVIATION),
49262306a36Sopenharmony_ci		REG_DEF(CRTC0_VSYNC_COUNTER),
49362306a36Sopenharmony_ci		REG_DEF(CRTC0_SCAN_POS),
49462306a36Sopenharmony_ci		REG_DEF(CRTC0_STRIDE),
49562306a36Sopenharmony_ci		REG_DEF(CRTC0_FB1_ADDR_HI),
49662306a36Sopenharmony_ci		REG_DEF(CRTC0_FB1_ADDR_LO),
49762306a36Sopenharmony_ci		REG_DEF(CRTC0_FB0_ADDR_HI),
49862306a36Sopenharmony_ci		REG_DEF(CRTC0_FB0_ADDR_LO),
49962306a36Sopenharmony_ci		REG_DEF(CURSOR0_CFG),
50062306a36Sopenharmony_ci		REG_DEF(CURSOR0_POSITION),
50162306a36Sopenharmony_ci		REG_DEF(CURSOR0_BG_COLOR),
50262306a36Sopenharmony_ci		REG_DEF(CURSOR0_FG_COLOR),
50362306a36Sopenharmony_ci	},
50462306a36Sopenharmony_ci	[1] = {
50562306a36Sopenharmony_ci		REG_DEF(CRTC1_CFG),
50662306a36Sopenharmony_ci		REG_DEF(CRTC1_FB_ORIGIN),
50762306a36Sopenharmony_ci		REG_DEF(CRTC1_DVO_CONF),
50862306a36Sopenharmony_ci		REG_DEF(CRTC1_HDISPLAY),
50962306a36Sopenharmony_ci		REG_DEF(CRTC1_HSYNC),
51062306a36Sopenharmony_ci		REG_DEF(CRTC1_VDISPLAY),
51162306a36Sopenharmony_ci		REG_DEF(CRTC1_VSYNC),
51262306a36Sopenharmony_ci		REG_DEF(CRTC1_GAMMA_INDEX),
51362306a36Sopenharmony_ci		REG_DEF(CRTC1_GAMMA_DATA),
51462306a36Sopenharmony_ci		REG_DEF(CRTC1_SYNC_DEVIATION),
51562306a36Sopenharmony_ci		REG_DEF(CRTC1_VSYNC_COUNTER),
51662306a36Sopenharmony_ci		REG_DEF(CRTC1_SCAN_POS),
51762306a36Sopenharmony_ci		REG_DEF(CRTC1_STRIDE),
51862306a36Sopenharmony_ci		REG_DEF(CRTC1_FB1_ADDR_HI),
51962306a36Sopenharmony_ci		REG_DEF(CRTC1_FB1_ADDR_LO),
52062306a36Sopenharmony_ci		REG_DEF(CRTC1_FB0_ADDR_HI),
52162306a36Sopenharmony_ci		REG_DEF(CRTC1_FB0_ADDR_LO),
52262306a36Sopenharmony_ci		REG_DEF(CURSOR1_CFG),
52362306a36Sopenharmony_ci		REG_DEF(CURSOR1_POSITION),
52462306a36Sopenharmony_ci		REG_DEF(CURSOR1_BG_COLOR),
52562306a36Sopenharmony_ci		REG_DEF(CURSOR1_FG_COLOR),
52662306a36Sopenharmony_ci	},
52762306a36Sopenharmony_ci};
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int lsdc_crtc_show_regs(struct seq_file *m, void *arg)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct drm_info_node *node = (struct drm_info_node *)m->private;
53262306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;
53362306a36Sopenharmony_ci	struct lsdc_device *ldev = lcrtc->ldev;
53462306a36Sopenharmony_ci	unsigned int i;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	for (i = 0; i < lcrtc->nreg; i++) {
53762306a36Sopenharmony_ci		const struct lsdc_reg32 *preg = &lcrtc->preg[i];
53862306a36Sopenharmony_ci		u32 offset = preg->offset;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		seq_printf(m, "%s (0x%04x): 0x%08x\n",
54162306a36Sopenharmony_ci			   preg->name, offset, lsdc_rreg32(ldev, offset));
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return 0;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int lsdc_crtc_show_scan_position(struct seq_file *m, void *arg)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct drm_info_node *node = (struct drm_info_node *)m->private;
55062306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;
55162306a36Sopenharmony_ci	int x, y;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	lcrtc->hw_ops->get_scan_pos(lcrtc, &x, &y);
55462306a36Sopenharmony_ci	seq_printf(m, "Scanout position: x: %08u, y: %08u\n", x, y);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int lsdc_crtc_show_vblank_counter(struct seq_file *m, void *arg)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct drm_info_node *node = (struct drm_info_node *)m->private;
56262306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (lcrtc->hw_ops->get_vblank_counter)
56562306a36Sopenharmony_ci		seq_printf(m, "%s vblank counter: %08u\n\n", lcrtc->base.name,
56662306a36Sopenharmony_ci			   lcrtc->hw_ops->get_vblank_counter(lcrtc));
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	return 0;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic int lsdc_pixpll_show_clock(struct seq_file *m, void *arg)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	struct drm_info_node *node = (struct drm_info_node *)m->private;
57462306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;
57562306a36Sopenharmony_ci	struct lsdc_pixpll *pixpll = &lcrtc->pixpll;
57662306a36Sopenharmony_ci	const struct lsdc_pixpll_funcs *funcs = pixpll->funcs;
57762306a36Sopenharmony_ci	struct drm_crtc *crtc = &lcrtc->base;
57862306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc->state->mode;
57962306a36Sopenharmony_ci	struct drm_printer printer = drm_seq_file_printer(m);
58062306a36Sopenharmony_ci	unsigned int out_khz;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	out_khz = funcs->get_rate(pixpll);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	seq_printf(m, "%s: %dx%d@%d\n", crtc->name,
58562306a36Sopenharmony_ci		   mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode));
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	seq_printf(m, "Pixel clock required: %d kHz\n", mode->clock);
58862306a36Sopenharmony_ci	seq_printf(m, "Actual frequency output: %u kHz\n", out_khz);
58962306a36Sopenharmony_ci	seq_printf(m, "Diff: %d kHz\n", out_khz - mode->clock);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	funcs->print(pixpll, &printer);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	return 0;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic struct drm_info_list lsdc_crtc_debugfs_list[2][4] = {
59762306a36Sopenharmony_ci	[0] = {
59862306a36Sopenharmony_ci		{ "regs", lsdc_crtc_show_regs, 0, NULL },
59962306a36Sopenharmony_ci		{ "pixclk", lsdc_pixpll_show_clock, 0, NULL },
60062306a36Sopenharmony_ci		{ "scanpos", lsdc_crtc_show_scan_position, 0, NULL },
60162306a36Sopenharmony_ci		{ "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL },
60262306a36Sopenharmony_ci	},
60362306a36Sopenharmony_ci	[1] = {
60462306a36Sopenharmony_ci		{ "regs", lsdc_crtc_show_regs, 0, NULL },
60562306a36Sopenharmony_ci		{ "pixclk", lsdc_pixpll_show_clock, 0, NULL },
60662306a36Sopenharmony_ci		{ "scanpos", lsdc_crtc_show_scan_position, 0, NULL },
60762306a36Sopenharmony_ci		{ "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL },
60862306a36Sopenharmony_ci	},
60962306a36Sopenharmony_ci};
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci/* operate manually */
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic int lsdc_crtc_man_op_show(struct seq_file *m, void *data)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	seq_puts(m, "soft_reset: soft reset this CRTC\n");
61662306a36Sopenharmony_ci	seq_puts(m, "enable: enable this CRTC\n");
61762306a36Sopenharmony_ci	seq_puts(m, "disable: disable this CRTC\n");
61862306a36Sopenharmony_ci	seq_puts(m, "flip: trigger the page flip\n");
61962306a36Sopenharmony_ci	seq_puts(m, "clone: clone the another crtc with hardware logic\n");
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return 0;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int lsdc_crtc_man_op_open(struct inode *inode, struct file *file)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct drm_crtc *crtc = inode->i_private;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	return single_open(file, lsdc_crtc_man_op_show, crtc);
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic ssize_t lsdc_crtc_man_op_write(struct file *file,
63262306a36Sopenharmony_ci				      const char __user *ubuf,
63362306a36Sopenharmony_ci				      size_t len,
63462306a36Sopenharmony_ci				      loff_t *offp)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct seq_file *m = file->private_data;
63762306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = m->private;
63862306a36Sopenharmony_ci	const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;
63962306a36Sopenharmony_ci	char buf[16];
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (len > sizeof(buf) - 1)
64262306a36Sopenharmony_ci		return -EINVAL;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (copy_from_user(buf, ubuf, len))
64562306a36Sopenharmony_ci		return -EFAULT;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	buf[len] = '\0';
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (sysfs_streq(buf, "soft_reset"))
65062306a36Sopenharmony_ci		ops->soft_reset(lcrtc);
65162306a36Sopenharmony_ci	else if (sysfs_streq(buf, "enable"))
65262306a36Sopenharmony_ci		ops->enable(lcrtc);
65362306a36Sopenharmony_ci	else if (sysfs_streq(buf, "disable"))
65462306a36Sopenharmony_ci		ops->disable(lcrtc);
65562306a36Sopenharmony_ci	else if (sysfs_streq(buf, "flip"))
65662306a36Sopenharmony_ci		ops->flip(lcrtc);
65762306a36Sopenharmony_ci	else if (sysfs_streq(buf, "clone"))
65862306a36Sopenharmony_ci		ops->clone(lcrtc);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	return len;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic const struct file_operations lsdc_crtc_man_op_fops = {
66462306a36Sopenharmony_ci	.owner = THIS_MODULE,
66562306a36Sopenharmony_ci	.open = lsdc_crtc_man_op_open,
66662306a36Sopenharmony_ci	.read = seq_read,
66762306a36Sopenharmony_ci	.llseek = seq_lseek,
66862306a36Sopenharmony_ci	.release = single_release,
66962306a36Sopenharmony_ci	.write = lsdc_crtc_man_op_write,
67062306a36Sopenharmony_ci};
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic int lsdc_crtc_late_register(struct drm_crtc *crtc)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc);
67562306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
67662306a36Sopenharmony_ci	struct drm_minor *minor = crtc->dev->primary;
67762306a36Sopenharmony_ci	unsigned int index = dispipe->index;
67862306a36Sopenharmony_ci	unsigned int i;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	lcrtc->preg = lsdc_crtc_regs_array[index];
68162306a36Sopenharmony_ci	lcrtc->nreg = ARRAY_SIZE(lsdc_crtc_regs_array[index]);
68262306a36Sopenharmony_ci	lcrtc->p_info_list = lsdc_crtc_debugfs_list[index];
68362306a36Sopenharmony_ci	lcrtc->n_info_list = ARRAY_SIZE(lsdc_crtc_debugfs_list[index]);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	for (i = 0; i < lcrtc->n_info_list; ++i)
68662306a36Sopenharmony_ci		lcrtc->p_info_list[i].data = lcrtc;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	drm_debugfs_create_files(lcrtc->p_info_list, lcrtc->n_info_list,
68962306a36Sopenharmony_ci				 crtc->debugfs_entry, minor);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* Manual operations supported */
69262306a36Sopenharmony_ci	debugfs_create_file("ops", 0644, crtc->debugfs_entry, lcrtc,
69362306a36Sopenharmony_ci			    &lsdc_crtc_man_op_fops);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic void lsdc_crtc_atomic_print_state(struct drm_printer *p,
69962306a36Sopenharmony_ci					 const struct drm_crtc_state *state)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	const struct lsdc_crtc_state *priv_state;
70262306a36Sopenharmony_ci	const struct lsdc_pixpll_parms *pparms;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	priv_state = container_of_const(state, struct lsdc_crtc_state, base);
70562306a36Sopenharmony_ci	pparms = &priv_state->pparms;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	drm_printf(p, "\tInput clock divider = %u\n", pparms->div_ref);
70862306a36Sopenharmony_ci	drm_printf(p, "\tMedium clock multiplier = %u\n", pparms->loopc);
70962306a36Sopenharmony_ci	drm_printf(p, "\tOutput clock divider = %u\n", pparms->div_out);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic const struct drm_crtc_funcs ls7a1000_crtc_funcs = {
71362306a36Sopenharmony_ci	.reset = lsdc_crtc_reset,
71462306a36Sopenharmony_ci	.destroy = drm_crtc_cleanup,
71562306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
71662306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
71762306a36Sopenharmony_ci	.atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state,
71862306a36Sopenharmony_ci	.atomic_destroy_state = lsdc_crtc_atomic_destroy_state,
71962306a36Sopenharmony_ci	.late_register = lsdc_crtc_late_register,
72062306a36Sopenharmony_ci	.enable_vblank = lsdc_crtc_enable_vblank,
72162306a36Sopenharmony_ci	.disable_vblank = lsdc_crtc_disable_vblank,
72262306a36Sopenharmony_ci	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
72362306a36Sopenharmony_ci	.atomic_print_state = lsdc_crtc_atomic_print_state,
72462306a36Sopenharmony_ci};
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic const struct drm_crtc_funcs ls7a2000_crtc_funcs = {
72762306a36Sopenharmony_ci	.reset = lsdc_crtc_reset,
72862306a36Sopenharmony_ci	.destroy = drm_crtc_cleanup,
72962306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
73062306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
73162306a36Sopenharmony_ci	.atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state,
73262306a36Sopenharmony_ci	.atomic_destroy_state = lsdc_crtc_atomic_destroy_state,
73362306a36Sopenharmony_ci	.late_register = lsdc_crtc_late_register,
73462306a36Sopenharmony_ci	.get_vblank_counter = lsdc_crtc_get_vblank_counter,
73562306a36Sopenharmony_ci	.enable_vblank = lsdc_crtc_enable_vblank,
73662306a36Sopenharmony_ci	.disable_vblank = lsdc_crtc_disable_vblank,
73762306a36Sopenharmony_ci	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
73862306a36Sopenharmony_ci	.atomic_print_state = lsdc_crtc_atomic_print_state,
73962306a36Sopenharmony_ci};
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic enum drm_mode_status
74262306a36Sopenharmony_cilsdc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct drm_device *ddev = crtc->dev;
74562306a36Sopenharmony_ci	struct lsdc_device *ldev = to_lsdc(ddev);
74662306a36Sopenharmony_ci	const struct lsdc_desc *descp = ldev->descp;
74762306a36Sopenharmony_ci	unsigned int pitch;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (mode->hdisplay > descp->max_width)
75062306a36Sopenharmony_ci		return MODE_BAD_HVALUE;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (mode->vdisplay > descp->max_height)
75362306a36Sopenharmony_ci		return MODE_BAD_VVALUE;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (mode->clock > descp->max_pixel_clk) {
75662306a36Sopenharmony_ci		drm_dbg_kms(ddev, "mode %dx%d, pixel clock=%d is too high\n",
75762306a36Sopenharmony_ci			    mode->hdisplay, mode->vdisplay, mode->clock);
75862306a36Sopenharmony_ci		return MODE_CLOCK_HIGH;
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/* 4 for DRM_FORMAT_XRGB8888 */
76262306a36Sopenharmony_ci	pitch = mode->hdisplay * 4;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (pitch % descp->pitch_align) {
76562306a36Sopenharmony_ci		drm_dbg_kms(ddev, "align to %u bytes is required: %u\n",
76662306a36Sopenharmony_ci			    descp->pitch_align, pitch);
76762306a36Sopenharmony_ci		return MODE_BAD_WIDTH;
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	return MODE_OK;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic int lsdc_pixpll_atomic_check(struct drm_crtc *crtc,
77462306a36Sopenharmony_ci				    struct drm_crtc_state *state)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
77762306a36Sopenharmony_ci	struct lsdc_pixpll *pixpll = &lcrtc->pixpll;
77862306a36Sopenharmony_ci	const struct lsdc_pixpll_funcs *pfuncs = pixpll->funcs;
77962306a36Sopenharmony_ci	struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);
78062306a36Sopenharmony_ci	unsigned int clock = state->mode.clock;
78162306a36Sopenharmony_ci	int ret;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	ret = pfuncs->compute(pixpll, clock, &priv_state->pparms);
78462306a36Sopenharmony_ci	if (ret) {
78562306a36Sopenharmony_ci		drm_warn(crtc->dev, "Failed to find PLL params for %ukHz\n",
78662306a36Sopenharmony_ci			 clock);
78762306a36Sopenharmony_ci		return -EINVAL;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	return 0;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic int lsdc_crtc_helper_atomic_check(struct drm_crtc *crtc,
79462306a36Sopenharmony_ci					 struct drm_atomic_state *state)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (!crtc_state->enable)
79962306a36Sopenharmony_ci		return 0;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	return lsdc_pixpll_atomic_check(crtc, crtc_state);
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic void lsdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
80762306a36Sopenharmony_ci	const struct lsdc_crtc_hw_ops *crtc_hw_ops = lcrtc->hw_ops;
80862306a36Sopenharmony_ci	struct lsdc_pixpll *pixpll = &lcrtc->pixpll;
80962306a36Sopenharmony_ci	const struct lsdc_pixpll_funcs *pixpll_funcs = pixpll->funcs;
81062306a36Sopenharmony_ci	struct drm_crtc_state *state = crtc->state;
81162306a36Sopenharmony_ci	struct drm_display_mode *mode = &state->mode;
81262306a36Sopenharmony_ci	struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	pixpll_funcs->update(pixpll, &priv_state->pparms);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (crtc_hw_ops->set_dma_step) {
81762306a36Sopenharmony_ci		unsigned int width_in_bytes = mode->hdisplay * 4;
81862306a36Sopenharmony_ci		enum lsdc_dma_steps dma_step;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		/*
82162306a36Sopenharmony_ci		 * Using DMA step as large as possible, for improving
82262306a36Sopenharmony_ci		 * hardware DMA efficiency.
82362306a36Sopenharmony_ci		 */
82462306a36Sopenharmony_ci		if (width_in_bytes % 256 == 0)
82562306a36Sopenharmony_ci			dma_step = LSDC_DMA_STEP_256_BYTES;
82662306a36Sopenharmony_ci		else if (width_in_bytes % 128 == 0)
82762306a36Sopenharmony_ci			dma_step = LSDC_DMA_STEP_128_BYTES;
82862306a36Sopenharmony_ci		else if (width_in_bytes % 64 == 0)
82962306a36Sopenharmony_ci			dma_step = LSDC_DMA_STEP_64_BYTES;
83062306a36Sopenharmony_ci		else  /* width_in_bytes % 32 == 0 */
83162306a36Sopenharmony_ci			dma_step = LSDC_DMA_STEP_32_BYTES;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		crtc_hw_ops->set_dma_step(lcrtc, dma_step);
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	crtc_hw_ops->set_mode(lcrtc, mode);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic void lsdc_crtc_send_vblank(struct drm_crtc *crtc)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct drm_device *ddev = crtc->dev;
84262306a36Sopenharmony_ci	unsigned long flags;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (!crtc->state || !crtc->state->event)
84562306a36Sopenharmony_ci		return;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	drm_dbg(ddev, "Send vblank manually\n");
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	spin_lock_irqsave(&ddev->event_lock, flags);
85062306a36Sopenharmony_ci	drm_crtc_send_vblank_event(crtc, crtc->state->event);
85162306a36Sopenharmony_ci	crtc->state->event = NULL;
85262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ddev->event_lock, flags);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic void lsdc_crtc_atomic_enable(struct drm_crtc *crtc,
85662306a36Sopenharmony_ci				    struct drm_atomic_state *state)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (lcrtc->has_vblank)
86162306a36Sopenharmony_ci		drm_crtc_vblank_on(crtc);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	lcrtc->hw_ops->enable(lcrtc);
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_cistatic void lsdc_crtc_atomic_disable(struct drm_crtc *crtc,
86762306a36Sopenharmony_ci				     struct drm_atomic_state *state)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (lcrtc->has_vblank)
87262306a36Sopenharmony_ci		drm_crtc_vblank_off(crtc);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	lcrtc->hw_ops->disable(lcrtc);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	/*
87762306a36Sopenharmony_ci	 * Make sure we issue a vblank event after disabling the CRTC if
87862306a36Sopenharmony_ci	 * someone was waiting it.
87962306a36Sopenharmony_ci	 */
88062306a36Sopenharmony_ci	lsdc_crtc_send_vblank(crtc);
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic void lsdc_crtc_atomic_flush(struct drm_crtc *crtc,
88462306a36Sopenharmony_ci				   struct drm_atomic_state *state)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
88762306a36Sopenharmony_ci	if (crtc->state->event) {
88862306a36Sopenharmony_ci		if (drm_crtc_vblank_get(crtc) == 0)
88962306a36Sopenharmony_ci			drm_crtc_arm_vblank_event(crtc, crtc->state->event);
89062306a36Sopenharmony_ci		else
89162306a36Sopenharmony_ci			drm_crtc_send_vblank_event(crtc, crtc->state->event);
89262306a36Sopenharmony_ci		crtc->state->event = NULL;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic bool lsdc_crtc_get_scanout_position(struct drm_crtc *crtc,
89862306a36Sopenharmony_ci					   bool in_vblank_irq,
89962306a36Sopenharmony_ci					   int *vpos,
90062306a36Sopenharmony_ci					   int *hpos,
90162306a36Sopenharmony_ci					   ktime_t *stime,
90262306a36Sopenharmony_ci					   ktime_t *etime,
90362306a36Sopenharmony_ci					   const struct drm_display_mode *mode)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
90662306a36Sopenharmony_ci	const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;
90762306a36Sopenharmony_ci	int vsw, vbp, vactive_start, vactive_end, vfp_end;
90862306a36Sopenharmony_ci	int x, y;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
91162306a36Sopenharmony_ci	vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	vactive_start = vsw + vbp + 1;
91462306a36Sopenharmony_ci	vactive_end = vactive_start + mode->crtc_vdisplay;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* last scan line before VSYNC */
91762306a36Sopenharmony_ci	vfp_end = mode->crtc_vtotal;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (stime)
92062306a36Sopenharmony_ci		*stime = ktime_get();
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	ops->get_scan_pos(lcrtc, &x, &y);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	if (y > vactive_end)
92562306a36Sopenharmony_ci		y = y - vfp_end - vactive_start;
92662306a36Sopenharmony_ci	else
92762306a36Sopenharmony_ci		y -= vactive_start;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	*vpos = y;
93062306a36Sopenharmony_ci	*hpos = 0;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (etime)
93362306a36Sopenharmony_ci		*etime = ktime_get();
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	return true;
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = {
93962306a36Sopenharmony_ci	.mode_valid = lsdc_crtc_mode_valid,
94062306a36Sopenharmony_ci	.mode_set_nofb = lsdc_crtc_mode_set_nofb,
94162306a36Sopenharmony_ci	.atomic_enable = lsdc_crtc_atomic_enable,
94262306a36Sopenharmony_ci	.atomic_disable = lsdc_crtc_atomic_disable,
94362306a36Sopenharmony_ci	.atomic_check = lsdc_crtc_helper_atomic_check,
94462306a36Sopenharmony_ci	.atomic_flush = lsdc_crtc_atomic_flush,
94562306a36Sopenharmony_ci	.get_scanout_position = lsdc_crtc_get_scanout_position,
94662306a36Sopenharmony_ci};
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ciint ls7a1000_crtc_init(struct drm_device *ddev,
94962306a36Sopenharmony_ci		       struct drm_crtc *crtc,
95062306a36Sopenharmony_ci		       struct drm_plane *primary,
95162306a36Sopenharmony_ci		       struct drm_plane *cursor,
95262306a36Sopenharmony_ci		       unsigned int index,
95362306a36Sopenharmony_ci		       bool has_vblank)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
95662306a36Sopenharmony_ci	int ret;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index);
95962306a36Sopenharmony_ci	if (ret) {
96062306a36Sopenharmony_ci		drm_err(ddev, "pixel pll init failed: %d\n", ret);
96162306a36Sopenharmony_ci		return ret;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	lcrtc->ldev = to_lsdc(ddev);
96562306a36Sopenharmony_ci	lcrtc->has_vblank = has_vblank;
96662306a36Sopenharmony_ci	lcrtc->hw_ops = &ls7a1000_crtc_hw_ops[index];
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor,
96962306a36Sopenharmony_ci					&ls7a1000_crtc_funcs,
97062306a36Sopenharmony_ci					"LS-CRTC-%d", index);
97162306a36Sopenharmony_ci	if (ret) {
97262306a36Sopenharmony_ci		drm_err(ddev, "crtc init with planes failed: %d\n", ret);
97362306a36Sopenharmony_ci		return ret;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	ret = drm_mode_crtc_set_gamma_size(crtc, 256);
97962306a36Sopenharmony_ci	if (ret)
98062306a36Sopenharmony_ci		return ret;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	drm_crtc_enable_color_mgmt(crtc, 0, false, 256);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	return 0;
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ciint ls7a2000_crtc_init(struct drm_device *ddev,
98862306a36Sopenharmony_ci		       struct drm_crtc *crtc,
98962306a36Sopenharmony_ci		       struct drm_plane *primary,
99062306a36Sopenharmony_ci		       struct drm_plane *cursor,
99162306a36Sopenharmony_ci		       unsigned int index,
99262306a36Sopenharmony_ci		       bool has_vblank)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
99562306a36Sopenharmony_ci	int ret;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index);
99862306a36Sopenharmony_ci	if (ret) {
99962306a36Sopenharmony_ci		drm_err(ddev, "crtc init with pll failed: %d\n", ret);
100062306a36Sopenharmony_ci		return ret;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	lcrtc->ldev = to_lsdc(ddev);
100462306a36Sopenharmony_ci	lcrtc->has_vblank = has_vblank;
100562306a36Sopenharmony_ci	lcrtc->hw_ops = &ls7a2000_crtc_hw_ops[index];
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor,
100862306a36Sopenharmony_ci					&ls7a2000_crtc_funcs,
100962306a36Sopenharmony_ci					"LS-CRTC-%u", index);
101062306a36Sopenharmony_ci	if (ret) {
101162306a36Sopenharmony_ci		drm_err(ddev, "crtc init with planes failed: %d\n", ret);
101262306a36Sopenharmony_ci		return ret;
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	ret = drm_mode_crtc_set_gamma_size(crtc, 256);
101862306a36Sopenharmony_ci	if (ret)
101962306a36Sopenharmony_ci		return ret;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	drm_crtc_enable_color_mgmt(crtc, 0, false, 256);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	return 0;
102462306a36Sopenharmony_ci}
1025