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