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