]>
Commit | Line | Data |
---|---|---|
9e8f664e VB |
1 | /* |
2 | * Copyright (c) 2014 The Chromium OS Authors. All rights reserved. | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | /* | |
8 | * This file is a driver for Parade dP<->LVDS bridges. The original submission | |
9 | * is for the ps8625 chip. | |
10 | */ | |
11 | #include <config.h> | |
12 | #include <common.h> | |
13 | #include <i2c.h> | |
14 | #include <fdtdec.h> | |
55e70929 | 15 | #include <asm/gpio.h> |
9e8f664e VB |
16 | |
17 | /* | |
18 | * Initialization of the chip is a process of writing certaing values into | |
19 | * certain registers over i2c bus. The chip in fact responds to a range of | |
20 | * addresses on the i2c bus, so for each written value three parameters are | |
21 | * required: i2c address, register address and the actual value. | |
22 | * | |
23 | * The base address is derived from the device tree, only address offset is | |
24 | * stored in the table below. | |
25 | */ | |
26 | /** | |
27 | * struct reg_data() - data for a parade register write | |
28 | * | |
29 | * @addr_off offset from the i2c base address for parade | |
30 | * @reg_addr register address to write | |
31 | * @value value to be written | |
32 | */ | |
33 | struct reg_data { | |
34 | uint8_t addr_off; | |
35 | uint8_t reg; | |
36 | uint8_t value; | |
37 | } _packed; | |
38 | ||
39 | #define END_OF_TABLE 0xff /* Ficticious offset */ | |
40 | ||
41 | static const struct reg_data parade_values[] = { | |
42 | {0x02, 0xa1, 0x01}, /* HPD low */ | |
43 | /* | |
44 | * SW setting | |
45 | * [1:0] SW output 1.2V voltage is lower to 96% | |
46 | */ | |
47 | {0x04, 0x14, 0x01}, | |
48 | /* | |
49 | * RCO SS setting | |
50 | * [5:4] = b01 0.5%, b10 1%, b11 1.5% | |
51 | */ | |
52 | {0x04, 0xe3, 0x20}, | |
53 | {0x04, 0xe2, 0x80}, /* [7] RCO SS enable */ | |
54 | /* | |
55 | * RPHY Setting | |
56 | * [3:2] CDR tune wait cycle before | |
57 | * measure for fine tune b00: 1us, | |
58 | * 01: 0.5us, 10:2us, 11:4us. | |
59 | */ | |
60 | {0x04, 0x8a, 0x0c}, | |
61 | {0x04, 0x89, 0x08}, /* [3] RFD always on */ | |
62 | /* | |
63 | * CTN lock in/out: | |
64 | * 20000ppm/80000ppm. Lock out 2 | |
65 | * times. | |
66 | */ | |
67 | {0x04, 0x71, 0x2d}, | |
68 | /* | |
69 | * 2.7G CDR settings | |
70 | * NOF=40LSB for HBR CDR setting | |
71 | */ | |
72 | {0x04, 0x7d, 0x07}, | |
73 | {0x04, 0x7b, 0x00}, /* [1:0] Fmin=+4bands */ | |
74 | {0x04, 0x7a, 0xfd}, /* [7:5] DCO_FTRNG=+-40% */ | |
75 | /* | |
76 | * 1.62G CDR settings | |
77 | * [5:2]NOF=64LSB [1:0]DCO scale is 2/5 | |
78 | */ | |
79 | {0x04, 0xc0, 0x12}, | |
80 | {0x04, 0xc1, 0x92}, /* Gitune=-37% */ | |
81 | {0x04, 0xc2, 0x1c}, /* Fbstep=100% */ | |
82 | {0x04, 0x32, 0x80}, /* [7] LOS signal disable */ | |
83 | /* | |
84 | * RPIO Setting | |
85 | * [7:4] LVDS driver bias current : | |
86 | * 75% (250mV swing) | |
87 | */ | |
88 | {0x04, 0x00, 0xb0}, | |
89 | /* | |
90 | * [7:6] Right-bar GPIO output strength is 8mA | |
91 | */ | |
92 | {0x04, 0x15, 0x40}, | |
93 | /* EQ Training State Machine Setting */ | |
94 | {0x04, 0x54, 0x10}, /* RCO calibration start */ | |
95 | /* [4:0] MAX_LANE_COUNT set to one lane */ | |
96 | {0x01, 0x02, 0x81}, | |
97 | /* [4:0] LANE_COUNT_SET set to one lane */ | |
98 | {0x01, 0x21, 0x81}, | |
99 | {0x00, 0x52, 0x20}, | |
100 | {0x00, 0xf1, 0x03}, /* HPD CP toggle enable */ | |
101 | {0x00, 0x62, 0x41}, | |
102 | /* Counter number, add 1ms counter delay */ | |
103 | {0x00, 0xf6, 0x01}, | |
104 | /* | |
105 | * [6]PWM function control by | |
106 | * DPCD0040f[7], default is PWM | |
107 | * block always works. | |
108 | */ | |
109 | {0x00, 0x77, 0x06}, | |
110 | /* | |
111 | * 04h Adjust VTotal tolerance to | |
112 | * fix the 30Hz no display issue | |
113 | */ | |
114 | {0x00, 0x4c, 0x04}, | |
115 | /* DPCD00400='h00, Parade OUI = 'h001cf8 */ | |
116 | {0x01, 0xc0, 0x00}, | |
117 | {0x01, 0xc1, 0x1c}, /* DPCD00401='h1c */ | |
118 | {0x01, 0xc2, 0xf8}, /* DPCD00402='hf8 */ | |
119 | /* | |
120 | * DPCD403~408 = ASCII code | |
121 | * D2SLV5='h4432534c5635 | |
122 | */ | |
123 | {0x01, 0xc3, 0x44}, | |
124 | {0x01, 0xc4, 0x32}, /* DPCD404 */ | |
125 | {0x01, 0xc5, 0x53}, /* DPCD405 */ | |
126 | {0x01, 0xc6, 0x4c}, /* DPCD406 */ | |
127 | {0x01, 0xc7, 0x56}, /* DPCD407 */ | |
128 | {0x01, 0xc8, 0x35}, /* DPCD408 */ | |
129 | /* | |
130 | * DPCD40A, Initial Code major revision | |
131 | * '01' | |
132 | */ | |
133 | {0x01, 0xca, 0x01}, | |
134 | /* DPCD40B, Initial Code minor revision '05' */ | |
135 | {0x01, 0xcb, 0x05}, | |
136 | /* DPCD720, Select internal PWM */ | |
137 | {0x01, 0xa5, 0xa0}, | |
138 | /* | |
139 | * FFh for 100% PWM of brightness, 0h for 0% | |
140 | * brightness | |
141 | */ | |
142 | {0x01, 0xa7, 0xff}, | |
143 | /* | |
144 | * Set LVDS output as 6bit-VESA mapping, | |
145 | * single LVDS channel | |
146 | */ | |
147 | {0x01, 0xcc, 0x13}, | |
148 | /* Enable SSC set by register */ | |
149 | {0x02, 0xb1, 0x20}, | |
150 | /* | |
151 | * Set SSC enabled and +/-1% central | |
152 | * spreading | |
153 | */ | |
154 | {0x04, 0x10, 0x16}, | |
155 | /* MPU Clock source: LC => RCO */ | |
156 | {0x04, 0x59, 0x60}, | |
157 | {0x04, 0x54, 0x14}, /* LC -> RCO */ | |
158 | {0x02, 0xa1, 0x91}, /* HPD high */ | |
159 | {END_OF_TABLE} | |
160 | }; | |
161 | ||
162 | /** | |
163 | * Write values table into the Parade eDP bridge | |
164 | * | |
165 | * @return 0 on success, non-0 on failure | |
166 | */ | |
167 | ||
168 | static int parade_write_regs(int base_addr, const struct reg_data *table) | |
169 | { | |
170 | int ret = 0; | |
171 | ||
172 | while (!ret && (table->addr_off != END_OF_TABLE)) { | |
173 | ret = i2c_write(base_addr + table->addr_off, | |
174 | table->reg, 1, | |
175 | (uint8_t *)&table->value, | |
176 | sizeof(table->value)); | |
177 | table++; | |
178 | } | |
179 | return ret; | |
180 | } | |
181 | ||
182 | int parade_init(const void *blob) | |
183 | { | |
55e70929 AK |
184 | struct gpio_desc rst_gpio; |
185 | struct gpio_desc slp_gpio; | |
9e8f664e VB |
186 | int bus, old_bus; |
187 | int parent; | |
188 | int node; | |
189 | int addr; | |
190 | int ret; | |
191 | ||
192 | node = fdtdec_next_compatible(blob, 0, COMPAT_PARADE_PS8625); | |
193 | if (node < 0) | |
194 | return 0; | |
195 | ||
196 | parent = fdt_parent_offset(blob, node); | |
197 | if (parent < 0) { | |
198 | debug("%s: Could not find parent i2c node\n", __func__); | |
199 | return -1; | |
200 | } | |
201 | addr = fdtdec_get_int(blob, node, "reg", -1); | |
202 | if (addr < 0) { | |
203 | debug("%s: Could not find i2c address\n", __func__); | |
204 | return -1; | |
205 | } | |
206 | ||
55e70929 AK |
207 | gpio_request_by_name_nodev(blob, node, "sleep-gpio", 0, &slp_gpio, |
208 | GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); | |
209 | ||
210 | mdelay(10); | |
211 | ||
212 | gpio_request_by_name_nodev(blob, node, "reset-gpio", 0, &rst_gpio, | |
213 | GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); | |
214 | ||
9e8f664e VB |
215 | bus = i2c_get_bus_num_fdt(parent); |
216 | old_bus = i2c_get_bus_num(); | |
217 | ||
218 | debug("%s: Using i2c bus %d\n", __func__, bus); | |
219 | ||
220 | /* | |
221 | * TODO(sjg@chromium.org): Hmmm we seem to need some sort of delay | |
222 | * here. | |
223 | */ | |
224 | mdelay(40); | |
225 | i2c_set_bus_num(bus); | |
226 | ret = parade_write_regs(addr, parade_values); | |
227 | ||
228 | i2c_set_bus_num(old_bus); | |
229 | ||
230 | return ret; | |
231 | } |