/* SPDX-License-Identifier: GPL-2.0 */
#ifndef LYNXDRV_H_
#define LYNXDRV_H_

#define FB_ACCEL_SMI 0xab

#define MHZ(x) ((x) * 1000000)

#define DEFAULT_SM750_CHIP_CLOCK	290
#define DEFAULT_SM750LE_CHIP_CLOCK	333
#ifndef SM750LE_REVISION_ID
#define SM750LE_REVISION_ID ((unsigned char)0xfe)
#endif

enum sm750_pnltype {
	sm750_24TFT = 0,	/* 24bit tft */
	sm750_dualTFT = 2,	/* dual 18 bit tft */
	sm750_doubleTFT = 1,	/* 36 bit double pixel tft */
};

/* vga channel is not concerned  */
enum sm750_dataflow {
	sm750_simul_pri,	/* primary => all head */
	sm750_simul_sec,	/* secondary => all head */
	sm750_dual_normal,	/* primary => panel head and secondary => crt */
	sm750_dual_swap,	/* primary => crt head and secondary => panel */
};

enum sm750_channel {
	sm750_primary = 0,
	/* enum value equal to the register filed data */
	sm750_secondary = 1,
};

enum sm750_path {
	sm750_panel = 1,
	sm750_crt = 2,
	sm750_pnc = 3,	/* panel and crt */
};

struct init_status {
	ushort powerMode;
	/* below three clocks are in unit of MHZ*/
	ushort chip_clk;
	ushort mem_clk;
	ushort master_clk;
	ushort setAllEngOff;
	ushort resetMemory;
};

struct lynx_accel {
	/* base virtual address of DPR registers */
	volatile unsigned char __iomem *dprBase;
	/* base virtual address of de data port */
	volatile unsigned char __iomem *dpPortBase;

	/* function pointers */
	void (*de_init)(struct lynx_accel *accel);

	int (*de_wait)(void);/* see if hardware ready to work */

	int (*de_fillrect)(struct lynx_accel *accel,
			   u32 base, u32 pitch, u32 bpp,
			   u32 x, u32 y, u32 width, u32 height,
			   u32 color, u32 rop);

	int (*de_copyarea)(struct lynx_accel *accel,
			   u32 s_base, u32 s_pitch,
			   u32 sx, u32 sy,
			   u32 d_base, u32 d_pitch,
			   u32 bpp, u32 dx, u32 dy,
			   u32 width, u32 height,
			   u32 rop2);

	int (*de_imageblit)(struct lynx_accel *accel, const char *p_srcbuf,
			    u32 src_delta, u32 start_bit, u32 d_base, u32 d_pitch,
			    u32 byte_per_pixel, u32 dx, u32 dy, u32 width,
			    u32 height, u32 f_color, u32 b_color, u32 rop2);

};

struct sm750_dev {
	/* common members */
	u16 devid;
	u8 revid;
	struct pci_dev *pdev;
	struct fb_info *fbinfo[2];
	struct lynx_accel accel;
	int accel_off;
	int fb_count;
	int mtrr_off;
	struct{
		int vram;
	} mtrr;
	/* all smi graphic adaptor got below attributes */
	unsigned long vidmem_start;
	unsigned long vidreg_start;
	__u32 vidmem_size;
	__u32 vidreg_size;
	void __iomem *pvReg;
	unsigned char __iomem *pvMem;
	/* locks*/
	spinlock_t slock;

	struct init_status initParm;
	enum sm750_pnltype pnltype;
	enum sm750_dataflow dataflow;
	int nocrt;

	/*
	 * 0: no hardware cursor
	 * 1: primary crtc hw cursor enabled,
	 * 2: secondary crtc hw cursor enabled
	 * 3: both ctrc hw cursor enabled
	 */
	int hwCursor;
};

struct lynx_cursor {
	/* cursor width ,height and size */
	int w;
	int h;
	int size;
	/* hardware limitation */
	int max_w;
	int max_h;
	/* base virtual address and offset  of cursor image */
	char __iomem *vstart;
	int offset;
	/* mmio addr of hw cursor */
	volatile char __iomem *mmio;
};

struct lynxfb_crtc {
	unsigned char __iomem *v_cursor; /* virtual address of cursor */
	unsigned char __iomem *v_screen; /* virtual address of on_screen */
	int o_cursor; /* cursor address offset in vidmem */
	int o_screen; /* onscreen address offset in vidmem */
	int channel;/* which channel this crtc stands for*/
	resource_size_t vidmem_size;/* this view's video memory max size */

	/* below attributes belong to info->fix, their value depends on specific adaptor*/
	u16 line_pad;/* padding information:0,1,2,4,8,16,... */
	u16 xpanstep;
	u16 ypanstep;
	u16 ywrapstep;

	void *priv;

	/* cursor information */
	struct lynx_cursor cursor;
};

struct lynxfb_output {
	int dpms;
	int paths;
	/*
	 * which paths(s) this output stands for,for sm750:
	 * paths=1:means output for panel paths
	 * paths=2:means output for crt paths
	 * paths=3:means output for both panel and crt paths
	 */

	int *channel;
	/*
	 * which channel these outputs linked with,for sm750:
	 * *channel=0 means primary channel
	 * *channel=1 means secondary channel
	 * output->channel ==> &crtc->channel
	 */
	void *priv;

	int (*proc_setBLANK)(struct lynxfb_output *output, int blank);
};

struct lynxfb_par {
	/* either 0 or 1 for dual head adaptor,0 is the older one registered */
	int index;
	unsigned int pseudo_palette[256];
	struct lynxfb_crtc crtc;
	struct lynxfb_output output;
	struct fb_info *info;
	struct sm750_dev *dev;
};

static inline unsigned long ps_to_hz(unsigned int psvalue)
{
	unsigned long long numerator = 1000 * 1000 * 1000 * 1000ULL;
	/* 10^12 / picosecond period gives frequency in Hz */
	do_div(numerator, psvalue);
	return (unsigned long)numerator;
}

int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev);
int hw_sm750_inithw(struct sm750_dev *sm750_dev, struct pci_dev *pdev);
void hw_sm750_initAccel(struct sm750_dev *sm750_dev);
int hw_sm750_deWait(void);
int hw_sm750le_deWait(void);

int hw_sm750_output_setMode(struct lynxfb_output *output,
			    struct fb_var_screeninfo *var,
			    struct fb_fix_screeninfo *fix);

int hw_sm750_crtc_checkMode(struct lynxfb_crtc *crtc,
			    struct fb_var_screeninfo *var);

int hw_sm750_crtc_setMode(struct lynxfb_crtc *crtc,
			  struct fb_var_screeninfo *var,
			  struct fb_fix_screeninfo *fix);

int hw_sm750_setColReg(struct lynxfb_crtc *crtc, ushort index,
		       ushort red, ushort green, ushort blue);

int hw_sm750_setBLANK(struct lynxfb_output *output, int blank);
int hw_sm750le_setBLANK(struct lynxfb_output *output, int blank);
int hw_sm750_pan_display(struct lynxfb_crtc *crtc,
			 const struct fb_var_screeninfo *var,
			 const struct fb_info *info);

#endif