xref: /rk3399_rockchip-uboot/drivers/video/drm/rk628/rk628_dsi.c (revision ab3bc87339b1566ceabcfb54995e11928492c356)
1*ab3bc873SGuochun Huang // SPDX-License-Identifier: GPL-2.0+
2*ab3bc873SGuochun Huang /*
3*ab3bc873SGuochun Huang  * Copyright (C) 2024 Rockchip Electronics Co., Ltd
4*ab3bc873SGuochun Huang  *
5*ab3bc873SGuochun Huang  * Author: Guochun Huang <hero.huang@rock-chips.com>
6*ab3bc873SGuochun Huang  */
7*ab3bc873SGuochun Huang 
8*ab3bc873SGuochun Huang #include <common.h>
9*ab3bc873SGuochun Huang #include <dm.h>
10*ab3bc873SGuochun Huang #include <dm/device-internal.h>
11*ab3bc873SGuochun Huang #include <asm/unaligned.h>
12*ab3bc873SGuochun Huang #include <linux/math64.h>
13*ab3bc873SGuochun Huang 
14*ab3bc873SGuochun Huang #include "rk628.h"
15*ab3bc873SGuochun Huang #include "rk628_dsi.h"
16*ab3bc873SGuochun Huang #include "rk628_combtxphy.h"
17*ab3bc873SGuochun Huang #include "rk628_cru.h"
18*ab3bc873SGuochun Huang #include "panel.h"
19*ab3bc873SGuochun Huang 
20*ab3bc873SGuochun Huang 
21*ab3bc873SGuochun Huang /* Test Code: 0x44 (HS RX Control of Lane 0) */
22*ab3bc873SGuochun Huang #define HSFREQRANGE(x)			UPDATE(x, 6, 1)
23*ab3bc873SGuochun Huang 
24*ab3bc873SGuochun Huang /* request ACK from peripheral */
25*ab3bc873SGuochun Huang #define RK628_MIPI_DSI_MSG_REQ_ACK	BIT(0)
26*ab3bc873SGuochun Huang /* use Low Power Mode to transmit message */
27*ab3bc873SGuochun Huang #define RK628_MIPI_DSI_MSG_USE_LPM	BIT(1)
28*ab3bc873SGuochun Huang 
29*ab3bc873SGuochun Huang enum rk628_dsi_vid_mode_type {
30*ab3bc873SGuochun Huang 	RK628_DSI_VIDEO_MODE,
31*ab3bc873SGuochun Huang 	RK628_DSI_COMMAND_MODE,
32*ab3bc873SGuochun Huang };
33*ab3bc873SGuochun Huang 
34*ab3bc873SGuochun Huang enum dpi_color_coding {
35*ab3bc873SGuochun Huang 	DPI_COLOR_CODING_16BIT_1,
36*ab3bc873SGuochun Huang 	DPI_COLOR_CODING_16BIT_2,
37*ab3bc873SGuochun Huang 	DPI_COLOR_CODING_16BIT_3,
38*ab3bc873SGuochun Huang 	DPI_COLOR_CODING_18BIT_1,
39*ab3bc873SGuochun Huang 	DPI_COLOR_CODING_18BIT_2,
40*ab3bc873SGuochun Huang 	DPI_COLOR_CODING_24BIT,
41*ab3bc873SGuochun Huang };
42*ab3bc873SGuochun Huang 
43*ab3bc873SGuochun Huang enum {
44*ab3bc873SGuochun Huang 	VID_MODE_TYPE_NON_BURST_SYNC_PULSES,
45*ab3bc873SGuochun Huang 	VID_MODE_TYPE_NON_BURST_SYNC_EVENTS,
46*ab3bc873SGuochun Huang 	VID_MODE_TYPE_BURST,
47*ab3bc873SGuochun Huang };
48*ab3bc873SGuochun Huang 
49*ab3bc873SGuochun Huang /* MIPI DSI Processor-to-Peripheral transaction types */
50*ab3bc873SGuochun Huang enum {
51*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_V_SYNC_START				= 0x01,
52*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_V_SYNC_END				= 0x11,
53*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_H_SYNC_START				= 0x21,
54*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_H_SYNC_END				= 0x31,
55*ab3bc873SGuochun Huang 
56*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_COLOR_MODE_OFF				= 0x02,
57*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_COLOR_MODE_ON				= 0x12,
58*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_SHUTDOWN_PERIPHERAL			= 0x22,
59*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_TURN_ON_PERIPHERAL			= 0x32,
60*ab3bc873SGuochun Huang 
61*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM		= 0x03,
62*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM		= 0x13,
63*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM		= 0x23,
64*ab3bc873SGuochun Huang 
65*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM		= 0x04,
66*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM		= 0x14,
67*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM		= 0x24,
68*ab3bc873SGuochun Huang 
69*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_DCS_SHORT_WRITE				= 0x05,
70*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_DCS_SHORT_WRITE_PARAM			= 0x15,
71*ab3bc873SGuochun Huang 
72*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_DCS_READ					= 0x06,
73*ab3bc873SGuochun Huang 
74*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_DCS_COMPRESSION_MODE			= 0x07,
75*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PPS_LONG_WRITE				= 0x0A,
76*ab3bc873SGuochun Huang 
77*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE		= 0x37,
78*ab3bc873SGuochun Huang 
79*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_END_OF_TRANSMISSION			= 0x08,
80*ab3bc873SGuochun Huang 
81*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_NULL_PACKET				= 0x09,
82*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_BLANKING_PACKET				= 0x19,
83*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_GENERIC_LONG_WRITE			= 0x29,
84*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_DCS_LONG_WRITE				= 0x39,
85*ab3bc873SGuochun Huang 
86*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20	= 0x0c,
87*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24		= 0x1c,
88*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16		= 0x2c,
89*ab3bc873SGuochun Huang 
90*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PACKED_PIXEL_STREAM_30			= 0x0d,
91*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PACKED_PIXEL_STREAM_36			= 0x1d,
92*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12		= 0x3d,
93*ab3bc873SGuochun Huang 
94*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PACKED_PIXEL_STREAM_16			= 0x0e,
95*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PACKED_PIXEL_STREAM_18			= 0x1e,
96*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PIXEL_STREAM_3BYTE_18			= 0x2e,
97*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_PACKED_PIXEL_STREAM_24			= 0x3e,
98*ab3bc873SGuochun Huang };
99*ab3bc873SGuochun Huang 
100*ab3bc873SGuochun Huang /* MIPI DSI Peripheral-to-Processor transaction types */
101*ab3bc873SGuochun Huang enum {
102*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT	= 0x02,
103*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_RX_END_OF_TRANSMISSION			= 0x08,
104*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE	= 0x11,
105*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE	= 0x12,
106*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE		= 0x1a,
107*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_RX_DCS_LONG_READ_RESPONSE		= 0x1c,
108*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE	= 0x21,
109*ab3bc873SGuochun Huang 	RK628_MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE	= 0x22,
110*ab3bc873SGuochun Huang };
111*ab3bc873SGuochun Huang 
112*ab3bc873SGuochun Huang /* MIPI DCS commands */
113*ab3bc873SGuochun Huang enum {
114*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_NOP			= 0x00,
115*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SOFT_RESET		= 0x01,
116*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_DISPLAY_ID		= 0x04,
117*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_RED_CHANNEL	= 0x06,
118*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_GREEN_CHANNEL	= 0x07,
119*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_BLUE_CHANNEL	= 0x08,
120*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_DISPLAY_STATUS	= 0x09,
121*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_POWER_MODE		= 0x0A,
122*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_ADDRESS_MODE	= 0x0B,
123*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_PIXEL_FORMAT	= 0x0C,
124*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_DISPLAY_MODE	= 0x0D,
125*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_SIGNAL_MODE	= 0x0E,
126*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_DIAGNOSTIC_RESULT	= 0x0F,
127*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_ENTER_SLEEP_MODE	= 0x10,
128*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_EXIT_SLEEP_MODE	= 0x11,
129*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_ENTER_PARTIAL_MODE	= 0x12,
130*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_ENTER_NORMAL_MODE	= 0x13,
131*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_EXIT_INVERT_MODE	= 0x20,
132*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_ENTER_INVERT_MODE	= 0x21,
133*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_GAMMA_CURVE	= 0x26,
134*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_DISPLAY_OFF	= 0x28,
135*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_DISPLAY_ON		= 0x29,
136*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_COLUMN_ADDRESS	= 0x2A,
137*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_PAGE_ADDRESS	= 0x2B,
138*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_WRITE_MEMORY_START	= 0x2C,
139*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_WRITE_LUT		= 0x2D,
140*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_READ_MEMORY_START	= 0x2E,
141*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_PARTIAL_AREA	= 0x30,
142*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_SCROLL_AREA	= 0x33,
143*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_TEAR_OFF		= 0x34,
144*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_TEAR_ON		= 0x35,
145*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_ADDRESS_MODE	= 0x36,
146*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_SCROLL_START	= 0x37,
147*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_EXIT_IDLE_MODE		= 0x38,
148*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_ENTER_IDLE_MODE	= 0x39,
149*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_PIXEL_FORMAT	= 0x3A,
150*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_WRITE_MEMORY_CONTINUE	= 0x3C,
151*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_READ_MEMORY_CONTINUE	= 0x3E,
152*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_TEAR_SCANLINE	= 0x44,
153*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_SCANLINE		= 0x45,
154*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_DISPLAY_BRIGHTNESS = 0x51,		/* MIPI DCS 1.3 */
155*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_DISPLAY_BRIGHTNESS = 0x52,		/* MIPI DCS 1.3 */
156*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_WRITE_CONTROL_DISPLAY  = 0x53,		/* MIPI DCS 1.3 */
157*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_CONTROL_DISPLAY	= 0x54,		/* MIPI DCS 1.3 */
158*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_WRITE_POWER_SAVE	= 0x55,		/* MIPI DCS 1.3 */
159*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_POWER_SAVE		= 0x56,		/* MIPI DCS 1.3 */
160*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_SET_CABC_MIN_BRIGHTNESS = 0x5E,	/* MIPI DCS 1.3 */
161*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_GET_CABC_MIN_BRIGHTNESS = 0x5F,	/* MIPI DCS 1.3 */
162*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_READ_DDB_START		= 0xA1,
163*ab3bc873SGuochun Huang 	RK628_MIPI_DCS_READ_DDB_CONTINUE	= 0xA8,
164*ab3bc873SGuochun Huang };
165*ab3bc873SGuochun Huang 
166*ab3bc873SGuochun Huang /**
167*ab3bc873SGuochun Huang  * struct rk628_mipi_dsi_msg - read/write DSI buffer
168*ab3bc873SGuochun Huang  * @channel: virtual channel id
169*ab3bc873SGuochun Huang  * @type: payload data type
170*ab3bc873SGuochun Huang  * @flags: flags controlling this message transmission
171*ab3bc873SGuochun Huang  * @tx_len: length of @tx_buf
172*ab3bc873SGuochun Huang  * @tx_buf: data to be written
173*ab3bc873SGuochun Huang  * @rx_len: length of @rx_buf
174*ab3bc873SGuochun Huang  * @rx_buf: data to be read, or NULL
175*ab3bc873SGuochun Huang  */
176*ab3bc873SGuochun Huang struct rk628_mipi_dsi_msg {
177*ab3bc873SGuochun Huang 	u8 channel;
178*ab3bc873SGuochun Huang 	u8 type;
179*ab3bc873SGuochun Huang 	u16 flags;
180*ab3bc873SGuochun Huang 
181*ab3bc873SGuochun Huang 	size_t tx_len;
182*ab3bc873SGuochun Huang 	const void *tx_buf;
183*ab3bc873SGuochun Huang 
184*ab3bc873SGuochun Huang 	size_t rx_len;
185*ab3bc873SGuochun Huang 	void *rx_buf;
186*ab3bc873SGuochun Huang };
187*ab3bc873SGuochun Huang 
188*ab3bc873SGuochun Huang /**
189*ab3bc873SGuochun Huang  * struct rk628_mipi_dsi_packet - represents a MIPI DSI packet in protocol format
190*ab3bc873SGuochun Huang  * @size: size (in bytes) of the packet
191*ab3bc873SGuochun Huang  * @header: the four bytes that make up the header (Data ID, Word Count or
192*ab3bc873SGuochun Huang  * Packet Data, and ECC)
193*ab3bc873SGuochun Huang  * @payload_length: number of bytes in the payload
194*ab3bc873SGuochun Huang  * @payload: a pointer to a buffer containing the payload, if any
195*ab3bc873SGuochun Huang  */
196*ab3bc873SGuochun Huang struct rk628_mipi_dsi_packet {
197*ab3bc873SGuochun Huang 	size_t size;
198*ab3bc873SGuochun Huang 	u8 header[4];
199*ab3bc873SGuochun Huang 	size_t payload_length;
200*ab3bc873SGuochun Huang 	const u8 *payload;
201*ab3bc873SGuochun Huang };
202*ab3bc873SGuochun Huang 
dsi_write(struct rk628 * rk628,const struct rk628_dsi * dsi,u32 reg,u32 val)203*ab3bc873SGuochun Huang static inline int dsi_write(struct rk628 *rk628, const struct rk628_dsi *dsi,
204*ab3bc873SGuochun Huang 			    u32 reg, u32 val)
205*ab3bc873SGuochun Huang {
206*ab3bc873SGuochun Huang 	unsigned int dsi_base;
207*ab3bc873SGuochun Huang 
208*ab3bc873SGuochun Huang 	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
209*ab3bc873SGuochun Huang 
210*ab3bc873SGuochun Huang 	return rk628_i2c_write(rk628, dsi_base + reg, val);
211*ab3bc873SGuochun Huang }
212*ab3bc873SGuochun Huang 
dsi_read(struct rk628 * rk628,const struct rk628_dsi * dsi,u32 reg,u32 * val)213*ab3bc873SGuochun Huang static inline int dsi_read(struct rk628 *rk628, const struct rk628_dsi *dsi,
214*ab3bc873SGuochun Huang 			   u32 reg, u32 *val)
215*ab3bc873SGuochun Huang {
216*ab3bc873SGuochun Huang 	unsigned int dsi_base;
217*ab3bc873SGuochun Huang 
218*ab3bc873SGuochun Huang 	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
219*ab3bc873SGuochun Huang 
220*ab3bc873SGuochun Huang 	return rk628_i2c_read(rk628, dsi_base + reg, val);
221*ab3bc873SGuochun Huang }
222*ab3bc873SGuochun Huang 
dsi_update_bits(struct rk628 * rk628,const struct rk628_dsi * dsi,u32 reg,u32 mask,u32 val)223*ab3bc873SGuochun Huang static inline int dsi_update_bits(struct rk628 *rk628,
224*ab3bc873SGuochun Huang 				  const struct rk628_dsi *dsi,
225*ab3bc873SGuochun Huang 				  u32 reg, u32 mask, u32 val)
226*ab3bc873SGuochun Huang {
227*ab3bc873SGuochun Huang 	unsigned int dsi_base;
228*ab3bc873SGuochun Huang 
229*ab3bc873SGuochun Huang 	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
230*ab3bc873SGuochun Huang 
231*ab3bc873SGuochun Huang 	return rk628_i2c_update_bits(rk628, dsi_base + reg, mask, val);
232*ab3bc873SGuochun Huang }
233*ab3bc873SGuochun Huang 
rk628_dsi_parse(struct rk628 * rk628,ofnode dsi_np)234*ab3bc873SGuochun Huang int rk628_dsi_parse(struct rk628 *rk628, ofnode dsi_np)
235*ab3bc873SGuochun Huang {
236*ab3bc873SGuochun Huang 	const char *string;
237*ab3bc873SGuochun Huang 	int ret;
238*ab3bc873SGuochun Huang 
239*ab3bc873SGuochun Huang 	if (!ofnode_valid(dsi_np))
240*ab3bc873SGuochun Huang 		return -EINVAL;
241*ab3bc873SGuochun Huang 
242*ab3bc873SGuochun Huang 	rk628->dsi0.id = 0;
243*ab3bc873SGuochun Huang 	rk628->dsi0.channel = 0;
244*ab3bc873SGuochun Huang 	rk628->dsi0.rk628 = rk628;
245*ab3bc873SGuochun Huang 
246*ab3bc873SGuochun Huang 	rk628->dsi0.lanes = ofnode_read_u32_default(dsi_np, "dsi,lanes", 4);
247*ab3bc873SGuochun Huang 	if (ofnode_read_bool(dsi_np, "dsi,video-mode"))
248*ab3bc873SGuochun Huang 		rk628->dsi0.mode_flags |= RK628_MIPI_DSI_MODE_LPM | RK628_MIPI_DSI_MODE_VIDEO |
249*ab3bc873SGuochun Huang 					  RK628_MIPI_DSI_MODE_VIDEO_BURST;
250*ab3bc873SGuochun Huang 	else
251*ab3bc873SGuochun Huang 		rk628->dsi0.mode_flags |= RK628_MIPI_DSI_MODE_LPM;
252*ab3bc873SGuochun Huang 
253*ab3bc873SGuochun Huang 	if (ofnode_read_bool(dsi_np, "dsi,eotp"))
254*ab3bc873SGuochun Huang 		rk628->dsi0.mode_flags |= RK628_MIPI_DSI_MODE_EOT_PACKET;
255*ab3bc873SGuochun Huang 
256*ab3bc873SGuochun Huang 	if (ofnode_read_bool(dsi_np, "dsi,clk-non-continuous"))
257*ab3bc873SGuochun Huang 		rk628->dsi0.mode_flags |= RK628_MIPI_DSI_CLOCK_NON_CONTINUOUS;
258*ab3bc873SGuochun Huang 
259*ab3bc873SGuochun Huang 	string = ofnode_read_string(dsi_np, "dsi,format");
260*ab3bc873SGuochun Huang 	if (!strcmp(string, "rgb666")) {
261*ab3bc873SGuochun Huang 		rk628->dsi0.bus_format = RK628_MIPI_DSI_FMT_RGB666;
262*ab3bc873SGuochun Huang 		rk628->dsi0.bpp = 24;
263*ab3bc873SGuochun Huang 	} else if (!strcmp(string, "rgb666-packed")) {
264*ab3bc873SGuochun Huang 		rk628->dsi0.bus_format = RK628_MIPI_DSI_FMT_RGB666_PACKED;
265*ab3bc873SGuochun Huang 		rk628->dsi0.bpp = 18;
266*ab3bc873SGuochun Huang 	} else if (!strcmp(string, "rgb565")) {
267*ab3bc873SGuochun Huang 		rk628->dsi0.bus_format = RK628_MIPI_DSI_FMT_RGB565;
268*ab3bc873SGuochun Huang 		rk628->dsi0.bpp = 16;
269*ab3bc873SGuochun Huang 	} else {
270*ab3bc873SGuochun Huang 		rk628->dsi0.bus_format = RK628_MIPI_DSI_FMT_RGB888;
271*ab3bc873SGuochun Huang 		rk628->dsi0.bpp = 24;
272*ab3bc873SGuochun Huang 	}
273*ab3bc873SGuochun Huang 
274*ab3bc873SGuochun Huang 	if (ofnode_read_bool(dsi_np, "rockchip,dual-channel")) {
275*ab3bc873SGuochun Huang 		rk628->dsi0.master = false;
276*ab3bc873SGuochun Huang 		rk628->dsi0.slave = true;
277*ab3bc873SGuochun Huang 
278*ab3bc873SGuochun Huang 		memcpy(&rk628->dsi1, &rk628->dsi0, sizeof(struct rk628_dsi));
279*ab3bc873SGuochun Huang 		rk628->dsi1.id = 1;
280*ab3bc873SGuochun Huang 		rk628->dsi1.master = true;
281*ab3bc873SGuochun Huang 		rk628->dsi1.slave = false;
282*ab3bc873SGuochun Huang 	}
283*ab3bc873SGuochun Huang 
284*ab3bc873SGuochun Huang 	ret = rk628_panel_info_get(rk628, dsi_np);
285*ab3bc873SGuochun Huang 	if (ret)
286*ab3bc873SGuochun Huang 		return ret;
287*ab3bc873SGuochun Huang 
288*ab3bc873SGuochun Huang 
289*ab3bc873SGuochun Huang 	return 0;
290*ab3bc873SGuochun Huang }
291*ab3bc873SGuochun Huang 
genif_wait_w_pld_fifo_not_full(struct rk628 * rk628,const struct rk628_dsi * dsi)292*ab3bc873SGuochun Huang static int genif_wait_w_pld_fifo_not_full(struct rk628 *rk628,
293*ab3bc873SGuochun Huang 					  const struct rk628_dsi *dsi)
294*ab3bc873SGuochun Huang {
295*ab3bc873SGuochun Huang 	u32 sts = 0;
296*ab3bc873SGuochun Huang 	int ret;
297*ab3bc873SGuochun Huang 	unsigned int dsi_base;
298*ab3bc873SGuochun Huang 
299*ab3bc873SGuochun Huang 	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
300*ab3bc873SGuochun Huang 
301*ab3bc873SGuochun Huang 	ret = rk628_read_poll_timeout(rk628, dsi_base + DSI_CMD_PKT_STATUS,
302*ab3bc873SGuochun Huang 				      sts, !(sts & GEN_PLD_W_FULL), 0, 1000);
303*ab3bc873SGuochun Huang 	if (ret < 0) {
304*ab3bc873SGuochun Huang 		printf("rk628 generic write payload fifo is full\n");
305*ab3bc873SGuochun Huang 		return ret;
306*ab3bc873SGuochun Huang 	}
307*ab3bc873SGuochun Huang 
308*ab3bc873SGuochun Huang 	return 0;
309*ab3bc873SGuochun Huang }
310*ab3bc873SGuochun Huang 
genif_wait_cmd_fifo_not_full(struct rk628 * rk628,const struct rk628_dsi * dsi)311*ab3bc873SGuochun Huang static int genif_wait_cmd_fifo_not_full(struct rk628 *rk628,
312*ab3bc873SGuochun Huang 					const struct rk628_dsi *dsi)
313*ab3bc873SGuochun Huang {
314*ab3bc873SGuochun Huang 	u32 sts = 0;
315*ab3bc873SGuochun Huang 	int ret = 0;
316*ab3bc873SGuochun Huang 	unsigned int dsi_base;
317*ab3bc873SGuochun Huang 
318*ab3bc873SGuochun Huang 	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
319*ab3bc873SGuochun Huang 
320*ab3bc873SGuochun Huang 	ret = rk628_read_poll_timeout(rk628, dsi_base + DSI_CMD_PKT_STATUS,
321*ab3bc873SGuochun Huang 				      sts, !(sts & GEN_CMD_FULL),
322*ab3bc873SGuochun Huang 				       0, 1000);
323*ab3bc873SGuochun Huang 	if (ret < 0) {
324*ab3bc873SGuochun Huang 		printf("rk628 generic write cmd fifo is full\n");
325*ab3bc873SGuochun Huang 		return ret;
326*ab3bc873SGuochun Huang 	}
327*ab3bc873SGuochun Huang 
328*ab3bc873SGuochun Huang 	return 0;
329*ab3bc873SGuochun Huang }
330*ab3bc873SGuochun Huang 
genif_wait_write_fifo_empty(struct rk628 * rk628,const struct rk628_dsi * dsi)331*ab3bc873SGuochun Huang static int genif_wait_write_fifo_empty(struct rk628 *rk628, const struct rk628_dsi *dsi)
332*ab3bc873SGuochun Huang {
333*ab3bc873SGuochun Huang 	u32 sts = 0;
334*ab3bc873SGuochun Huang 	u32 mask;
335*ab3bc873SGuochun Huang 	int ret;
336*ab3bc873SGuochun Huang 	unsigned int dsi_base;
337*ab3bc873SGuochun Huang 
338*ab3bc873SGuochun Huang 	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
339*ab3bc873SGuochun Huang 
340*ab3bc873SGuochun Huang 	mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
341*ab3bc873SGuochun Huang 
342*ab3bc873SGuochun Huang 	ret = rk628_read_poll_timeout(rk628, dsi_base + DSI_CMD_PKT_STATUS,
343*ab3bc873SGuochun Huang 				      sts, (sts & mask) == mask, 0, 1000);
344*ab3bc873SGuochun Huang 	if (ret < 0) {
345*ab3bc873SGuochun Huang 		printf("rk628 generic write fifo is full\n");
346*ab3bc873SGuochun Huang 		return ret;
347*ab3bc873SGuochun Huang 	}
348*ab3bc873SGuochun Huang 
349*ab3bc873SGuochun Huang 	return 0;
350*ab3bc873SGuochun Huang }
351*ab3bc873SGuochun Huang 
rk628_dsi_read_from_fifo(struct rk628 * rk628,const struct rk628_dsi * dsi,const struct rk628_mipi_dsi_msg * msg)352*ab3bc873SGuochun Huang static int rk628_dsi_read_from_fifo(struct rk628 *rk628,
353*ab3bc873SGuochun Huang 				    const struct rk628_dsi *dsi,
354*ab3bc873SGuochun Huang 				    const struct rk628_mipi_dsi_msg *msg)
355*ab3bc873SGuochun Huang {
356*ab3bc873SGuochun Huang 	u8 *payload = msg->rx_buf;
357*ab3bc873SGuochun Huang 	unsigned int vrefresh = 60;
358*ab3bc873SGuochun Huang 	u16 length;
359*ab3bc873SGuochun Huang 	u32 val = 0;
360*ab3bc873SGuochun Huang 	int ret;
361*ab3bc873SGuochun Huang 	unsigned int dsi_base;
362*ab3bc873SGuochun Huang 
363*ab3bc873SGuochun Huang 	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
364*ab3bc873SGuochun Huang 
365*ab3bc873SGuochun Huang 	ret = rk628_read_poll_timeout(rk628, dsi_base + DSI_CMD_PKT_STATUS,
366*ab3bc873SGuochun Huang 				      val, !(val & GEN_RD_CMD_BUSY),
367*ab3bc873SGuochun Huang 				      0, DIV_ROUND_UP(1000000, vrefresh));
368*ab3bc873SGuochun Huang 	if (ret) {
369*ab3bc873SGuochun Huang 		printf("rk628 entire response isn't stored in the FIFO\n");
370*ab3bc873SGuochun Huang 		return ret;
371*ab3bc873SGuochun Huang 	}
372*ab3bc873SGuochun Huang 
373*ab3bc873SGuochun Huang 	/* Receive payload */
374*ab3bc873SGuochun Huang 	for (length = msg->rx_len; length; length -= 4) {
375*ab3bc873SGuochun Huang 		dsi_read(rk628, dsi, DSI_CMD_PKT_STATUS, &val);
376*ab3bc873SGuochun Huang 		if (val & GEN_PLD_R_EMPTY)
377*ab3bc873SGuochun Huang 			ret = -ETIMEDOUT;
378*ab3bc873SGuochun Huang 		if (ret) {
379*ab3bc873SGuochun Huang 			printf("rk628 dsi Read payload FIFO is empty\n");
380*ab3bc873SGuochun Huang 			return ret;
381*ab3bc873SGuochun Huang 		}
382*ab3bc873SGuochun Huang 
383*ab3bc873SGuochun Huang 		dsi_read(rk628, dsi, DSI_GEN_PLD_DATA, &val);
384*ab3bc873SGuochun Huang 
385*ab3bc873SGuochun Huang 		switch (length) {
386*ab3bc873SGuochun Huang 		case 3:
387*ab3bc873SGuochun Huang 			payload[2] = (val >> 16) & 0xff;
388*ab3bc873SGuochun Huang 			/* Fall through */
389*ab3bc873SGuochun Huang 		case 2:
390*ab3bc873SGuochun Huang 			payload[1] = (val >> 8) & 0xff;
391*ab3bc873SGuochun Huang 			/* Fall through */
392*ab3bc873SGuochun Huang 		case 1:
393*ab3bc873SGuochun Huang 			payload[0] = val & 0xff;
394*ab3bc873SGuochun Huang 			return 0;
395*ab3bc873SGuochun Huang 		}
396*ab3bc873SGuochun Huang 
397*ab3bc873SGuochun Huang 		payload[0] = (val >>  0) & 0xff;
398*ab3bc873SGuochun Huang 		payload[1] = (val >>  8) & 0xff;
399*ab3bc873SGuochun Huang 		payload[2] = (val >> 16) & 0xff;
400*ab3bc873SGuochun Huang 		payload[3] = (val >> 24) & 0xff;
401*ab3bc873SGuochun Huang 		payload += 4;
402*ab3bc873SGuochun Huang 	}
403*ab3bc873SGuochun Huang 
404*ab3bc873SGuochun Huang 	return 0;
405*ab3bc873SGuochun Huang }
406*ab3bc873SGuochun Huang 
407*ab3bc873SGuochun Huang /**
408*ab3bc873SGuochun Huang  * rk628_mipi_dsi_packet_format_is_short - check if a packet is of the short format
409*ab3bc873SGuochun Huang  * @type: MIPI DSI data type of the packet
410*ab3bc873SGuochun Huang  *
411*ab3bc873SGuochun Huang  * Return: true if the packet for the given data type is a short packet, false
412*ab3bc873SGuochun Huang  * otherwise.
413*ab3bc873SGuochun Huang  */
rk628_mipi_dsi_packet_format_is_short(u8 type)414*ab3bc873SGuochun Huang static bool rk628_mipi_dsi_packet_format_is_short(u8 type)
415*ab3bc873SGuochun Huang {
416*ab3bc873SGuochun Huang 	switch (type) {
417*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_V_SYNC_START:
418*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_V_SYNC_END:
419*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_H_SYNC_START:
420*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_H_SYNC_END:
421*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_END_OF_TRANSMISSION:
422*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_COLOR_MODE_OFF:
423*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_COLOR_MODE_ON:
424*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_SHUTDOWN_PERIPHERAL:
425*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_TURN_ON_PERIPHERAL:
426*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
427*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
428*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
429*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
430*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
431*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
432*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_SHORT_WRITE:
433*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_SHORT_WRITE_PARAM:
434*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_READ:
435*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_COMPRESSION_MODE:
436*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
437*ab3bc873SGuochun Huang 		return true;
438*ab3bc873SGuochun Huang 	}
439*ab3bc873SGuochun Huang 
440*ab3bc873SGuochun Huang 	return false;
441*ab3bc873SGuochun Huang }
442*ab3bc873SGuochun Huang 
443*ab3bc873SGuochun Huang /**
444*ab3bc873SGuochun Huang  * rk628_mipi_dsi_packet_format_is_long - check if a packet is of the long format
445*ab3bc873SGuochun Huang  * @type: MIPI DSI data type of the packet
446*ab3bc873SGuochun Huang  *
447*ab3bc873SGuochun Huang  * Return: true if the packet for the given data type is a long packet, false
448*ab3bc873SGuochun Huang  * otherwise.
449*ab3bc873SGuochun Huang  */
rk628_mipi_dsi_packet_format_is_long(u8 type)450*ab3bc873SGuochun Huang static bool rk628_mipi_dsi_packet_format_is_long(u8 type)
451*ab3bc873SGuochun Huang {
452*ab3bc873SGuochun Huang 	switch (type) {
453*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PPS_LONG_WRITE:
454*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_NULL_PACKET:
455*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_BLANKING_PACKET:
456*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_LONG_WRITE:
457*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_LONG_WRITE:
458*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
459*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
460*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
461*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PACKED_PIXEL_STREAM_30:
462*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PACKED_PIXEL_STREAM_36:
463*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
464*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PACKED_PIXEL_STREAM_16:
465*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PACKED_PIXEL_STREAM_18:
466*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PIXEL_STREAM_3BYTE_18:
467*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_PACKED_PIXEL_STREAM_24:
468*ab3bc873SGuochun Huang 		return true;
469*ab3bc873SGuochun Huang 	}
470*ab3bc873SGuochun Huang 
471*ab3bc873SGuochun Huang 	return false;
472*ab3bc873SGuochun Huang }
473*ab3bc873SGuochun Huang 
474*ab3bc873SGuochun Huang /**
475*ab3bc873SGuochun Huang  * rk628_mipi_dsi_create_packet - create a packet from a message according to the
476*ab3bc873SGuochun Huang  *     DSI protocol
477*ab3bc873SGuochun Huang  * @packet: pointer to a DSI packet structure
478*ab3bc873SGuochun Huang  * @msg: message to translate into a packet
479*ab3bc873SGuochun Huang  *
480*ab3bc873SGuochun Huang  * Return: 0 on success or a negative error code on failure.
481*ab3bc873SGuochun Huang  */
rk628_mipi_dsi_create_packet(struct rk628_mipi_dsi_packet * packet,const struct rk628_mipi_dsi_msg * msg)482*ab3bc873SGuochun Huang static int rk628_mipi_dsi_create_packet(struct rk628_mipi_dsi_packet *packet,
483*ab3bc873SGuochun Huang 			   const struct rk628_mipi_dsi_msg *msg)
484*ab3bc873SGuochun Huang {
485*ab3bc873SGuochun Huang 	if (!packet || !msg)
486*ab3bc873SGuochun Huang 		return -EINVAL;
487*ab3bc873SGuochun Huang 
488*ab3bc873SGuochun Huang 	/* do some minimum sanity checking */
489*ab3bc873SGuochun Huang 	if (!rk628_mipi_dsi_packet_format_is_short(msg->type) &&
490*ab3bc873SGuochun Huang 	    !rk628_mipi_dsi_packet_format_is_long(msg->type))
491*ab3bc873SGuochun Huang 		return -EINVAL;
492*ab3bc873SGuochun Huang 
493*ab3bc873SGuochun Huang 	if (msg->channel > 3)
494*ab3bc873SGuochun Huang 		return -EINVAL;
495*ab3bc873SGuochun Huang 
496*ab3bc873SGuochun Huang 	memset(packet, 0, sizeof(*packet));
497*ab3bc873SGuochun Huang 	packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
498*ab3bc873SGuochun Huang 
499*ab3bc873SGuochun Huang 	/* TODO: compute ECC if hardware support is not available */
500*ab3bc873SGuochun Huang 
501*ab3bc873SGuochun Huang 	/*
502*ab3bc873SGuochun Huang 	 * Long write packets contain the word count in header bytes 1 and 2.
503*ab3bc873SGuochun Huang 	 * The payload follows the header and is word count bytes long.
504*ab3bc873SGuochun Huang 	 *
505*ab3bc873SGuochun Huang 	 * Short write packets encode up to two parameters in header bytes 1
506*ab3bc873SGuochun Huang 	 * and 2.
507*ab3bc873SGuochun Huang 	 */
508*ab3bc873SGuochun Huang 	if (rk628_mipi_dsi_packet_format_is_long(msg->type)) {
509*ab3bc873SGuochun Huang 		packet->header[1] = (msg->tx_len >> 0) & 0xff;
510*ab3bc873SGuochun Huang 		packet->header[2] = (msg->tx_len >> 8) & 0xff;
511*ab3bc873SGuochun Huang 
512*ab3bc873SGuochun Huang 		packet->payload_length = msg->tx_len;
513*ab3bc873SGuochun Huang 		packet->payload = msg->tx_buf;
514*ab3bc873SGuochun Huang 	} else {
515*ab3bc873SGuochun Huang 		const u8 *tx = msg->tx_buf;
516*ab3bc873SGuochun Huang 
517*ab3bc873SGuochun Huang 		packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
518*ab3bc873SGuochun Huang 		packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
519*ab3bc873SGuochun Huang 	}
520*ab3bc873SGuochun Huang 
521*ab3bc873SGuochun Huang 	packet->size = sizeof(packet->header) + packet->payload_length;
522*ab3bc873SGuochun Huang 
523*ab3bc873SGuochun Huang 	return 0;
524*ab3bc873SGuochun Huang }
525*ab3bc873SGuochun Huang 
rk628_dsi_transfer(struct rk628 * rk628,const struct rk628_dsi * dsi,const struct rk628_mipi_dsi_msg * msg)526*ab3bc873SGuochun Huang static int rk628_dsi_transfer(struct rk628 *rk628, const struct rk628_dsi *dsi,
527*ab3bc873SGuochun Huang 			      const struct rk628_mipi_dsi_msg *msg)
528*ab3bc873SGuochun Huang {
529*ab3bc873SGuochun Huang 	struct rk628_mipi_dsi_packet packet;
530*ab3bc873SGuochun Huang 	int ret;
531*ab3bc873SGuochun Huang 	u32 val;
532*ab3bc873SGuochun Huang 
533*ab3bc873SGuochun Huang 	if (msg->flags & RK628_MIPI_DSI_MSG_REQ_ACK)
534*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG,
535*ab3bc873SGuochun Huang 				ACK_RQST_EN, ACK_RQST_EN);
536*ab3bc873SGuochun Huang 
537*ab3bc873SGuochun Huang 	if (msg->flags & RK628_MIPI_DSI_MSG_USE_LPM) {
538*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_VID_MODE_CFG,
539*ab3bc873SGuochun Huang 				LP_CMD_EN, LP_CMD_EN);
540*ab3bc873SGuochun Huang 	} else {
541*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_VID_MODE_CFG, LP_CMD_EN, 0);
542*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_LPCLK_CTRL,
543*ab3bc873SGuochun Huang 				PHY_TXREQUESTCLKHS, PHY_TXREQUESTCLKHS);
544*ab3bc873SGuochun Huang 	}
545*ab3bc873SGuochun Huang 
546*ab3bc873SGuochun Huang 	switch (msg->type) {
547*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_SHUTDOWN_PERIPHERAL:
548*ab3bc873SGuochun Huang 		//return rk628_dsi_shutdown_peripheral(dsi);
549*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_TURN_ON_PERIPHERAL:
550*ab3bc873SGuochun Huang 		//return rk628_dsi_turn_on_peripheral(dsi);
551*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_SHORT_WRITE:
552*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_SW_0P_TX,
553*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
554*ab3bc873SGuochun Huang 				DCS_SW_0P_TX : 0);
555*ab3bc873SGuochun Huang 		break;
556*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_SHORT_WRITE_PARAM:
557*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_SW_1P_TX,
558*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
559*ab3bc873SGuochun Huang 				DCS_SW_1P_TX : 0);
560*ab3bc873SGuochun Huang 		break;
561*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_LONG_WRITE:
562*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_LW_TX,
563*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
564*ab3bc873SGuochun Huang 				DCS_LW_TX : 0);
565*ab3bc873SGuochun Huang 		break;
566*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_DCS_READ:
567*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_SR_0P_TX,
568*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
569*ab3bc873SGuochun Huang 				DCS_SR_0P_TX : 0);
570*ab3bc873SGuochun Huang 		break;
571*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
572*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, MAX_RD_PKT_SIZE,
573*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
574*ab3bc873SGuochun Huang 				MAX_RD_PKT_SIZE : 0);
575*ab3bc873SGuochun Huang 		break;
576*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
577*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SW_0P_TX,
578*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
579*ab3bc873SGuochun Huang 				GEN_SW_0P_TX : 0);
580*ab3bc873SGuochun Huang 		break;
581*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
582*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SW_1P_TX,
583*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
584*ab3bc873SGuochun Huang 				GEN_SW_1P_TX : 0);
585*ab3bc873SGuochun Huang 		break;
586*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
587*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SW_2P_TX,
588*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
589*ab3bc873SGuochun Huang 				GEN_SW_2P_TX : 0);
590*ab3bc873SGuochun Huang 		break;
591*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_LONG_WRITE:
592*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_LW_TX,
593*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ?
594*ab3bc873SGuochun Huang 				GEN_LW_TX : 0);
595*ab3bc873SGuochun Huang 		break;
596*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
597*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SR_0P_TX,
598*ab3bc873SGuochun Huang 		msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ? GEN_SR_0P_TX : 0);
599*ab3bc873SGuochun Huang 		break;
600*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
601*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SR_1P_TX,
602*ab3bc873SGuochun Huang 		msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ? GEN_SR_1P_TX : 0);
603*ab3bc873SGuochun Huang 		break;
604*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
605*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SR_2P_TX,
606*ab3bc873SGuochun Huang 				msg->flags & RK628_MIPI_DSI_MSG_USE_LPM ? GEN_SR_2P_TX : 0);
607*ab3bc873SGuochun Huang 		break;
608*ab3bc873SGuochun Huang 	default:
609*ab3bc873SGuochun Huang 		return -EINVAL;
610*ab3bc873SGuochun Huang 	}
611*ab3bc873SGuochun Huang 
612*ab3bc873SGuochun Huang 	/* create a packet to the DSI protocol */
613*ab3bc873SGuochun Huang 	ret = rk628_mipi_dsi_create_packet(&packet, msg);
614*ab3bc873SGuochun Huang 	if (ret) {
615*ab3bc873SGuochun Huang 		printf("rk628 dsi failed to create packet\n");
616*ab3bc873SGuochun Huang 		return ret;
617*ab3bc873SGuochun Huang 	}
618*ab3bc873SGuochun Huang 
619*ab3bc873SGuochun Huang 	/* Send payload */
620*ab3bc873SGuochun Huang 	while (packet.payload_length >= 4) {
621*ab3bc873SGuochun Huang 		/*
622*ab3bc873SGuochun Huang 		 * Alternatively, you can always keep the FIFO
623*ab3bc873SGuochun Huang 		 * nearly full by monitoring the FIFO state until
624*ab3bc873SGuochun Huang 		 * it is not full, and then writea single word of data.
625*ab3bc873SGuochun Huang 		 * This solution is more resource consuming
626*ab3bc873SGuochun Huang 		 * but it simultaneously avoids FIFO starvation,
627*ab3bc873SGuochun Huang 		 * making it possible to use FIFO sizes smaller than
628*ab3bc873SGuochun Huang 		 * the amount of data of the longest packet to be written.
629*ab3bc873SGuochun Huang 		 */
630*ab3bc873SGuochun Huang 		ret = genif_wait_w_pld_fifo_not_full(rk628, dsi);
631*ab3bc873SGuochun Huang 		if (ret)
632*ab3bc873SGuochun Huang 			return ret;
633*ab3bc873SGuochun Huang 
634*ab3bc873SGuochun Huang 		val = get_unaligned_le32(packet.payload);
635*ab3bc873SGuochun Huang 
636*ab3bc873SGuochun Huang 		dsi_write(rk628, dsi, DSI_GEN_PLD_DATA, val);
637*ab3bc873SGuochun Huang 
638*ab3bc873SGuochun Huang 
639*ab3bc873SGuochun Huang 		packet.payload += 4;
640*ab3bc873SGuochun Huang 		packet.payload_length -= 4;
641*ab3bc873SGuochun Huang 	}
642*ab3bc873SGuochun Huang 
643*ab3bc873SGuochun Huang 	val = 0;
644*ab3bc873SGuochun Huang 	switch (packet.payload_length) {
645*ab3bc873SGuochun Huang 	case 3:
646*ab3bc873SGuochun Huang 		val |= packet.payload[2] << 16;
647*ab3bc873SGuochun Huang 		/* Fall through */
648*ab3bc873SGuochun Huang 	case 2:
649*ab3bc873SGuochun Huang 		val |= packet.payload[1] << 8;
650*ab3bc873SGuochun Huang 		/* Fall through */
651*ab3bc873SGuochun Huang 	case 1:
652*ab3bc873SGuochun Huang 		val |= packet.payload[0];
653*ab3bc873SGuochun Huang 
654*ab3bc873SGuochun Huang 		dsi_write(rk628, dsi, DSI_GEN_PLD_DATA, val);
655*ab3bc873SGuochun Huang 		break;
656*ab3bc873SGuochun Huang 	}
657*ab3bc873SGuochun Huang 
658*ab3bc873SGuochun Huang 	ret = genif_wait_cmd_fifo_not_full(rk628, dsi);
659*ab3bc873SGuochun Huang 	if (ret)
660*ab3bc873SGuochun Huang 		return ret;
661*ab3bc873SGuochun Huang 
662*ab3bc873SGuochun Huang 	/* Send packet header */
663*ab3bc873SGuochun Huang 	val = get_unaligned_le32(packet.header);
664*ab3bc873SGuochun Huang 
665*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_GEN_HDR, val);
666*ab3bc873SGuochun Huang 
667*ab3bc873SGuochun Huang 	ret = genif_wait_write_fifo_empty(rk628, dsi);
668*ab3bc873SGuochun Huang 	if (ret)
669*ab3bc873SGuochun Huang 		return ret;
670*ab3bc873SGuochun Huang 
671*ab3bc873SGuochun Huang 	if (msg->rx_len) {
672*ab3bc873SGuochun Huang 		ret = rk628_dsi_read_from_fifo(rk628, dsi, msg);
673*ab3bc873SGuochun Huang 		if (ret < 0)
674*ab3bc873SGuochun Huang 			return ret;
675*ab3bc873SGuochun Huang 	}
676*ab3bc873SGuochun Huang 
677*ab3bc873SGuochun Huang 	if (dsi->slave) {
678*ab3bc873SGuochun Huang 		const struct rk628_dsi *dsi1 = &rk628->dsi1;
679*ab3bc873SGuochun Huang 
680*ab3bc873SGuochun Huang 		rk628_dsi_transfer(rk628, dsi1, msg);
681*ab3bc873SGuochun Huang 	}
682*ab3bc873SGuochun Huang 
683*ab3bc873SGuochun Huang 	return msg->tx_len;
684*ab3bc873SGuochun Huang }
685*ab3bc873SGuochun Huang 
rk628_mipi_dsi_generic_write(struct rk628 * rk628,const void * payload,size_t size)686*ab3bc873SGuochun Huang static int rk628_mipi_dsi_generic_write(struct rk628 *rk628,
687*ab3bc873SGuochun Huang 					const void *payload, size_t size)
688*ab3bc873SGuochun Huang {
689*ab3bc873SGuochun Huang 	const struct rk628_dsi *dsi = &rk628->dsi0;
690*ab3bc873SGuochun Huang 	struct rk628_mipi_dsi_msg msg;
691*ab3bc873SGuochun Huang 
692*ab3bc873SGuochun Huang 	memset(&msg, 0, sizeof(msg));
693*ab3bc873SGuochun Huang 	msg.channel = dsi->channel;
694*ab3bc873SGuochun Huang 	msg.tx_buf = payload;
695*ab3bc873SGuochun Huang 	msg.tx_len = size;
696*ab3bc873SGuochun Huang 	msg.rx_len = 0;
697*ab3bc873SGuochun Huang 
698*ab3bc873SGuochun Huang 	switch (size) {
699*ab3bc873SGuochun Huang 	case 0:
700*ab3bc873SGuochun Huang 		msg.type = RK628_MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
701*ab3bc873SGuochun Huang 		break;
702*ab3bc873SGuochun Huang 
703*ab3bc873SGuochun Huang 	case 1:
704*ab3bc873SGuochun Huang 		msg.type = RK628_MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
705*ab3bc873SGuochun Huang 		break;
706*ab3bc873SGuochun Huang 	case 2:
707*ab3bc873SGuochun Huang 		msg.type = RK628_MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
708*ab3bc873SGuochun Huang 		break;
709*ab3bc873SGuochun Huang 	default:
710*ab3bc873SGuochun Huang 		msg.type = RK628_MIPI_DSI_GENERIC_LONG_WRITE;
711*ab3bc873SGuochun Huang 		break;
712*ab3bc873SGuochun Huang 	}
713*ab3bc873SGuochun Huang 
714*ab3bc873SGuochun Huang 		if (dsi->mode_flags & RK628_MIPI_DSI_MODE_LPM)
715*ab3bc873SGuochun Huang 			msg.flags |= RK628_MIPI_DSI_MSG_USE_LPM;
716*ab3bc873SGuochun Huang 
717*ab3bc873SGuochun Huang 		return rk628_dsi_transfer(rk628, dsi, &msg);
718*ab3bc873SGuochun Huang }
719*ab3bc873SGuochun Huang 
rk628_mipi_dsi_dcs_write_buffer(struct rk628 * rk628,const void * data,size_t len)720*ab3bc873SGuochun Huang static int rk628_mipi_dsi_dcs_write_buffer(struct rk628 *rk628,
721*ab3bc873SGuochun Huang 					   const void *data, size_t len)
722*ab3bc873SGuochun Huang {
723*ab3bc873SGuochun Huang 	const struct rk628_dsi *dsi = &rk628->dsi0;
724*ab3bc873SGuochun Huang 	struct rk628_mipi_dsi_msg msg;
725*ab3bc873SGuochun Huang 
726*ab3bc873SGuochun Huang 	memset(&msg, 0, sizeof(msg));
727*ab3bc873SGuochun Huang 	msg.channel = dsi->channel;
728*ab3bc873SGuochun Huang 	msg.tx_buf = data;
729*ab3bc873SGuochun Huang 	msg.tx_len = len;
730*ab3bc873SGuochun Huang 	msg.rx_len = 0;
731*ab3bc873SGuochun Huang 
732*ab3bc873SGuochun Huang 	switch (len) {
733*ab3bc873SGuochun Huang 	case 0:
734*ab3bc873SGuochun Huang 		return -EINVAL;
735*ab3bc873SGuochun Huang 	case 1:
736*ab3bc873SGuochun Huang 		msg.type = RK628_MIPI_DSI_DCS_SHORT_WRITE;
737*ab3bc873SGuochun Huang 		break;
738*ab3bc873SGuochun Huang 	case 2:
739*ab3bc873SGuochun Huang 		msg.type = RK628_MIPI_DSI_DCS_SHORT_WRITE_PARAM;
740*ab3bc873SGuochun Huang 		break;
741*ab3bc873SGuochun Huang 	default:
742*ab3bc873SGuochun Huang 		msg.type = RK628_MIPI_DSI_DCS_LONG_WRITE;
743*ab3bc873SGuochun Huang 		break;
744*ab3bc873SGuochun Huang 	}
745*ab3bc873SGuochun Huang 
746*ab3bc873SGuochun Huang 	if (dsi->mode_flags & RK628_MIPI_DSI_MODE_LPM)
747*ab3bc873SGuochun Huang 		msg.flags |= RK628_MIPI_DSI_MSG_USE_LPM;
748*ab3bc873SGuochun Huang 
749*ab3bc873SGuochun Huang 	return rk628_dsi_transfer(rk628, dsi, &msg);
750*ab3bc873SGuochun Huang }
751*ab3bc873SGuochun Huang 
rk628_mipi_dsi_dcs_read(struct rk628 * rk628,u8 cmd,void * data,size_t len)752*ab3bc873SGuochun Huang static __maybe_unused int rk628_mipi_dsi_dcs_read(struct rk628 *rk628, u8 cmd,
753*ab3bc873SGuochun Huang 						  void *data, size_t len)
754*ab3bc873SGuochun Huang {
755*ab3bc873SGuochun Huang 	const struct rk628_dsi *dsi = &rk628->dsi0;
756*ab3bc873SGuochun Huang 	struct rk628_mipi_dsi_msg msg;
757*ab3bc873SGuochun Huang 
758*ab3bc873SGuochun Huang 	memset(&msg, 0, sizeof(msg));
759*ab3bc873SGuochun Huang 	msg.channel = dsi->channel;
760*ab3bc873SGuochun Huang 	msg.type = RK628_MIPI_DSI_DCS_READ;
761*ab3bc873SGuochun Huang 	msg.tx_buf = &cmd;
762*ab3bc873SGuochun Huang 	msg.tx_len = 1;
763*ab3bc873SGuochun Huang 	msg.rx_buf = data;
764*ab3bc873SGuochun Huang 	msg.rx_len = len;
765*ab3bc873SGuochun Huang 
766*ab3bc873SGuochun Huang 	return rk628_dsi_transfer(rk628, dsi, &msg);
767*ab3bc873SGuochun Huang }
768*ab3bc873SGuochun Huang 
769*ab3bc873SGuochun Huang static int
panel_simple_xfer_dsi_cmd_seq(struct rk628 * rk628,struct panel_cmds * cmds)770*ab3bc873SGuochun Huang panel_simple_xfer_dsi_cmd_seq(struct rk628 *rk628, struct panel_cmds *cmds)
771*ab3bc873SGuochun Huang {
772*ab3bc873SGuochun Huang 	u16 i;
773*ab3bc873SGuochun Huang 	int err;
774*ab3bc873SGuochun Huang 
775*ab3bc873SGuochun Huang 	if (!cmds)
776*ab3bc873SGuochun Huang 		return 0;
777*ab3bc873SGuochun Huang 
778*ab3bc873SGuochun Huang 	for (i = 0; i < cmds->cmd_cnt; i++) {
779*ab3bc873SGuochun Huang 		struct cmd_desc *cmd = &cmds->cmds[i];
780*ab3bc873SGuochun Huang 
781*ab3bc873SGuochun Huang 		switch (cmd->dchdr.dtype) {
782*ab3bc873SGuochun Huang 		case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
783*ab3bc873SGuochun Huang 		case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
784*ab3bc873SGuochun Huang 		case RK628_MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
785*ab3bc873SGuochun Huang 		case RK628_MIPI_DSI_GENERIC_LONG_WRITE:
786*ab3bc873SGuochun Huang 			err = rk628_mipi_dsi_generic_write(rk628, cmd->payload,
787*ab3bc873SGuochun Huang 							   cmd->dchdr.dlen);
788*ab3bc873SGuochun Huang 			break;
789*ab3bc873SGuochun Huang 		case RK628_MIPI_DSI_DCS_SHORT_WRITE:
790*ab3bc873SGuochun Huang 		case RK628_MIPI_DSI_DCS_SHORT_WRITE_PARAM:
791*ab3bc873SGuochun Huang 		case RK628_MIPI_DSI_DCS_LONG_WRITE:
792*ab3bc873SGuochun Huang 			err = rk628_mipi_dsi_dcs_write_buffer(rk628, cmd->payload,
793*ab3bc873SGuochun Huang 							      cmd->dchdr.dlen);
794*ab3bc873SGuochun Huang 			break;
795*ab3bc873SGuochun Huang 		default:
796*ab3bc873SGuochun Huang 			printf("rk628 dsi panel cmd desc invalid data type\n");
797*ab3bc873SGuochun Huang 			return -EINVAL;
798*ab3bc873SGuochun Huang 		}
799*ab3bc873SGuochun Huang 
800*ab3bc873SGuochun Huang 		if (err < 0)
801*ab3bc873SGuochun Huang 			printf("rk628 dsi failed to write cmd\n");
802*ab3bc873SGuochun Huang 
803*ab3bc873SGuochun Huang 		if (cmd->dchdr.wait)
804*ab3bc873SGuochun Huang 			mdelay(cmd->dchdr.wait);
805*ab3bc873SGuochun Huang 	}
806*ab3bc873SGuochun Huang 
807*ab3bc873SGuochun Huang 	return 0;
808*ab3bc873SGuochun Huang }
809*ab3bc873SGuochun Huang 
rk628_dsi_get_lane_rate(const struct rk628_dsi * dsi)810*ab3bc873SGuochun Huang static u32 rk628_dsi_get_lane_rate(const struct rk628_dsi *dsi)
811*ab3bc873SGuochun Huang {
812*ab3bc873SGuochun Huang 	const struct drm_display_mode *mode = &dsi->rk628->dst_mode;
813*ab3bc873SGuochun Huang 	ofnode dsi_np;
814*ab3bc873SGuochun Huang 	u32 lane_rate, value;
815*ab3bc873SGuochun Huang 	u32 max_lane_rate = 1500;
816*ab3bc873SGuochun Huang 	u8 bpp, lanes;
817*ab3bc873SGuochun Huang 
818*ab3bc873SGuochun Huang 	dsi_np = dev_read_subnode(dsi->rk628->dev, "rk628-dsi-out");
819*ab3bc873SGuochun Huang 	if (!ofnode_valid(dsi_np))
820*ab3bc873SGuochun Huang 		dsi_np = dev_read_subnode(dsi->rk628->dev, "rk628-dsi");
821*ab3bc873SGuochun Huang 	if (ofnode_valid(dsi_np) && !ofnode_read_u32(dsi_np, "rockchip,lane-mbps", &value)) {
822*ab3bc873SGuochun Huang 		lane_rate = value;
823*ab3bc873SGuochun Huang 	} else {
824*ab3bc873SGuochun Huang 		bpp = dsi->bpp;
825*ab3bc873SGuochun Huang 		lanes = dsi->slave ? dsi->lanes * 2 : dsi->lanes;
826*ab3bc873SGuochun Huang 		lane_rate = mode->clock / 1000 * bpp / lanes;
827*ab3bc873SGuochun Huang 		lane_rate = DIV_ROUND_UP(lane_rate * 5, 4);
828*ab3bc873SGuochun Huang 	}
829*ab3bc873SGuochun Huang 
830*ab3bc873SGuochun Huang 	if (lane_rate > max_lane_rate)
831*ab3bc873SGuochun Huang 		lane_rate = max_lane_rate;
832*ab3bc873SGuochun Huang 
833*ab3bc873SGuochun Huang 	return lane_rate;
834*ab3bc873SGuochun Huang }
835*ab3bc873SGuochun Huang 
testif_testclk_assert(struct rk628 * rk628,const struct rk628_dsi * dsi)836*ab3bc873SGuochun Huang static void testif_testclk_assert(struct rk628 *rk628,
837*ab3bc873SGuochun Huang 				  const struct rk628_dsi *dsi)
838*ab3bc873SGuochun Huang {
839*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, dsi->id ?
840*ab3bc873SGuochun Huang 			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
841*ab3bc873SGuochun Huang 			   PHY_TESTCLK, PHY_TESTCLK);
842*ab3bc873SGuochun Huang 	udelay(1);
843*ab3bc873SGuochun Huang }
844*ab3bc873SGuochun Huang 
testif_testclk_deassert(struct rk628 * rk628,const struct rk628_dsi * dsi)845*ab3bc873SGuochun Huang static void testif_testclk_deassert(struct rk628 *rk628,
846*ab3bc873SGuochun Huang 				    const struct rk628_dsi *dsi)
847*ab3bc873SGuochun Huang {
848*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, dsi->id ?
849*ab3bc873SGuochun Huang 			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
850*ab3bc873SGuochun Huang 			   PHY_TESTCLK, 0);
851*ab3bc873SGuochun Huang 	udelay(1);
852*ab3bc873SGuochun Huang }
853*ab3bc873SGuochun Huang 
testif_testclr_assert(struct rk628 * rk628,const struct rk628_dsi * dsi)854*ab3bc873SGuochun Huang static void testif_testclr_assert(struct rk628 *rk628,
855*ab3bc873SGuochun Huang 				  const struct rk628_dsi *dsi)
856*ab3bc873SGuochun Huang {
857*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, dsi->id ?
858*ab3bc873SGuochun Huang 			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
859*ab3bc873SGuochun Huang 			   PHY_TESTCLR, PHY_TESTCLR);
860*ab3bc873SGuochun Huang 	udelay(1);
861*ab3bc873SGuochun Huang }
862*ab3bc873SGuochun Huang 
testif_testclr_deassert(struct rk628 * rk628,const struct rk628_dsi * dsi)863*ab3bc873SGuochun Huang static void testif_testclr_deassert(struct rk628 *rk628,
864*ab3bc873SGuochun Huang 				    const struct rk628_dsi *dsi)
865*ab3bc873SGuochun Huang {
866*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, dsi->id ?
867*ab3bc873SGuochun Huang 			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
868*ab3bc873SGuochun Huang 			   PHY_TESTCLR, 0);
869*ab3bc873SGuochun Huang 	udelay(1);
870*ab3bc873SGuochun Huang }
871*ab3bc873SGuochun Huang 
testif_set_data(struct rk628 * rk628,const struct rk628_dsi * dsi,u8 data)872*ab3bc873SGuochun Huang static void testif_set_data(struct rk628 *rk628,
873*ab3bc873SGuochun Huang 			    const struct rk628_dsi *dsi, u8 data)
874*ab3bc873SGuochun Huang {
875*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, dsi->id ?
876*ab3bc873SGuochun Huang 			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
877*ab3bc873SGuochun Huang 			   PHY_TESTDIN_MASK, PHY_TESTDIN(data));
878*ab3bc873SGuochun Huang 	udelay(1);
879*ab3bc873SGuochun Huang }
880*ab3bc873SGuochun Huang 
testif_testen_assert(struct rk628 * rk628,const struct rk628_dsi * dsi)881*ab3bc873SGuochun Huang static void testif_testen_assert(struct rk628 *rk628,
882*ab3bc873SGuochun Huang 				 const struct rk628_dsi *dsi)
883*ab3bc873SGuochun Huang {
884*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, dsi->id ?
885*ab3bc873SGuochun Huang 			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
886*ab3bc873SGuochun Huang 			   PHY_TESTEN, PHY_TESTEN);
887*ab3bc873SGuochun Huang 	udelay(1);
888*ab3bc873SGuochun Huang }
889*ab3bc873SGuochun Huang 
testif_testen_deassert(struct rk628 * rk628,const struct rk628_dsi * dsi)890*ab3bc873SGuochun Huang static void testif_testen_deassert(struct rk628 *rk628,
891*ab3bc873SGuochun Huang 				   const struct rk628_dsi *dsi)
892*ab3bc873SGuochun Huang {
893*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628,  dsi->id ?
894*ab3bc873SGuochun Huang 			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
895*ab3bc873SGuochun Huang 			   PHY_TESTEN, 0);
896*ab3bc873SGuochun Huang 	udelay(1);
897*ab3bc873SGuochun Huang }
898*ab3bc873SGuochun Huang 
testif_test_code_write(struct rk628 * rk628,const struct rk628_dsi * dsi,u8 test_code)899*ab3bc873SGuochun Huang static void testif_test_code_write(struct rk628 *rk628,
900*ab3bc873SGuochun Huang 				   const struct rk628_dsi *dsi, u8 test_code)
901*ab3bc873SGuochun Huang {
902*ab3bc873SGuochun Huang 	testif_testclk_assert(rk628, dsi);
903*ab3bc873SGuochun Huang 	testif_set_data(rk628, dsi, test_code);
904*ab3bc873SGuochun Huang 	testif_testen_assert(rk628, dsi);
905*ab3bc873SGuochun Huang 	testif_testclk_deassert(rk628, dsi);
906*ab3bc873SGuochun Huang 	testif_testen_deassert(rk628, dsi);
907*ab3bc873SGuochun Huang }
908*ab3bc873SGuochun Huang 
testif_test_data_write(struct rk628 * rk628,const struct rk628_dsi * dsi,u8 test_data)909*ab3bc873SGuochun Huang static void testif_test_data_write(struct rk628 *rk628,
910*ab3bc873SGuochun Huang 				   const struct rk628_dsi *dsi, u8 test_data)
911*ab3bc873SGuochun Huang {
912*ab3bc873SGuochun Huang 	testif_testclk_deassert(rk628, dsi);
913*ab3bc873SGuochun Huang 	testif_set_data(rk628, dsi, test_data);
914*ab3bc873SGuochun Huang 	testif_testclk_assert(rk628, dsi);
915*ab3bc873SGuochun Huang }
916*ab3bc873SGuochun Huang 
testif_get_data(struct rk628 * rk628,const struct rk628_dsi * dsi)917*ab3bc873SGuochun Huang static u8 testif_get_data(struct rk628 *rk628, const struct rk628_dsi *dsi)
918*ab3bc873SGuochun Huang {
919*ab3bc873SGuochun Huang 	u32 data = 0;
920*ab3bc873SGuochun Huang 
921*ab3bc873SGuochun Huang 	rk628_i2c_read(rk628, dsi->id ? GRF_DPHY1_STATUS : GRF_DPHY0_STATUS, &data);
922*ab3bc873SGuochun Huang 
923*ab3bc873SGuochun Huang 	return data >> PHY_TESTDOUT_SHIFT;
924*ab3bc873SGuochun Huang }
925*ab3bc873SGuochun Huang 
testif_write(struct rk628 * rk628,const struct rk628_dsi * dsi,u8 reg,u8 value)926*ab3bc873SGuochun Huang static void testif_write(struct rk628 *rk628, const struct rk628_dsi *dsi,
927*ab3bc873SGuochun Huang 			 u8 reg, u8 value)
928*ab3bc873SGuochun Huang {
929*ab3bc873SGuochun Huang 	u8 monitor_data;
930*ab3bc873SGuochun Huang 
931*ab3bc873SGuochun Huang 	testif_test_code_write(rk628, dsi, reg);
932*ab3bc873SGuochun Huang 	testif_test_data_write(rk628, dsi, value);
933*ab3bc873SGuochun Huang 	monitor_data = testif_get_data(rk628, dsi);
934*ab3bc873SGuochun Huang 	printf("rk628 dsi monitor_data: 0x%x\n", monitor_data);
935*ab3bc873SGuochun Huang }
936*ab3bc873SGuochun Huang 
testif_set_timing(const struct rk628_dsi * dsi,u8 addr,u8 max,u8 val)937*ab3bc873SGuochun Huang static void testif_set_timing(const struct rk628_dsi *dsi, u8 addr,
938*ab3bc873SGuochun Huang 			      u8 max, u8 val)
939*ab3bc873SGuochun Huang {
940*ab3bc873SGuochun Huang 	struct rk628 *rk628 = dsi->rk628;
941*ab3bc873SGuochun Huang 
942*ab3bc873SGuochun Huang 	if (val > max)
943*ab3bc873SGuochun Huang 		return;
944*ab3bc873SGuochun Huang 
945*ab3bc873SGuochun Huang 	testif_write(rk628, dsi, addr, (max + 1) | val);
946*ab3bc873SGuochun Huang }
947*ab3bc873SGuochun Huang 
mipi_dphy_set_timing(const struct rk628_dsi * dsi)948*ab3bc873SGuochun Huang static void mipi_dphy_set_timing(const struct rk628_dsi *dsi)
949*ab3bc873SGuochun Huang {
950*ab3bc873SGuochun Huang 	const struct {
951*ab3bc873SGuochun Huang 		unsigned int min_lane_mbps;
952*ab3bc873SGuochun Huang 		unsigned int max_lane_mbps;
953*ab3bc873SGuochun Huang 		u8 clk_lp;
954*ab3bc873SGuochun Huang 		u8 clk_hs_prepare;
955*ab3bc873SGuochun Huang 		u8 clk_hs_zero;
956*ab3bc873SGuochun Huang 		u8 clk_hs_trail;
957*ab3bc873SGuochun Huang 		u8 clk_post;
958*ab3bc873SGuochun Huang 		u8 data_lp;
959*ab3bc873SGuochun Huang 		u8 data_hs_prepare;
960*ab3bc873SGuochun Huang 		u8 data_hs_zero;
961*ab3bc873SGuochun Huang 		u8 data_hs_trail;
962*ab3bc873SGuochun Huang 	} timing_table[] = {
963*ab3bc873SGuochun Huang 		{800, 899, 0x07, 0x30, 0x25, 0x3c, 0x0f, 0x07, 0x40, 0x09, 0x40},
964*ab3bc873SGuochun Huang 		{1100, 1249, 0x0a, 0x43, 0x2c, 0x50, 0x0f, 0x0a, 0x43, 0x10, 0x55},
965*ab3bc873SGuochun Huang 		{1250, 1349, 0x0b, 0x43, 0x2c, 0x50, 0x0f, 0x0b, 0x53, 0x10, 0x5b},
966*ab3bc873SGuochun Huang 		{1350, 1449, 0x0c, 0x43, 0x36, 0x60, 0x0f, 0x0c, 0x53, 0x10, 0x65},
967*ab3bc873SGuochun Huang 		{1450, 1500, 0x0f, 0x60, 0x31, 0x60, 0x0f, 0x0e, 0x60, 0x11, 0x6a}
968*ab3bc873SGuochun Huang 	};
969*ab3bc873SGuochun Huang 	unsigned int index;
970*ab3bc873SGuochun Huang 
971*ab3bc873SGuochun Huang 	if (dsi->lane_mbps < timing_table[0].min_lane_mbps)
972*ab3bc873SGuochun Huang 		return;
973*ab3bc873SGuochun Huang 
974*ab3bc873SGuochun Huang 	for (index = 0; index < ARRAY_SIZE(timing_table); index++)
975*ab3bc873SGuochun Huang 		if (dsi->lane_mbps >= timing_table[index].min_lane_mbps &&
976*ab3bc873SGuochun Huang 		    dsi->lane_mbps < timing_table[index].max_lane_mbps)
977*ab3bc873SGuochun Huang 			break;
978*ab3bc873SGuochun Huang 
979*ab3bc873SGuochun Huang 	if (index == ARRAY_SIZE(timing_table))
980*ab3bc873SGuochun Huang 		--index;
981*ab3bc873SGuochun Huang 
982*ab3bc873SGuochun Huang 	if (dsi->lane_mbps < timing_table[index].max_lane_mbps)
983*ab3bc873SGuochun Huang 		return;
984*ab3bc873SGuochun Huang 
985*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x60, 0x3f, timing_table[index].clk_lp);
986*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x61, 0x7f, timing_table[index].clk_hs_prepare);
987*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x62, 0x3f, timing_table[index].clk_hs_zero);
988*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x63, 0x7f, timing_table[index].clk_hs_trail);
989*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x65, 0x0f, timing_table[index].clk_post);
990*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x70, 0x3f, timing_table[index].data_lp);
991*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x71, 0x7f, timing_table[index].data_hs_prepare);
992*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x72, 0x3f, timing_table[index].data_hs_zero);
993*ab3bc873SGuochun Huang 	testif_set_timing(dsi, 0x73, 0x7f, timing_table[index].data_hs_trail);
994*ab3bc873SGuochun Huang }
995*ab3bc873SGuochun Huang 
mipi_dphy_init(struct rk628 * rk628,const struct rk628_dsi * dsi)996*ab3bc873SGuochun Huang static void mipi_dphy_init(struct rk628 *rk628, const struct rk628_dsi *dsi)
997*ab3bc873SGuochun Huang {
998*ab3bc873SGuochun Huang 	const struct {
999*ab3bc873SGuochun Huang 		unsigned long max_lane_mbps;
1000*ab3bc873SGuochun Huang 		u8 hsfreqrange;
1001*ab3bc873SGuochun Huang 	} hsfreqrange_table[] = {
1002*ab3bc873SGuochun Huang 		{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
1003*ab3bc873SGuochun Huang 		{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
1004*ab3bc873SGuochun Huang 		{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
1005*ab3bc873SGuochun Huang 		{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
1006*ab3bc873SGuochun Huang 		{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
1007*ab3bc873SGuochun Huang 		{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
1008*ab3bc873SGuochun Huang 		{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
1009*ab3bc873SGuochun Huang 		{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
1010*ab3bc873SGuochun Huang 		{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
1011*ab3bc873SGuochun Huang 		{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
1012*ab3bc873SGuochun Huang 	};
1013*ab3bc873SGuochun Huang 	u8 hsfreqrange;
1014*ab3bc873SGuochun Huang 	unsigned int index;
1015*ab3bc873SGuochun Huang 
1016*ab3bc873SGuochun Huang 	for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
1017*ab3bc873SGuochun Huang 		if (dsi->lane_mbps <= hsfreqrange_table[index].max_lane_mbps)
1018*ab3bc873SGuochun Huang 			break;
1019*ab3bc873SGuochun Huang 
1020*ab3bc873SGuochun Huang 	if (index == ARRAY_SIZE(hsfreqrange_table))
1021*ab3bc873SGuochun Huang 		--index;
1022*ab3bc873SGuochun Huang 
1023*ab3bc873SGuochun Huang 	hsfreqrange = hsfreqrange_table[index].hsfreqrange;
1024*ab3bc873SGuochun Huang 	testif_write(rk628, dsi, 0x44, HSFREQRANGE(hsfreqrange));
1025*ab3bc873SGuochun Huang 
1026*ab3bc873SGuochun Huang 	if (rk628->version == RK628F_VERSION)
1027*ab3bc873SGuochun Huang 		mipi_dphy_set_timing(dsi);
1028*ab3bc873SGuochun Huang }
1029*ab3bc873SGuochun Huang 
mipi_dphy_power_on(struct rk628 * rk628,const struct rk628_dsi * dsi)1030*ab3bc873SGuochun Huang static void mipi_dphy_power_on(struct rk628 *rk628, const struct rk628_dsi *dsi)
1031*ab3bc873SGuochun Huang {
1032*ab3bc873SGuochun Huang 	unsigned int dsi_base;
1033*ab3bc873SGuochun Huang 	unsigned int val = 0, mask;
1034*ab3bc873SGuochun Huang 	int ret;
1035*ab3bc873SGuochun Huang 
1036*ab3bc873SGuochun Huang 	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
1037*ab3bc873SGuochun Huang 
1038*ab3bc873SGuochun Huang 	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ, PHY_ENABLECLK, 0);
1039*ab3bc873SGuochun Huang 	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ, PHY_SHUTDOWNZ, 0);
1040*ab3bc873SGuochun Huang 	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ, PHY_RSTZ, 0);
1041*ab3bc873SGuochun Huang 	testif_testclr_assert(rk628, dsi);
1042*ab3bc873SGuochun Huang 
1043*ab3bc873SGuochun Huang 	/* Set all REQUEST inputs to zero */
1044*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, dsi->id ?
1045*ab3bc873SGuochun Huang 			      GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
1046*ab3bc873SGuochun Huang 			      FORCERXMODE_MASK | FORCETXSTOPMODE_MASK,
1047*ab3bc873SGuochun Huang 			      FORCETXSTOPMODE(0) | FORCERXMODE(0));
1048*ab3bc873SGuochun Huang 	udelay(1);
1049*ab3bc873SGuochun Huang 
1050*ab3bc873SGuochun Huang 	testif_testclr_deassert(rk628, dsi);
1051*ab3bc873SGuochun Huang 
1052*ab3bc873SGuochun Huang 	mipi_dphy_init(rk628, dsi);
1053*ab3bc873SGuochun Huang 
1054*ab3bc873SGuochun Huang 	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ,
1055*ab3bc873SGuochun Huang 			PHY_ENABLECLK, PHY_ENABLECLK);
1056*ab3bc873SGuochun Huang 	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ,
1057*ab3bc873SGuochun Huang 			PHY_SHUTDOWNZ, PHY_SHUTDOWNZ);
1058*ab3bc873SGuochun Huang 	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ, PHY_RSTZ, PHY_RSTZ);
1059*ab3bc873SGuochun Huang 	udelay(2000);
1060*ab3bc873SGuochun Huang 
1061*ab3bc873SGuochun Huang 	rk628_combtxphy_power_on(rk628);
1062*ab3bc873SGuochun Huang 
1063*ab3bc873SGuochun Huang 	ret = rk628_read_poll_timeout(rk628, dsi_base + DSI_PHY_STATUS,
1064*ab3bc873SGuochun Huang 				      val, val & PHY_LOCK, 0, 1000);
1065*ab3bc873SGuochun Huang 	if (ret < 0)
1066*ab3bc873SGuochun Huang 		printf("rk628 dsi PHY is not locked\n");
1067*ab3bc873SGuochun Huang 
1068*ab3bc873SGuochun Huang 	udelay(200);
1069*ab3bc873SGuochun Huang 
1070*ab3bc873SGuochun Huang 	mask = PHY_STOPSTATELANE;
1071*ab3bc873SGuochun Huang 	ret = rk628_read_poll_timeout(rk628, dsi_base + DSI_PHY_STATUS,
1072*ab3bc873SGuochun Huang 				      val, (val & mask) == mask,
1073*ab3bc873SGuochun Huang 				       0, 1000);
1074*ab3bc873SGuochun Huang 	if (ret < 0)
1075*ab3bc873SGuochun Huang 		printf("rk628 dsi lane module is not in stop state\n");
1076*ab3bc873SGuochun Huang 
1077*ab3bc873SGuochun Huang 	udelay(10);
1078*ab3bc873SGuochun Huang }
1079*ab3bc873SGuochun Huang 
rk628_dsi0_reset_control_assert(struct rk628 * rk628)1080*ab3bc873SGuochun Huang static void rk628_dsi0_reset_control_assert(struct rk628 *rk628)
1081*ab3bc873SGuochun Huang {
1082*ab3bc873SGuochun Huang 	rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x400040);
1083*ab3bc873SGuochun Huang }
1084*ab3bc873SGuochun Huang 
rk628_dsi0_reset_control_deassert(struct rk628 * rk628)1085*ab3bc873SGuochun Huang static void rk628_dsi0_reset_control_deassert(struct rk628 *rk628)
1086*ab3bc873SGuochun Huang {
1087*ab3bc873SGuochun Huang 	rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x400000);
1088*ab3bc873SGuochun Huang }
1089*ab3bc873SGuochun Huang 
rk628_dsi1_reset_control_assert(struct rk628 * rk628)1090*ab3bc873SGuochun Huang static void rk628_dsi1_reset_control_assert(struct rk628 *rk628)
1091*ab3bc873SGuochun Huang {
1092*ab3bc873SGuochun Huang 	rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x800080);
1093*ab3bc873SGuochun Huang }
1094*ab3bc873SGuochun Huang 
rk628_dsi1_reset_control_deassert(struct rk628 * rk628)1095*ab3bc873SGuochun Huang static void rk628_dsi1_reset_control_deassert(struct rk628 *rk628)
1096*ab3bc873SGuochun Huang {
1097*ab3bc873SGuochun Huang 	rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x800000);
1098*ab3bc873SGuochun Huang }
1099*ab3bc873SGuochun Huang 
rk628_dsi_bridge_pre_enable(struct rk628 * rk628,const struct rk628_dsi * dsi)1100*ab3bc873SGuochun Huang static void rk628_dsi_bridge_pre_enable(struct rk628 *rk628,
1101*ab3bc873SGuochun Huang 					const struct rk628_dsi *dsi)
1102*ab3bc873SGuochun Huang {
1103*ab3bc873SGuochun Huang 	u32 val;
1104*ab3bc873SGuochun Huang 
1105*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PWR_UP, RESET);
1106*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(RK628_DSI_COMMAND_MODE));
1107*ab3bc873SGuochun Huang 
1108*ab3bc873SGuochun Huang 	val = DIV_ROUND_UP(dsi->lane_mbps >> 3, 20);
1109*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_CLKMGR_CFG,
1110*ab3bc873SGuochun Huang 		  TO_CLK_DIVISION(10) | TX_ESC_CLK_DIVISION(val));
1111*ab3bc873SGuochun Huang 
1112*ab3bc873SGuochun Huang 	val = CRC_RX_EN | ECC_RX_EN | BTA_EN | EOTP_TX_EN;
1113*ab3bc873SGuochun Huang 	if (dsi->mode_flags & RK628_MIPI_DSI_MODE_EOT_PACKET)
1114*ab3bc873SGuochun Huang 		val &= ~EOTP_TX_EN;
1115*ab3bc873SGuochun Huang 
1116*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PCKHDL_CFG, val);
1117*ab3bc873SGuochun Huang 
1118*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_TO_CNT_CFG,
1119*ab3bc873SGuochun Huang 		  HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
1120*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_BTA_TO_CNT, 0xd00);
1121*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PHY_TMR_CFG,
1122*ab3bc873SGuochun Huang 		  PHY_HS2LP_TIME(0x14) | PHY_LP2HS_TIME(0x10) |
1123*ab3bc873SGuochun Huang 		  MAX_RD_TIME(10000));
1124*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PHY_TMR_LPCLK_CFG,
1125*ab3bc873SGuochun Huang 		  PHY_CLKHS2LP_TIME(0x40) | PHY_CLKLP2HS_TIME(0x40));
1126*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PHY_IF_CFG,
1127*ab3bc873SGuochun Huang 		  PHY_STOP_WAIT_TIME(0x20) | N_LANES(dsi->lanes - 1));
1128*ab3bc873SGuochun Huang 
1129*ab3bc873SGuochun Huang 	mipi_dphy_power_on(rk628, dsi);
1130*ab3bc873SGuochun Huang 
1131*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PWR_UP, POWER_UP);
1132*ab3bc873SGuochun Huang }
1133*ab3bc873SGuochun Huang 
rk628_dsi_set_vid_mode(struct rk628 * rk628,const struct rk628_dsi * dsi,const struct drm_display_mode * mode)1134*ab3bc873SGuochun Huang static void rk628_dsi_set_vid_mode(struct rk628 *rk628,
1135*ab3bc873SGuochun Huang 				   const struct rk628_dsi *dsi,
1136*ab3bc873SGuochun Huang 				   const struct drm_display_mode *mode)
1137*ab3bc873SGuochun Huang {
1138*ab3bc873SGuochun Huang 	unsigned int lanebyteclk = (dsi->lane_mbps * 1000L) >> 3;
1139*ab3bc873SGuochun Huang 	unsigned int dpipclk = mode->clock;
1140*ab3bc873SGuochun Huang 	u32 hline, hsa, hbp, hline_time, hsa_time, hbp_time;
1141*ab3bc873SGuochun Huang 	u32 vactive, vsa, vfp, vbp;
1142*ab3bc873SGuochun Huang 	u32 val;
1143*ab3bc873SGuochun Huang 	int pkt_size;
1144*ab3bc873SGuochun Huang 
1145*ab3bc873SGuochun Huang 	val = LP_HFP_EN | LP_HBP_EN | LP_VACT_EN | LP_VFP_EN | LP_VBP_EN |
1146*ab3bc873SGuochun Huang 	      LP_VSA_EN;
1147*ab3bc873SGuochun Huang 
1148*ab3bc873SGuochun Huang 	if (dsi->mode_flags & RK628_MIPI_DSI_MODE_VIDEO_HFP)
1149*ab3bc873SGuochun Huang 		val &= ~LP_HFP_EN;
1150*ab3bc873SGuochun Huang 
1151*ab3bc873SGuochun Huang 	if (dsi->mode_flags & RK628_MIPI_DSI_MODE_VIDEO_HBP)
1152*ab3bc873SGuochun Huang 		val &= ~LP_HBP_EN;
1153*ab3bc873SGuochun Huang 
1154*ab3bc873SGuochun Huang 	if (dsi->mode_flags & RK628_MIPI_DSI_MODE_VIDEO_BURST)
1155*ab3bc873SGuochun Huang 		val |= VID_MODE_TYPE_BURST;
1156*ab3bc873SGuochun Huang 	else if (dsi->mode_flags & RK628_MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
1157*ab3bc873SGuochun Huang 		val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
1158*ab3bc873SGuochun Huang 	else
1159*ab3bc873SGuochun Huang 		val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
1160*ab3bc873SGuochun Huang 
1161*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_MODE_CFG, val);
1162*ab3bc873SGuochun Huang 
1163*ab3bc873SGuochun Huang 	if (dsi->mode_flags & RK628_MIPI_DSI_CLOCK_NON_CONTINUOUS)
1164*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_LPCLK_CTRL,
1165*ab3bc873SGuochun Huang 				AUTO_CLKLANE_CTRL, AUTO_CLKLANE_CTRL);
1166*ab3bc873SGuochun Huang 
1167*ab3bc873SGuochun Huang 	if (dsi->slave || dsi->master)
1168*ab3bc873SGuochun Huang 		pkt_size = VID_PKT_SIZE(mode->hdisplay / 2);
1169*ab3bc873SGuochun Huang 	else
1170*ab3bc873SGuochun Huang 		pkt_size = VID_PKT_SIZE(mode->hdisplay);
1171*ab3bc873SGuochun Huang 
1172*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_PKT_SIZE, pkt_size);
1173*ab3bc873SGuochun Huang 
1174*ab3bc873SGuochun Huang 	vactive = mode->vdisplay;
1175*ab3bc873SGuochun Huang 	vsa = mode->vsync_end - mode->vsync_start;
1176*ab3bc873SGuochun Huang 	vfp = mode->vsync_start - mode->vdisplay;
1177*ab3bc873SGuochun Huang 	vbp = mode->vtotal - mode->vsync_end;
1178*ab3bc873SGuochun Huang 	hsa = mode->hsync_end - mode->hsync_start;
1179*ab3bc873SGuochun Huang 	hbp = mode->htotal - mode->hsync_end;
1180*ab3bc873SGuochun Huang 	hline = mode->htotal;
1181*ab3bc873SGuochun Huang 
1182*ab3bc873SGuochun Huang 	//hline_time = hline * lanebyteclk / dpipclk;
1183*ab3bc873SGuochun Huang 	hline_time = DIV_ROUND_CLOSEST(hline * lanebyteclk, dpipclk);
1184*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_HLINE_TIME,
1185*ab3bc873SGuochun Huang 		  VID_HLINE_TIME(hline_time));
1186*ab3bc873SGuochun Huang 	//hsa_time = hsa * lanebyteclk / dpipclk;
1187*ab3bc873SGuochun Huang 	hsa_time = DIV_ROUND_CLOSEST(hsa * lanebyteclk, dpipclk);
1188*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_HSA_TIME, VID_HSA_TIME(hsa_time));
1189*ab3bc873SGuochun Huang 	//hbp_time = hbp * lanebyteclk / dpipclk;
1190*ab3bc873SGuochun Huang 	hbp_time = DIV_ROUND_CLOSEST(hbp * lanebyteclk, dpipclk);
1191*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_HBP_TIME, VID_HBP_TIME(hbp_time));
1192*ab3bc873SGuochun Huang 
1193*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_VACTIVE_LINES, vactive);
1194*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_VSA_LINES, vsa);
1195*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_VFP_LINES, vfp);
1196*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_VID_VBP_LINES, vbp);
1197*ab3bc873SGuochun Huang 
1198*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(RK628_DSI_VIDEO_MODE));
1199*ab3bc873SGuochun Huang }
1200*ab3bc873SGuochun Huang 
rk628_dsi_set_cmd_mode(struct rk628 * rk628,const struct rk628_dsi * dsi,const struct drm_display_mode * mode)1201*ab3bc873SGuochun Huang static void rk628_dsi_set_cmd_mode(struct rk628 *rk628,
1202*ab3bc873SGuochun Huang 				   const struct rk628_dsi *dsi,
1203*ab3bc873SGuochun Huang 				   const struct drm_display_mode *mode)
1204*ab3bc873SGuochun Huang {
1205*ab3bc873SGuochun Huang 	int cmd_size;
1206*ab3bc873SGuochun Huang 
1207*ab3bc873SGuochun Huang 	dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_LW_TX, 0);
1208*ab3bc873SGuochun Huang 
1209*ab3bc873SGuochun Huang 	/* rk628: The maximum capacity of dsi memory is 2048*32 bits */
1210*ab3bc873SGuochun Huang 	if (mode->hdisplay > 2048)
1211*ab3bc873SGuochun Huang 		cmd_size = EDPI_ALLOWED_CMD_SIZE(mode->hdisplay / 2);
1212*ab3bc873SGuochun Huang 	else
1213*ab3bc873SGuochun Huang 		cmd_size = EDPI_ALLOWED_CMD_SIZE(mode->hdisplay);
1214*ab3bc873SGuochun Huang 
1215*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_EDPI_CMD_SIZE, cmd_size);
1216*ab3bc873SGuochun Huang 
1217*ab3bc873SGuochun Huang 	if (dsi->mode_flags & RK628_MIPI_DSI_CLOCK_NON_CONTINUOUS)
1218*ab3bc873SGuochun Huang 		dsi_update_bits(rk628, dsi, DSI_LPCLK_CTRL,
1219*ab3bc873SGuochun Huang 				AUTO_CLKLANE_CTRL, AUTO_CLKLANE_CTRL);
1220*ab3bc873SGuochun Huang 
1221*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(RK628_DSI_COMMAND_MODE));
1222*ab3bc873SGuochun Huang }
1223*ab3bc873SGuochun Huang 
rk628_dsi_bridge_enable(struct rk628 * rk628,const struct rk628_dsi * dsi)1224*ab3bc873SGuochun Huang static void rk628_dsi_bridge_enable(struct rk628 *rk628,
1225*ab3bc873SGuochun Huang 				    const struct rk628_dsi *dsi)
1226*ab3bc873SGuochun Huang {
1227*ab3bc873SGuochun Huang 	u32 val;
1228*ab3bc873SGuochun Huang 	const struct drm_display_mode *mode = &rk628->dst_mode;
1229*ab3bc873SGuochun Huang 
1230*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PWR_UP, RESET);
1231*ab3bc873SGuochun Huang 
1232*ab3bc873SGuochun Huang 	switch (dsi->bus_format) {
1233*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_FMT_RGB666:
1234*ab3bc873SGuochun Huang 		val = DPI_COLOR_CODING(DPI_COLOR_CODING_18BIT_2) |
1235*ab3bc873SGuochun Huang 		      LOOSELY18_EN;
1236*ab3bc873SGuochun Huang 		break;
1237*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_FMT_RGB666_PACKED:
1238*ab3bc873SGuochun Huang 		val = DPI_COLOR_CODING(DPI_COLOR_CODING_18BIT_1);
1239*ab3bc873SGuochun Huang 		break;
1240*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_FMT_RGB565:
1241*ab3bc873SGuochun Huang 		val = DPI_COLOR_CODING(DPI_COLOR_CODING_16BIT_1);
1242*ab3bc873SGuochun Huang 		break;
1243*ab3bc873SGuochun Huang 	case RK628_MIPI_DSI_FMT_RGB888:
1244*ab3bc873SGuochun Huang 	default:
1245*ab3bc873SGuochun Huang 		val = DPI_COLOR_CODING(DPI_COLOR_CODING_24BIT);
1246*ab3bc873SGuochun Huang 		break;
1247*ab3bc873SGuochun Huang 	}
1248*ab3bc873SGuochun Huang 
1249*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_DPI_COLOR_CODING, val);
1250*ab3bc873SGuochun Huang 
1251*ab3bc873SGuochun Huang 	val = 0;
1252*ab3bc873SGuochun Huang 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
1253*ab3bc873SGuochun Huang 		val |= VSYNC_ACTIVE_LOW;
1254*ab3bc873SGuochun Huang 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
1255*ab3bc873SGuochun Huang 		val |= HSYNC_ACTIVE_LOW;
1256*ab3bc873SGuochun Huang 
1257*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_DPI_CFG_POL, val);
1258*ab3bc873SGuochun Huang 
1259*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_DPI_VCID, DPI_VID(0));
1260*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_DPI_LP_CMD_TIM,
1261*ab3bc873SGuochun Huang 		  OUTVACT_LPCMD_TIME(4) | INVACT_LPCMD_TIME(4));
1262*ab3bc873SGuochun Huang 	dsi_update_bits(rk628, dsi, DSI_LPCLK_CTRL,
1263*ab3bc873SGuochun Huang 			PHY_TXREQUESTCLKHS, PHY_TXREQUESTCLKHS);
1264*ab3bc873SGuochun Huang 
1265*ab3bc873SGuochun Huang 	if (dsi->mode_flags & RK628_MIPI_DSI_MODE_VIDEO)
1266*ab3bc873SGuochun Huang 		rk628_dsi_set_vid_mode(rk628, dsi, mode);
1267*ab3bc873SGuochun Huang 	else
1268*ab3bc873SGuochun Huang 		rk628_dsi_set_cmd_mode(rk628, dsi, mode);
1269*ab3bc873SGuochun Huang 
1270*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PWR_UP, POWER_UP);
1271*ab3bc873SGuochun Huang }
1272*ab3bc873SGuochun Huang 
rk628_mipi_dsi_pre_enable(struct rk628 * rk628)1273*ab3bc873SGuochun Huang void rk628_mipi_dsi_pre_enable(struct rk628 *rk628)
1274*ab3bc873SGuochun Huang {
1275*ab3bc873SGuochun Huang 	struct rk628_dsi *dsi = &rk628->dsi0;
1276*ab3bc873SGuochun Huang 	struct rk628_dsi *dsi1 = &rk628->dsi1;
1277*ab3bc873SGuochun Huang 	u32 rate = rk628_dsi_get_lane_rate(dsi);
1278*ab3bc873SGuochun Huang 	int bus_width;
1279*ab3bc873SGuochun Huang 	u32 mask = SW_OUTPUT_MODE_MASK;
1280*ab3bc873SGuochun Huang 	u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_DSI);
1281*ab3bc873SGuochun Huang 
1282*ab3bc873SGuochun Huang 	if (rk628->version == RK628F_VERSION) {
1283*ab3bc873SGuochun Huang 		mask = SW_OUTPUT_COMBTX_MODE_MASK;
1284*ab3bc873SGuochun Huang 		val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_DSI - 2);
1285*ab3bc873SGuochun Huang 
1286*ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON3,
1287*ab3bc873SGuochun Huang 				      GRF_DPHY_CH1_EN_MASK |
1288*ab3bc873SGuochun Huang 				      GRF_AS_DSIPHY_MASK,
1289*ab3bc873SGuochun Huang 				      (dsi->slave ? GRF_DPHY_CH1_EN(1) : 0) |
1290*ab3bc873SGuochun Huang 				      GRF_AS_DSIPHY(1));
1291*ab3bc873SGuochun Huang 	}
1292*ab3bc873SGuochun Huang 
1293*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, mask, val);
1294*ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, SW_SPLIT_EN,
1295*ab3bc873SGuochun Huang 			      dsi->slave ? SW_SPLIT_EN : 0);
1296*ab3bc873SGuochun Huang 
1297*ab3bc873SGuochun Huang 	bus_width = rate << 8;
1298*ab3bc873SGuochun Huang 	if (dsi->slave)
1299*ab3bc873SGuochun Huang 		bus_width |= COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN;
1300*ab3bc873SGuochun Huang 	else if (dsi->id)
1301*ab3bc873SGuochun Huang 		bus_width |= COMBTXPHY_MODULEB_EN;
1302*ab3bc873SGuochun Huang 	else
1303*ab3bc873SGuochun Huang 		bus_width |= COMBTXPHY_MODULEA_EN;
1304*ab3bc873SGuochun Huang 
1305*ab3bc873SGuochun Huang 	rk628_combtxphy_set_bus_width(rk628, bus_width);
1306*ab3bc873SGuochun Huang 	rk628_combtxphy_set_mode(rk628, RK628_PHY_MODE_VIDEO_MIPI);
1307*ab3bc873SGuochun Huang 	dsi->lane_mbps = rk628_combtxphy_get_bus_width(rk628);
1308*ab3bc873SGuochun Huang 
1309*ab3bc873SGuochun Huang 	if (dsi->slave && dsi1)
1310*ab3bc873SGuochun Huang 		dsi1->lane_mbps = rk628_combtxphy_get_bus_width(rk628);
1311*ab3bc873SGuochun Huang 
1312*ab3bc873SGuochun Huang 	/* rst for dsi0 */
1313*ab3bc873SGuochun Huang 	rk628_dsi0_reset_control_assert(rk628);
1314*ab3bc873SGuochun Huang 	udelay(40);
1315*ab3bc873SGuochun Huang 	rk628_dsi0_reset_control_deassert(rk628);
1316*ab3bc873SGuochun Huang 	udelay(40);
1317*ab3bc873SGuochun Huang 
1318*ab3bc873SGuochun Huang 	rk628_dsi_bridge_pre_enable(rk628, dsi);
1319*ab3bc873SGuochun Huang 
1320*ab3bc873SGuochun Huang 	if (dsi->slave) {
1321*ab3bc873SGuochun Huang 		/* rst for dsi1 */
1322*ab3bc873SGuochun Huang 		rk628_dsi1_reset_control_assert(rk628);
1323*ab3bc873SGuochun Huang 		udelay(40);
1324*ab3bc873SGuochun Huang 		rk628_dsi1_reset_control_deassert(rk628);
1325*ab3bc873SGuochun Huang 		udelay(40);
1326*ab3bc873SGuochun Huang 
1327*ab3bc873SGuochun Huang 		rk628_dsi_bridge_pre_enable(rk628, dsi1);
1328*ab3bc873SGuochun Huang 	}
1329*ab3bc873SGuochun Huang 
1330*ab3bc873SGuochun Huang 	rk628_panel_prepare(rk628);
1331*ab3bc873SGuochun Huang 	panel_simple_xfer_dsi_cmd_seq(rk628, rk628->panel->on_cmds);
1332*ab3bc873SGuochun Huang 
1333*ab3bc873SGuochun Huang #ifdef DSI_READ_POWER_MODE
1334*ab3bc873SGuochun Huang 	u8 mode;
1335*ab3bc873SGuochun Huang 
1336*ab3bc873SGuochun Huang 	rk628_mipi_dsi_dcs_read(rk628, MIPI_DCS_GET_POWER_MODE,
1337*ab3bc873SGuochun Huang 				&mode, sizeof(mode));
1338*ab3bc873SGuochun Huang 
1339*ab3bc873SGuochun Huang 	printf("rk628 dsi: mode: 0x%x\n", mode);
1340*ab3bc873SGuochun Huang #endif
1341*ab3bc873SGuochun Huang 
1342*ab3bc873SGuochun Huang 	printf("rk628_dsi final DSI-Link bandwidth: %d x %d\n",
1343*ab3bc873SGuochun Huang 	       dsi->lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes);
1344*ab3bc873SGuochun Huang }
1345*ab3bc873SGuochun Huang 
rk628_mipi_dsi_enable(struct rk628 * rk628)1346*ab3bc873SGuochun Huang void rk628_mipi_dsi_enable(struct rk628 *rk628)
1347*ab3bc873SGuochun Huang {
1348*ab3bc873SGuochun Huang 	const struct rk628_dsi *dsi = &rk628->dsi0;
1349*ab3bc873SGuochun Huang 	const struct rk628_dsi *dsi1 = &rk628->dsi1;
1350*ab3bc873SGuochun Huang 
1351*ab3bc873SGuochun Huang 	rk628_dsi_bridge_enable(rk628, dsi);
1352*ab3bc873SGuochun Huang 
1353*ab3bc873SGuochun Huang 	if (dsi->slave)
1354*ab3bc873SGuochun Huang 		rk628_dsi_bridge_enable(rk628, dsi1);
1355*ab3bc873SGuochun Huang 
1356*ab3bc873SGuochun Huang 	rk628_panel_enable(rk628);
1357*ab3bc873SGuochun Huang }
1358*ab3bc873SGuochun Huang 
rk628_dsi_disable(struct rk628 * rk628)1359*ab3bc873SGuochun Huang void rk628_dsi_disable(struct rk628 *rk628)
1360*ab3bc873SGuochun Huang {
1361*ab3bc873SGuochun Huang 	const struct rk628_dsi *dsi = &rk628->dsi0;
1362*ab3bc873SGuochun Huang 	const struct rk628_dsi *dsi1 = &rk628->dsi1;
1363*ab3bc873SGuochun Huang 
1364*ab3bc873SGuochun Huang 	rk628_panel_disable(rk628);
1365*ab3bc873SGuochun Huang 
1366*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PWR_UP, RESET);
1367*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_LPCLK_CTRL, 0);
1368*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_EDPI_CMD_SIZE, 0);
1369*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(RK628_DSI_COMMAND_MODE));
1370*ab3bc873SGuochun Huang 	dsi_write(rk628, dsi, DSI_PWR_UP, POWER_UP);
1371*ab3bc873SGuochun Huang 
1372*ab3bc873SGuochun Huang 	//dsi_write(rk628, dsi, DSI_PHY_RSTZ, 0);
1373*ab3bc873SGuochun Huang 
1374*ab3bc873SGuochun Huang 	if (dsi->slave) {
1375*ab3bc873SGuochun Huang 		dsi_write(rk628, dsi1, DSI_PWR_UP, RESET);
1376*ab3bc873SGuochun Huang 		dsi_write(rk628, dsi1, DSI_LPCLK_CTRL, 0);
1377*ab3bc873SGuochun Huang 		dsi_write(rk628, dsi1, DSI_EDPI_CMD_SIZE, 0);
1378*ab3bc873SGuochun Huang 		dsi_write(rk628, dsi1, DSI_MODE_CFG,
1379*ab3bc873SGuochun Huang 			  CMD_VIDEO_MODE(RK628_DSI_COMMAND_MODE));
1380*ab3bc873SGuochun Huang 		dsi_write(rk628, dsi1, DSI_PWR_UP, POWER_UP);
1381*ab3bc873SGuochun Huang 
1382*ab3bc873SGuochun Huang 		//dsi_write(rk628, dsi1, DSI_PHY_RSTZ, 0);
1383*ab3bc873SGuochun Huang 	}
1384*ab3bc873SGuochun Huang 
1385*ab3bc873SGuochun Huang 	panel_simple_xfer_dsi_cmd_seq(rk628, rk628->panel->off_cmds);
1386*ab3bc873SGuochun Huang 	rk628_panel_unprepare(rk628);
1387*ab3bc873SGuochun Huang 	rk628_combtxphy_power_off(rk628);
1388*ab3bc873SGuochun Huang }
1389*ab3bc873SGuochun Huang 
1390