1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * T1042 platform DIU operation
4 *
5 * Copyright 2014 Freescale Semiconductor Inc.
6 */
7
8#include <linux/init.h>
9#include <linux/io.h>
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/of.h>
13#include <linux/of_address.h>
14
15#include <sysdev/fsl_soc.h>
16
17/*DIU Pixel ClockCR offset in scfg*/
18#define CCSR_SCFG_PIXCLKCR      0x28
19
20/* DIU Pixel Clock bits of the PIXCLKCR */
21#define PIXCLKCR_PXCKEN		0x80000000
22#define PIXCLKCR_PXCKINV	0x40000000
23#define PIXCLKCR_PXCKDLY	0x0000FF00
24#define PIXCLKCR_PXCLK_MASK	0x00FF0000
25
26/* Some CPLD register definitions */
27#define CPLD_DIUCSR		0x16
28#define CPLD_DIUCSR_DVIEN	0x80
29#define CPLD_DIUCSR_BACKLIGHT	0x0f
30
31struct device_node *cpld_node;
32
33/**
34 * t1042rdb_set_monitor_port: switch the output to a different monitor port
35 */
36static void t1042rdb_set_monitor_port(enum fsl_diu_monitor_port port)
37{
38	void __iomem *cpld_base;
39
40	cpld_base = of_iomap(cpld_node, 0);
41	if (!cpld_base) {
42		pr_err("%s: Could not map cpld registers\n", __func__);
43		goto exit;
44	}
45
46	switch (port) {
47	case FSL_DIU_PORT_DVI:
48		/* Enable the DVI(HDMI) port, disable the DFP and
49		 * the backlight
50		 */
51		clrbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_DVIEN);
52		break;
53	case FSL_DIU_PORT_LVDS:
54		/*
55		 * LVDS also needs backlight enabled, otherwise the display
56		 * will be blank.
57		 */
58		/* Enable the DFP port, disable the DVI*/
59		setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 8);
60		setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 4);
61		setbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_BACKLIGHT);
62		break;
63	default:
64		pr_err("%s: Unsupported monitor port %i\n", __func__, port);
65	}
66
67	iounmap(cpld_base);
68exit:
69	of_node_put(cpld_node);
70}
71
72/**
73 * t1042rdb_set_pixel_clock: program the DIU's clock
74 * @pixclock: pixel clock in ps (pico seconds)
75 */
76static void t1042rdb_set_pixel_clock(unsigned int pixclock)
77{
78	struct device_node *scfg_np;
79	void __iomem *scfg;
80	unsigned long freq;
81	u64 temp;
82	u32 pxclk;
83
84	scfg_np = of_find_compatible_node(NULL, NULL, "fsl,t1040-scfg");
85	if (!scfg_np) {
86		pr_err("%s: Missing scfg node. Can not display video.\n",
87		       __func__);
88		return;
89	}
90
91	scfg = of_iomap(scfg_np, 0);
92	of_node_put(scfg_np);
93	if (!scfg) {
94		pr_err("%s: Could not map device. Can not display video.\n",
95		       __func__);
96		return;
97	}
98
99	/* Convert pixclock into frequency */
100	temp = 1000000000000ULL;
101	do_div(temp, pixclock);
102	freq = temp;
103
104	/*
105	 * 'pxclk' is the ratio of the platform clock to the pixel clock.
106	 * This number is programmed into the PIXCLKCR register, and the valid
107	 * range of values is 2-255.
108	 */
109	pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq);
110	pxclk = clamp_t(u32, pxclk, 2, 255);
111
112	/* Disable the pixel clock, and set it to non-inverted and no delay */
113	clrbits32(scfg + CCSR_SCFG_PIXCLKCR,
114		  PIXCLKCR_PXCKEN | PIXCLKCR_PXCKDLY | PIXCLKCR_PXCLK_MASK);
115
116	/* Enable the clock and set the pxclk */
117	setbits32(scfg + CCSR_SCFG_PIXCLKCR, PIXCLKCR_PXCKEN | (pxclk << 16));
118
119	iounmap(scfg);
120}
121
122/**
123 * t1042rdb_valid_monitor_port: set the monitor port for sysfs
124 */
125static enum fsl_diu_monitor_port
126t1042rdb_valid_monitor_port(enum fsl_diu_monitor_port port)
127{
128	switch (port) {
129	case FSL_DIU_PORT_DVI:
130	case FSL_DIU_PORT_LVDS:
131		return port;
132	default:
133		return FSL_DIU_PORT_DVI; /* Dual-link LVDS is not supported */
134	}
135}
136
137static int __init t1042rdb_diu_init(void)
138{
139	cpld_node = of_find_compatible_node(NULL, NULL, "fsl,t1042rdb-cpld");
140	if (!cpld_node)
141		return 0;
142
143	diu_ops.set_monitor_port	= t1042rdb_set_monitor_port;
144	diu_ops.set_pixel_clock		= t1042rdb_set_pixel_clock;
145	diu_ops.valid_monitor_port	= t1042rdb_valid_monitor_port;
146
147	return 0;
148}
149
150early_initcall(t1042rdb_diu_init);
151
152MODULE_LICENSE("GPL");
153