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