// SPDX-License-Identifier: GPL-2.0+
/*
* rcar_du_crtc.c -- R-Car Display Unit CRTCs
*
* Copyright (C) 2013-2015 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/sys_soc.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_vblank.h>
#include "rcar_du_crtc.h"
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
#include "rcar_du_kms.h"
#include "rcar_du_plane.h"
#include "rcar_du_regs.h"
#include "rcar_du_vsp.h"
#include "rcar_lvds.h"
static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
{
struct rcar_du_device *rcdu = rcrtc->dev;
return rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
}
static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data)
{
struct rcar_du_device *rcdu = rcrtc->dev;
rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data);
}
static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
{
struct rcar_du_device *rcdu = rcrtc->dev;
rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
}
static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
{
struct rcar_du_device *rcdu = rcrtc->dev;
rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
}
void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set)
{
struct rcar_du_device *rcdu = rcrtc->dev;
rcrtc->dsysr = (rcrtc->dsysr & ~clr) | set;
rcar_du_write(rcdu, rcrtc->mmio_offset + DSYSR, rcrtc->dsysr);
}
/* -----------------------------------------------------------------------------
* Hardware Setup
*/
struct dpll_info {
unsigned int output;
unsigned int fdpll;
unsigned int n;
unsigned int m;
};
static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc,
struct dpll_info *dpll,
unsigned long input,
unsigned long target)
{
unsigned long best_diff = (unsigned long)-1;
unsigned long diff;
unsigned int fdpll;
unsigned int m;
unsigned int n;
/*
* fin fvco fout fclkout
* in --> [1/M] --> |PD| -> [LPF] -> [VCO] -> [1/P] -+-> [1/FDPLL] -> out
* +-> | | |
* | |
* +---------------- [1/N] <------------+
*
* fclkout = fvco / P / FDPLL -- (1)
*
* fin/M = fvco/P/N
*
* fvco = fin * P * N / M -- (2)
*
* (1) + (2) indicates
*
* fclkout = fin * N / M / FDPLL
*
* NOTES
* N : (n + 1)
* M : (m + 1)
* FDPLL : (fdpll + 1)
* P : 2
* 2kHz < fvco < 4096MHz
*
* To minimize the jitter,
* N : as large as possible
* M : as small as possible
*/
for (m = 0; m < 4; m++) {
for (n = 119; n > 38; n--) {
/*
* This code only runs on 64-bit architectures, the
* unsigned long type can thus be used for 64-bit
* computation. It will still compile without any
* warning on 32-bit architectures.
*
* To optimize calculations, use fout instead of fvco
* to verify the VCO frequency constraint.
*/
unsigned long fout = input * (n + 1) / (m + 1);
if (fout