162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2007-8 Advanced Micro Devices, Inc.
362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
662306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
762306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
862306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
962306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1062306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1362306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1962306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2062306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2162306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Authors: Dave Airlie
2462306a36Sopenharmony_ci *          Alex Deucher
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/pci.h>
2862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2962306a36Sopenharmony_ci#include <linux/gcd.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <asm/div64.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h>
3462306a36Sopenharmony_ci#include <drm/drm_device.h>
3562306a36Sopenharmony_ci#include <drm/drm_drv.h>
3662306a36Sopenharmony_ci#include <drm/drm_edid.h>
3762306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
3862306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
3962306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
4062306a36Sopenharmony_ci#include <drm/drm_modeset_helper.h>
4162306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
4262306a36Sopenharmony_ci#include <drm/drm_vblank.h>
4362306a36Sopenharmony_ci#include <drm/radeon_drm.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include "atom.h"
4662306a36Sopenharmony_ci#include "radeon.h"
4762306a36Sopenharmony_ci#include "radeon_kms.h"
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void avivo_crtc_load_lut(struct drm_crtc *crtc)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
5262306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
5362306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
5462306a36Sopenharmony_ci	u16 *r, *g, *b;
5562306a36Sopenharmony_ci	int i;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id);
5862306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUTA_CONTROL + radeon_crtc->crtc_offset, 0);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0);
6162306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0);
6262306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff);
6562306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff);
6662306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUT_RW_SELECT, radeon_crtc->crtc_id);
6962306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUT_RW_MODE, 0);
7062306a36Sopenharmony_ci	WREG32(AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	WREG8(AVIVO_DC_LUT_RW_INDEX, 0);
7362306a36Sopenharmony_ci	r = crtc->gamma_store;
7462306a36Sopenharmony_ci	g = r + crtc->gamma_size;
7562306a36Sopenharmony_ci	b = g + crtc->gamma_size;
7662306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
7762306a36Sopenharmony_ci		WREG32(AVIVO_DC_LUT_30_COLOR,
7862306a36Sopenharmony_ci		       ((*r++ & 0xffc0) << 14) |
7962306a36Sopenharmony_ci		       ((*g++ & 0xffc0) << 4) |
8062306a36Sopenharmony_ci		       (*b++ >> 6));
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* Only change bit 0 of LUT_SEL, other bits are set elsewhere */
8462306a36Sopenharmony_ci	WREG32_P(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id, ~1);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void dce4_crtc_load_lut(struct drm_crtc *crtc)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
9062306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
9162306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
9262306a36Sopenharmony_ci	u16 *r, *g, *b;
9362306a36Sopenharmony_ci	int i;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id);
9662306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0);
9962306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0);
10062306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff);
10362306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff);
10462306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_RW_MODE + radeon_crtc->crtc_offset, 0);
10762306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x00000007);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0);
11062306a36Sopenharmony_ci	r = crtc->gamma_store;
11162306a36Sopenharmony_ci	g = r + crtc->gamma_size;
11262306a36Sopenharmony_ci	b = g + crtc->gamma_size;
11362306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
11462306a36Sopenharmony_ci		WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset,
11562306a36Sopenharmony_ci		       ((*r++ & 0xffc0) << 14) |
11662306a36Sopenharmony_ci		       ((*g++ & 0xffc0) << 4) |
11762306a36Sopenharmony_ci		       (*b++ >> 6));
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void dce5_crtc_load_lut(struct drm_crtc *crtc)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
12462306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
12562306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
12662306a36Sopenharmony_ci	u16 *r, *g, *b;
12762306a36Sopenharmony_ci	int i;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	msleep(10);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	WREG32(NI_INPUT_CSC_CONTROL + radeon_crtc->crtc_offset,
13462306a36Sopenharmony_ci	       (NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS) |
13562306a36Sopenharmony_ci		NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS)));
13662306a36Sopenharmony_ci	WREG32(NI_PRESCALE_GRPH_CONTROL + radeon_crtc->crtc_offset,
13762306a36Sopenharmony_ci	       NI_GRPH_PRESCALE_BYPASS);
13862306a36Sopenharmony_ci	WREG32(NI_PRESCALE_OVL_CONTROL + radeon_crtc->crtc_offset,
13962306a36Sopenharmony_ci	       NI_OVL_PRESCALE_BYPASS);
14062306a36Sopenharmony_ci	WREG32(NI_INPUT_GAMMA_CONTROL + radeon_crtc->crtc_offset,
14162306a36Sopenharmony_ci	       (NI_GRPH_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT) |
14262306a36Sopenharmony_ci		NI_OVL_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT)));
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0);
14762306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0);
14862306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff);
15162306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff);
15262306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_RW_MODE + radeon_crtc->crtc_offset, 0);
15562306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x00000007);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0);
15862306a36Sopenharmony_ci	r = crtc->gamma_store;
15962306a36Sopenharmony_ci	g = r + crtc->gamma_size;
16062306a36Sopenharmony_ci	b = g + crtc->gamma_size;
16162306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
16262306a36Sopenharmony_ci		WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset,
16362306a36Sopenharmony_ci		       ((*r++ & 0xffc0) << 14) |
16462306a36Sopenharmony_ci		       ((*g++ & 0xffc0) << 4) |
16562306a36Sopenharmony_ci		       (*b++ >> 6));
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	WREG32(NI_DEGAMMA_CONTROL + radeon_crtc->crtc_offset,
16962306a36Sopenharmony_ci	       (NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
17062306a36Sopenharmony_ci		NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
17162306a36Sopenharmony_ci		NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
17262306a36Sopenharmony_ci		NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS)));
17362306a36Sopenharmony_ci	WREG32(NI_GAMUT_REMAP_CONTROL + radeon_crtc->crtc_offset,
17462306a36Sopenharmony_ci	       (NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) |
17562306a36Sopenharmony_ci		NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS)));
17662306a36Sopenharmony_ci	WREG32(NI_REGAMMA_CONTROL + radeon_crtc->crtc_offset,
17762306a36Sopenharmony_ci	       (NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) |
17862306a36Sopenharmony_ci		NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS)));
17962306a36Sopenharmony_ci	WREG32(NI_OUTPUT_CSC_CONTROL + radeon_crtc->crtc_offset,
18062306a36Sopenharmony_ci	       (NI_OUTPUT_CSC_GRPH_MODE(radeon_crtc->output_csc) |
18162306a36Sopenharmony_ci		NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS)));
18262306a36Sopenharmony_ci	/* XXX match this to the depth of the crtc fmt block, move to modeset? */
18362306a36Sopenharmony_ci	WREG32(0x6940 + radeon_crtc->crtc_offset, 0);
18462306a36Sopenharmony_ci	if (ASIC_IS_DCE8(rdev)) {
18562306a36Sopenharmony_ci		/* XXX this only needs to be programmed once per crtc at startup,
18662306a36Sopenharmony_ci		 * not sure where the best place for it is
18762306a36Sopenharmony_ci		 */
18862306a36Sopenharmony_ci		WREG32(CIK_ALPHA_CONTROL + radeon_crtc->crtc_offset,
18962306a36Sopenharmony_ci		       CIK_CURSOR_ALPHA_BLND_ENA);
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void legacy_crtc_load_lut(struct drm_crtc *crtc)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
19662306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
19762306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
19862306a36Sopenharmony_ci	u16 *r, *g, *b;
19962306a36Sopenharmony_ci	int i;
20062306a36Sopenharmony_ci	uint32_t dac2_cntl;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	dac2_cntl = RREG32(RADEON_DAC_CNTL2);
20362306a36Sopenharmony_ci	if (radeon_crtc->crtc_id == 0)
20462306a36Sopenharmony_ci		dac2_cntl &= (uint32_t)~RADEON_DAC2_PALETTE_ACC_CTL;
20562306a36Sopenharmony_ci	else
20662306a36Sopenharmony_ci		dac2_cntl |= RADEON_DAC2_PALETTE_ACC_CTL;
20762306a36Sopenharmony_ci	WREG32(RADEON_DAC_CNTL2, dac2_cntl);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	WREG8(RADEON_PALETTE_INDEX, 0);
21062306a36Sopenharmony_ci	r = crtc->gamma_store;
21162306a36Sopenharmony_ci	g = r + crtc->gamma_size;
21262306a36Sopenharmony_ci	b = g + crtc->gamma_size;
21362306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
21462306a36Sopenharmony_ci		WREG32(RADEON_PALETTE_30_DATA,
21562306a36Sopenharmony_ci		       ((*r++ & 0xffc0) << 14) |
21662306a36Sopenharmony_ci		       ((*g++ & 0xffc0) << 4) |
21762306a36Sopenharmony_ci		       (*b++ >> 6));
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_civoid radeon_crtc_load_lut(struct drm_crtc *crtc)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
22462306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (!crtc->enabled)
22762306a36Sopenharmony_ci		return;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (ASIC_IS_DCE5(rdev))
23062306a36Sopenharmony_ci		dce5_crtc_load_lut(crtc);
23162306a36Sopenharmony_ci	else if (ASIC_IS_DCE4(rdev))
23262306a36Sopenharmony_ci		dce4_crtc_load_lut(crtc);
23362306a36Sopenharmony_ci	else if (ASIC_IS_AVIVO(rdev))
23462306a36Sopenharmony_ci		avivo_crtc_load_lut(crtc);
23562306a36Sopenharmony_ci	else
23662306a36Sopenharmony_ci		legacy_crtc_load_lut(crtc);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
24062306a36Sopenharmony_ci				 u16 *blue, uint32_t size,
24162306a36Sopenharmony_ci				 struct drm_modeset_acquire_ctx *ctx)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	radeon_crtc_load_lut(crtc);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return 0;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic void radeon_crtc_destroy(struct drm_crtc *crtc)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	drm_crtc_cleanup(crtc);
25362306a36Sopenharmony_ci	destroy_workqueue(radeon_crtc->flip_queue);
25462306a36Sopenharmony_ci	kfree(radeon_crtc);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/**
25862306a36Sopenharmony_ci * radeon_unpin_work_func - unpin old buffer object
25962306a36Sopenharmony_ci *
26062306a36Sopenharmony_ci * @__work: kernel work item
26162306a36Sopenharmony_ci *
26262306a36Sopenharmony_ci * Unpin the old frame buffer object outside of the interrupt handler
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_cistatic void radeon_unpin_work_func(struct work_struct *__work)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct radeon_flip_work *work =
26762306a36Sopenharmony_ci		container_of(__work, struct radeon_flip_work, unpin_work);
26862306a36Sopenharmony_ci	int r;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* unpin of the old buffer */
27162306a36Sopenharmony_ci	r = radeon_bo_reserve(work->old_rbo, false);
27262306a36Sopenharmony_ci	if (likely(r == 0)) {
27362306a36Sopenharmony_ci		radeon_bo_unpin(work->old_rbo);
27462306a36Sopenharmony_ci		radeon_bo_unreserve(work->old_rbo);
27562306a36Sopenharmony_ci	} else
27662306a36Sopenharmony_ci		DRM_ERROR("failed to reserve buffer after flip\n");
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	drm_gem_object_put(&work->old_rbo->tbo.base);
27962306a36Sopenharmony_ci	kfree(work);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_civoid radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
28562306a36Sopenharmony_ci	unsigned long flags;
28662306a36Sopenharmony_ci	u32 update_pending;
28762306a36Sopenharmony_ci	int vpos, hpos;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* can happen during initialization */
29062306a36Sopenharmony_ci	if (radeon_crtc == NULL)
29162306a36Sopenharmony_ci		return;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Skip the pageflip completion check below (based on polling) on
29462306a36Sopenharmony_ci	 * asics which reliably support hw pageflip completion irqs. pflip
29562306a36Sopenharmony_ci	 * irqs are a reliable and race-free method of handling pageflip
29662306a36Sopenharmony_ci	 * completion detection. A use_pflipirq module parameter < 2 allows
29762306a36Sopenharmony_ci	 * to override this in case of asics with faulty pflip irqs.
29862306a36Sopenharmony_ci	 * A module parameter of 0 would only use this polling based path,
29962306a36Sopenharmony_ci	 * a parameter of 1 would use pflip irq only as a backup to this
30062306a36Sopenharmony_ci	 * path, as in Linux 3.16.
30162306a36Sopenharmony_ci	 */
30262306a36Sopenharmony_ci	if ((radeon_use_pflipirq == 2) && ASIC_IS_DCE4(rdev))
30362306a36Sopenharmony_ci		return;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	spin_lock_irqsave(&rdev->ddev->event_lock, flags);
30662306a36Sopenharmony_ci	if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) {
30762306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != "
30862306a36Sopenharmony_ci				 "RADEON_FLIP_SUBMITTED(%d)\n",
30962306a36Sopenharmony_ci				 radeon_crtc->flip_status,
31062306a36Sopenharmony_ci				 RADEON_FLIP_SUBMITTED);
31162306a36Sopenharmony_ci		spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
31262306a36Sopenharmony_ci		return;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	update_pending = radeon_page_flip_pending(rdev, crtc_id);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* Has the pageflip already completed in crtc, or is it certain
31862306a36Sopenharmony_ci	 * to complete in this vblank? GET_DISTANCE_TO_VBLANKSTART provides
31962306a36Sopenharmony_ci	 * distance to start of "fudged earlier" vblank in vpos, distance to
32062306a36Sopenharmony_ci	 * start of real vblank in hpos. vpos >= 0 && hpos < 0 means we are in
32162306a36Sopenharmony_ci	 * the last few scanlines before start of real vblank, where the vblank
32262306a36Sopenharmony_ci	 * irq can fire, so we have sampled update_pending a bit too early and
32362306a36Sopenharmony_ci	 * know the flip will complete at leading edge of the upcoming real
32462306a36Sopenharmony_ci	 * vblank. On pre-AVIVO hardware, flips also complete inside the real
32562306a36Sopenharmony_ci	 * vblank, not only at leading edge, so if update_pending for hpos >= 0
32662306a36Sopenharmony_ci	 *  == inside real vblank, the flip will complete almost immediately.
32762306a36Sopenharmony_ci	 * Note that this method of completion handling is still not 100% race
32862306a36Sopenharmony_ci	 * free, as we could execute before the radeon_flip_work_func managed
32962306a36Sopenharmony_ci	 * to run and set the RADEON_FLIP_SUBMITTED status, thereby we no-op,
33062306a36Sopenharmony_ci	 * but the flip still gets programmed into hw and completed during
33162306a36Sopenharmony_ci	 * vblank, leading to a delayed emission of the flip completion event.
33262306a36Sopenharmony_ci	 * This applies at least to pre-AVIVO hardware, where flips are always
33362306a36Sopenharmony_ci	 * completing inside vblank, not only at leading edge of vblank.
33462306a36Sopenharmony_ci	 */
33562306a36Sopenharmony_ci	if (update_pending &&
33662306a36Sopenharmony_ci	    (DRM_SCANOUTPOS_VALID &
33762306a36Sopenharmony_ci	     radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id,
33862306a36Sopenharmony_ci					GET_DISTANCE_TO_VBLANKSTART,
33962306a36Sopenharmony_ci					&vpos, &hpos, NULL, NULL,
34062306a36Sopenharmony_ci					&rdev->mode_info.crtcs[crtc_id]->base.hwmode)) &&
34162306a36Sopenharmony_ci	    ((vpos >= 0 && hpos < 0) || (hpos >= 0 && !ASIC_IS_AVIVO(rdev)))) {
34262306a36Sopenharmony_ci		/* crtc didn't flip in this target vblank interval,
34362306a36Sopenharmony_ci		 * but flip is pending in crtc. Based on the current
34462306a36Sopenharmony_ci		 * scanout position we know that the current frame is
34562306a36Sopenharmony_ci		 * (nearly) complete and the flip will (likely)
34662306a36Sopenharmony_ci		 * complete before the start of the next frame.
34762306a36Sopenharmony_ci		 */
34862306a36Sopenharmony_ci		update_pending = 0;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci	spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
35162306a36Sopenharmony_ci	if (!update_pending)
35262306a36Sopenharmony_ci		radeon_crtc_handle_flip(rdev, crtc_id);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/**
35662306a36Sopenharmony_ci * radeon_crtc_handle_flip - page flip completed
35762306a36Sopenharmony_ci *
35862306a36Sopenharmony_ci * @rdev: radeon device pointer
35962306a36Sopenharmony_ci * @crtc_id: crtc number this event is for
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * Called when we are sure that a page flip for this crtc is completed.
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_civoid radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
36662306a36Sopenharmony_ci	struct radeon_flip_work *work;
36762306a36Sopenharmony_ci	unsigned long flags;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* this can happen at init */
37062306a36Sopenharmony_ci	if (radeon_crtc == NULL)
37162306a36Sopenharmony_ci		return;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	spin_lock_irqsave(&rdev->ddev->event_lock, flags);
37462306a36Sopenharmony_ci	work = radeon_crtc->flip_work;
37562306a36Sopenharmony_ci	if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) {
37662306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != "
37762306a36Sopenharmony_ci				 "RADEON_FLIP_SUBMITTED(%d)\n",
37862306a36Sopenharmony_ci				 radeon_crtc->flip_status,
37962306a36Sopenharmony_ci				 RADEON_FLIP_SUBMITTED);
38062306a36Sopenharmony_ci		spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
38162306a36Sopenharmony_ci		return;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/* Pageflip completed. Clean up. */
38562306a36Sopenharmony_ci	radeon_crtc->flip_status = RADEON_FLIP_NONE;
38662306a36Sopenharmony_ci	radeon_crtc->flip_work = NULL;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* wakeup userspace */
38962306a36Sopenharmony_ci	if (work->event)
39062306a36Sopenharmony_ci		drm_crtc_send_vblank_event(&radeon_crtc->base, work->event);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	drm_crtc_vblank_put(&radeon_crtc->base);
39562306a36Sopenharmony_ci	radeon_irq_kms_pflip_irq_put(rdev, work->crtc_id);
39662306a36Sopenharmony_ci	queue_work(radeon_crtc->flip_queue, &work->unpin_work);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/**
40062306a36Sopenharmony_ci * radeon_flip_work_func - page flip framebuffer
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * @__work: kernel work item
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci * Wait for the buffer object to become idle and do the actual page flip
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_cistatic void radeon_flip_work_func(struct work_struct *__work)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct radeon_flip_work *work =
40962306a36Sopenharmony_ci		container_of(__work, struct radeon_flip_work, flip_work);
41062306a36Sopenharmony_ci	struct radeon_device *rdev = work->rdev;
41162306a36Sopenharmony_ci	struct drm_device *dev = rdev->ddev;
41262306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id];
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	struct drm_crtc *crtc = &radeon_crtc->base;
41562306a36Sopenharmony_ci	unsigned long flags;
41662306a36Sopenharmony_ci	int r;
41762306a36Sopenharmony_ci	int vpos, hpos;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	down_read(&rdev->exclusive_lock);
42062306a36Sopenharmony_ci	if (work->fence) {
42162306a36Sopenharmony_ci		struct radeon_fence *fence;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		fence = to_radeon_fence(work->fence);
42462306a36Sopenharmony_ci		if (fence && fence->rdev == rdev) {
42562306a36Sopenharmony_ci			r = radeon_fence_wait(fence, false);
42662306a36Sopenharmony_ci			if (r == -EDEADLK) {
42762306a36Sopenharmony_ci				up_read(&rdev->exclusive_lock);
42862306a36Sopenharmony_ci				do {
42962306a36Sopenharmony_ci					r = radeon_gpu_reset(rdev);
43062306a36Sopenharmony_ci				} while (r == -EAGAIN);
43162306a36Sopenharmony_ci				down_read(&rdev->exclusive_lock);
43262306a36Sopenharmony_ci			}
43362306a36Sopenharmony_ci		} else
43462306a36Sopenharmony_ci			r = dma_fence_wait(work->fence, false);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		if (r)
43762306a36Sopenharmony_ci			DRM_ERROR("failed to wait on page flip fence (%d)!\n", r);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		/* We continue with the page flip even if we failed to wait on
44062306a36Sopenharmony_ci		 * the fence, otherwise the DRM core and userspace will be
44162306a36Sopenharmony_ci		 * confused about which BO the CRTC is scanning out
44262306a36Sopenharmony_ci		 */
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		dma_fence_put(work->fence);
44562306a36Sopenharmony_ci		work->fence = NULL;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Wait until we're out of the vertical blank period before the one
44962306a36Sopenharmony_ci	 * targeted by the flip. Always wait on pre DCE4 to avoid races with
45062306a36Sopenharmony_ci	 * flip completion handling from vblank irq, as these old asics don't
45162306a36Sopenharmony_ci	 * have reliable pageflip completion interrupts.
45262306a36Sopenharmony_ci	 */
45362306a36Sopenharmony_ci	while (radeon_crtc->enabled &&
45462306a36Sopenharmony_ci		(radeon_get_crtc_scanoutpos(dev, work->crtc_id, 0,
45562306a36Sopenharmony_ci					    &vpos, &hpos, NULL, NULL,
45662306a36Sopenharmony_ci					    &crtc->hwmode)
45762306a36Sopenharmony_ci		& (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
45862306a36Sopenharmony_ci		(DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
45962306a36Sopenharmony_ci		(!ASIC_IS_AVIVO(rdev) ||
46062306a36Sopenharmony_ci		((int) (work->target_vblank -
46162306a36Sopenharmony_ci		crtc->funcs->get_vblank_counter(crtc)) > 0)))
46262306a36Sopenharmony_ci		usleep_range(1000, 2000);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* We borrow the event spin lock for protecting flip_status */
46562306a36Sopenharmony_ci	spin_lock_irqsave(&crtc->dev->event_lock, flags);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* set the proper interrupt */
46862306a36Sopenharmony_ci	radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* do the flip (mmio) */
47162306a36Sopenharmony_ci	radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base, work->async);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	radeon_crtc->flip_status = RADEON_FLIP_SUBMITTED;
47462306a36Sopenharmony_ci	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
47562306a36Sopenharmony_ci	up_read(&rdev->exclusive_lock);
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic int radeon_crtc_page_flip_target(struct drm_crtc *crtc,
47962306a36Sopenharmony_ci					struct drm_framebuffer *fb,
48062306a36Sopenharmony_ci					struct drm_pending_vblank_event *event,
48162306a36Sopenharmony_ci					uint32_t page_flip_flags,
48262306a36Sopenharmony_ci					uint32_t target,
48362306a36Sopenharmony_ci					struct drm_modeset_acquire_ctx *ctx)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
48662306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
48762306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
48862306a36Sopenharmony_ci	struct drm_gem_object *obj;
48962306a36Sopenharmony_ci	struct radeon_flip_work *work;
49062306a36Sopenharmony_ci	struct radeon_bo *new_rbo;
49162306a36Sopenharmony_ci	uint32_t tiling_flags, pitch_pixels;
49262306a36Sopenharmony_ci	uint64_t base;
49362306a36Sopenharmony_ci	unsigned long flags;
49462306a36Sopenharmony_ci	int r;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	work = kzalloc(sizeof *work, GFP_KERNEL);
49762306a36Sopenharmony_ci	if (work == NULL)
49862306a36Sopenharmony_ci		return -ENOMEM;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	INIT_WORK(&work->flip_work, radeon_flip_work_func);
50162306a36Sopenharmony_ci	INIT_WORK(&work->unpin_work, radeon_unpin_work_func);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	work->rdev = rdev;
50462306a36Sopenharmony_ci	work->crtc_id = radeon_crtc->crtc_id;
50562306a36Sopenharmony_ci	work->event = event;
50662306a36Sopenharmony_ci	work->async = (page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC) != 0;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* schedule unpin of the old buffer */
50962306a36Sopenharmony_ci	obj = crtc->primary->fb->obj[0];
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* take a reference to the old object */
51262306a36Sopenharmony_ci	drm_gem_object_get(obj);
51362306a36Sopenharmony_ci	work->old_rbo = gem_to_radeon_bo(obj);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	obj = fb->obj[0];
51662306a36Sopenharmony_ci	new_rbo = gem_to_radeon_bo(obj);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* pin the new buffer */
51962306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("flip-ioctl() cur_rbo = %p, new_rbo = %p\n",
52062306a36Sopenharmony_ci			 work->old_rbo, new_rbo);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	r = radeon_bo_reserve(new_rbo, false);
52362306a36Sopenharmony_ci	if (unlikely(r != 0)) {
52462306a36Sopenharmony_ci		DRM_ERROR("failed to reserve new rbo buffer before flip\n");
52562306a36Sopenharmony_ci		goto cleanup;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci	/* Only 27 bit offset for legacy CRTC */
52862306a36Sopenharmony_ci	r = radeon_bo_pin_restricted(new_rbo, RADEON_GEM_DOMAIN_VRAM,
52962306a36Sopenharmony_ci				     ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base);
53062306a36Sopenharmony_ci	if (unlikely(r != 0)) {
53162306a36Sopenharmony_ci		radeon_bo_unreserve(new_rbo);
53262306a36Sopenharmony_ci		r = -EINVAL;
53362306a36Sopenharmony_ci		DRM_ERROR("failed to pin new rbo buffer before flip\n");
53462306a36Sopenharmony_ci		goto cleanup;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci	r = dma_resv_get_singleton(new_rbo->tbo.base.resv, DMA_RESV_USAGE_WRITE,
53762306a36Sopenharmony_ci				   &work->fence);
53862306a36Sopenharmony_ci	if (r) {
53962306a36Sopenharmony_ci		radeon_bo_unreserve(new_rbo);
54062306a36Sopenharmony_ci		DRM_ERROR("failed to get new rbo buffer fences\n");
54162306a36Sopenharmony_ci		goto cleanup;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci	radeon_bo_get_tiling_flags(new_rbo, &tiling_flags, NULL);
54462306a36Sopenharmony_ci	radeon_bo_unreserve(new_rbo);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (!ASIC_IS_AVIVO(rdev)) {
54762306a36Sopenharmony_ci		/* crtc offset is from display base addr not FB location */
54862306a36Sopenharmony_ci		base -= radeon_crtc->legacy_display_base_addr;
54962306a36Sopenharmony_ci		pitch_pixels = fb->pitches[0] / fb->format->cpp[0];
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		if (tiling_flags & RADEON_TILING_MACRO) {
55262306a36Sopenharmony_ci			if (ASIC_IS_R300(rdev)) {
55362306a36Sopenharmony_ci				base &= ~0x7ff;
55462306a36Sopenharmony_ci			} else {
55562306a36Sopenharmony_ci				int byteshift = fb->format->cpp[0] * 8 >> 4;
55662306a36Sopenharmony_ci				int tile_addr = (((crtc->y >> 3) * pitch_pixels +  crtc->x) >> (8 - byteshift)) << 11;
55762306a36Sopenharmony_ci				base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8);
55862306a36Sopenharmony_ci			}
55962306a36Sopenharmony_ci		} else {
56062306a36Sopenharmony_ci			int offset = crtc->y * pitch_pixels + crtc->x;
56162306a36Sopenharmony_ci			switch (fb->format->cpp[0] * 8) {
56262306a36Sopenharmony_ci			case 8:
56362306a36Sopenharmony_ci			default:
56462306a36Sopenharmony_ci				offset *= 1;
56562306a36Sopenharmony_ci				break;
56662306a36Sopenharmony_ci			case 15:
56762306a36Sopenharmony_ci			case 16:
56862306a36Sopenharmony_ci				offset *= 2;
56962306a36Sopenharmony_ci				break;
57062306a36Sopenharmony_ci			case 24:
57162306a36Sopenharmony_ci				offset *= 3;
57262306a36Sopenharmony_ci				break;
57362306a36Sopenharmony_ci			case 32:
57462306a36Sopenharmony_ci				offset *= 4;
57562306a36Sopenharmony_ci				break;
57662306a36Sopenharmony_ci			}
57762306a36Sopenharmony_ci			base += offset;
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci		base &= ~7;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	work->base = base;
58262306a36Sopenharmony_ci	work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) +
58362306a36Sopenharmony_ci		crtc->funcs->get_vblank_counter(crtc);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* We borrow the event spin lock for protecting flip_work */
58662306a36Sopenharmony_ci	spin_lock_irqsave(&crtc->dev->event_lock, flags);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (radeon_crtc->flip_status != RADEON_FLIP_NONE) {
58962306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
59062306a36Sopenharmony_ci		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
59162306a36Sopenharmony_ci		r = -EBUSY;
59262306a36Sopenharmony_ci		goto pflip_cleanup;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci	radeon_crtc->flip_status = RADEON_FLIP_PENDING;
59562306a36Sopenharmony_ci	radeon_crtc->flip_work = work;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* update crtc fb */
59862306a36Sopenharmony_ci	crtc->primary->fb = fb;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	queue_work(radeon_crtc->flip_queue, &work->flip_work);
60362306a36Sopenharmony_ci	return 0;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cipflip_cleanup:
60662306a36Sopenharmony_ci	if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) {
60762306a36Sopenharmony_ci		DRM_ERROR("failed to reserve new rbo in error path\n");
60862306a36Sopenharmony_ci		goto cleanup;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci	radeon_bo_unpin(new_rbo);
61162306a36Sopenharmony_ci	radeon_bo_unreserve(new_rbo);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cicleanup:
61462306a36Sopenharmony_ci	drm_gem_object_put(&work->old_rbo->tbo.base);
61562306a36Sopenharmony_ci	dma_fence_put(work->fence);
61662306a36Sopenharmony_ci	kfree(work);
61762306a36Sopenharmony_ci	return r;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int
62162306a36Sopenharmony_ciradeon_crtc_set_config(struct drm_mode_set *set,
62262306a36Sopenharmony_ci		       struct drm_modeset_acquire_ctx *ctx)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct drm_device *dev;
62562306a36Sopenharmony_ci	struct radeon_device *rdev;
62662306a36Sopenharmony_ci	struct drm_crtc *crtc;
62762306a36Sopenharmony_ci	bool active = false;
62862306a36Sopenharmony_ci	int ret;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (!set || !set->crtc)
63162306a36Sopenharmony_ci		return -EINVAL;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	dev = set->crtc->dev;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	ret = pm_runtime_get_sync(dev->dev);
63662306a36Sopenharmony_ci	if (ret < 0) {
63762306a36Sopenharmony_ci		pm_runtime_put_autosuspend(dev->dev);
63862306a36Sopenharmony_ci		return ret;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ret = drm_crtc_helper_set_config(set, ctx);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
64462306a36Sopenharmony_ci		if (crtc->enabled)
64562306a36Sopenharmony_ci			active = true;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev->dev);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	rdev = dev->dev_private;
65062306a36Sopenharmony_ci	/* if we have active crtcs and we don't have a power ref,
65162306a36Sopenharmony_ci	   take the current one */
65262306a36Sopenharmony_ci	if (active && !rdev->have_disp_power_ref) {
65362306a36Sopenharmony_ci		rdev->have_disp_power_ref = true;
65462306a36Sopenharmony_ci		return ret;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci	/* if we have no active crtcs, then drop the power ref
65762306a36Sopenharmony_ci	   we got before */
65862306a36Sopenharmony_ci	if (!active && rdev->have_disp_power_ref) {
65962306a36Sopenharmony_ci		pm_runtime_put_autosuspend(dev->dev);
66062306a36Sopenharmony_ci		rdev->have_disp_power_ref = false;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* drop the power reference we got coming in here */
66462306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev->dev);
66562306a36Sopenharmony_ci	return ret;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic const struct drm_crtc_funcs radeon_crtc_funcs = {
66962306a36Sopenharmony_ci	.cursor_set2 = radeon_crtc_cursor_set2,
67062306a36Sopenharmony_ci	.cursor_move = radeon_crtc_cursor_move,
67162306a36Sopenharmony_ci	.gamma_set = radeon_crtc_gamma_set,
67262306a36Sopenharmony_ci	.set_config = radeon_crtc_set_config,
67362306a36Sopenharmony_ci	.destroy = radeon_crtc_destroy,
67462306a36Sopenharmony_ci	.page_flip_target = radeon_crtc_page_flip_target,
67562306a36Sopenharmony_ci	.get_vblank_counter = radeon_get_vblank_counter_kms,
67662306a36Sopenharmony_ci	.enable_vblank = radeon_enable_vblank_kms,
67762306a36Sopenharmony_ci	.disable_vblank = radeon_disable_vblank_kms,
67862306a36Sopenharmony_ci	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
67962306a36Sopenharmony_ci};
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic void radeon_crtc_init(struct drm_device *dev, int index)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
68462306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	radeon_crtc = kzalloc(sizeof(struct radeon_crtc) + (RADEONFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
68762306a36Sopenharmony_ci	if (radeon_crtc == NULL)
68862306a36Sopenharmony_ci		return;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	radeon_crtc->flip_queue = alloc_workqueue("radeon-crtc", WQ_HIGHPRI, 0);
69162306a36Sopenharmony_ci	if (!radeon_crtc->flip_queue) {
69262306a36Sopenharmony_ci		kfree(radeon_crtc);
69362306a36Sopenharmony_ci		return;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256);
69962306a36Sopenharmony_ci	radeon_crtc->crtc_id = index;
70062306a36Sopenharmony_ci	rdev->mode_info.crtcs[index] = radeon_crtc;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (rdev->family >= CHIP_BONAIRE) {
70362306a36Sopenharmony_ci		radeon_crtc->max_cursor_width = CIK_CURSOR_WIDTH;
70462306a36Sopenharmony_ci		radeon_crtc->max_cursor_height = CIK_CURSOR_HEIGHT;
70562306a36Sopenharmony_ci	} else {
70662306a36Sopenharmony_ci		radeon_crtc->max_cursor_width = CURSOR_WIDTH;
70762306a36Sopenharmony_ci		radeon_crtc->max_cursor_height = CURSOR_HEIGHT;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci	dev->mode_config.cursor_width = radeon_crtc->max_cursor_width;
71062306a36Sopenharmony_ci	dev->mode_config.cursor_height = radeon_crtc->max_cursor_height;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci#if 0
71362306a36Sopenharmony_ci	radeon_crtc->mode_set.crtc = &radeon_crtc->base;
71462306a36Sopenharmony_ci	radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1);
71562306a36Sopenharmony_ci	radeon_crtc->mode_set.num_connectors = 0;
71662306a36Sopenharmony_ci#endif
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (rdev->is_atom_bios && (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom))
71962306a36Sopenharmony_ci		radeon_atombios_init_crtc(dev, radeon_crtc);
72062306a36Sopenharmony_ci	else
72162306a36Sopenharmony_ci		radeon_legacy_init_crtc(dev, radeon_crtc);
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic const char *encoder_names[38] = {
72562306a36Sopenharmony_ci	"NONE",
72662306a36Sopenharmony_ci	"INTERNAL_LVDS",
72762306a36Sopenharmony_ci	"INTERNAL_TMDS1",
72862306a36Sopenharmony_ci	"INTERNAL_TMDS2",
72962306a36Sopenharmony_ci	"INTERNAL_DAC1",
73062306a36Sopenharmony_ci	"INTERNAL_DAC2",
73162306a36Sopenharmony_ci	"INTERNAL_SDVOA",
73262306a36Sopenharmony_ci	"INTERNAL_SDVOB",
73362306a36Sopenharmony_ci	"SI170B",
73462306a36Sopenharmony_ci	"CH7303",
73562306a36Sopenharmony_ci	"CH7301",
73662306a36Sopenharmony_ci	"INTERNAL_DVO1",
73762306a36Sopenharmony_ci	"EXTERNAL_SDVOA",
73862306a36Sopenharmony_ci	"EXTERNAL_SDVOB",
73962306a36Sopenharmony_ci	"TITFP513",
74062306a36Sopenharmony_ci	"INTERNAL_LVTM1",
74162306a36Sopenharmony_ci	"VT1623",
74262306a36Sopenharmony_ci	"HDMI_SI1930",
74362306a36Sopenharmony_ci	"HDMI_INTERNAL",
74462306a36Sopenharmony_ci	"INTERNAL_KLDSCP_TMDS1",
74562306a36Sopenharmony_ci	"INTERNAL_KLDSCP_DVO1",
74662306a36Sopenharmony_ci	"INTERNAL_KLDSCP_DAC1",
74762306a36Sopenharmony_ci	"INTERNAL_KLDSCP_DAC2",
74862306a36Sopenharmony_ci	"SI178",
74962306a36Sopenharmony_ci	"MVPU_FPGA",
75062306a36Sopenharmony_ci	"INTERNAL_DDI",
75162306a36Sopenharmony_ci	"VT1625",
75262306a36Sopenharmony_ci	"HDMI_SI1932",
75362306a36Sopenharmony_ci	"DP_AN9801",
75462306a36Sopenharmony_ci	"DP_DP501",
75562306a36Sopenharmony_ci	"INTERNAL_UNIPHY",
75662306a36Sopenharmony_ci	"INTERNAL_KLDSCP_LVTMA",
75762306a36Sopenharmony_ci	"INTERNAL_UNIPHY1",
75862306a36Sopenharmony_ci	"INTERNAL_UNIPHY2",
75962306a36Sopenharmony_ci	"NUTMEG",
76062306a36Sopenharmony_ci	"TRAVIS",
76162306a36Sopenharmony_ci	"INTERNAL_VCE",
76262306a36Sopenharmony_ci	"INTERNAL_UNIPHY3",
76362306a36Sopenharmony_ci};
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic const char *hpd_names[6] = {
76662306a36Sopenharmony_ci	"HPD1",
76762306a36Sopenharmony_ci	"HPD2",
76862306a36Sopenharmony_ci	"HPD3",
76962306a36Sopenharmony_ci	"HPD4",
77062306a36Sopenharmony_ci	"HPD5",
77162306a36Sopenharmony_ci	"HPD6",
77262306a36Sopenharmony_ci};
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic void radeon_print_display_setup(struct drm_device *dev)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct drm_connector *connector;
77762306a36Sopenharmony_ci	struct radeon_connector *radeon_connector;
77862306a36Sopenharmony_ci	struct drm_encoder *encoder;
77962306a36Sopenharmony_ci	struct radeon_encoder *radeon_encoder;
78062306a36Sopenharmony_ci	uint32_t devices;
78162306a36Sopenharmony_ci	int i = 0;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	DRM_INFO("Radeon Display Connectors\n");
78462306a36Sopenharmony_ci	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
78562306a36Sopenharmony_ci		radeon_connector = to_radeon_connector(connector);
78662306a36Sopenharmony_ci		DRM_INFO("Connector %d:\n", i);
78762306a36Sopenharmony_ci		DRM_INFO("  %s\n", connector->name);
78862306a36Sopenharmony_ci		if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
78962306a36Sopenharmony_ci			DRM_INFO("  %s\n", hpd_names[radeon_connector->hpd.hpd]);
79062306a36Sopenharmony_ci		if (radeon_connector->ddc_bus) {
79162306a36Sopenharmony_ci			DRM_INFO("  DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
79262306a36Sopenharmony_ci				 radeon_connector->ddc_bus->rec.mask_clk_reg,
79362306a36Sopenharmony_ci				 radeon_connector->ddc_bus->rec.mask_data_reg,
79462306a36Sopenharmony_ci				 radeon_connector->ddc_bus->rec.a_clk_reg,
79562306a36Sopenharmony_ci				 radeon_connector->ddc_bus->rec.a_data_reg,
79662306a36Sopenharmony_ci				 radeon_connector->ddc_bus->rec.en_clk_reg,
79762306a36Sopenharmony_ci				 radeon_connector->ddc_bus->rec.en_data_reg,
79862306a36Sopenharmony_ci				 radeon_connector->ddc_bus->rec.y_clk_reg,
79962306a36Sopenharmony_ci				 radeon_connector->ddc_bus->rec.y_data_reg);
80062306a36Sopenharmony_ci			if (radeon_connector->router.ddc_valid)
80162306a36Sopenharmony_ci				DRM_INFO("  DDC Router 0x%x/0x%x\n",
80262306a36Sopenharmony_ci					 radeon_connector->router.ddc_mux_control_pin,
80362306a36Sopenharmony_ci					 radeon_connector->router.ddc_mux_state);
80462306a36Sopenharmony_ci			if (radeon_connector->router.cd_valid)
80562306a36Sopenharmony_ci				DRM_INFO("  Clock/Data Router 0x%x/0x%x\n",
80662306a36Sopenharmony_ci					 radeon_connector->router.cd_mux_control_pin,
80762306a36Sopenharmony_ci					 radeon_connector->router.cd_mux_state);
80862306a36Sopenharmony_ci		} else {
80962306a36Sopenharmony_ci			if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||
81062306a36Sopenharmony_ci			    connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
81162306a36Sopenharmony_ci			    connector->connector_type == DRM_MODE_CONNECTOR_DVID ||
81262306a36Sopenharmony_ci			    connector->connector_type == DRM_MODE_CONNECTOR_DVIA ||
81362306a36Sopenharmony_ci			    connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
81462306a36Sopenharmony_ci			    connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)
81562306a36Sopenharmony_ci				DRM_INFO("  DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n");
81662306a36Sopenharmony_ci		}
81762306a36Sopenharmony_ci		DRM_INFO("  Encoders:\n");
81862306a36Sopenharmony_ci		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
81962306a36Sopenharmony_ci			radeon_encoder = to_radeon_encoder(encoder);
82062306a36Sopenharmony_ci			devices = radeon_encoder->devices & radeon_connector->devices;
82162306a36Sopenharmony_ci			if (devices) {
82262306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_CRT1_SUPPORT)
82362306a36Sopenharmony_ci					DRM_INFO("    CRT1: %s\n", encoder_names[radeon_encoder->encoder_id]);
82462306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_CRT2_SUPPORT)
82562306a36Sopenharmony_ci					DRM_INFO("    CRT2: %s\n", encoder_names[radeon_encoder->encoder_id]);
82662306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_LCD1_SUPPORT)
82762306a36Sopenharmony_ci					DRM_INFO("    LCD1: %s\n", encoder_names[radeon_encoder->encoder_id]);
82862306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_DFP1_SUPPORT)
82962306a36Sopenharmony_ci					DRM_INFO("    DFP1: %s\n", encoder_names[radeon_encoder->encoder_id]);
83062306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_DFP2_SUPPORT)
83162306a36Sopenharmony_ci					DRM_INFO("    DFP2: %s\n", encoder_names[radeon_encoder->encoder_id]);
83262306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_DFP3_SUPPORT)
83362306a36Sopenharmony_ci					DRM_INFO("    DFP3: %s\n", encoder_names[radeon_encoder->encoder_id]);
83462306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_DFP4_SUPPORT)
83562306a36Sopenharmony_ci					DRM_INFO("    DFP4: %s\n", encoder_names[radeon_encoder->encoder_id]);
83662306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_DFP5_SUPPORT)
83762306a36Sopenharmony_ci					DRM_INFO("    DFP5: %s\n", encoder_names[radeon_encoder->encoder_id]);
83862306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_DFP6_SUPPORT)
83962306a36Sopenharmony_ci					DRM_INFO("    DFP6: %s\n", encoder_names[radeon_encoder->encoder_id]);
84062306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_TV1_SUPPORT)
84162306a36Sopenharmony_ci					DRM_INFO("    TV1: %s\n", encoder_names[radeon_encoder->encoder_id]);
84262306a36Sopenharmony_ci				if (devices & ATOM_DEVICE_CV_SUPPORT)
84362306a36Sopenharmony_ci					DRM_INFO("    CV: %s\n", encoder_names[radeon_encoder->encoder_id]);
84462306a36Sopenharmony_ci			}
84562306a36Sopenharmony_ci		}
84662306a36Sopenharmony_ci		i++;
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic bool radeon_setup_enc_conn(struct drm_device *dev)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
85362306a36Sopenharmony_ci	bool ret = false;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (rdev->bios) {
85662306a36Sopenharmony_ci		if (rdev->is_atom_bios) {
85762306a36Sopenharmony_ci			ret = radeon_get_atom_connector_info_from_supported_devices_table(dev);
85862306a36Sopenharmony_ci			if (!ret)
85962306a36Sopenharmony_ci				ret = radeon_get_atom_connector_info_from_object_table(dev);
86062306a36Sopenharmony_ci		} else {
86162306a36Sopenharmony_ci			ret = radeon_get_legacy_connector_info_from_bios(dev);
86262306a36Sopenharmony_ci			if (!ret)
86362306a36Sopenharmony_ci				ret = radeon_get_legacy_connector_info_from_table(dev);
86462306a36Sopenharmony_ci		}
86562306a36Sopenharmony_ci	} else {
86662306a36Sopenharmony_ci		if (!ASIC_IS_AVIVO(rdev))
86762306a36Sopenharmony_ci			ret = radeon_get_legacy_connector_info_from_table(dev);
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci	if (ret) {
87062306a36Sopenharmony_ci		radeon_setup_encoder_clones(dev);
87162306a36Sopenharmony_ci		radeon_print_display_setup(dev);
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	return ret;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci/* avivo */
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci/**
88062306a36Sopenharmony_ci * avivo_reduce_ratio - fractional number reduction
88162306a36Sopenharmony_ci *
88262306a36Sopenharmony_ci * @nom: nominator
88362306a36Sopenharmony_ci * @den: denominator
88462306a36Sopenharmony_ci * @nom_min: minimum value for nominator
88562306a36Sopenharmony_ci * @den_min: minimum value for denominator
88662306a36Sopenharmony_ci *
88762306a36Sopenharmony_ci * Find the greatest common divisor and apply it on both nominator and
88862306a36Sopenharmony_ci * denominator, but make nominator and denominator are at least as large
88962306a36Sopenharmony_ci * as their minimum values.
89062306a36Sopenharmony_ci */
89162306a36Sopenharmony_cistatic void avivo_reduce_ratio(unsigned *nom, unsigned *den,
89262306a36Sopenharmony_ci			       unsigned nom_min, unsigned den_min)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	unsigned tmp;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/* reduce the numbers to a simpler ratio */
89762306a36Sopenharmony_ci	tmp = gcd(*nom, *den);
89862306a36Sopenharmony_ci	*nom /= tmp;
89962306a36Sopenharmony_ci	*den /= tmp;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	/* make sure nominator is large enough */
90262306a36Sopenharmony_ci	if (*nom < nom_min) {
90362306a36Sopenharmony_ci		tmp = DIV_ROUND_UP(nom_min, *nom);
90462306a36Sopenharmony_ci		*nom *= tmp;
90562306a36Sopenharmony_ci		*den *= tmp;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/* make sure the denominator is large enough */
90962306a36Sopenharmony_ci	if (*den < den_min) {
91062306a36Sopenharmony_ci		tmp = DIV_ROUND_UP(den_min, *den);
91162306a36Sopenharmony_ci		*nom *= tmp;
91262306a36Sopenharmony_ci		*den *= tmp;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci/**
91762306a36Sopenharmony_ci * avivo_get_fb_ref_div - feedback and ref divider calculation
91862306a36Sopenharmony_ci *
91962306a36Sopenharmony_ci * @nom: nominator
92062306a36Sopenharmony_ci * @den: denominator
92162306a36Sopenharmony_ci * @post_div: post divider
92262306a36Sopenharmony_ci * @fb_div_max: feedback divider maximum
92362306a36Sopenharmony_ci * @ref_div_max: reference divider maximum
92462306a36Sopenharmony_ci * @fb_div: resulting feedback divider
92562306a36Sopenharmony_ci * @ref_div: resulting reference divider
92662306a36Sopenharmony_ci *
92762306a36Sopenharmony_ci * Calculate feedback and reference divider for a given post divider. Makes
92862306a36Sopenharmony_ci * sure we stay within the limits.
92962306a36Sopenharmony_ci */
93062306a36Sopenharmony_cistatic void avivo_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div,
93162306a36Sopenharmony_ci				 unsigned fb_div_max, unsigned ref_div_max,
93262306a36Sopenharmony_ci				 unsigned *fb_div, unsigned *ref_div)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	/* limit reference * post divider to a maximum */
93562306a36Sopenharmony_ci	ref_div_max = max(min(100 / post_div, ref_div_max), 1u);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* get matching reference and feedback divider */
93862306a36Sopenharmony_ci	*ref_div = min(max(den/post_div, 1u), ref_div_max);
93962306a36Sopenharmony_ci	*fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* limit fb divider to its maximum */
94262306a36Sopenharmony_ci	if (*fb_div > fb_div_max) {
94362306a36Sopenharmony_ci		*ref_div = (*ref_div * fb_div_max)/(*fb_div);
94462306a36Sopenharmony_ci		*fb_div = fb_div_max;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci/**
94962306a36Sopenharmony_ci * radeon_compute_pll_avivo - compute PLL paramaters
95062306a36Sopenharmony_ci *
95162306a36Sopenharmony_ci * @pll: information about the PLL
95262306a36Sopenharmony_ci * @freq: target frequency
95362306a36Sopenharmony_ci * @dot_clock_p: resulting pixel clock
95462306a36Sopenharmony_ci * @fb_div_p: resulting feedback divider
95562306a36Sopenharmony_ci * @frac_fb_div_p: fractional part of the feedback divider
95662306a36Sopenharmony_ci * @ref_div_p: resulting reference divider
95762306a36Sopenharmony_ci * @post_div_p: resulting reference divider
95862306a36Sopenharmony_ci *
95962306a36Sopenharmony_ci * Try to calculate the PLL parameters to generate the given frequency:
96062306a36Sopenharmony_ci * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div)
96162306a36Sopenharmony_ci */
96262306a36Sopenharmony_civoid radeon_compute_pll_avivo(struct radeon_pll *pll,
96362306a36Sopenharmony_ci			      u32 freq,
96462306a36Sopenharmony_ci			      u32 *dot_clock_p,
96562306a36Sopenharmony_ci			      u32 *fb_div_p,
96662306a36Sopenharmony_ci			      u32 *frac_fb_div_p,
96762306a36Sopenharmony_ci			      u32 *ref_div_p,
96862306a36Sopenharmony_ci			      u32 *post_div_p)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	unsigned target_clock = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ?
97162306a36Sopenharmony_ci		freq : freq / 10;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	unsigned fb_div_min, fb_div_max, fb_div;
97462306a36Sopenharmony_ci	unsigned post_div_min, post_div_max, post_div;
97562306a36Sopenharmony_ci	unsigned ref_div_min, ref_div_max, ref_div;
97662306a36Sopenharmony_ci	unsigned post_div_best, diff_best;
97762306a36Sopenharmony_ci	unsigned nom, den;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* determine allowed feedback divider range */
98062306a36Sopenharmony_ci	fb_div_min = pll->min_feedback_div;
98162306a36Sopenharmony_ci	fb_div_max = pll->max_feedback_div;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
98462306a36Sopenharmony_ci		fb_div_min *= 10;
98562306a36Sopenharmony_ci		fb_div_max *= 10;
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	/* determine allowed ref divider range */
98962306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_REF_DIV)
99062306a36Sopenharmony_ci		ref_div_min = pll->reference_div;
99162306a36Sopenharmony_ci	else
99262306a36Sopenharmony_ci		ref_div_min = pll->min_ref_div;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV &&
99562306a36Sopenharmony_ci	    pll->flags & RADEON_PLL_USE_REF_DIV)
99662306a36Sopenharmony_ci		ref_div_max = pll->reference_div;
99762306a36Sopenharmony_ci	else if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP)
99862306a36Sopenharmony_ci		/* fix for problems on RS880 */
99962306a36Sopenharmony_ci		ref_div_max = min(pll->max_ref_div, 7u);
100062306a36Sopenharmony_ci	else
100162306a36Sopenharmony_ci		ref_div_max = pll->max_ref_div;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	/* determine allowed post divider range */
100462306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_POST_DIV) {
100562306a36Sopenharmony_ci		post_div_min = pll->post_div;
100662306a36Sopenharmony_ci		post_div_max = pll->post_div;
100762306a36Sopenharmony_ci	} else {
100862306a36Sopenharmony_ci		unsigned vco_min, vco_max;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		if (pll->flags & RADEON_PLL_IS_LCD) {
101162306a36Sopenharmony_ci			vco_min = pll->lcd_pll_out_min;
101262306a36Sopenharmony_ci			vco_max = pll->lcd_pll_out_max;
101362306a36Sopenharmony_ci		} else {
101462306a36Sopenharmony_ci			vco_min = pll->pll_out_min;
101562306a36Sopenharmony_ci			vco_max = pll->pll_out_max;
101662306a36Sopenharmony_ci		}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
101962306a36Sopenharmony_ci			vco_min *= 10;
102062306a36Sopenharmony_ci			vco_max *= 10;
102162306a36Sopenharmony_ci		}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		post_div_min = vco_min / target_clock;
102462306a36Sopenharmony_ci		if ((target_clock * post_div_min) < vco_min)
102562306a36Sopenharmony_ci			++post_div_min;
102662306a36Sopenharmony_ci		if (post_div_min < pll->min_post_div)
102762306a36Sopenharmony_ci			post_div_min = pll->min_post_div;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci		post_div_max = vco_max / target_clock;
103062306a36Sopenharmony_ci		if ((target_clock * post_div_max) > vco_max)
103162306a36Sopenharmony_ci			--post_div_max;
103262306a36Sopenharmony_ci		if (post_div_max > pll->max_post_div)
103362306a36Sopenharmony_ci			post_div_max = pll->max_post_div;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/* represent the searched ratio as fractional number */
103762306a36Sopenharmony_ci	nom = target_clock;
103862306a36Sopenharmony_ci	den = pll->reference_freq;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* reduce the numbers to a simpler ratio */
104162306a36Sopenharmony_ci	avivo_reduce_ratio(&nom, &den, fb_div_min, post_div_min);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	/* now search for a post divider */
104462306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP)
104562306a36Sopenharmony_ci		post_div_best = post_div_min;
104662306a36Sopenharmony_ci	else
104762306a36Sopenharmony_ci		post_div_best = post_div_max;
104862306a36Sopenharmony_ci	diff_best = ~0;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	for (post_div = post_div_min; post_div <= post_div_max; ++post_div) {
105162306a36Sopenharmony_ci		unsigned diff;
105262306a36Sopenharmony_ci		avivo_get_fb_ref_div(nom, den, post_div, fb_div_max,
105362306a36Sopenharmony_ci				     ref_div_max, &fb_div, &ref_div);
105462306a36Sopenharmony_ci		diff = abs(target_clock - (pll->reference_freq * fb_div) /
105562306a36Sopenharmony_ci			(ref_div * post_div));
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci		if (diff < diff_best || (diff == diff_best &&
105862306a36Sopenharmony_ci		    !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) {
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci			post_div_best = post_div;
106162306a36Sopenharmony_ci			diff_best = diff;
106262306a36Sopenharmony_ci		}
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci	post_div = post_div_best;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* get the feedback and reference divider for the optimal value */
106762306a36Sopenharmony_ci	avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, ref_div_max,
106862306a36Sopenharmony_ci			     &fb_div, &ref_div);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	/* reduce the numbers to a simpler ratio once more */
107162306a36Sopenharmony_ci	/* this also makes sure that the reference divider is large enough */
107262306a36Sopenharmony_ci	avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	/* avoid high jitter with small fractional dividers */
107562306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) {
107662306a36Sopenharmony_ci		fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 50);
107762306a36Sopenharmony_ci		if (fb_div < fb_div_min) {
107862306a36Sopenharmony_ci			unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div);
107962306a36Sopenharmony_ci			fb_div *= tmp;
108062306a36Sopenharmony_ci			ref_div *= tmp;
108162306a36Sopenharmony_ci		}
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/* and finally save the result */
108562306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
108662306a36Sopenharmony_ci		*fb_div_p = fb_div / 10;
108762306a36Sopenharmony_ci		*frac_fb_div_p = fb_div % 10;
108862306a36Sopenharmony_ci	} else {
108962306a36Sopenharmony_ci		*fb_div_p = fb_div;
109062306a36Sopenharmony_ci		*frac_fb_div_p = 0;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	*dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) +
109462306a36Sopenharmony_ci			(pll->reference_freq * *frac_fb_div_p)) /
109562306a36Sopenharmony_ci		       (ref_div * post_div * 10);
109662306a36Sopenharmony_ci	*ref_div_p = ref_div;
109762306a36Sopenharmony_ci	*post_div_p = post_div;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
110062306a36Sopenharmony_ci		      freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p,
110162306a36Sopenharmony_ci		      ref_div, post_div);
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci/* pre-avivo */
110562306a36Sopenharmony_cistatic inline uint32_t radeon_div(uint64_t n, uint32_t d)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	n += d / 2;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	do_div(n, d);
111062306a36Sopenharmony_ci	return n;
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_civoid radeon_compute_pll_legacy(struct radeon_pll *pll,
111462306a36Sopenharmony_ci			       uint64_t freq,
111562306a36Sopenharmony_ci			       uint32_t *dot_clock_p,
111662306a36Sopenharmony_ci			       uint32_t *fb_div_p,
111762306a36Sopenharmony_ci			       uint32_t *frac_fb_div_p,
111862306a36Sopenharmony_ci			       uint32_t *ref_div_p,
111962306a36Sopenharmony_ci			       uint32_t *post_div_p)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	uint32_t min_ref_div = pll->min_ref_div;
112262306a36Sopenharmony_ci	uint32_t max_ref_div = pll->max_ref_div;
112362306a36Sopenharmony_ci	uint32_t min_post_div = pll->min_post_div;
112462306a36Sopenharmony_ci	uint32_t max_post_div = pll->max_post_div;
112562306a36Sopenharmony_ci	uint32_t min_fractional_feed_div = 0;
112662306a36Sopenharmony_ci	uint32_t max_fractional_feed_div = 0;
112762306a36Sopenharmony_ci	uint32_t best_vco = pll->best_vco;
112862306a36Sopenharmony_ci	uint32_t best_post_div = 1;
112962306a36Sopenharmony_ci	uint32_t best_ref_div = 1;
113062306a36Sopenharmony_ci	uint32_t best_feedback_div = 1;
113162306a36Sopenharmony_ci	uint32_t best_frac_feedback_div = 0;
113262306a36Sopenharmony_ci	uint32_t best_freq = -1;
113362306a36Sopenharmony_ci	uint32_t best_error = 0xffffffff;
113462306a36Sopenharmony_ci	uint32_t best_vco_diff = 1;
113562306a36Sopenharmony_ci	uint32_t post_div;
113662306a36Sopenharmony_ci	u32 pll_out_min, pll_out_max;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	DRM_DEBUG_KMS("PLL freq %llu %u %u\n", freq, pll->min_ref_div, pll->max_ref_div);
113962306a36Sopenharmony_ci	freq = freq * 1000;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_IS_LCD) {
114262306a36Sopenharmony_ci		pll_out_min = pll->lcd_pll_out_min;
114362306a36Sopenharmony_ci		pll_out_max = pll->lcd_pll_out_max;
114462306a36Sopenharmony_ci	} else {
114562306a36Sopenharmony_ci		pll_out_min = pll->pll_out_min;
114662306a36Sopenharmony_ci		pll_out_max = pll->pll_out_max;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (pll_out_min > 64800)
115062306a36Sopenharmony_ci		pll_out_min = 64800;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_REF_DIV)
115362306a36Sopenharmony_ci		min_ref_div = max_ref_div = pll->reference_div;
115462306a36Sopenharmony_ci	else {
115562306a36Sopenharmony_ci		while (min_ref_div < max_ref_div-1) {
115662306a36Sopenharmony_ci			uint32_t mid = (min_ref_div + max_ref_div) / 2;
115762306a36Sopenharmony_ci			uint32_t pll_in = pll->reference_freq / mid;
115862306a36Sopenharmony_ci			if (pll_in < pll->pll_in_min)
115962306a36Sopenharmony_ci				max_ref_div = mid;
116062306a36Sopenharmony_ci			else if (pll_in > pll->pll_in_max)
116162306a36Sopenharmony_ci				min_ref_div = mid;
116262306a36Sopenharmony_ci			else
116362306a36Sopenharmony_ci				break;
116462306a36Sopenharmony_ci		}
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_POST_DIV)
116862306a36Sopenharmony_ci		min_post_div = max_post_div = pll->post_div;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
117162306a36Sopenharmony_ci		min_fractional_feed_div = pll->min_frac_feedback_div;
117262306a36Sopenharmony_ci		max_fractional_feed_div = pll->max_frac_feedback_div;
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	for (post_div = max_post_div; post_div >= min_post_div; --post_div) {
117662306a36Sopenharmony_ci		uint32_t ref_div;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci		if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
117962306a36Sopenharmony_ci			continue;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci		/* legacy radeons only have a few post_divs */
118262306a36Sopenharmony_ci		if (pll->flags & RADEON_PLL_LEGACY) {
118362306a36Sopenharmony_ci			if ((post_div == 5) ||
118462306a36Sopenharmony_ci			    (post_div == 7) ||
118562306a36Sopenharmony_ci			    (post_div == 9) ||
118662306a36Sopenharmony_ci			    (post_div == 10) ||
118762306a36Sopenharmony_ci			    (post_div == 11) ||
118862306a36Sopenharmony_ci			    (post_div == 13) ||
118962306a36Sopenharmony_ci			    (post_div == 14) ||
119062306a36Sopenharmony_ci			    (post_div == 15))
119162306a36Sopenharmony_ci				continue;
119262306a36Sopenharmony_ci		}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		for (ref_div = min_ref_div; ref_div <= max_ref_div; ++ref_div) {
119562306a36Sopenharmony_ci			uint32_t feedback_div, current_freq = 0, error, vco_diff;
119662306a36Sopenharmony_ci			uint32_t pll_in = pll->reference_freq / ref_div;
119762306a36Sopenharmony_ci			uint32_t min_feed_div = pll->min_feedback_div;
119862306a36Sopenharmony_ci			uint32_t max_feed_div = pll->max_feedback_div + 1;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci			if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max)
120162306a36Sopenharmony_ci				continue;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci			while (min_feed_div < max_feed_div) {
120462306a36Sopenharmony_ci				uint32_t vco;
120562306a36Sopenharmony_ci				uint32_t min_frac_feed_div = min_fractional_feed_div;
120662306a36Sopenharmony_ci				uint32_t max_frac_feed_div = max_fractional_feed_div + 1;
120762306a36Sopenharmony_ci				uint32_t frac_feedback_div;
120862306a36Sopenharmony_ci				uint64_t tmp;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci				feedback_div = (min_feed_div + max_feed_div) / 2;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci				tmp = (uint64_t)pll->reference_freq * feedback_div;
121362306a36Sopenharmony_ci				vco = radeon_div(tmp, ref_div);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci				if (vco < pll_out_min) {
121662306a36Sopenharmony_ci					min_feed_div = feedback_div + 1;
121762306a36Sopenharmony_ci					continue;
121862306a36Sopenharmony_ci				} else if (vco > pll_out_max) {
121962306a36Sopenharmony_ci					max_feed_div = feedback_div;
122062306a36Sopenharmony_ci					continue;
122162306a36Sopenharmony_ci				}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci				while (min_frac_feed_div < max_frac_feed_div) {
122462306a36Sopenharmony_ci					frac_feedback_div = (min_frac_feed_div + max_frac_feed_div) / 2;
122562306a36Sopenharmony_ci					tmp = (uint64_t)pll->reference_freq * 10000 * feedback_div;
122662306a36Sopenharmony_ci					tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div;
122762306a36Sopenharmony_ci					current_freq = radeon_div(tmp, ref_div * post_div);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci					if (pll->flags & RADEON_PLL_PREFER_CLOSEST_LOWER) {
123062306a36Sopenharmony_ci						if (freq < current_freq)
123162306a36Sopenharmony_ci							error = 0xffffffff;
123262306a36Sopenharmony_ci						else
123362306a36Sopenharmony_ci							error = freq - current_freq;
123462306a36Sopenharmony_ci					} else
123562306a36Sopenharmony_ci						error = abs(current_freq - freq);
123662306a36Sopenharmony_ci					vco_diff = abs(vco - best_vco);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci					if ((best_vco == 0 && error < best_error) ||
123962306a36Sopenharmony_ci					    (best_vco != 0 &&
124062306a36Sopenharmony_ci					     ((best_error > 100 && error < best_error - 100) ||
124162306a36Sopenharmony_ci					      (abs(error - best_error) < 100 && vco_diff < best_vco_diff)))) {
124262306a36Sopenharmony_ci						best_post_div = post_div;
124362306a36Sopenharmony_ci						best_ref_div = ref_div;
124462306a36Sopenharmony_ci						best_feedback_div = feedback_div;
124562306a36Sopenharmony_ci						best_frac_feedback_div = frac_feedback_div;
124662306a36Sopenharmony_ci						best_freq = current_freq;
124762306a36Sopenharmony_ci						best_error = error;
124862306a36Sopenharmony_ci						best_vco_diff = vco_diff;
124962306a36Sopenharmony_ci					} else if (current_freq == freq) {
125062306a36Sopenharmony_ci						if (best_freq == -1) {
125162306a36Sopenharmony_ci							best_post_div = post_div;
125262306a36Sopenharmony_ci							best_ref_div = ref_div;
125362306a36Sopenharmony_ci							best_feedback_div = feedback_div;
125462306a36Sopenharmony_ci							best_frac_feedback_div = frac_feedback_div;
125562306a36Sopenharmony_ci							best_freq = current_freq;
125662306a36Sopenharmony_ci							best_error = error;
125762306a36Sopenharmony_ci							best_vco_diff = vco_diff;
125862306a36Sopenharmony_ci						} else if (((pll->flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) ||
125962306a36Sopenharmony_ci							   ((pll->flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) ||
126062306a36Sopenharmony_ci							   ((pll->flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) ||
126162306a36Sopenharmony_ci							   ((pll->flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) ||
126262306a36Sopenharmony_ci							   ((pll->flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) ||
126362306a36Sopenharmony_ci							   ((pll->flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) {
126462306a36Sopenharmony_ci							best_post_div = post_div;
126562306a36Sopenharmony_ci							best_ref_div = ref_div;
126662306a36Sopenharmony_ci							best_feedback_div = feedback_div;
126762306a36Sopenharmony_ci							best_frac_feedback_div = frac_feedback_div;
126862306a36Sopenharmony_ci							best_freq = current_freq;
126962306a36Sopenharmony_ci							best_error = error;
127062306a36Sopenharmony_ci							best_vco_diff = vco_diff;
127162306a36Sopenharmony_ci						}
127262306a36Sopenharmony_ci					}
127362306a36Sopenharmony_ci					if (current_freq < freq)
127462306a36Sopenharmony_ci						min_frac_feed_div = frac_feedback_div + 1;
127562306a36Sopenharmony_ci					else
127662306a36Sopenharmony_ci						max_frac_feed_div = frac_feedback_div;
127762306a36Sopenharmony_ci				}
127862306a36Sopenharmony_ci				if (current_freq < freq)
127962306a36Sopenharmony_ci					min_feed_div = feedback_div + 1;
128062306a36Sopenharmony_ci				else
128162306a36Sopenharmony_ci					max_feed_div = feedback_div;
128262306a36Sopenharmony_ci			}
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	*dot_clock_p = best_freq / 10000;
128762306a36Sopenharmony_ci	*fb_div_p = best_feedback_div;
128862306a36Sopenharmony_ci	*frac_fb_div_p = best_frac_feedback_div;
128962306a36Sopenharmony_ci	*ref_div_p = best_ref_div;
129062306a36Sopenharmony_ci	*post_div_p = best_post_div;
129162306a36Sopenharmony_ci	DRM_DEBUG_KMS("%lld %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
129262306a36Sopenharmony_ci		      (long long)freq,
129362306a36Sopenharmony_ci		      best_freq / 1000, best_feedback_div, best_frac_feedback_div,
129462306a36Sopenharmony_ci		      best_ref_div, best_post_div);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs radeon_fb_funcs = {
129962306a36Sopenharmony_ci	.destroy = drm_gem_fb_destroy,
130062306a36Sopenharmony_ci	.create_handle = drm_gem_fb_create_handle,
130162306a36Sopenharmony_ci};
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ciint
130462306a36Sopenharmony_ciradeon_framebuffer_init(struct drm_device *dev,
130562306a36Sopenharmony_ci			struct drm_framebuffer *fb,
130662306a36Sopenharmony_ci			const struct drm_mode_fb_cmd2 *mode_cmd,
130762306a36Sopenharmony_ci			struct drm_gem_object *obj)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	int ret;
131062306a36Sopenharmony_ci	fb->obj[0] = obj;
131162306a36Sopenharmony_ci	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
131262306a36Sopenharmony_ci	ret = drm_framebuffer_init(dev, fb, &radeon_fb_funcs);
131362306a36Sopenharmony_ci	if (ret) {
131462306a36Sopenharmony_ci		fb->obj[0] = NULL;
131562306a36Sopenharmony_ci		return ret;
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci	return 0;
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cistatic struct drm_framebuffer *
132162306a36Sopenharmony_ciradeon_user_framebuffer_create(struct drm_device *dev,
132262306a36Sopenharmony_ci			       struct drm_file *file_priv,
132362306a36Sopenharmony_ci			       const struct drm_mode_fb_cmd2 *mode_cmd)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	struct drm_gem_object *obj;
132662306a36Sopenharmony_ci	struct drm_framebuffer *fb;
132762306a36Sopenharmony_ci	int ret;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
133062306a36Sopenharmony_ci	if (obj ==  NULL) {
133162306a36Sopenharmony_ci		dev_err(dev->dev, "No GEM object associated to handle 0x%08X, "
133262306a36Sopenharmony_ci			"can't create framebuffer\n", mode_cmd->handles[0]);
133362306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	/* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */
133762306a36Sopenharmony_ci	if (obj->import_attach) {
133862306a36Sopenharmony_ci		DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n");
133962306a36Sopenharmony_ci		drm_gem_object_put(obj);
134062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
134462306a36Sopenharmony_ci	if (fb == NULL) {
134562306a36Sopenharmony_ci		drm_gem_object_put(obj);
134662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	ret = radeon_framebuffer_init(dev, fb, mode_cmd, obj);
135062306a36Sopenharmony_ci	if (ret) {
135162306a36Sopenharmony_ci		kfree(fb);
135262306a36Sopenharmony_ci		drm_gem_object_put(obj);
135362306a36Sopenharmony_ci		return ERR_PTR(ret);
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	return fb;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_cistatic const struct drm_mode_config_funcs radeon_mode_funcs = {
136062306a36Sopenharmony_ci	.fb_create = radeon_user_framebuffer_create,
136162306a36Sopenharmony_ci};
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_tmds_pll_enum_list[] =
136462306a36Sopenharmony_ci{	{ 0, "driver" },
136562306a36Sopenharmony_ci	{ 1, "bios" },
136662306a36Sopenharmony_ci};
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_tv_std_enum_list[] =
136962306a36Sopenharmony_ci{	{ TV_STD_NTSC, "ntsc" },
137062306a36Sopenharmony_ci	{ TV_STD_PAL, "pal" },
137162306a36Sopenharmony_ci	{ TV_STD_PAL_M, "pal-m" },
137262306a36Sopenharmony_ci	{ TV_STD_PAL_60, "pal-60" },
137362306a36Sopenharmony_ci	{ TV_STD_NTSC_J, "ntsc-j" },
137462306a36Sopenharmony_ci	{ TV_STD_SCART_PAL, "scart-pal" },
137562306a36Sopenharmony_ci	{ TV_STD_PAL_CN, "pal-cn" },
137662306a36Sopenharmony_ci	{ TV_STD_SECAM, "secam" },
137762306a36Sopenharmony_ci};
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_underscan_enum_list[] =
138062306a36Sopenharmony_ci{	{ UNDERSCAN_OFF, "off" },
138162306a36Sopenharmony_ci	{ UNDERSCAN_ON, "on" },
138262306a36Sopenharmony_ci	{ UNDERSCAN_AUTO, "auto" },
138362306a36Sopenharmony_ci};
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_audio_enum_list[] =
138662306a36Sopenharmony_ci{	{ RADEON_AUDIO_DISABLE, "off" },
138762306a36Sopenharmony_ci	{ RADEON_AUDIO_ENABLE, "on" },
138862306a36Sopenharmony_ci	{ RADEON_AUDIO_AUTO, "auto" },
138962306a36Sopenharmony_ci};
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci/* XXX support different dither options? spatial, temporal, both, etc. */
139262306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_dither_enum_list[] =
139362306a36Sopenharmony_ci{	{ RADEON_FMT_DITHER_DISABLE, "off" },
139462306a36Sopenharmony_ci	{ RADEON_FMT_DITHER_ENABLE, "on" },
139562306a36Sopenharmony_ci};
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_output_csc_enum_list[] =
139862306a36Sopenharmony_ci{	{ RADEON_OUTPUT_CSC_BYPASS, "bypass" },
139962306a36Sopenharmony_ci	{ RADEON_OUTPUT_CSC_TVRGB, "tvrgb" },
140062306a36Sopenharmony_ci	{ RADEON_OUTPUT_CSC_YCBCR601, "ycbcr601" },
140162306a36Sopenharmony_ci	{ RADEON_OUTPUT_CSC_YCBCR709, "ycbcr709" },
140262306a36Sopenharmony_ci};
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cistatic int radeon_modeset_create_props(struct radeon_device *rdev)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	int sz;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	if (rdev->is_atom_bios) {
140962306a36Sopenharmony_ci		rdev->mode_info.coherent_mode_property =
141062306a36Sopenharmony_ci			drm_property_create_range(rdev->ddev, 0 , "coherent", 0, 1);
141162306a36Sopenharmony_ci		if (!rdev->mode_info.coherent_mode_property)
141262306a36Sopenharmony_ci			return -ENOMEM;
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	if (!ASIC_IS_AVIVO(rdev)) {
141662306a36Sopenharmony_ci		sz = ARRAY_SIZE(radeon_tmds_pll_enum_list);
141762306a36Sopenharmony_ci		rdev->mode_info.tmds_pll_property =
141862306a36Sopenharmony_ci			drm_property_create_enum(rdev->ddev, 0,
141962306a36Sopenharmony_ci					    "tmds_pll",
142062306a36Sopenharmony_ci					    radeon_tmds_pll_enum_list, sz);
142162306a36Sopenharmony_ci	}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	rdev->mode_info.load_detect_property =
142462306a36Sopenharmony_ci		drm_property_create_range(rdev->ddev, 0, "load detection", 0, 1);
142562306a36Sopenharmony_ci	if (!rdev->mode_info.load_detect_property)
142662306a36Sopenharmony_ci		return -ENOMEM;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	drm_mode_create_scaling_mode_property(rdev->ddev);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	sz = ARRAY_SIZE(radeon_tv_std_enum_list);
143162306a36Sopenharmony_ci	rdev->mode_info.tv_std_property =
143262306a36Sopenharmony_ci		drm_property_create_enum(rdev->ddev, 0,
143362306a36Sopenharmony_ci				    "tv standard",
143462306a36Sopenharmony_ci				    radeon_tv_std_enum_list, sz);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	sz = ARRAY_SIZE(radeon_underscan_enum_list);
143762306a36Sopenharmony_ci	rdev->mode_info.underscan_property =
143862306a36Sopenharmony_ci		drm_property_create_enum(rdev->ddev, 0,
143962306a36Sopenharmony_ci				    "underscan",
144062306a36Sopenharmony_ci				    radeon_underscan_enum_list, sz);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	rdev->mode_info.underscan_hborder_property =
144362306a36Sopenharmony_ci		drm_property_create_range(rdev->ddev, 0,
144462306a36Sopenharmony_ci					"underscan hborder", 0, 128);
144562306a36Sopenharmony_ci	if (!rdev->mode_info.underscan_hborder_property)
144662306a36Sopenharmony_ci		return -ENOMEM;
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	rdev->mode_info.underscan_vborder_property =
144962306a36Sopenharmony_ci		drm_property_create_range(rdev->ddev, 0,
145062306a36Sopenharmony_ci					"underscan vborder", 0, 128);
145162306a36Sopenharmony_ci	if (!rdev->mode_info.underscan_vborder_property)
145262306a36Sopenharmony_ci		return -ENOMEM;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	sz = ARRAY_SIZE(radeon_audio_enum_list);
145562306a36Sopenharmony_ci	rdev->mode_info.audio_property =
145662306a36Sopenharmony_ci		drm_property_create_enum(rdev->ddev, 0,
145762306a36Sopenharmony_ci					 "audio",
145862306a36Sopenharmony_ci					 radeon_audio_enum_list, sz);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	sz = ARRAY_SIZE(radeon_dither_enum_list);
146162306a36Sopenharmony_ci	rdev->mode_info.dither_property =
146262306a36Sopenharmony_ci		drm_property_create_enum(rdev->ddev, 0,
146362306a36Sopenharmony_ci					 "dither",
146462306a36Sopenharmony_ci					 radeon_dither_enum_list, sz);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	sz = ARRAY_SIZE(radeon_output_csc_enum_list);
146762306a36Sopenharmony_ci	rdev->mode_info.output_csc_property =
146862306a36Sopenharmony_ci		drm_property_create_enum(rdev->ddev, 0,
146962306a36Sopenharmony_ci					 "output_csc",
147062306a36Sopenharmony_ci					 radeon_output_csc_enum_list, sz);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	return 0;
147362306a36Sopenharmony_ci}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_civoid radeon_update_display_priority(struct radeon_device *rdev)
147662306a36Sopenharmony_ci{
147762306a36Sopenharmony_ci	/* adjustment options for the display watermarks */
147862306a36Sopenharmony_ci	if ((radeon_disp_priority == 0) || (radeon_disp_priority > 2)) {
147962306a36Sopenharmony_ci		/* set display priority to high for r3xx, rv515 chips
148062306a36Sopenharmony_ci		 * this avoids flickering due to underflow to the
148162306a36Sopenharmony_ci		 * display controllers during heavy acceleration.
148262306a36Sopenharmony_ci		 * Don't force high on rs4xx igp chips as it seems to
148362306a36Sopenharmony_ci		 * affect the sound card.  See kernel bug 15982.
148462306a36Sopenharmony_ci		 */
148562306a36Sopenharmony_ci		if ((ASIC_IS_R300(rdev) || (rdev->family == CHIP_RV515)) &&
148662306a36Sopenharmony_ci		    !(rdev->flags & RADEON_IS_IGP))
148762306a36Sopenharmony_ci			rdev->disp_priority = 2;
148862306a36Sopenharmony_ci		else
148962306a36Sopenharmony_ci			rdev->disp_priority = 0;
149062306a36Sopenharmony_ci	} else
149162306a36Sopenharmony_ci		rdev->disp_priority = radeon_disp_priority;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci/*
149662306a36Sopenharmony_ci * Allocate hdmi structs and determine register offsets
149762306a36Sopenharmony_ci */
149862306a36Sopenharmony_cistatic void radeon_afmt_init(struct radeon_device *rdev)
149962306a36Sopenharmony_ci{
150062306a36Sopenharmony_ci	int i;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++)
150362306a36Sopenharmony_ci		rdev->mode_info.afmt[i] = NULL;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	if (ASIC_IS_NODCE(rdev)) {
150662306a36Sopenharmony_ci		/* nothing to do */
150762306a36Sopenharmony_ci	} else if (ASIC_IS_DCE4(rdev)) {
150862306a36Sopenharmony_ci		static uint32_t eg_offsets[] = {
150962306a36Sopenharmony_ci			EVERGREEN_CRTC0_REGISTER_OFFSET,
151062306a36Sopenharmony_ci			EVERGREEN_CRTC1_REGISTER_OFFSET,
151162306a36Sopenharmony_ci			EVERGREEN_CRTC2_REGISTER_OFFSET,
151262306a36Sopenharmony_ci			EVERGREEN_CRTC3_REGISTER_OFFSET,
151362306a36Sopenharmony_ci			EVERGREEN_CRTC4_REGISTER_OFFSET,
151462306a36Sopenharmony_ci			EVERGREEN_CRTC5_REGISTER_OFFSET,
151562306a36Sopenharmony_ci			0x13830 - 0x7030,
151662306a36Sopenharmony_ci		};
151762306a36Sopenharmony_ci		int num_afmt;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci		/* DCE8 has 7 audio blocks tied to DIG encoders */
152062306a36Sopenharmony_ci		/* DCE6 has 6 audio blocks tied to DIG encoders */
152162306a36Sopenharmony_ci		/* DCE4/5 has 6 audio blocks tied to DIG encoders */
152262306a36Sopenharmony_ci		/* DCE4.1 has 2 audio blocks tied to DIG encoders */
152362306a36Sopenharmony_ci		if (ASIC_IS_DCE8(rdev))
152462306a36Sopenharmony_ci			num_afmt = 7;
152562306a36Sopenharmony_ci		else if (ASIC_IS_DCE6(rdev))
152662306a36Sopenharmony_ci			num_afmt = 6;
152762306a36Sopenharmony_ci		else if (ASIC_IS_DCE5(rdev))
152862306a36Sopenharmony_ci			num_afmt = 6;
152962306a36Sopenharmony_ci		else if (ASIC_IS_DCE41(rdev))
153062306a36Sopenharmony_ci			num_afmt = 2;
153162306a36Sopenharmony_ci		else /* DCE4 */
153262306a36Sopenharmony_ci			num_afmt = 6;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci		BUG_ON(num_afmt > ARRAY_SIZE(eg_offsets));
153562306a36Sopenharmony_ci		for (i = 0; i < num_afmt; i++) {
153662306a36Sopenharmony_ci			rdev->mode_info.afmt[i] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL);
153762306a36Sopenharmony_ci			if (rdev->mode_info.afmt[i]) {
153862306a36Sopenharmony_ci				rdev->mode_info.afmt[i]->offset = eg_offsets[i];
153962306a36Sopenharmony_ci				rdev->mode_info.afmt[i]->id = i;
154062306a36Sopenharmony_ci			}
154162306a36Sopenharmony_ci		}
154262306a36Sopenharmony_ci	} else if (ASIC_IS_DCE3(rdev)) {
154362306a36Sopenharmony_ci		/* DCE3.x has 2 audio blocks tied to DIG encoders */
154462306a36Sopenharmony_ci		rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL);
154562306a36Sopenharmony_ci		if (rdev->mode_info.afmt[0]) {
154662306a36Sopenharmony_ci			rdev->mode_info.afmt[0]->offset = DCE3_HDMI_OFFSET0;
154762306a36Sopenharmony_ci			rdev->mode_info.afmt[0]->id = 0;
154862306a36Sopenharmony_ci		}
154962306a36Sopenharmony_ci		rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL);
155062306a36Sopenharmony_ci		if (rdev->mode_info.afmt[1]) {
155162306a36Sopenharmony_ci			rdev->mode_info.afmt[1]->offset = DCE3_HDMI_OFFSET1;
155262306a36Sopenharmony_ci			rdev->mode_info.afmt[1]->id = 1;
155362306a36Sopenharmony_ci		}
155462306a36Sopenharmony_ci	} else if (ASIC_IS_DCE2(rdev)) {
155562306a36Sopenharmony_ci		/* DCE2 has at least 1 routable audio block */
155662306a36Sopenharmony_ci		rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL);
155762306a36Sopenharmony_ci		if (rdev->mode_info.afmt[0]) {
155862306a36Sopenharmony_ci			rdev->mode_info.afmt[0]->offset = DCE2_HDMI_OFFSET0;
155962306a36Sopenharmony_ci			rdev->mode_info.afmt[0]->id = 0;
156062306a36Sopenharmony_ci		}
156162306a36Sopenharmony_ci		/* r6xx has 2 routable audio blocks */
156262306a36Sopenharmony_ci		if (rdev->family >= CHIP_R600) {
156362306a36Sopenharmony_ci			rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL);
156462306a36Sopenharmony_ci			if (rdev->mode_info.afmt[1]) {
156562306a36Sopenharmony_ci				rdev->mode_info.afmt[1]->offset = DCE2_HDMI_OFFSET1;
156662306a36Sopenharmony_ci				rdev->mode_info.afmt[1]->id = 1;
156762306a36Sopenharmony_ci			}
156862306a36Sopenharmony_ci		}
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_cistatic void radeon_afmt_fini(struct radeon_device *rdev)
157362306a36Sopenharmony_ci{
157462306a36Sopenharmony_ci	int i;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) {
157762306a36Sopenharmony_ci		kfree(rdev->mode_info.afmt[i]);
157862306a36Sopenharmony_ci		rdev->mode_info.afmt[i] = NULL;
157962306a36Sopenharmony_ci	}
158062306a36Sopenharmony_ci}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ciint radeon_modeset_init(struct radeon_device *rdev)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	int i;
158562306a36Sopenharmony_ci	int ret;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	drm_mode_config_init(rdev->ddev);
158862306a36Sopenharmony_ci	rdev->mode_info.mode_config_initialized = true;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	rdev->ddev->mode_config.funcs = &radeon_mode_funcs;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	if (radeon_use_pflipirq == 2 && rdev->family >= CHIP_R600)
159362306a36Sopenharmony_ci		rdev->ddev->mode_config.async_page_flip = true;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	if (ASIC_IS_DCE5(rdev)) {
159662306a36Sopenharmony_ci		rdev->ddev->mode_config.max_width = 16384;
159762306a36Sopenharmony_ci		rdev->ddev->mode_config.max_height = 16384;
159862306a36Sopenharmony_ci	} else if (ASIC_IS_AVIVO(rdev)) {
159962306a36Sopenharmony_ci		rdev->ddev->mode_config.max_width = 8192;
160062306a36Sopenharmony_ci		rdev->ddev->mode_config.max_height = 8192;
160162306a36Sopenharmony_ci	} else {
160262306a36Sopenharmony_ci		rdev->ddev->mode_config.max_width = 4096;
160362306a36Sopenharmony_ci		rdev->ddev->mode_config.max_height = 4096;
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	rdev->ddev->mode_config.preferred_depth = 24;
160762306a36Sopenharmony_ci	rdev->ddev->mode_config.prefer_shadow = 1;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	rdev->ddev->mode_config.fb_modifiers_not_supported = true;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	ret = radeon_modeset_create_props(rdev);
161262306a36Sopenharmony_ci	if (ret) {
161362306a36Sopenharmony_ci		return ret;
161462306a36Sopenharmony_ci	}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	/* init i2c buses */
161762306a36Sopenharmony_ci	radeon_i2c_init(rdev);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	/* check combios for a valid hardcoded EDID - Sun servers */
162062306a36Sopenharmony_ci	if (!rdev->is_atom_bios) {
162162306a36Sopenharmony_ci		/* check for hardcoded EDID in BIOS */
162262306a36Sopenharmony_ci		radeon_combios_check_hardcoded_edid(rdev);
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	/* allocate crtcs */
162662306a36Sopenharmony_ci	for (i = 0; i < rdev->num_crtc; i++) {
162762306a36Sopenharmony_ci		radeon_crtc_init(rdev->ddev, i);
162862306a36Sopenharmony_ci	}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	/* okay we should have all the bios connectors */
163162306a36Sopenharmony_ci	ret = radeon_setup_enc_conn(rdev->ddev);
163262306a36Sopenharmony_ci	if (!ret) {
163362306a36Sopenharmony_ci		return ret;
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	/* init dig PHYs, disp eng pll */
163762306a36Sopenharmony_ci	if (rdev->is_atom_bios) {
163862306a36Sopenharmony_ci		radeon_atom_encoder_init(rdev);
163962306a36Sopenharmony_ci		radeon_atom_disp_eng_pll_init(rdev);
164062306a36Sopenharmony_ci	}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	/* initialize hpd */
164362306a36Sopenharmony_ci	radeon_hpd_init(rdev);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	/* setup afmt */
164662306a36Sopenharmony_ci	radeon_afmt_init(rdev);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	drm_kms_helper_poll_init(rdev->ddev);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	/* do pm late init */
165162306a36Sopenharmony_ci	ret = radeon_pm_late_init(rdev);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	return 0;
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_civoid radeon_modeset_fini(struct radeon_device *rdev)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	if (rdev->mode_info.mode_config_initialized) {
165962306a36Sopenharmony_ci		drm_kms_helper_poll_fini(rdev->ddev);
166062306a36Sopenharmony_ci		radeon_hpd_fini(rdev);
166162306a36Sopenharmony_ci		drm_helper_force_disable_all(rdev->ddev);
166262306a36Sopenharmony_ci		radeon_afmt_fini(rdev);
166362306a36Sopenharmony_ci		drm_mode_config_cleanup(rdev->ddev);
166462306a36Sopenharmony_ci		rdev->mode_info.mode_config_initialized = false;
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	kfree(rdev->mode_info.bios_hardcoded_edid);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	/* free i2c buses */
167062306a36Sopenharmony_ci	radeon_i2c_fini(rdev);
167162306a36Sopenharmony_ci}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_cistatic bool is_hdtv_mode(const struct drm_display_mode *mode)
167462306a36Sopenharmony_ci{
167562306a36Sopenharmony_ci	/* try and guess if this is a tv or a monitor */
167662306a36Sopenharmony_ci	if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */
167762306a36Sopenharmony_ci	    (mode->vdisplay == 576) || /* 576p */
167862306a36Sopenharmony_ci	    (mode->vdisplay == 720) || /* 720p */
167962306a36Sopenharmony_ci	    (mode->vdisplay == 1080)) /* 1080p */
168062306a36Sopenharmony_ci		return true;
168162306a36Sopenharmony_ci	else
168262306a36Sopenharmony_ci		return false;
168362306a36Sopenharmony_ci}
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_cibool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
168662306a36Sopenharmony_ci				const struct drm_display_mode *mode,
168762306a36Sopenharmony_ci				struct drm_display_mode *adjusted_mode)
168862306a36Sopenharmony_ci{
168962306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
169062306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
169162306a36Sopenharmony_ci	struct drm_encoder *encoder;
169262306a36Sopenharmony_ci	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
169362306a36Sopenharmony_ci	struct radeon_encoder *radeon_encoder;
169462306a36Sopenharmony_ci	struct drm_connector *connector;
169562306a36Sopenharmony_ci	bool first = true;
169662306a36Sopenharmony_ci	u32 src_v = 1, dst_v = 1;
169762306a36Sopenharmony_ci	u32 src_h = 1, dst_h = 1;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	radeon_crtc->h_border = 0;
170062306a36Sopenharmony_ci	radeon_crtc->v_border = 0;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
170362306a36Sopenharmony_ci		if (encoder->crtc != crtc)
170462306a36Sopenharmony_ci			continue;
170562306a36Sopenharmony_ci		radeon_encoder = to_radeon_encoder(encoder);
170662306a36Sopenharmony_ci		connector = radeon_get_connector_for_encoder(encoder);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		if (first) {
170962306a36Sopenharmony_ci			/* set scaling */
171062306a36Sopenharmony_ci			if (radeon_encoder->rmx_type == RMX_OFF)
171162306a36Sopenharmony_ci				radeon_crtc->rmx_type = RMX_OFF;
171262306a36Sopenharmony_ci			else if (mode->hdisplay < radeon_encoder->native_mode.hdisplay ||
171362306a36Sopenharmony_ci				 mode->vdisplay < radeon_encoder->native_mode.vdisplay)
171462306a36Sopenharmony_ci				radeon_crtc->rmx_type = radeon_encoder->rmx_type;
171562306a36Sopenharmony_ci			else
171662306a36Sopenharmony_ci				radeon_crtc->rmx_type = RMX_OFF;
171762306a36Sopenharmony_ci			/* copy native mode */
171862306a36Sopenharmony_ci			memcpy(&radeon_crtc->native_mode,
171962306a36Sopenharmony_ci			       &radeon_encoder->native_mode,
172062306a36Sopenharmony_ci				sizeof(struct drm_display_mode));
172162306a36Sopenharmony_ci			src_v = crtc->mode.vdisplay;
172262306a36Sopenharmony_ci			dst_v = radeon_crtc->native_mode.vdisplay;
172362306a36Sopenharmony_ci			src_h = crtc->mode.hdisplay;
172462306a36Sopenharmony_ci			dst_h = radeon_crtc->native_mode.hdisplay;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci			/* fix up for overscan on hdmi */
172762306a36Sopenharmony_ci			if (ASIC_IS_AVIVO(rdev) &&
172862306a36Sopenharmony_ci			    (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) &&
172962306a36Sopenharmony_ci			    ((radeon_encoder->underscan_type == UNDERSCAN_ON) ||
173062306a36Sopenharmony_ci			     ((radeon_encoder->underscan_type == UNDERSCAN_AUTO) &&
173162306a36Sopenharmony_ci			      drm_detect_hdmi_monitor(radeon_connector_edid(connector)) &&
173262306a36Sopenharmony_ci			      is_hdtv_mode(mode)))) {
173362306a36Sopenharmony_ci				if (radeon_encoder->underscan_hborder != 0)
173462306a36Sopenharmony_ci					radeon_crtc->h_border = radeon_encoder->underscan_hborder;
173562306a36Sopenharmony_ci				else
173662306a36Sopenharmony_ci					radeon_crtc->h_border = (mode->hdisplay >> 5) + 16;
173762306a36Sopenharmony_ci				if (radeon_encoder->underscan_vborder != 0)
173862306a36Sopenharmony_ci					radeon_crtc->v_border = radeon_encoder->underscan_vborder;
173962306a36Sopenharmony_ci				else
174062306a36Sopenharmony_ci					radeon_crtc->v_border = (mode->vdisplay >> 5) + 16;
174162306a36Sopenharmony_ci				radeon_crtc->rmx_type = RMX_FULL;
174262306a36Sopenharmony_ci				src_v = crtc->mode.vdisplay;
174362306a36Sopenharmony_ci				dst_v = crtc->mode.vdisplay - (radeon_crtc->v_border * 2);
174462306a36Sopenharmony_ci				src_h = crtc->mode.hdisplay;
174562306a36Sopenharmony_ci				dst_h = crtc->mode.hdisplay - (radeon_crtc->h_border * 2);
174662306a36Sopenharmony_ci			}
174762306a36Sopenharmony_ci			first = false;
174862306a36Sopenharmony_ci		} else {
174962306a36Sopenharmony_ci			if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) {
175062306a36Sopenharmony_ci				/* WARNING: Right now this can't happen but
175162306a36Sopenharmony_ci				 * in the future we need to check that scaling
175262306a36Sopenharmony_ci				 * are consistent across different encoder
175362306a36Sopenharmony_ci				 * (ie all encoder can work with the same
175462306a36Sopenharmony_ci				 *  scaling).
175562306a36Sopenharmony_ci				 */
175662306a36Sopenharmony_ci				DRM_ERROR("Scaling not consistent across encoder.\n");
175762306a36Sopenharmony_ci				return false;
175862306a36Sopenharmony_ci			}
175962306a36Sopenharmony_ci		}
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci	if (radeon_crtc->rmx_type != RMX_OFF) {
176262306a36Sopenharmony_ci		fixed20_12 a, b;
176362306a36Sopenharmony_ci		a.full = dfixed_const(src_v);
176462306a36Sopenharmony_ci		b.full = dfixed_const(dst_v);
176562306a36Sopenharmony_ci		radeon_crtc->vsc.full = dfixed_div(a, b);
176662306a36Sopenharmony_ci		a.full = dfixed_const(src_h);
176762306a36Sopenharmony_ci		b.full = dfixed_const(dst_h);
176862306a36Sopenharmony_ci		radeon_crtc->hsc.full = dfixed_div(a, b);
176962306a36Sopenharmony_ci	} else {
177062306a36Sopenharmony_ci		radeon_crtc->vsc.full = dfixed_const(1);
177162306a36Sopenharmony_ci		radeon_crtc->hsc.full = dfixed_const(1);
177262306a36Sopenharmony_ci	}
177362306a36Sopenharmony_ci	return true;
177462306a36Sopenharmony_ci}
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci/*
177762306a36Sopenharmony_ci * Retrieve current video scanout position of crtc on a given gpu, and
177862306a36Sopenharmony_ci * an optional accurate timestamp of when query happened.
177962306a36Sopenharmony_ci *
178062306a36Sopenharmony_ci * \param dev Device to query.
178162306a36Sopenharmony_ci * \param crtc Crtc to query.
178262306a36Sopenharmony_ci * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0).
178362306a36Sopenharmony_ci *              For driver internal use only also supports these flags:
178462306a36Sopenharmony_ci *
178562306a36Sopenharmony_ci *              USE_REAL_VBLANKSTART to use the real start of vblank instead
178662306a36Sopenharmony_ci *              of a fudged earlier start of vblank.
178762306a36Sopenharmony_ci *
178862306a36Sopenharmony_ci *              GET_DISTANCE_TO_VBLANKSTART to return distance to the
178962306a36Sopenharmony_ci *              fudged earlier start of vblank in *vpos and the distance
179062306a36Sopenharmony_ci *              to true start of vblank in *hpos.
179162306a36Sopenharmony_ci *
179262306a36Sopenharmony_ci * \param *vpos Location where vertical scanout position should be stored.
179362306a36Sopenharmony_ci * \param *hpos Location where horizontal scanout position should go.
179462306a36Sopenharmony_ci * \param *stime Target location for timestamp taken immediately before
179562306a36Sopenharmony_ci *               scanout position query. Can be NULL to skip timestamp.
179662306a36Sopenharmony_ci * \param *etime Target location for timestamp taken immediately after
179762306a36Sopenharmony_ci *               scanout position query. Can be NULL to skip timestamp.
179862306a36Sopenharmony_ci *
179962306a36Sopenharmony_ci * Returns vpos as a positive number while in active scanout area.
180062306a36Sopenharmony_ci * Returns vpos as a negative number inside vblank, counting the number
180162306a36Sopenharmony_ci * of scanlines to go until end of vblank, e.g., -1 means "one scanline
180262306a36Sopenharmony_ci * until start of active scanout / end of vblank."
180362306a36Sopenharmony_ci *
180462306a36Sopenharmony_ci * \return Flags, or'ed together as follows:
180562306a36Sopenharmony_ci *
180662306a36Sopenharmony_ci * DRM_SCANOUTPOS_VALID = Query successful.
180762306a36Sopenharmony_ci * DRM_SCANOUTPOS_INVBL = Inside vblank.
180862306a36Sopenharmony_ci * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
180962306a36Sopenharmony_ci * this flag means that returned position may be offset by a constant but
181062306a36Sopenharmony_ci * unknown small number of scanlines wrt. real scanout position.
181162306a36Sopenharmony_ci *
181262306a36Sopenharmony_ci */
181362306a36Sopenharmony_ciint radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
181462306a36Sopenharmony_ci			       unsigned int flags, int *vpos, int *hpos,
181562306a36Sopenharmony_ci			       ktime_t *stime, ktime_t *etime,
181662306a36Sopenharmony_ci			       const struct drm_display_mode *mode)
181762306a36Sopenharmony_ci{
181862306a36Sopenharmony_ci	u32 stat_crtc = 0, vbl = 0, position = 0;
181962306a36Sopenharmony_ci	int vbl_start, vbl_end, vtotal, ret = 0;
182062306a36Sopenharmony_ci	bool in_vbl = true;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	/* Get optional system timestamp before query. */
182762306a36Sopenharmony_ci	if (stime)
182862306a36Sopenharmony_ci		*stime = ktime_get();
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	if (ASIC_IS_DCE4(rdev)) {
183162306a36Sopenharmony_ci		if (pipe == 0) {
183262306a36Sopenharmony_ci			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
183362306a36Sopenharmony_ci				     EVERGREEN_CRTC0_REGISTER_OFFSET);
183462306a36Sopenharmony_ci			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
183562306a36Sopenharmony_ci					  EVERGREEN_CRTC0_REGISTER_OFFSET);
183662306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
183762306a36Sopenharmony_ci		}
183862306a36Sopenharmony_ci		if (pipe == 1) {
183962306a36Sopenharmony_ci			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
184062306a36Sopenharmony_ci				     EVERGREEN_CRTC1_REGISTER_OFFSET);
184162306a36Sopenharmony_ci			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
184262306a36Sopenharmony_ci					  EVERGREEN_CRTC1_REGISTER_OFFSET);
184362306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
184462306a36Sopenharmony_ci		}
184562306a36Sopenharmony_ci		if (pipe == 2) {
184662306a36Sopenharmony_ci			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
184762306a36Sopenharmony_ci				     EVERGREEN_CRTC2_REGISTER_OFFSET);
184862306a36Sopenharmony_ci			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
184962306a36Sopenharmony_ci					  EVERGREEN_CRTC2_REGISTER_OFFSET);
185062306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
185162306a36Sopenharmony_ci		}
185262306a36Sopenharmony_ci		if (pipe == 3) {
185362306a36Sopenharmony_ci			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
185462306a36Sopenharmony_ci				     EVERGREEN_CRTC3_REGISTER_OFFSET);
185562306a36Sopenharmony_ci			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
185662306a36Sopenharmony_ci					  EVERGREEN_CRTC3_REGISTER_OFFSET);
185762306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
185862306a36Sopenharmony_ci		}
185962306a36Sopenharmony_ci		if (pipe == 4) {
186062306a36Sopenharmony_ci			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
186162306a36Sopenharmony_ci				     EVERGREEN_CRTC4_REGISTER_OFFSET);
186262306a36Sopenharmony_ci			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
186362306a36Sopenharmony_ci					  EVERGREEN_CRTC4_REGISTER_OFFSET);
186462306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
186562306a36Sopenharmony_ci		}
186662306a36Sopenharmony_ci		if (pipe == 5) {
186762306a36Sopenharmony_ci			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
186862306a36Sopenharmony_ci				     EVERGREEN_CRTC5_REGISTER_OFFSET);
186962306a36Sopenharmony_ci			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
187062306a36Sopenharmony_ci					  EVERGREEN_CRTC5_REGISTER_OFFSET);
187162306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
187262306a36Sopenharmony_ci		}
187362306a36Sopenharmony_ci	} else if (ASIC_IS_AVIVO(rdev)) {
187462306a36Sopenharmony_ci		if (pipe == 0) {
187562306a36Sopenharmony_ci			vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END);
187662306a36Sopenharmony_ci			position = RREG32(AVIVO_D1CRTC_STATUS_POSITION);
187762306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
187862306a36Sopenharmony_ci		}
187962306a36Sopenharmony_ci		if (pipe == 1) {
188062306a36Sopenharmony_ci			vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END);
188162306a36Sopenharmony_ci			position = RREG32(AVIVO_D2CRTC_STATUS_POSITION);
188262306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
188362306a36Sopenharmony_ci		}
188462306a36Sopenharmony_ci	} else {
188562306a36Sopenharmony_ci		/* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */
188662306a36Sopenharmony_ci		if (pipe == 0) {
188762306a36Sopenharmony_ci			/* Assume vbl_end == 0, get vbl_start from
188862306a36Sopenharmony_ci			 * upper 16 bits.
188962306a36Sopenharmony_ci			 */
189062306a36Sopenharmony_ci			vbl = (RREG32(RADEON_CRTC_V_TOTAL_DISP) &
189162306a36Sopenharmony_ci				RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
189262306a36Sopenharmony_ci			/* Only retrieve vpos from upper 16 bits, set hpos == 0. */
189362306a36Sopenharmony_ci			position = (RREG32(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
189462306a36Sopenharmony_ci			stat_crtc = RREG32(RADEON_CRTC_STATUS);
189562306a36Sopenharmony_ci			if (!(stat_crtc & 1))
189662306a36Sopenharmony_ci				in_vbl = false;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
189962306a36Sopenharmony_ci		}
190062306a36Sopenharmony_ci		if (pipe == 1) {
190162306a36Sopenharmony_ci			vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) &
190262306a36Sopenharmony_ci				RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
190362306a36Sopenharmony_ci			position = (RREG32(RADEON_CRTC2_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
190462306a36Sopenharmony_ci			stat_crtc = RREG32(RADEON_CRTC2_STATUS);
190562306a36Sopenharmony_ci			if (!(stat_crtc & 1))
190662306a36Sopenharmony_ci				in_vbl = false;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci			ret |= DRM_SCANOUTPOS_VALID;
190962306a36Sopenharmony_ci		}
191062306a36Sopenharmony_ci	}
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	/* Get optional system timestamp after query. */
191362306a36Sopenharmony_ci	if (etime)
191462306a36Sopenharmony_ci		*etime = ktime_get();
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	/* Decode into vertical and horizontal scanout position. */
191962306a36Sopenharmony_ci	*vpos = position & 0x1fff;
192062306a36Sopenharmony_ci	*hpos = (position >> 16) & 0x1fff;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	/* Valid vblank area boundaries from gpu retrieved? */
192362306a36Sopenharmony_ci	if (vbl > 0) {
192462306a36Sopenharmony_ci		/* Yes: Decode. */
192562306a36Sopenharmony_ci		ret |= DRM_SCANOUTPOS_ACCURATE;
192662306a36Sopenharmony_ci		vbl_start = vbl & 0x1fff;
192762306a36Sopenharmony_ci		vbl_end = (vbl >> 16) & 0x1fff;
192862306a36Sopenharmony_ci	}
192962306a36Sopenharmony_ci	else {
193062306a36Sopenharmony_ci		/* No: Fake something reasonable which gives at least ok results. */
193162306a36Sopenharmony_ci		vbl_start = mode->crtc_vdisplay;
193262306a36Sopenharmony_ci		vbl_end = 0;
193362306a36Sopenharmony_ci	}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	/* Called from driver internal vblank counter query code? */
193662306a36Sopenharmony_ci	if (flags & GET_DISTANCE_TO_VBLANKSTART) {
193762306a36Sopenharmony_ci	    /* Caller wants distance from real vbl_start in *hpos */
193862306a36Sopenharmony_ci	    *hpos = *vpos - vbl_start;
193962306a36Sopenharmony_ci	}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	/* Fudge vblank to start a few scanlines earlier to handle the
194262306a36Sopenharmony_ci	 * problem that vblank irqs fire a few scanlines before start
194362306a36Sopenharmony_ci	 * of vblank. Some driver internal callers need the true vblank
194462306a36Sopenharmony_ci	 * start to be used and signal this via the USE_REAL_VBLANKSTART flag.
194562306a36Sopenharmony_ci	 *
194662306a36Sopenharmony_ci	 * The cause of the "early" vblank irq is that the irq is triggered
194762306a36Sopenharmony_ci	 * by the line buffer logic when the line buffer read position enters
194862306a36Sopenharmony_ci	 * the vblank, whereas our crtc scanout position naturally lags the
194962306a36Sopenharmony_ci	 * line buffer read position.
195062306a36Sopenharmony_ci	 */
195162306a36Sopenharmony_ci	if (!(flags & USE_REAL_VBLANKSTART))
195262306a36Sopenharmony_ci		vbl_start -= rdev->mode_info.crtcs[pipe]->lb_vblank_lead_lines;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	/* Test scanout position against vblank region. */
195562306a36Sopenharmony_ci	if ((*vpos < vbl_start) && (*vpos >= vbl_end))
195662306a36Sopenharmony_ci		in_vbl = false;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	/* In vblank? */
195962306a36Sopenharmony_ci	if (in_vbl)
196062306a36Sopenharmony_ci	    ret |= DRM_SCANOUTPOS_IN_VBLANK;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	/* Called from driver internal vblank counter query code? */
196362306a36Sopenharmony_ci	if (flags & GET_DISTANCE_TO_VBLANKSTART) {
196462306a36Sopenharmony_ci		/* Caller wants distance from fudged earlier vbl_start */
196562306a36Sopenharmony_ci		*vpos -= vbl_start;
196662306a36Sopenharmony_ci		return ret;
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	/* Check if inside vblank area and apply corrective offsets:
197062306a36Sopenharmony_ci	 * vpos will then be >=0 in video scanout area, but negative
197162306a36Sopenharmony_ci	 * within vblank area, counting down the number of lines until
197262306a36Sopenharmony_ci	 * start of scanout.
197362306a36Sopenharmony_ci	 */
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	/* Inside "upper part" of vblank area? Apply corrective offset if so: */
197662306a36Sopenharmony_ci	if (in_vbl && (*vpos >= vbl_start)) {
197762306a36Sopenharmony_ci		vtotal = mode->crtc_vtotal;
197862306a36Sopenharmony_ci		*vpos = *vpos - vtotal;
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	/* Correct for shifted end of vbl at vbl_end. */
198262306a36Sopenharmony_ci	*vpos = *vpos - vbl_end;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	return ret;
198562306a36Sopenharmony_ci}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_cibool
198862306a36Sopenharmony_ciradeon_get_crtc_scanout_position(struct drm_crtc *crtc,
198962306a36Sopenharmony_ci				 bool in_vblank_irq, int *vpos, int *hpos,
199062306a36Sopenharmony_ci				 ktime_t *stime, ktime_t *etime,
199162306a36Sopenharmony_ci				 const struct drm_display_mode *mode)
199262306a36Sopenharmony_ci{
199362306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
199462306a36Sopenharmony_ci	unsigned int pipe = crtc->index;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	return radeon_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
199762306a36Sopenharmony_ci					  stime, etime, mode);
199862306a36Sopenharmony_ci}
1999