/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Copyright (C) 2023 Loongson Technology Corporation Limited
 */

#ifndef __LSDC_PIXPLL_H__
#define __LSDC_PIXPLL_H__

#include <drm/drm_device.h>

/*
 * Loongson Pixel PLL hardware structure
 *
 * refclk: reference frequency, 100 MHz from external oscillator
 * outclk: output frequency desired.
 *
 *
 *               L1       Fref                      Fvco     L2
 * refclk   +-----------+      +------------------+      +---------+   outclk
 * ---+---> | Prescaler | ---> | Clock Multiplier | ---> | divider | -------->
 *    |     +-----------+      +------------------+      +---------+     ^
 *    |           ^                      ^                    ^          |
 *    |           |                      |                    |          |
 *    |           |                      |                    |          |
 *    |        div_ref                 loopc               div_out       |
 *    |                                                                  |
 *    +---- bypass (bypass above software configurable clock if set) ----+
 *
 *   outclk = refclk / div_ref * loopc / div_out;
 *
 *   sel_out: PLL clock output selector(enable).
 *
 *   If sel_out == 1, then enable output clock (turn On);
 *   If sel_out == 0, then disable output clock (turn Off);
 *
 * PLL working requirements:
 *
 *  1) 20 MHz <= refclk / div_ref <= 40Mhz
 *  2) 1.2 GHz <= refclk /div_out * loopc <= 3.2 Ghz
 */

struct lsdc_pixpll_parms {
	unsigned int ref_clock;
	unsigned int div_ref;
	unsigned int loopc;
	unsigned int div_out;
};

struct lsdc_pixpll;

struct lsdc_pixpll_funcs {
	int (*setup)(struct lsdc_pixpll * const this);

	int (*compute)(struct lsdc_pixpll * const this,
		       unsigned int clock,
		       struct lsdc_pixpll_parms *pout);

	int (*update)(struct lsdc_pixpll * const this,
		      struct lsdc_pixpll_parms const *pin);

	unsigned int (*get_rate)(struct lsdc_pixpll * const this);

	void (*print)(struct lsdc_pixpll * const this,
		      struct drm_printer *printer);
};

struct lsdc_pixpll {
	const struct lsdc_pixpll_funcs *funcs;

	struct drm_device *ddev;

	/* PLL register offset */
	u32 reg_base;
	/* PLL register size in bytes */
	u32 reg_size;

	void __iomem *mmio;

	struct lsdc_pixpll_parms *priv;
};

int lsdc_pixpll_init(struct lsdc_pixpll * const this,
		     struct drm_device *ddev,
		     unsigned int index);

#endif