xref: /rk3399_rockchip-uboot/drivers/video/drm/rk628/panel.c (revision ab3bc87339b1566ceabcfb54995e11928492c356)
1*ab3bc873SGuochun Huang // SPDX-License-Identifier: GPL-2.0
2*ab3bc873SGuochun Huang /*
3*ab3bc873SGuochun Huang  * Copyright (C) 2020 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 <drm/drm_mipi_dsi.h>
9*ab3bc873SGuochun Huang 
10*ab3bc873SGuochun Huang #include <config.h>
11*ab3bc873SGuochun Huang #include <common.h>
12*ab3bc873SGuochun Huang #include <errno.h>
13*ab3bc873SGuochun Huang #include <malloc.h>
14*ab3bc873SGuochun Huang #include <video.h>
15*ab3bc873SGuochun Huang #include <backlight.h>
16*ab3bc873SGuochun Huang #include <spi.h>
17*ab3bc873SGuochun Huang #include <asm/gpio.h>
18*ab3bc873SGuochun Huang #include <dm/device.h>
19*ab3bc873SGuochun Huang #include <dm/read.h>
20*ab3bc873SGuochun Huang #include <dm/uclass.h>
21*ab3bc873SGuochun Huang #include <dm/uclass-id.h>
22*ab3bc873SGuochun Huang #include <linux/media-bus-format.h>
23*ab3bc873SGuochun Huang #include <power/regulator.h>
24*ab3bc873SGuochun Huang 
25*ab3bc873SGuochun Huang #include "rk628.h"
26*ab3bc873SGuochun Huang #include "panel.h"
27*ab3bc873SGuochun Huang 
kmemdup(const void * src,size_t len,gfp_t gfp)28*ab3bc873SGuochun Huang void *kmemdup(const void *src, size_t len, gfp_t gfp)
29*ab3bc873SGuochun Huang {
30*ab3bc873SGuochun Huang 	void *p;
31*ab3bc873SGuochun Huang 
32*ab3bc873SGuochun Huang 	p = kmalloc(len, gfp);
33*ab3bc873SGuochun Huang 	if (p)
34*ab3bc873SGuochun Huang 		memcpy(p, src, len);
35*ab3bc873SGuochun Huang 	return p;
36*ab3bc873SGuochun Huang }
37*ab3bc873SGuochun Huang 
38*ab3bc873SGuochun Huang static int
dsi_panel_parse_cmds(const u8 * data,int blen,struct panel_cmds * pcmds)39*ab3bc873SGuochun Huang dsi_panel_parse_cmds(const u8 *data, int blen, struct panel_cmds *pcmds)
40*ab3bc873SGuochun Huang {
41*ab3bc873SGuochun Huang 	unsigned int len;
42*ab3bc873SGuochun Huang 	u8 *buf, *bp;
43*ab3bc873SGuochun Huang 	struct cmd_ctrl_hdr *dchdr;
44*ab3bc873SGuochun Huang 	int i, cnt;
45*ab3bc873SGuochun Huang 
46*ab3bc873SGuochun Huang 	if (!pcmds)
47*ab3bc873SGuochun Huang 		return -EINVAL;
48*ab3bc873SGuochun Huang 
49*ab3bc873SGuochun Huang 	buf = kmemdup(data, blen, GFP_KERNEL);
50*ab3bc873SGuochun Huang 	if (!buf)
51*ab3bc873SGuochun Huang 		return -ENOMEM;
52*ab3bc873SGuochun Huang 
53*ab3bc873SGuochun Huang 	/* scan init commands */
54*ab3bc873SGuochun Huang 	bp = buf;
55*ab3bc873SGuochun Huang 	len = blen;
56*ab3bc873SGuochun Huang 	cnt = 0;
57*ab3bc873SGuochun Huang 	while (len > sizeof(*dchdr)) {
58*ab3bc873SGuochun Huang 		dchdr = (struct cmd_ctrl_hdr *)bp;
59*ab3bc873SGuochun Huang 
60*ab3bc873SGuochun Huang 		if (dchdr->dlen > len) {
61*ab3bc873SGuochun Huang 			pr_err("%s: error, len=%d", __func__, dchdr->dlen);
62*ab3bc873SGuochun Huang 			return -EINVAL;
63*ab3bc873SGuochun Huang 		}
64*ab3bc873SGuochun Huang 
65*ab3bc873SGuochun Huang 		bp += sizeof(*dchdr);
66*ab3bc873SGuochun Huang 		len -= sizeof(*dchdr);
67*ab3bc873SGuochun Huang 		bp += dchdr->dlen;
68*ab3bc873SGuochun Huang 		len -= dchdr->dlen;
69*ab3bc873SGuochun Huang 		cnt++;
70*ab3bc873SGuochun Huang 	}
71*ab3bc873SGuochun Huang 
72*ab3bc873SGuochun Huang 	if (len != 0) {
73*ab3bc873SGuochun Huang 		pr_err("%s: dcs_cmd=%x len=%d error!", __func__, buf[0], blen);
74*ab3bc873SGuochun Huang 		kfree(buf);
75*ab3bc873SGuochun Huang 		return -EINVAL;
76*ab3bc873SGuochun Huang 	}
77*ab3bc873SGuochun Huang 
78*ab3bc873SGuochun Huang 	pcmds->cmds = kcalloc(cnt, sizeof(struct cmd_desc), GFP_KERNEL);
79*ab3bc873SGuochun Huang 	if (!pcmds->cmds) {
80*ab3bc873SGuochun Huang 		kfree(buf);
81*ab3bc873SGuochun Huang 		return -ENOMEM;
82*ab3bc873SGuochun Huang 	}
83*ab3bc873SGuochun Huang 
84*ab3bc873SGuochun Huang 	pcmds->cmd_cnt = cnt;
85*ab3bc873SGuochun Huang 	pcmds->buf = buf;
86*ab3bc873SGuochun Huang 	pcmds->blen = blen;
87*ab3bc873SGuochun Huang 
88*ab3bc873SGuochun Huang 	bp = buf;
89*ab3bc873SGuochun Huang 	len = blen;
90*ab3bc873SGuochun Huang 	for (i = 0; i < cnt; i++) {
91*ab3bc873SGuochun Huang 		dchdr = (struct cmd_ctrl_hdr *)bp;
92*ab3bc873SGuochun Huang 		len -= sizeof(*dchdr);
93*ab3bc873SGuochun Huang 		bp += sizeof(*dchdr);
94*ab3bc873SGuochun Huang 		pcmds->cmds[i].dchdr = *dchdr;
95*ab3bc873SGuochun Huang 		pcmds->cmds[i].payload = bp;
96*ab3bc873SGuochun Huang 		bp += dchdr->dlen;
97*ab3bc873SGuochun Huang 		len -= dchdr->dlen;
98*ab3bc873SGuochun Huang 	}
99*ab3bc873SGuochun Huang 
100*ab3bc873SGuochun Huang 	return 0;
101*ab3bc873SGuochun Huang }
102*ab3bc873SGuochun Huang 
dsi_panel_get_cmds(struct rk628 * rk628,ofnode dsi_np)103*ab3bc873SGuochun Huang static int dsi_panel_get_cmds(struct rk628 *rk628, ofnode dsi_np)
104*ab3bc873SGuochun Huang {
105*ab3bc873SGuochun Huang 	ofnode np;
106*ab3bc873SGuochun Huang 	const void *data;
107*ab3bc873SGuochun Huang 	int len;
108*ab3bc873SGuochun Huang 	int ret, err;
109*ab3bc873SGuochun Huang 
110*ab3bc873SGuochun Huang 	np = ofnode_find_subnode(dsi_np, "rk628-panel");
111*ab3bc873SGuochun Huang 	if (!ofnode_valid(np))
112*ab3bc873SGuochun Huang 		return -EINVAL;
113*ab3bc873SGuochun Huang 
114*ab3bc873SGuochun Huang 	data = ofnode_get_property(np, "panel-init-sequence", &len);
115*ab3bc873SGuochun Huang 	if (data) {
116*ab3bc873SGuochun Huang 		rk628->panel->on_cmds = kcalloc(1, sizeof(struct panel_cmds), GFP_KERNEL);
117*ab3bc873SGuochun Huang 		if (!rk628->panel->on_cmds)
118*ab3bc873SGuochun Huang 			return -ENOMEM;
119*ab3bc873SGuochun Huang 
120*ab3bc873SGuochun Huang 		err = dsi_panel_parse_cmds(data, len, rk628->panel->on_cmds);
121*ab3bc873SGuochun Huang 		if (err) {
122*ab3bc873SGuochun Huang 			printf("rk628 failed to parse dsi panel init sequence\n");
123*ab3bc873SGuochun Huang 			ret = err;
124*ab3bc873SGuochun Huang 			goto init_err;
125*ab3bc873SGuochun Huang 		}
126*ab3bc873SGuochun Huang 	}
127*ab3bc873SGuochun Huang 
128*ab3bc873SGuochun Huang 	data = ofnode_get_property(np, "panel-exit-sequence", &len);
129*ab3bc873SGuochun Huang 	if (data) {
130*ab3bc873SGuochun Huang 		rk628->panel->off_cmds = kcalloc(1, sizeof(struct panel_cmds), GFP_KERNEL);
131*ab3bc873SGuochun Huang 		if (!rk628->panel->off_cmds) {
132*ab3bc873SGuochun Huang 			ret = -ENOMEM;
133*ab3bc873SGuochun Huang 			goto on_err;
134*ab3bc873SGuochun Huang 		}
135*ab3bc873SGuochun Huang 
136*ab3bc873SGuochun Huang 		err = dsi_panel_parse_cmds(data, len, rk628->panel->off_cmds);
137*ab3bc873SGuochun Huang 		if (err) {
138*ab3bc873SGuochun Huang 			printf("rk628 failed to parse dsi panel exit sequence\n");
139*ab3bc873SGuochun Huang 			ret = err;
140*ab3bc873SGuochun Huang 			goto exit_err;
141*ab3bc873SGuochun Huang 		}
142*ab3bc873SGuochun Huang 	}
143*ab3bc873SGuochun Huang 
144*ab3bc873SGuochun Huang 	return 0;
145*ab3bc873SGuochun Huang 
146*ab3bc873SGuochun Huang exit_err:
147*ab3bc873SGuochun Huang 	kfree(rk628->panel->off_cmds);
148*ab3bc873SGuochun Huang on_err:
149*ab3bc873SGuochun Huang 	kfree(rk628->panel->on_cmds->cmds);
150*ab3bc873SGuochun Huang 	kfree(rk628->panel->on_cmds->buf);
151*ab3bc873SGuochun Huang init_err:
152*ab3bc873SGuochun Huang 	kfree(rk628->panel->on_cmds);
153*ab3bc873SGuochun Huang 
154*ab3bc873SGuochun Huang 	return ret;
155*ab3bc873SGuochun Huang }
156*ab3bc873SGuochun Huang 
rk628_panel_info_get(struct rk628 * rk628,ofnode np)157*ab3bc873SGuochun Huang int rk628_panel_info_get(struct rk628 *rk628, ofnode np)
158*ab3bc873SGuochun Huang {
159*ab3bc873SGuochun Huang 	struct rk628_panel_simple *panel;
160*ab3bc873SGuochun Huang 	struct udevice *dev = rk628->dev;
161*ab3bc873SGuochun Huang 	int ret;
162*ab3bc873SGuochun Huang 
163*ab3bc873SGuochun Huang 	panel = devm_kzalloc(dev, sizeof(struct rk628_panel_simple), GFP_KERNEL);
164*ab3bc873SGuochun Huang 	if (!panel)
165*ab3bc873SGuochun Huang 		return -ENOMEM;
166*ab3bc873SGuochun Huang 
167*ab3bc873SGuochun Huang 	ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, "panel-power-supply", &panel->supply);
168*ab3bc873SGuochun Huang 	if (ret && ret != -ENOENT) {
169*ab3bc873SGuochun Huang 		printf("rk628 failed to get power supply: %d\n", ret);
170*ab3bc873SGuochun Huang 		return ret;
171*ab3bc873SGuochun Huang 	}
172*ab3bc873SGuochun Huang 
173*ab3bc873SGuochun Huang 	ret = gpio_request_by_name(dev, "panel-enable-gpios", 0,
174*ab3bc873SGuochun Huang 				   &panel->enable_gpio, GPIOD_IS_OUT);
175*ab3bc873SGuochun Huang 	if (ret && ret != -ENOENT) {
176*ab3bc873SGuochun Huang 		printf("%s: Cannot get enable GPIO: %d\n", __func__, ret);
177*ab3bc873SGuochun Huang 		return ret;
178*ab3bc873SGuochun Huang 	}
179*ab3bc873SGuochun Huang 
180*ab3bc873SGuochun Huang 	ret = gpio_request_by_name(dev, "panel-reset-gpios", 0,
181*ab3bc873SGuochun Huang 				   &panel->reset_gpio, GPIOD_IS_OUT);
182*ab3bc873SGuochun Huang 	if (ret && ret != -ENOENT) {
183*ab3bc873SGuochun Huang 		printf("%s: Cannot get reset GPIO: %d\n", __func__, ret);
184*ab3bc873SGuochun Huang 		return ret;
185*ab3bc873SGuochun Huang 	}
186*ab3bc873SGuochun Huang 
187*ab3bc873SGuochun Huang 	ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
188*ab3bc873SGuochun Huang 					   "panel-backlight", &panel->backlight);
189*ab3bc873SGuochun Huang 	if (ret && ret != -ENOENT) {
190*ab3bc873SGuochun Huang 		printf("%s: Cannot get backlight: %d\n", __func__, ret);
191*ab3bc873SGuochun Huang 		return ret;
192*ab3bc873SGuochun Huang 	}
193*ab3bc873SGuochun Huang 
194*ab3bc873SGuochun Huang 	panel->delay.prepare = dev_read_u32_default(dev, "panel-prepare-delay-ms", 0);
195*ab3bc873SGuochun Huang 	panel->delay.enable = dev_read_u32_default(dev, "panel-enable-delay-ms", 0);
196*ab3bc873SGuochun Huang 	panel->delay.disable = dev_read_u32_default(dev, "panel-disable-delay-ms", 0);
197*ab3bc873SGuochun Huang 	panel->delay.unprepare = dev_read_u32_default(dev, "panel-unprepare-delay-ms", 0);
198*ab3bc873SGuochun Huang 	panel->delay.reset = dev_read_u32_default(dev, "panel-reset-delay-ms", 0);
199*ab3bc873SGuochun Huang 	panel->delay.init = dev_read_u32_default(dev, "panel-init-delay-ms", 0);
200*ab3bc873SGuochun Huang 
201*ab3bc873SGuochun Huang 	rk628->panel = panel;
202*ab3bc873SGuochun Huang 
203*ab3bc873SGuochun Huang 	if (rk628_output_is_dsi(rk628)) {
204*ab3bc873SGuochun Huang 		ret = dsi_panel_get_cmds(rk628, np);
205*ab3bc873SGuochun Huang 		if (ret) {
206*ab3bc873SGuochun Huang 			dev_err(dev, "failed to get cmds\n");
207*ab3bc873SGuochun Huang 			return ret;
208*ab3bc873SGuochun Huang 		}
209*ab3bc873SGuochun Huang 	}
210*ab3bc873SGuochun Huang 
211*ab3bc873SGuochun Huang 	return 0;
212*ab3bc873SGuochun Huang }
213*ab3bc873SGuochun Huang 
rk628_panel_prepare(struct rk628 * rk628)214*ab3bc873SGuochun Huang void rk628_panel_prepare(struct rk628 *rk628)
215*ab3bc873SGuochun Huang {
216*ab3bc873SGuochun Huang 	struct rk628_panel_simple *p = rk628->panel;
217*ab3bc873SGuochun Huang 
218*ab3bc873SGuochun Huang 	if (!p)
219*ab3bc873SGuochun Huang 		return;
220*ab3bc873SGuochun Huang 
221*ab3bc873SGuochun Huang 	if (p->supply)
222*ab3bc873SGuochun Huang 		regulator_set_enable(p->supply, 1);
223*ab3bc873SGuochun Huang 
224*ab3bc873SGuochun Huang 	if (dm_gpio_is_valid(&p->enable_gpio))
225*ab3bc873SGuochun Huang 		dm_gpio_set_value(&p->enable_gpio, 1);
226*ab3bc873SGuochun Huang 
227*ab3bc873SGuochun Huang 	if (p->delay.prepare)
228*ab3bc873SGuochun Huang 		mdelay(p->delay.prepare);
229*ab3bc873SGuochun Huang 
230*ab3bc873SGuochun Huang 	if (dm_gpio_is_valid(&p->reset_gpio))
231*ab3bc873SGuochun Huang 		dm_gpio_set_value(&p->reset_gpio, 1);
232*ab3bc873SGuochun Huang 
233*ab3bc873SGuochun Huang 	if (p->delay.reset)
234*ab3bc873SGuochun Huang 		mdelay(p->delay.reset);
235*ab3bc873SGuochun Huang 
236*ab3bc873SGuochun Huang 	if (dm_gpio_is_valid(&p->reset_gpio))
237*ab3bc873SGuochun Huang 		dm_gpio_set_value(&p->reset_gpio, 0);
238*ab3bc873SGuochun Huang 
239*ab3bc873SGuochun Huang 	if (p->delay.init)
240*ab3bc873SGuochun Huang 		mdelay(p->delay.init);
241*ab3bc873SGuochun Huang }
242*ab3bc873SGuochun Huang 
rk628_panel_enable(struct rk628 * rk628)243*ab3bc873SGuochun Huang void rk628_panel_enable(struct rk628 *rk628)
244*ab3bc873SGuochun Huang {
245*ab3bc873SGuochun Huang 	struct rk628_panel_simple *p = rk628->panel;
246*ab3bc873SGuochun Huang 
247*ab3bc873SGuochun Huang 	if (!p)
248*ab3bc873SGuochun Huang 		return;
249*ab3bc873SGuochun Huang 
250*ab3bc873SGuochun Huang 	if (p->delay.enable)
251*ab3bc873SGuochun Huang 		mdelay(p->delay.enable);
252*ab3bc873SGuochun Huang 
253*ab3bc873SGuochun Huang 
254*ab3bc873SGuochun Huang 	if (p->backlight)
255*ab3bc873SGuochun Huang 		backlight_enable(p->backlight);
256*ab3bc873SGuochun Huang }
257*ab3bc873SGuochun Huang 
rk628_panel_unprepare(struct rk628 * rk628)258*ab3bc873SGuochun Huang void rk628_panel_unprepare(struct rk628 *rk628)
259*ab3bc873SGuochun Huang {
260*ab3bc873SGuochun Huang 	struct rk628_panel_simple *p = rk628->panel;
261*ab3bc873SGuochun Huang 
262*ab3bc873SGuochun Huang 	if (!p)
263*ab3bc873SGuochun Huang 		return;
264*ab3bc873SGuochun Huang 
265*ab3bc873SGuochun Huang 	if (dm_gpio_is_valid(&p->reset_gpio))
266*ab3bc873SGuochun Huang 		dm_gpio_set_value(&p->reset_gpio, 1);
267*ab3bc873SGuochun Huang 
268*ab3bc873SGuochun Huang 	if (dm_gpio_is_valid(&p->enable_gpio))
269*ab3bc873SGuochun Huang 		dm_gpio_set_value(&p->enable_gpio, 0);
270*ab3bc873SGuochun Huang 
271*ab3bc873SGuochun Huang 	if (rk628->panel->supply)
272*ab3bc873SGuochun Huang 		regulator_set_enable(p->supply, 0);
273*ab3bc873SGuochun Huang 
274*ab3bc873SGuochun Huang 	if (p->delay.unprepare)
275*ab3bc873SGuochun Huang 		mdelay(p->delay.unprepare);
276*ab3bc873SGuochun Huang }
277*ab3bc873SGuochun Huang 
rk628_panel_disable(struct rk628 * rk628)278*ab3bc873SGuochun Huang void rk628_panel_disable(struct rk628 *rk628)
279*ab3bc873SGuochun Huang {
280*ab3bc873SGuochun Huang 	struct rk628_panel_simple *p = rk628->panel;
281*ab3bc873SGuochun Huang 
282*ab3bc873SGuochun Huang 	if (!p)
283*ab3bc873SGuochun Huang 		return;
284*ab3bc873SGuochun Huang 
285*ab3bc873SGuochun Huang 	if (p->backlight)
286*ab3bc873SGuochun Huang 		backlight_disable(p->backlight);
287*ab3bc873SGuochun Huang 
288*ab3bc873SGuochun Huang 	if (p->delay.disable)
289*ab3bc873SGuochun Huang 		mdelay(p->delay.disable);
290*ab3bc873SGuochun Huang }
291