aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/85xx/t1042rdb_diu.c
blob: 767eed98a0a8b13a4076afa8b46fc6a40813b71f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * T1042 platform DIU operation
 *
 * Copyright 2014 Freescale Semiconductor Inc.
 */

#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>

#include <sysdev/fsl_soc.h>

/*DIU Pixel ClockCR offset in scfg*/
#define CCSR_SCFG_PIXCLKCR      0x28

/* DIU Pixel Clock bits of the PIXCLKCR */
#define PIXCLKCR_PXCKEN		0x80000000
#define PIXCLKCR_PXCKINV	0x40000000
#define PIXCLKCR_PXCKDLY	0x0000FF00
#define PIXCLKCR_PXCLK_MASK	0x00FF0000

/* Some CPLD register definitions */
#define CPLD_DIUCSR		0x16
#define CPLD_DIUCSR_DVIEN	0x80
#define CPLD_DIUCSR_BACKLIGHT	0x0f

struct device_node *cpld_node;

/**
 * t1042rdb_set_monitor_port: switch the output to a different monitor port
 */
static void t1042rdb_set_monitor_port(enum fsl_diu_monitor_port port)
{
	void __iomem *cpld_base;

	cpld_base = of_iomap(cpld_node, 0);
	if (!cpld_base) {
		pr_err("%s: Could not map cpld registers\n", __func__);
		goto exit;
	}

	switch (port) {
	case FSL_DIU_PORT_DVI:
		/* Enable the DVI(HDMI) port, disable the DFP and
		 * the backlight
		 */
		clrbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_DVIEN);
		break;
	case FSL_DIU_PORT_LVDS:
		/*
		 * LVDS also needs backlight enabled, otherwise the display
		 * will be blank.
		 */
		/* Enable the DFP port, disable the DVI*/
		setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 8);
		setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 4);
		setbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_BACKLIGHT);
		break;
	default:
		pr_err("%s: Unsupported monitor port %i\n", __func__, port);
	}

	iounmap(cpld_base);
exit:
	of_node_put(cpld_node);
}

/**
 * t1042rdb_set_pixel_clock: program the DIU's clock
 * @pixclock: pixel clock in ps (pico seconds)
 */
static void t1042rdb_set_pixel_clock(unsigned int pixclock)
{
	struct device_node *scfg_np;
	void __iomem *scfg;
	unsigned long freq;
	u64 temp;
	u32 pxclk;

	scfg_np = of_find_compatible_node(NULL, NULL, "fsl,t1040-scfg");
	if (!scfg_np) {
		pr_err("%s: Missing scfg node. Can not display video.\n",
		       __func__);
		return;
	}

	scfg = of_iomap(scfg_np, 0);
	of_node_put(scfg_np);
	if (!scfg) {
		pr_err("%s: Could not map device. Can not display video.\n",
		       __func__);
		return;
	}

	/* Convert pixclock into frequency */
	temp = 1000000000000ULL;
	do_div(temp, pixclock);
	freq = temp;

	/*
	 * 'pxclk' is the ratio of the platform clock to the pixel clock.
	 * This number is programmed into the PIXCLKCR register, and the valid
	 * range of values is 2-255.
	 */
	pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq);
	pxclk = clamp_t(u32, pxclk, 2, 255);

	/* Disable the pixel clock, and set it to non-inverted and no delay */
	clrbits32(scfg + CCSR_SCFG_PIXCLKCR,
		  PIXCLKCR_PXCKEN | PIXCLKCR_PXCKDLY | PIXCLKCR_PXCLK_MASK);

	/* Enable the clock and set the pxclk */
	setbits32(scfg + CCSR_SCFG_PIXCLKCR, PIXCLKCR_PXCKEN | (pxclk << 16));

	iounmap(scfg);
}

/**
 * t1042rdb_valid_monitor_port: set the monitor port for sysfs
 */
static enum fsl_diu_monitor_port
t1042rdb_valid_monitor_port(enum fsl_diu_monitor_port port)
{
	switch (port) {
	case FSL_DIU_PORT_DVI:
	case FSL_DIU_PORT_LVDS:
		return port;
	default:
		return FSL_DIU_PORT_DVI; /* Dual-link LVDS is not supported */
	}
}

static int __init t1042rdb_diu_init(void)
{
	cpld_node = of_find_compatible_node(NULL, NULL, "fsl,t1042rdb-cpld");
	if (!cpld_node)
		return 0;

	diu_ops.set_monitor_port	= t1042rdb_set_monitor_port;
	diu_ops.set_pixel_clock		= t1042rdb_set_pixel_clock;
	diu_ops.valid_monitor_port	= t1042rdb_valid_monitor_port;

	return 0;
}

early_initcall(t1042rdb_diu_init);

MODULE_LICENSE("GPL");