1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * (C) Copyright 2023 Rockchip Electronics Co., Ltd
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <asm/io.h>
9*4882a593Smuzhiyun #include <malloc.h>
10*4882a593Smuzhiyun #include <mp_boot.h>
11*4882a593Smuzhiyun #include <spl.h>
12*4882a593Smuzhiyun #include <part.h>
13*4882a593Smuzhiyun #include <drm_modes.h>
14*4882a593Smuzhiyun #include <spl_display.h>
15*4882a593Smuzhiyun #include <linux/hdmi.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include "rockchip_display.h"
18*4882a593Smuzhiyun #include "rockchip_crtc.h"
19*4882a593Smuzhiyun #include "rockchip_connector.h"
20*4882a593Smuzhiyun #include "rockchip_phy.h"
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun static struct base2_info base_parameter;
23*4882a593Smuzhiyun
rockchip_spl_display_drv_probe(void)24*4882a593Smuzhiyun struct display_state *rockchip_spl_display_drv_probe(void)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun struct display_state *state = malloc(sizeof(struct display_state));
27*4882a593Smuzhiyun if (!state)
28*4882a593Smuzhiyun return NULL;
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun memset(state, 0, sizeof(*state));
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun rockchip_spl_vop_probe(&state->crtc_state);
33*4882a593Smuzhiyun rockchip_spl_dw_hdmi_probe(&state->conn_state);
34*4882a593Smuzhiyun inno_spl_hdmi_phy_probe(state);
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun return state;
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun
rockchip_spl_display_init(struct display_state * state)39*4882a593Smuzhiyun static int rockchip_spl_display_init(struct display_state *state)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun struct crtc_state *crtc_state = &state->crtc_state;
42*4882a593Smuzhiyun struct connector_state *conn_state = &state->conn_state;
43*4882a593Smuzhiyun struct rockchip_connector *conn = conn_state->connector;
44*4882a593Smuzhiyun const struct rockchip_crtc *crtc = crtc_state->crtc;
45*4882a593Smuzhiyun const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs;
46*4882a593Smuzhiyun const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
47*4882a593Smuzhiyun struct drm_display_mode *mode = &state->conn_state.mode;
48*4882a593Smuzhiyun int ret = 0;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun if (!crtc_funcs) {
51*4882a593Smuzhiyun printf("failed to find crtc functions\n");
52*4882a593Smuzhiyun return -ENXIO;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun if (crtc_funcs->preinit) {
56*4882a593Smuzhiyun ret = crtc_funcs->preinit(state);
57*4882a593Smuzhiyun if (ret)
58*4882a593Smuzhiyun return ret;
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun rockchip_display_make_crc32_table();
62*4882a593Smuzhiyun if (conn_funcs->pre_init) {
63*4882a593Smuzhiyun ret = conn_funcs->pre_init(conn, state);
64*4882a593Smuzhiyun if (ret)
65*4882a593Smuzhiyun return ret;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun if (conn_funcs->init) {
69*4882a593Smuzhiyun ret = conn_funcs->init(conn, state);
70*4882a593Smuzhiyun if (ret)
71*4882a593Smuzhiyun goto deinit;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun if (conn->phy)
75*4882a593Smuzhiyun rockchip_phy_init(conn->phy);
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun if (conn_funcs->detect) {
78*4882a593Smuzhiyun conn->hpd = conn_funcs->detect(conn, state);
79*4882a593Smuzhiyun if (!conn->hpd)
80*4882a593Smuzhiyun goto deinit;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun if (conn_funcs->get_timing) {
84*4882a593Smuzhiyun ret = conn_funcs->get_timing(conn, state);
85*4882a593Smuzhiyun if (ret)
86*4882a593Smuzhiyun goto deinit;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
90*4882a593Smuzhiyun if (crtc_funcs->init) {
91*4882a593Smuzhiyun ret = crtc_funcs->init(state);
92*4882a593Smuzhiyun if (ret)
93*4882a593Smuzhiyun goto deinit;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun deinit:
99*4882a593Smuzhiyun rockchip_connector_deinit(state);
100*4882a593Smuzhiyun return ret;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
rockchip_spl_display_post_enable(struct display_state * state)103*4882a593Smuzhiyun static int rockchip_spl_display_post_enable(struct display_state *state)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct crtc_state *crtc_state = &state->crtc_state;
106*4882a593Smuzhiyun struct connector_state *conn_state = &state->conn_state;
107*4882a593Smuzhiyun struct rockchip_connector *conn = conn_state->connector;
108*4882a593Smuzhiyun const struct rockchip_crtc *crtc = crtc_state->crtc;
109*4882a593Smuzhiyun const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs;
110*4882a593Smuzhiyun const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun if (crtc_funcs->enable)
113*4882a593Smuzhiyun crtc_funcs->enable(state);
114*4882a593Smuzhiyun state->crtc_state.crtc->active = true;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (conn_funcs->enable)
117*4882a593Smuzhiyun conn_funcs->enable(conn, state);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun return 0;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
rockchip_spl_display_transmit_info_to_uboot(struct display_state * state)122*4882a593Smuzhiyun static void rockchip_spl_display_transmit_info_to_uboot(struct display_state *state)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun struct connector_state *conn_state = &state->conn_state;
125*4882a593Smuzhiyun struct spl_display_info *spl_disp_info = (struct spl_display_info *)CONFIG_SPL_VIDEO_BUF;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun /* transmit mode and bus_format to uboot */
128*4882a593Smuzhiyun memcpy(&spl_disp_info->mode, &conn_state->mode, sizeof(conn_state->mode));
129*4882a593Smuzhiyun spl_disp_info->bus_format = state->conn_state.bus_format;
130*4882a593Smuzhiyun spl_disp_info->enabled = 1;
131*4882a593Smuzhiyun flush_dcache_all();
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
spl_init_display(struct task_data * data)134*4882a593Smuzhiyun int spl_init_display(struct task_data *data)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun struct display_state *state = NULL;
137*4882a593Smuzhiyun struct drm_display_mode *mode;
138*4882a593Smuzhiyun int ret = 0;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun state = rockchip_spl_display_drv_probe();
141*4882a593Smuzhiyun if (!state) {
142*4882a593Smuzhiyun printf("rockchip_spl_display_drv_probe failed\n");
143*4882a593Smuzhiyun return -1;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun ret = rockchip_spl_display_init(state);
147*4882a593Smuzhiyun if (ret) {
148*4882a593Smuzhiyun printf("rockchip_spl_display_init failed ret:%d\n", ret);
149*4882a593Smuzhiyun return -1;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun if (!state->conn_state.connector->hpd) {
153*4882a593Smuzhiyun printf("HDMI is unplug and exit\n");
154*4882a593Smuzhiyun return 0;
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun ret = rockchip_spl_display_post_enable(state);
158*4882a593Smuzhiyun if (ret) {
159*4882a593Smuzhiyun printf("rockchip_spl_display_post_enable failed ret:%d\n", ret);
160*4882a593Smuzhiyun return -1;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun rockchip_spl_display_transmit_info_to_uboot(state);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun mode = &state->conn_state.mode;
166*4882a593Smuzhiyun printf("SPL enable hdmi, detailed mode clock %u kHz, flags[%x]\n"
167*4882a593Smuzhiyun " H: %04d %04d %04d %04d\n"
168*4882a593Smuzhiyun " V: %04d %04d %04d %04d\n"
169*4882a593Smuzhiyun "bus_format: %x\n",
170*4882a593Smuzhiyun mode->clock, mode->flags,
171*4882a593Smuzhiyun mode->hdisplay, mode->hsync_start,
172*4882a593Smuzhiyun mode->hsync_end, mode->htotal,
173*4882a593Smuzhiyun mode->vdisplay, mode->vsync_start,
174*4882a593Smuzhiyun mode->vsync_end, mode->vtotal,
175*4882a593Smuzhiyun state->conn_state.bus_format);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun return ret;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun
rockchip_get_disp_info(int type,int id)180*4882a593Smuzhiyun struct base2_disp_info *rockchip_get_disp_info(int type, int id)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun struct base2_disp_info *disp_info;
183*4882a593Smuzhiyun struct base2_disp_header *disp_header;
184*4882a593Smuzhiyun int i = 0, offset = -1;
185*4882a593Smuzhiyun u32 crc_val;
186*4882a593Smuzhiyun u32 base2_length;
187*4882a593Smuzhiyun void *base_parameter_addr = (void *)&base_parameter;
188*4882a593Smuzhiyun #ifdef CONFIG_MP_BOOT
189*4882a593Smuzhiyun void *bp_addr = (void *)CONFIG_SPL_VIDEO_BUF;
190*4882a593Smuzhiyun ulong ret;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun /* make sure the baseparameter is ready */
193*4882a593Smuzhiyun ret = mpb_post(6);
194*4882a593Smuzhiyun printf("SPL read baseparameter %s\n", ret < 0 ? "failed" : "success");
195*4882a593Smuzhiyun memcpy(&base_parameter, bp_addr, sizeof(base_parameter));
196*4882a593Smuzhiyun #endif
197*4882a593Smuzhiyun for (i = 0; i < 8; i++) {
198*4882a593Smuzhiyun disp_header = &base_parameter.disp_header[i];
199*4882a593Smuzhiyun if (disp_header->connector_type == type &&
200*4882a593Smuzhiyun disp_header->connector_id == id) {
201*4882a593Smuzhiyun printf("disp info %d, type:%d, id:%d\n", i, type, id);
202*4882a593Smuzhiyun offset = disp_header->offset;
203*4882a593Smuzhiyun break;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun if (offset < 0)
208*4882a593Smuzhiyun return NULL;
209*4882a593Smuzhiyun disp_info = base_parameter_addr + offset;
210*4882a593Smuzhiyun if (disp_info->screen_info[0].type != type ||
211*4882a593Smuzhiyun disp_info->screen_info[0].id != id) {
212*4882a593Smuzhiyun printf("base2_disp_info couldn't be found, screen_info type[%d] or id[%d] mismatched\n",
213*4882a593Smuzhiyun disp_info->screen_info[0].type,
214*4882a593Smuzhiyun disp_info->screen_info[0].id);
215*4882a593Smuzhiyun return NULL;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun if (strncasecmp(disp_info->disp_head_flag, "DISP", 4))
219*4882a593Smuzhiyun return NULL;
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun if (base_parameter.major_version == 3 && base_parameter.minor_version == 0) {
222*4882a593Smuzhiyun crc_val = rockchip_display_crc32c_cal((unsigned char *)disp_info,
223*4882a593Smuzhiyun sizeof(struct base2_disp_info) - 4);
224*4882a593Smuzhiyun if (crc_val != disp_info->crc2) {
225*4882a593Smuzhiyun printf("error: connector type[%d], id[%d] disp info crc2 check error\n",
226*4882a593Smuzhiyun type, id);
227*4882a593Smuzhiyun return NULL;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun } else {
230*4882a593Smuzhiyun base2_length = sizeof(struct base2_disp_info) - sizeof(struct csc_info) -
231*4882a593Smuzhiyun sizeof(struct acm_data) - 10 * 1024 - 4;
232*4882a593Smuzhiyun crc_val = rockchip_display_crc32c_cal((unsigned char *)disp_info, base2_length - 4);
233*4882a593Smuzhiyun if (crc_val != disp_info->crc) {
234*4882a593Smuzhiyun printf("error: connector type[%d], id[%d] disp info crc check error\n",
235*4882a593Smuzhiyun type, id);
236*4882a593Smuzhiyun return NULL;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun return disp_info;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun
spl_load_baseparamter(struct task_data * data)243*4882a593Smuzhiyun int spl_load_baseparamter(struct task_data *data)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun struct spl_load_info *info = &data->info;
246*4882a593Smuzhiyun ulong addr = CONFIG_SPL_VIDEO_BUF;
247*4882a593Smuzhiyun disk_partition_t part;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun debug("== Baseparam: start\n");
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun if (part_get_info_by_name(info->dev, "baseparameter", &part) < 0) {
252*4882a593Smuzhiyun printf("No baseparameter partition\n");
253*4882a593Smuzhiyun return -ENOENT;
254*4882a593Smuzhiyun } else {
255*4882a593Smuzhiyun if (info->read(info, part.start, part.size, (void *)addr) != part.size)
256*4882a593Smuzhiyun return -EIO;
257*4882a593Smuzhiyun else
258*4882a593Smuzhiyun flush_dcache_range(addr, addr + part.size * info->bl_len);
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun debug("== Baseparam: load OK\n");
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun return 0;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
266