]>
Commit | Line | Data |
---|---|---|
026abc33 KS |
1 | /* |
2 | * Copyright © 2011 Intel Corporation | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice (including the next | |
12 | * paragraph) shall be included in all copies or substantial portions of the | |
13 | * Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
21 | * DEALINGS IN THE SOFTWARE. | |
22 | * | |
23 | */ | |
24 | ||
25 | #include "mdfld_dsi_dpi.h" | |
26 | #include "mdfld_output.h" | |
27 | #include "mdfld_dsi_pkg_sender.h" | |
28 | #include "tc35876x-dsi-lvds.h" | |
29 | #include <linux/i2c/tc35876x.h> | |
30 | #include <linux/kernel.h> | |
31 | #include <linux/module.h> | |
32 | #include <asm/intel_scu_ipc.h> | |
33 | ||
34 | static struct i2c_client *tc35876x_client; | |
35 | static struct i2c_client *cmi_lcd_i2c_client; | |
36 | ||
37 | #define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) | |
38 | #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) | |
39 | ||
40 | /* DSI D-PHY Layer Registers */ | |
41 | #define D0W_DPHYCONTTX 0x0004 | |
42 | #define CLW_DPHYCONTRX 0x0020 | |
43 | #define D0W_DPHYCONTRX 0x0024 | |
44 | #define D1W_DPHYCONTRX 0x0028 | |
45 | #define D2W_DPHYCONTRX 0x002C | |
46 | #define D3W_DPHYCONTRX 0x0030 | |
47 | #define COM_DPHYCONTRX 0x0038 | |
48 | #define CLW_CNTRL 0x0040 | |
49 | #define D0W_CNTRL 0x0044 | |
50 | #define D1W_CNTRL 0x0048 | |
51 | #define D2W_CNTRL 0x004C | |
52 | #define D3W_CNTRL 0x0050 | |
53 | #define DFTMODE_CNTRL 0x0054 | |
54 | ||
55 | /* DSI PPI Layer Registers */ | |
56 | #define PPI_STARTPPI 0x0104 | |
57 | #define PPI_BUSYPPI 0x0108 | |
58 | #define PPI_LINEINITCNT 0x0110 | |
59 | #define PPI_LPTXTIMECNT 0x0114 | |
60 | #define PPI_LANEENABLE 0x0134 | |
61 | #define PPI_TX_RX_TA 0x013C | |
62 | #define PPI_CLS_ATMR 0x0140 | |
63 | #define PPI_D0S_ATMR 0x0144 | |
64 | #define PPI_D1S_ATMR 0x0148 | |
65 | #define PPI_D2S_ATMR 0x014C | |
66 | #define PPI_D3S_ATMR 0x0150 | |
67 | #define PPI_D0S_CLRSIPOCOUNT 0x0164 | |
68 | #define PPI_D1S_CLRSIPOCOUNT 0x0168 | |
69 | #define PPI_D2S_CLRSIPOCOUNT 0x016C | |
70 | #define PPI_D3S_CLRSIPOCOUNT 0x0170 | |
71 | #define CLS_PRE 0x0180 | |
72 | #define D0S_PRE 0x0184 | |
73 | #define D1S_PRE 0x0188 | |
74 | #define D2S_PRE 0x018C | |
75 | #define D3S_PRE 0x0190 | |
76 | #define CLS_PREP 0x01A0 | |
77 | #define D0S_PREP 0x01A4 | |
78 | #define D1S_PREP 0x01A8 | |
79 | #define D2S_PREP 0x01AC | |
80 | #define D3S_PREP 0x01B0 | |
81 | #define CLS_ZERO 0x01C0 | |
82 | #define D0S_ZERO 0x01C4 | |
83 | #define D1S_ZERO 0x01C8 | |
84 | #define D2S_ZERO 0x01CC | |
85 | #define D3S_ZERO 0x01D0 | |
86 | #define PPI_CLRFLG 0x01E0 | |
87 | #define PPI_CLRSIPO 0x01E4 | |
88 | #define HSTIMEOUT 0x01F0 | |
89 | #define HSTIMEOUTENABLE 0x01F4 | |
90 | ||
91 | /* DSI Protocol Layer Registers */ | |
92 | #define DSI_STARTDSI 0x0204 | |
93 | #define DSI_BUSYDSI 0x0208 | |
94 | #define DSI_LANEENABLE 0x0210 | |
95 | #define DSI_LANESTATUS0 0x0214 | |
96 | #define DSI_LANESTATUS1 0x0218 | |
97 | #define DSI_INTSTATUS 0x0220 | |
98 | #define DSI_INTMASK 0x0224 | |
99 | #define DSI_INTCLR 0x0228 | |
100 | #define DSI_LPTXTO 0x0230 | |
101 | ||
102 | /* DSI General Registers */ | |
103 | #define DSIERRCNT 0x0300 | |
104 | ||
105 | /* DSI Application Layer Registers */ | |
106 | #define APLCTRL 0x0400 | |
107 | #define RDPKTLN 0x0404 | |
108 | ||
109 | /* Video Path Registers */ | |
110 | #define VPCTRL 0x0450 | |
111 | #define HTIM1 0x0454 | |
112 | #define HTIM2 0x0458 | |
113 | #define VTIM1 0x045C | |
114 | #define VTIM2 0x0460 | |
115 | #define VFUEN 0x0464 | |
116 | ||
117 | /* LVDS Registers */ | |
118 | #define LVMX0003 0x0480 | |
119 | #define LVMX0407 0x0484 | |
120 | #define LVMX0811 0x0488 | |
121 | #define LVMX1215 0x048C | |
122 | #define LVMX1619 0x0490 | |
123 | #define LVMX2023 0x0494 | |
124 | #define LVMX2427 0x0498 | |
125 | #define LVCFG 0x049C | |
126 | #define LVPHY0 0x04A0 | |
127 | #define LVPHY1 0x04A4 | |
128 | ||
129 | /* System Registers */ | |
130 | #define SYSSTAT 0x0500 | |
131 | #define SYSRST 0x0504 | |
132 | ||
133 | /* GPIO Registers */ | |
134 | /*#define GPIOC 0x0520*/ | |
135 | #define GPIOO 0x0524 | |
136 | #define GPIOI 0x0528 | |
137 | ||
138 | /* I2C Registers */ | |
139 | #define I2CTIMCTRL 0x0540 | |
140 | #define I2CMADDR 0x0544 | |
141 | #define WDATAQ 0x0548 | |
142 | #define RDATAQ 0x054C | |
143 | ||
144 | /* Chip/Rev Registers */ | |
145 | #define IDREG 0x0580 | |
146 | ||
147 | /* Debug Registers */ | |
148 | #define DEBUG00 0x05A0 | |
149 | #define DEBUG01 0x05A4 | |
150 | ||
151 | /* Panel CABC registers */ | |
152 | #define PANEL_PWM_CONTROL 0x90 | |
153 | #define PANEL_FREQ_DIVIDER_HI 0x91 | |
154 | #define PANEL_FREQ_DIVIDER_LO 0x92 | |
155 | #define PANEL_DUTY_CONTROL 0x93 | |
156 | #define PANEL_MODIFY_RGB 0x94 | |
157 | #define PANEL_FRAMERATE_CONTROL 0x96 | |
158 | #define PANEL_PWM_MIN 0x97 | |
159 | #define PANEL_PWM_REF 0x98 | |
160 | #define PANEL_PWM_MAX 0x99 | |
161 | #define PANEL_ALLOW_DISTORT 0x9A | |
162 | #define PANEL_BYPASS_PWMI 0x9B | |
163 | ||
164 | /* Panel color management registers */ | |
165 | #define PANEL_CM_ENABLE 0x700 | |
166 | #define PANEL_CM_HUE 0x701 | |
167 | #define PANEL_CM_SATURATION 0x702 | |
168 | #define PANEL_CM_INTENSITY 0x703 | |
169 | #define PANEL_CM_BRIGHTNESS 0x704 | |
170 | #define PANEL_CM_CE_ENABLE 0x705 | |
171 | #define PANEL_CM_PEAK_EN 0x710 | |
172 | #define PANEL_CM_GAIN 0x711 | |
173 | #define PANEL_CM_HUETABLE_START 0x730 | |
174 | #define PANEL_CM_HUETABLE_END 0x747 /* inclusive */ | |
175 | ||
176 | /* Input muxing for registers LVMX0003...LVMX2427 */ | |
177 | enum { | |
178 | INPUT_R0, /* 0 */ | |
179 | INPUT_R1, | |
180 | INPUT_R2, | |
181 | INPUT_R3, | |
182 | INPUT_R4, | |
183 | INPUT_R5, | |
184 | INPUT_R6, | |
185 | INPUT_R7, | |
186 | INPUT_G0, /* 8 */ | |
187 | INPUT_G1, | |
188 | INPUT_G2, | |
189 | INPUT_G3, | |
190 | INPUT_G4, | |
191 | INPUT_G5, | |
192 | INPUT_G6, | |
193 | INPUT_G7, | |
194 | INPUT_B0, /* 16 */ | |
195 | INPUT_B1, | |
196 | INPUT_B2, | |
197 | INPUT_B3, | |
198 | INPUT_B4, | |
199 | INPUT_B5, | |
200 | INPUT_B6, | |
201 | INPUT_B7, | |
202 | INPUT_HSYNC, /* 24 */ | |
203 | INPUT_VSYNC, | |
204 | INPUT_DE, | |
205 | LOGIC_0, | |
206 | /* 28...31 undefined */ | |
207 | }; | |
208 | ||
209 | #define INPUT_MUX(lvmx03, lvmx02, lvmx01, lvmx00) \ | |
210 | (FLD_VAL(lvmx03, 29, 24) | FLD_VAL(lvmx02, 20, 16) | \ | |
211 | FLD_VAL(lvmx01, 12, 8) | FLD_VAL(lvmx00, 4, 0)) | |
212 | ||
213 | /** | |
214 | * tc35876x_regw - Write DSI-LVDS bridge register using I2C | |
215 | * @client: struct i2c_client to use | |
216 | * @reg: register address | |
217 | * @value: value to write | |
218 | * | |
219 | * Returns 0 on success, or a negative error value. | |
220 | */ | |
221 | static int tc35876x_regw(struct i2c_client *client, u16 reg, u32 value) | |
222 | { | |
223 | int r; | |
224 | u8 tx_data[] = { | |
225 | /* NOTE: Register address big-endian, data little-endian. */ | |
226 | (reg >> 8) & 0xff, | |
227 | reg & 0xff, | |
228 | value & 0xff, | |
229 | (value >> 8) & 0xff, | |
230 | (value >> 16) & 0xff, | |
231 | (value >> 24) & 0xff, | |
232 | }; | |
233 | struct i2c_msg msgs[] = { | |
234 | { | |
235 | .addr = client->addr, | |
236 | .flags = 0, | |
237 | .buf = tx_data, | |
238 | .len = ARRAY_SIZE(tx_data), | |
239 | }, | |
240 | }; | |
241 | ||
242 | r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | |
243 | if (r < 0) { | |
244 | dev_err(&client->dev, "%s: reg 0x%04x val 0x%08x error %d\n", | |
245 | __func__, reg, value, r); | |
246 | return r; | |
247 | } | |
248 | ||
249 | if (r < ARRAY_SIZE(msgs)) { | |
250 | dev_err(&client->dev, "%s: reg 0x%04x val 0x%08x msgs %d\n", | |
251 | __func__, reg, value, r); | |
252 | return -EAGAIN; | |
253 | } | |
254 | ||
255 | dev_dbg(&client->dev, "%s: reg 0x%04x val 0x%08x\n", | |
256 | __func__, reg, value); | |
257 | ||
258 | return 0; | |
259 | } | |
260 | ||
261 | /** | |
262 | * tc35876x_regr - Read DSI-LVDS bridge register using I2C | |
263 | * @client: struct i2c_client to use | |
264 | * @reg: register address | |
265 | * @value: pointer for storing the value | |
266 | * | |
267 | * Returns 0 on success, or a negative error value. | |
268 | */ | |
269 | static int tc35876x_regr(struct i2c_client *client, u16 reg, u32 *value) | |
270 | { | |
271 | int r; | |
272 | u8 tx_data[] = { | |
273 | (reg >> 8) & 0xff, | |
274 | reg & 0xff, | |
275 | }; | |
276 | u8 rx_data[4]; | |
277 | struct i2c_msg msgs[] = { | |
278 | { | |
279 | .addr = client->addr, | |
280 | .flags = 0, | |
281 | .buf = tx_data, | |
282 | .len = ARRAY_SIZE(tx_data), | |
283 | }, | |
284 | { | |
285 | .addr = client->addr, | |
286 | .flags = I2C_M_RD, | |
287 | .buf = rx_data, | |
288 | .len = ARRAY_SIZE(rx_data), | |
289 | }, | |
290 | }; | |
291 | ||
292 | r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | |
293 | if (r < 0) { | |
294 | dev_err(&client->dev, "%s: reg 0x%04x error %d\n", __func__, | |
295 | reg, r); | |
296 | return r; | |
297 | } | |
298 | ||
299 | if (r < ARRAY_SIZE(msgs)) { | |
300 | dev_err(&client->dev, "%s: reg 0x%04x msgs %d\n", __func__, | |
301 | reg, r); | |
302 | return -EAGAIN; | |
303 | } | |
304 | ||
305 | *value = rx_data[0] << 24 | rx_data[1] << 16 | | |
306 | rx_data[2] << 8 | rx_data[3]; | |
307 | ||
308 | dev_dbg(&client->dev, "%s: reg 0x%04x value 0x%08x\n", __func__, | |
309 | reg, *value); | |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
314 | void tc35876x_set_bridge_reset_state(struct drm_device *dev, int state) | |
315 | { | |
316 | struct tc35876x_platform_data *pdata; | |
317 | ||
318 | if (WARN(!tc35876x_client, "%s called before probe", __func__)) | |
319 | return; | |
320 | ||
321 | dev_dbg(&tc35876x_client->dev, "%s: state %d\n", __func__, state); | |
322 | ||
323 | pdata = dev_get_platdata(&tc35876x_client->dev); | |
324 | ||
325 | if (pdata->gpio_bridge_reset == -1) | |
326 | return; | |
327 | ||
328 | if (state) { | |
329 | gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0); | |
330 | mdelay(10); | |
331 | } else { | |
332 | /* Pull MIPI Bridge reset pin to Low */ | |
333 | gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0); | |
334 | mdelay(20); | |
335 | /* Pull MIPI Bridge reset pin to High */ | |
336 | gpio_set_value_cansleep(pdata->gpio_bridge_reset, 1); | |
337 | mdelay(40); | |
338 | } | |
339 | } | |
340 | ||
341 | void tc35876x_configure_lvds_bridge(struct drm_device *dev) | |
342 | { | |
343 | struct i2c_client *i2c = tc35876x_client; | |
344 | u32 ppi_lptxtimecnt; | |
345 | u32 txtagocnt; | |
346 | u32 txtasurecnt; | |
347 | u32 id; | |
348 | ||
349 | if (WARN(!tc35876x_client, "%s called before probe", __func__)) | |
350 | return; | |
351 | ||
352 | dev_dbg(&tc35876x_client->dev, "%s\n", __func__); | |
353 | ||
354 | if (!tc35876x_regr(i2c, IDREG, &id)) | |
355 | dev_info(&tc35876x_client->dev, "tc35876x ID 0x%08x\n", id); | |
356 | else | |
357 | dev_err(&tc35876x_client->dev, "Cannot read ID\n"); | |
358 | ||
359 | ppi_lptxtimecnt = 4; | |
360 | txtagocnt = (5 * ppi_lptxtimecnt - 3) / 4; | |
361 | txtasurecnt = 3 * ppi_lptxtimecnt / 2; | |
362 | tc35876x_regw(i2c, PPI_TX_RX_TA, FLD_VAL(txtagocnt, 26, 16) | | |
363 | FLD_VAL(txtasurecnt, 10, 0)); | |
364 | tc35876x_regw(i2c, PPI_LPTXTIMECNT, FLD_VAL(ppi_lptxtimecnt, 10, 0)); | |
365 | ||
366 | tc35876x_regw(i2c, PPI_D0S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0)); | |
367 | tc35876x_regw(i2c, PPI_D1S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0)); | |
368 | tc35876x_regw(i2c, PPI_D2S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0)); | |
369 | tc35876x_regw(i2c, PPI_D3S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0)); | |
370 | ||
371 | /* Enabling MIPI & PPI lanes, Enable 4 lanes */ | |
372 | tc35876x_regw(i2c, PPI_LANEENABLE, | |
373 | BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0)); | |
374 | tc35876x_regw(i2c, DSI_LANEENABLE, | |
375 | BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0)); | |
376 | tc35876x_regw(i2c, PPI_STARTPPI, BIT(0)); | |
377 | tc35876x_regw(i2c, DSI_STARTDSI, BIT(0)); | |
378 | ||
379 | /* Setting LVDS output frequency */ | |
380 | tc35876x_regw(i2c, LVPHY0, FLD_VAL(1, 20, 16) | | |
381 | FLD_VAL(2, 15, 14) | FLD_VAL(6, 4, 0)); /* 0x00048006 */ | |
382 | ||
383 | /* Setting video panel control register,0x00000120 VTGen=ON ?!?!? */ | |
384 | tc35876x_regw(i2c, VPCTRL, BIT(8) | BIT(5)); | |
385 | ||
386 | /* Horizontal back porch and horizontal pulse width. 0x00280028 */ | |
387 | tc35876x_regw(i2c, HTIM1, FLD_VAL(40, 24, 16) | FLD_VAL(40, 8, 0)); | |
388 | ||
389 | /* Horizontal front porch and horizontal active video size. 0x00500500*/ | |
390 | tc35876x_regw(i2c, HTIM2, FLD_VAL(80, 24, 16) | FLD_VAL(1280, 10, 0)); | |
391 | ||
392 | /* Vertical back porch and vertical sync pulse width. 0x000e000a */ | |
393 | tc35876x_regw(i2c, VTIM1, FLD_VAL(14, 23, 16) | FLD_VAL(10, 7, 0)); | |
394 | ||
395 | /* Vertical front porch and vertical display size. 0x000e0320 */ | |
396 | tc35876x_regw(i2c, VTIM2, FLD_VAL(14, 23, 16) | FLD_VAL(800, 10, 0)); | |
397 | ||
398 | /* Set above HTIM1, HTIM2, VTIM1, and VTIM2 at next VSYNC. */ | |
399 | tc35876x_regw(i2c, VFUEN, BIT(0)); | |
400 | ||
401 | /* Soft reset LCD controller. */ | |
402 | tc35876x_regw(i2c, SYSRST, BIT(2)); | |
403 | ||
404 | /* LVDS-TX input muxing */ | |
405 | tc35876x_regw(i2c, LVMX0003, | |
406 | INPUT_MUX(INPUT_R5, INPUT_R4, INPUT_R3, INPUT_R2)); | |
407 | tc35876x_regw(i2c, LVMX0407, | |
408 | INPUT_MUX(INPUT_G2, INPUT_R7, INPUT_R1, INPUT_R6)); | |
409 | tc35876x_regw(i2c, LVMX0811, | |
410 | INPUT_MUX(INPUT_G1, INPUT_G0, INPUT_G4, INPUT_G3)); | |
411 | tc35876x_regw(i2c, LVMX1215, | |
412 | INPUT_MUX(INPUT_B2, INPUT_G7, INPUT_G6, INPUT_G5)); | |
413 | tc35876x_regw(i2c, LVMX1619, | |
414 | INPUT_MUX(INPUT_B4, INPUT_B3, INPUT_B1, INPUT_B0)); | |
415 | tc35876x_regw(i2c, LVMX2023, | |
416 | INPUT_MUX(LOGIC_0, INPUT_B7, INPUT_B6, INPUT_B5)); | |
417 | tc35876x_regw(i2c, LVMX2427, | |
418 | INPUT_MUX(INPUT_R0, INPUT_DE, INPUT_VSYNC, INPUT_HSYNC)); | |
419 | ||
420 | /* Enable LVDS transmitter. */ | |
421 | tc35876x_regw(i2c, LVCFG, BIT(0)); | |
422 | ||
423 | /* Clear notifications. Don't write reserved bits. Was write 0xffffffff | |
424 | * to 0x0288, must be in error?! */ | |
425 | tc35876x_regw(i2c, DSI_INTCLR, FLD_MASK(31, 30) | FLD_MASK(22, 0)); | |
426 | } | |
427 | ||
428 | #define GPIOPWMCTRL 0x38F | |
429 | #define PWM0CLKDIV0 0x62 /* low byte */ | |
430 | #define PWM0CLKDIV1 0x61 /* high byte */ | |
431 | ||
432 | #define SYSTEMCLK 19200000UL /* 19.2 MHz */ | |
433 | #define PWM_FREQUENCY 9600 /* Hz */ | |
434 | ||
435 | /* f = baseclk / (clkdiv + 1) => clkdiv = (baseclk - f) / f */ | |
436 | static inline u16 calc_clkdiv(unsigned long baseclk, unsigned int f) | |
437 | { | |
438 | return (baseclk - f) / f; | |
439 | } | |
440 | ||
441 | static void tc35876x_brightness_init(struct drm_device *dev) | |
442 | { | |
443 | int ret; | |
444 | u8 pwmctrl; | |
445 | u16 clkdiv; | |
446 | ||
447 | /* Make sure the PWM reference is the 19.2 MHz system clock. Read first | |
448 | * instead of setting directly to catch potential conflicts between PWM | |
449 | * users. */ | |
450 | ret = intel_scu_ipc_ioread8(GPIOPWMCTRL, &pwmctrl); | |
451 | if (ret || pwmctrl != 0x01) { | |
452 | if (ret) | |
453 | dev_err(&dev->pdev->dev, "GPIOPWMCTRL read failed\n"); | |
454 | else | |
455 | dev_warn(&dev->pdev->dev, "GPIOPWMCTRL was not set to system clock (pwmctrl = 0x%02x)\n", pwmctrl); | |
456 | ||
457 | ret = intel_scu_ipc_iowrite8(GPIOPWMCTRL, 0x01); | |
458 | if (ret) | |
459 | dev_err(&dev->pdev->dev, "GPIOPWMCTRL set failed\n"); | |
460 | } | |
461 | ||
462 | clkdiv = calc_clkdiv(SYSTEMCLK, PWM_FREQUENCY); | |
463 | ||
464 | ret = intel_scu_ipc_iowrite8(PWM0CLKDIV1, (clkdiv >> 8) & 0xff); | |
465 | if (!ret) | |
466 | ret = intel_scu_ipc_iowrite8(PWM0CLKDIV0, clkdiv & 0xff); | |
467 | ||
468 | if (ret) | |
469 | dev_err(&dev->pdev->dev, "PWM0CLKDIV set failed\n"); | |
470 | else | |
471 | dev_dbg(&dev->pdev->dev, "PWM0CLKDIV set to 0x%04x (%d Hz)\n", | |
472 | clkdiv, PWM_FREQUENCY); | |
473 | } | |
474 | ||
475 | #define PWM0DUTYCYCLE 0x67 | |
476 | ||
477 | void tc35876x_brightness_control(struct drm_device *dev, int level) | |
478 | { | |
479 | int ret; | |
480 | u8 duty_val; | |
481 | u8 panel_duty_val; | |
482 | ||
483 | level = clamp(level, 0, MDFLD_DSI_BRIGHTNESS_MAX_LEVEL); | |
484 | ||
485 | /* PWM duty cycle 0x00...0x63 corresponds to 0...99% */ | |
486 | duty_val = level * 0x63 / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL; | |
487 | ||
488 | /* I won't pretend to understand this formula. The panel spec is quite | |
489 | * bad engrish. | |
490 | */ | |
491 | panel_duty_val = (2 * level - 100) * 0xA9 / | |
492 | MDFLD_DSI_BRIGHTNESS_MAX_LEVEL + 0x56; | |
493 | ||
494 | ret = intel_scu_ipc_iowrite8(PWM0DUTYCYCLE, duty_val); | |
495 | if (ret) | |
496 | dev_err(&tc35876x_client->dev, "%s: ipc write fail\n", | |
497 | __func__); | |
498 | ||
499 | if (cmi_lcd_i2c_client) { | |
500 | ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client, | |
501 | PANEL_PWM_MAX, panel_duty_val); | |
502 | if (ret < 0) | |
503 | dev_err(&cmi_lcd_i2c_client->dev, "%s: i2c write failed\n", | |
504 | __func__); | |
505 | } | |
506 | } | |
507 | ||
508 | void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev) | |
509 | { | |
510 | struct tc35876x_platform_data *pdata; | |
511 | ||
512 | if (WARN(!tc35876x_client, "%s called before probe", __func__)) | |
513 | return; | |
514 | ||
515 | dev_dbg(&tc35876x_client->dev, "%s\n", __func__); | |
516 | ||
517 | pdata = dev_get_platdata(&tc35876x_client->dev); | |
518 | ||
519 | if (pdata->gpio_panel_bl_en != -1) | |
520 | gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 0); | |
521 | ||
522 | if (pdata->gpio_panel_vadd != -1) | |
523 | gpio_set_value_cansleep(pdata->gpio_panel_vadd, 0); | |
524 | } | |
525 | ||
526 | void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev) | |
527 | { | |
528 | struct tc35876x_platform_data *pdata; | |
529 | struct drm_psb_private *dev_priv = dev->dev_private; | |
530 | ||
531 | if (WARN(!tc35876x_client, "%s called before probe", __func__)) | |
532 | return; | |
533 | ||
534 | dev_dbg(&tc35876x_client->dev, "%s\n", __func__); | |
535 | ||
536 | pdata = dev_get_platdata(&tc35876x_client->dev); | |
537 | ||
538 | if (pdata->gpio_panel_vadd != -1) { | |
539 | gpio_set_value_cansleep(pdata->gpio_panel_vadd, 1); | |
540 | msleep(260); | |
541 | } | |
542 | ||
543 | if (cmi_lcd_i2c_client) { | |
544 | int ret; | |
545 | dev_dbg(&cmi_lcd_i2c_client->dev, "setting TCON\n"); | |
546 | /* Bit 4 is average_saving. Setting it to 1, the brightness is | |
547 | * referenced to the average of the frame content. 0 means | |
548 | * reference to the maximum of frame contents. Bits 3:0 are | |
549 | * allow_distort. When set to a nonzero value, all color values | |
550 | * between 255-allow_distort*2 and 255 are mapped to the | |
551 | * 255-allow_distort*2 value. | |
552 | */ | |
553 | ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client, | |
554 | PANEL_ALLOW_DISTORT, 0x10); | |
555 | if (ret < 0) | |
556 | dev_err(&cmi_lcd_i2c_client->dev, | |
557 | "i2c write failed (%d)\n", ret); | |
558 | ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client, | |
559 | PANEL_BYPASS_PWMI, 0); | |
560 | if (ret < 0) | |
561 | dev_err(&cmi_lcd_i2c_client->dev, | |
562 | "i2c write failed (%d)\n", ret); | |
563 | /* Set minimum brightness value - this is tunable */ | |
564 | ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client, | |
565 | PANEL_PWM_MIN, 0x35); | |
566 | if (ret < 0) | |
567 | dev_err(&cmi_lcd_i2c_client->dev, | |
568 | "i2c write failed (%d)\n", ret); | |
569 | } | |
570 | ||
571 | if (pdata->gpio_panel_bl_en != -1) | |
572 | gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 1); | |
573 | ||
574 | tc35876x_brightness_control(dev, dev_priv->brightness_adjusted); | |
575 | } | |
576 | ||
577 | static struct drm_display_mode *tc35876x_get_config_mode(struct drm_device *dev) | |
578 | { | |
579 | struct drm_display_mode *mode; | |
580 | ||
581 | dev_dbg(&dev->pdev->dev, "%s\n", __func__); | |
582 | ||
583 | mode = kzalloc(sizeof(*mode), GFP_KERNEL); | |
584 | if (!mode) | |
585 | return NULL; | |
586 | ||
587 | /* FIXME: do this properly. */ | |
588 | mode->hdisplay = 1280; | |
589 | mode->vdisplay = 800; | |
590 | mode->hsync_start = 1360; | |
591 | mode->hsync_end = 1400; | |
592 | mode->htotal = 1440; | |
593 | mode->vsync_start = 814; | |
594 | mode->vsync_end = 824; | |
595 | mode->vtotal = 838; | |
596 | mode->clock = 33324 << 1; | |
597 | ||
598 | dev_info(&dev->pdev->dev, "hdisplay(w) = %d\n", mode->hdisplay); | |
599 | dev_info(&dev->pdev->dev, "vdisplay(h) = %d\n", mode->vdisplay); | |
600 | dev_info(&dev->pdev->dev, "HSS = %d\n", mode->hsync_start); | |
601 | dev_info(&dev->pdev->dev, "HSE = %d\n", mode->hsync_end); | |
602 | dev_info(&dev->pdev->dev, "htotal = %d\n", mode->htotal); | |
603 | dev_info(&dev->pdev->dev, "VSS = %d\n", mode->vsync_start); | |
604 | dev_info(&dev->pdev->dev, "VSE = %d\n", mode->vsync_end); | |
605 | dev_info(&dev->pdev->dev, "vtotal = %d\n", mode->vtotal); | |
606 | dev_info(&dev->pdev->dev, "clock = %d\n", mode->clock); | |
607 | ||
608 | drm_mode_set_name(mode); | |
609 | drm_mode_set_crtcinfo(mode, 0); | |
610 | ||
611 | mode->type |= DRM_MODE_TYPE_PREFERRED; | |
612 | ||
613 | return mode; | |
614 | } | |
615 | ||
616 | /* DV1 Active area 216.96 x 135.6 mm */ | |
617 | #define DV1_PANEL_WIDTH 217 | |
618 | #define DV1_PANEL_HEIGHT 136 | |
619 | ||
620 | static int tc35876x_get_panel_info(struct drm_device *dev, int pipe, | |
621 | struct panel_info *pi) | |
622 | { | |
623 | if (!dev || !pi) | |
624 | return -EINVAL; | |
625 | ||
626 | pi->width_mm = DV1_PANEL_WIDTH; | |
627 | pi->height_mm = DV1_PANEL_HEIGHT; | |
628 | ||
629 | return 0; | |
630 | } | |
631 | ||
632 | static int tc35876x_bridge_probe(struct i2c_client *client, | |
633 | const struct i2c_device_id *id) | |
634 | { | |
635 | struct tc35876x_platform_data *pdata; | |
636 | ||
637 | dev_info(&client->dev, "%s\n", __func__); | |
638 | ||
639 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | |
640 | dev_err(&client->dev, "%s: i2c_check_functionality() failed\n", | |
641 | __func__); | |
642 | return -ENODEV; | |
643 | } | |
644 | ||
645 | pdata = dev_get_platdata(&client->dev); | |
646 | if (!pdata) { | |
647 | dev_err(&client->dev, "%s: no platform data\n", __func__); | |
648 | return -ENODEV; | |
649 | } | |
650 | ||
651 | if (pdata->gpio_bridge_reset != -1) { | |
652 | gpio_request(pdata->gpio_bridge_reset, "tc35876x bridge reset"); | |
653 | gpio_direction_output(pdata->gpio_bridge_reset, 0); | |
654 | } | |
655 | ||
656 | if (pdata->gpio_panel_bl_en != -1) { | |
657 | gpio_request(pdata->gpio_panel_bl_en, "tc35876x panel bl en"); | |
658 | gpio_direction_output(pdata->gpio_panel_bl_en, 0); | |
659 | } | |
660 | ||
661 | if (pdata->gpio_panel_vadd != -1) { | |
662 | gpio_request(pdata->gpio_panel_vadd, "tc35876x panel vadd"); | |
663 | gpio_direction_output(pdata->gpio_panel_vadd, 0); | |
664 | } | |
665 | ||
666 | tc35876x_client = client; | |
667 | ||
668 | return 0; | |
669 | } | |
670 | ||
671 | static int tc35876x_bridge_remove(struct i2c_client *client) | |
672 | { | |
673 | struct tc35876x_platform_data *pdata = dev_get_platdata(&client->dev); | |
674 | ||
675 | dev_dbg(&client->dev, "%s\n", __func__); | |
676 | ||
677 | if (pdata->gpio_bridge_reset != -1) | |
678 | gpio_free(pdata->gpio_bridge_reset); | |
679 | ||
680 | if (pdata->gpio_panel_bl_en != -1) | |
681 | gpio_free(pdata->gpio_panel_bl_en); | |
682 | ||
683 | if (pdata->gpio_panel_vadd != -1) | |
684 | gpio_free(pdata->gpio_panel_vadd); | |
685 | ||
686 | tc35876x_client = NULL; | |
687 | ||
688 | return 0; | |
689 | } | |
690 | ||
691 | static const struct i2c_device_id tc35876x_bridge_id[] = { | |
692 | { "i2c_disp_brig", 0 }, | |
693 | { } | |
694 | }; | |
695 | MODULE_DEVICE_TABLE(i2c, tc35876x_bridge_id); | |
696 | ||
697 | static struct i2c_driver tc35876x_bridge_i2c_driver = { | |
698 | .driver = { | |
699 | .name = "i2c_disp_brig", | |
700 | }, | |
701 | .id_table = tc35876x_bridge_id, | |
702 | .probe = tc35876x_bridge_probe, | |
703 | .remove = __devexit_p(tc35876x_bridge_remove), | |
704 | }; | |
705 | ||
706 | /* LCD panel I2C */ | |
707 | static int cmi_lcd_i2c_probe(struct i2c_client *client, | |
708 | const struct i2c_device_id *id) | |
709 | { | |
710 | dev_info(&client->dev, "%s\n", __func__); | |
711 | ||
712 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | |
713 | dev_err(&client->dev, "%s: i2c_check_functionality() failed\n", | |
714 | __func__); | |
715 | return -ENODEV; | |
716 | } | |
717 | ||
718 | cmi_lcd_i2c_client = client; | |
719 | ||
720 | return 0; | |
721 | } | |
722 | ||
723 | static int cmi_lcd_i2c_remove(struct i2c_client *client) | |
724 | { | |
725 | dev_dbg(&client->dev, "%s\n", __func__); | |
726 | ||
727 | cmi_lcd_i2c_client = NULL; | |
728 | ||
729 | return 0; | |
730 | } | |
731 | ||
732 | static const struct i2c_device_id cmi_lcd_i2c_id[] = { | |
733 | { "cmi-lcd", 0 }, | |
734 | { } | |
735 | }; | |
736 | MODULE_DEVICE_TABLE(i2c, cmi_lcd_i2c_id); | |
737 | ||
738 | static struct i2c_driver cmi_lcd_i2c_driver = { | |
739 | .driver = { | |
740 | .name = "cmi-lcd", | |
741 | }, | |
742 | .id_table = cmi_lcd_i2c_id, | |
743 | .probe = cmi_lcd_i2c_probe, | |
744 | .remove = __devexit_p(cmi_lcd_i2c_remove), | |
745 | }; | |
746 | ||
747 | /* HACK to create I2C device while it's not created by platform code */ | |
748 | #define CMI_LCD_I2C_ADAPTER 2 | |
749 | #define CMI_LCD_I2C_ADDR 0x60 | |
750 | ||
751 | static int cmi_lcd_hack_create_device(void) | |
752 | { | |
753 | struct i2c_adapter *adapter; | |
754 | struct i2c_client *client; | |
755 | struct i2c_board_info info = { | |
756 | .type = "cmi-lcd", | |
757 | .addr = CMI_LCD_I2C_ADDR, | |
758 | }; | |
759 | ||
760 | pr_debug("%s\n", __func__); | |
761 | ||
762 | adapter = i2c_get_adapter(CMI_LCD_I2C_ADAPTER); | |
763 | if (!adapter) { | |
764 | pr_err("%s: i2c_get_adapter(%d) failed\n", __func__, | |
765 | CMI_LCD_I2C_ADAPTER); | |
766 | return -EINVAL; | |
767 | } | |
768 | ||
769 | client = i2c_new_device(adapter, &info); | |
770 | if (!client) { | |
771 | pr_err("%s: i2c_new_device() failed\n", __func__); | |
772 | i2c_put_adapter(adapter); | |
773 | return -EINVAL; | |
774 | } | |
775 | ||
776 | return 0; | |
777 | } | |
778 | ||
779 | static const struct drm_encoder_helper_funcs tc35876x_encoder_helper_funcs = { | |
780 | .dpms = mdfld_dsi_dpi_dpms, | |
781 | .mode_fixup = mdfld_dsi_dpi_mode_fixup, | |
782 | .prepare = mdfld_dsi_dpi_prepare, | |
783 | .mode_set = mdfld_dsi_dpi_mode_set, | |
784 | .commit = mdfld_dsi_dpi_commit, | |
785 | }; | |
786 | ||
787 | static const struct drm_encoder_funcs tc35876x_encoder_funcs = { | |
788 | .destroy = drm_encoder_cleanup, | |
789 | }; | |
790 | ||
791 | const struct panel_funcs mdfld_tc35876x_funcs = { | |
792 | .encoder_funcs = &tc35876x_encoder_funcs, | |
793 | .encoder_helper_funcs = &tc35876x_encoder_helper_funcs, | |
794 | .get_config_mode = tc35876x_get_config_mode, | |
795 | .get_panel_info = tc35876x_get_panel_info, | |
796 | }; | |
797 | ||
798 | void tc35876x_init(struct drm_device *dev) | |
799 | { | |
800 | int r; | |
801 | ||
802 | dev_dbg(&dev->pdev->dev, "%s\n", __func__); | |
803 | ||
804 | cmi_lcd_hack_create_device(); | |
805 | ||
806 | r = i2c_add_driver(&cmi_lcd_i2c_driver); | |
807 | if (r < 0) | |
808 | dev_err(&dev->pdev->dev, | |
809 | "%s: i2c_add_driver() for %s failed (%d)\n", | |
810 | __func__, cmi_lcd_i2c_driver.driver.name, r); | |
811 | ||
812 | r = i2c_add_driver(&tc35876x_bridge_i2c_driver); | |
813 | if (r < 0) | |
814 | dev_err(&dev->pdev->dev, | |
815 | "%s: i2c_add_driver() for %s failed (%d)\n", | |
816 | __func__, tc35876x_bridge_i2c_driver.driver.name, r); | |
817 | ||
818 | tc35876x_brightness_init(dev); | |
819 | } | |
820 | ||
821 | void tc35876x_exit(void) | |
822 | { | |
823 | pr_debug("%s\n", __func__); | |
824 | ||
825 | i2c_del_driver(&tc35876x_bridge_i2c_driver); | |
826 | ||
827 | if (cmi_lcd_i2c_client) | |
828 | i2c_del_driver(&cmi_lcd_i2c_driver); | |
829 | } |