1690e9ed1SSandy Huang /*
2690e9ed1SSandy Huang * (C) Copyright 2023 Rockchip Electronics Co., Ltd
3690e9ed1SSandy Huang *
4690e9ed1SSandy Huang * SPDX-License-Identifier: GPL-2.0+
5690e9ed1SSandy Huang */
6690e9ed1SSandy Huang
7690e9ed1SSandy Huang #include <common.h>
8690e9ed1SSandy Huang #include <asm/io.h>
9690e9ed1SSandy Huang #include <malloc.h>
10690e9ed1SSandy Huang #include <mp_boot.h>
11690e9ed1SSandy Huang #include <spl.h>
12690e9ed1SSandy Huang #include <part.h>
13690e9ed1SSandy Huang #include <drm_modes.h>
14690e9ed1SSandy Huang #include <spl_display.h>
15690e9ed1SSandy Huang #include <linux/hdmi.h>
16690e9ed1SSandy Huang
17690e9ed1SSandy Huang #include "rockchip_display.h"
18690e9ed1SSandy Huang #include "rockchip_crtc.h"
19690e9ed1SSandy Huang #include "rockchip_connector.h"
20690e9ed1SSandy Huang #include "rockchip_phy.h"
21690e9ed1SSandy Huang
22690e9ed1SSandy Huang static struct base2_info base_parameter;
23690e9ed1SSandy Huang
rockchip_spl_display_drv_probe(void)24690e9ed1SSandy Huang struct display_state *rockchip_spl_display_drv_probe(void)
25690e9ed1SSandy Huang {
26690e9ed1SSandy Huang struct display_state *state = malloc(sizeof(struct display_state));
27690e9ed1SSandy Huang if (!state)
28690e9ed1SSandy Huang return NULL;
29690e9ed1SSandy Huang
30690e9ed1SSandy Huang memset(state, 0, sizeof(*state));
31690e9ed1SSandy Huang
32690e9ed1SSandy Huang rockchip_spl_vop_probe(&state->crtc_state);
33690e9ed1SSandy Huang rockchip_spl_dw_hdmi_probe(&state->conn_state);
34690e9ed1SSandy Huang inno_spl_hdmi_phy_probe(state);
35690e9ed1SSandy Huang
36690e9ed1SSandy Huang return state;
37690e9ed1SSandy Huang }
38690e9ed1SSandy Huang
rockchip_spl_display_init(struct display_state * state)39690e9ed1SSandy Huang static int rockchip_spl_display_init(struct display_state *state)
40690e9ed1SSandy Huang {
41690e9ed1SSandy Huang struct crtc_state *crtc_state = &state->crtc_state;
42690e9ed1SSandy Huang struct connector_state *conn_state = &state->conn_state;
43690e9ed1SSandy Huang struct rockchip_connector *conn = conn_state->connector;
44690e9ed1SSandy Huang const struct rockchip_crtc *crtc = crtc_state->crtc;
45690e9ed1SSandy Huang const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs;
46690e9ed1SSandy Huang const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
47690e9ed1SSandy Huang struct drm_display_mode *mode = &state->conn_state.mode;
48690e9ed1SSandy Huang int ret = 0;
49690e9ed1SSandy Huang
50690e9ed1SSandy Huang if (!crtc_funcs) {
51690e9ed1SSandy Huang printf("failed to find crtc functions\n");
52690e9ed1SSandy Huang return -ENXIO;
53690e9ed1SSandy Huang }
54690e9ed1SSandy Huang
55690e9ed1SSandy Huang if (crtc_funcs->preinit) {
56690e9ed1SSandy Huang ret = crtc_funcs->preinit(state);
57690e9ed1SSandy Huang if (ret)
58690e9ed1SSandy Huang return ret;
59690e9ed1SSandy Huang }
60690e9ed1SSandy Huang
61690e9ed1SSandy Huang rockchip_display_make_crc32_table();
62690e9ed1SSandy Huang if (conn_funcs->pre_init) {
63690e9ed1SSandy Huang ret = conn_funcs->pre_init(conn, state);
64690e9ed1SSandy Huang if (ret)
65690e9ed1SSandy Huang return ret;
66690e9ed1SSandy Huang }
67690e9ed1SSandy Huang
68690e9ed1SSandy Huang if (conn_funcs->init) {
69690e9ed1SSandy Huang ret = conn_funcs->init(conn, state);
70690e9ed1SSandy Huang if (ret)
71690e9ed1SSandy Huang goto deinit;
72690e9ed1SSandy Huang }
73690e9ed1SSandy Huang
74690e9ed1SSandy Huang if (conn->phy)
75690e9ed1SSandy Huang rockchip_phy_init(conn->phy);
76690e9ed1SSandy Huang
77690e9ed1SSandy Huang if (conn_funcs->detect) {
78690e9ed1SSandy Huang conn->hpd = conn_funcs->detect(conn, state);
79690e9ed1SSandy Huang if (!conn->hpd)
80690e9ed1SSandy Huang goto deinit;
81690e9ed1SSandy Huang }
82690e9ed1SSandy Huang
83690e9ed1SSandy Huang if (conn_funcs->get_timing) {
84690e9ed1SSandy Huang ret = conn_funcs->get_timing(conn, state);
85690e9ed1SSandy Huang if (ret)
86690e9ed1SSandy Huang goto deinit;
87690e9ed1SSandy Huang }
88690e9ed1SSandy Huang
89690e9ed1SSandy Huang drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
90*75f2890aSDamon Ding if (p->flags & DRM_MODE_FLAG_DBLCLK)
91*75f2890aSDamon Ding p->crtc_clock = 2 * p->clock;
92*75f2890aSDamon Ding
93690e9ed1SSandy Huang if (crtc_funcs->init) {
94690e9ed1SSandy Huang ret = crtc_funcs->init(state);
95690e9ed1SSandy Huang if (ret)
96690e9ed1SSandy Huang goto deinit;
97690e9ed1SSandy Huang }
98690e9ed1SSandy Huang
99690e9ed1SSandy Huang return 0;
100690e9ed1SSandy Huang
101690e9ed1SSandy Huang deinit:
102690e9ed1SSandy Huang rockchip_connector_deinit(state);
103690e9ed1SSandy Huang return ret;
104690e9ed1SSandy Huang }
105690e9ed1SSandy Huang
rockchip_spl_display_post_enable(struct display_state * state)106690e9ed1SSandy Huang static int rockchip_spl_display_post_enable(struct display_state *state)
107690e9ed1SSandy Huang {
108690e9ed1SSandy Huang struct crtc_state *crtc_state = &state->crtc_state;
109690e9ed1SSandy Huang struct connector_state *conn_state = &state->conn_state;
110690e9ed1SSandy Huang struct rockchip_connector *conn = conn_state->connector;
111690e9ed1SSandy Huang const struct rockchip_crtc *crtc = crtc_state->crtc;
112690e9ed1SSandy Huang const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs;
113690e9ed1SSandy Huang const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
114690e9ed1SSandy Huang
115690e9ed1SSandy Huang if (crtc_funcs->enable)
116690e9ed1SSandy Huang crtc_funcs->enable(state);
117690e9ed1SSandy Huang state->crtc_state.crtc->active = true;
118690e9ed1SSandy Huang
119690e9ed1SSandy Huang if (conn_funcs->enable)
120690e9ed1SSandy Huang conn_funcs->enable(conn, state);
121690e9ed1SSandy Huang
122690e9ed1SSandy Huang return 0;
123690e9ed1SSandy Huang }
124690e9ed1SSandy Huang
rockchip_spl_display_transmit_info_to_uboot(struct display_state * state)125690e9ed1SSandy Huang static void rockchip_spl_display_transmit_info_to_uboot(struct display_state *state)
126690e9ed1SSandy Huang {
127690e9ed1SSandy Huang struct connector_state *conn_state = &state->conn_state;
128690e9ed1SSandy Huang struct spl_display_info *spl_disp_info = (struct spl_display_info *)CONFIG_SPL_VIDEO_BUF;
129690e9ed1SSandy Huang
130690e9ed1SSandy Huang /* transmit mode and bus_format to uboot */
131690e9ed1SSandy Huang memcpy(&spl_disp_info->mode, &conn_state->mode, sizeof(conn_state->mode));
132690e9ed1SSandy Huang spl_disp_info->bus_format = state->conn_state.bus_format;
133690e9ed1SSandy Huang spl_disp_info->enabled = 1;
134690e9ed1SSandy Huang flush_dcache_all();
135690e9ed1SSandy Huang }
136690e9ed1SSandy Huang
spl_init_display(struct task_data * data)137690e9ed1SSandy Huang int spl_init_display(struct task_data *data)
138690e9ed1SSandy Huang {
139690e9ed1SSandy Huang struct display_state *state = NULL;
140690e9ed1SSandy Huang struct drm_display_mode *mode;
141690e9ed1SSandy Huang int ret = 0;
142690e9ed1SSandy Huang
143690e9ed1SSandy Huang state = rockchip_spl_display_drv_probe();
144690e9ed1SSandy Huang if (!state) {
145690e9ed1SSandy Huang printf("rockchip_spl_display_drv_probe failed\n");
146690e9ed1SSandy Huang return -1;
147690e9ed1SSandy Huang }
148690e9ed1SSandy Huang
149690e9ed1SSandy Huang ret = rockchip_spl_display_init(state);
150690e9ed1SSandy Huang if (ret) {
151690e9ed1SSandy Huang printf("rockchip_spl_display_init failed ret:%d\n", ret);
152690e9ed1SSandy Huang return -1;
153690e9ed1SSandy Huang }
154690e9ed1SSandy Huang
155690e9ed1SSandy Huang if (!state->conn_state.connector->hpd) {
156690e9ed1SSandy Huang printf("HDMI is unplug and exit\n");
157690e9ed1SSandy Huang return 0;
158690e9ed1SSandy Huang }
159690e9ed1SSandy Huang
160690e9ed1SSandy Huang ret = rockchip_spl_display_post_enable(state);
161690e9ed1SSandy Huang if (ret) {
162690e9ed1SSandy Huang printf("rockchip_spl_display_post_enable failed ret:%d\n", ret);
163690e9ed1SSandy Huang return -1;
164690e9ed1SSandy Huang }
165690e9ed1SSandy Huang
166690e9ed1SSandy Huang rockchip_spl_display_transmit_info_to_uboot(state);
167690e9ed1SSandy Huang
168690e9ed1SSandy Huang mode = &state->conn_state.mode;
169690e9ed1SSandy Huang printf("SPL enable hdmi, detailed mode clock %u kHz, flags[%x]\n"
170690e9ed1SSandy Huang " H: %04d %04d %04d %04d\n"
171690e9ed1SSandy Huang " V: %04d %04d %04d %04d\n"
172690e9ed1SSandy Huang "bus_format: %x\n",
173690e9ed1SSandy Huang mode->clock, mode->flags,
174690e9ed1SSandy Huang mode->hdisplay, mode->hsync_start,
175690e9ed1SSandy Huang mode->hsync_end, mode->htotal,
176690e9ed1SSandy Huang mode->vdisplay, mode->vsync_start,
177690e9ed1SSandy Huang mode->vsync_end, mode->vtotal,
178690e9ed1SSandy Huang state->conn_state.bus_format);
179690e9ed1SSandy Huang
180690e9ed1SSandy Huang return ret;
181690e9ed1SSandy Huang }
182690e9ed1SSandy Huang
rockchip_get_disp_info(int type,int id)183690e9ed1SSandy Huang struct base2_disp_info *rockchip_get_disp_info(int type, int id)
184690e9ed1SSandy Huang {
185690e9ed1SSandy Huang struct base2_disp_info *disp_info;
186690e9ed1SSandy Huang struct base2_disp_header *disp_header;
187690e9ed1SSandy Huang int i = 0, offset = -1;
188690e9ed1SSandy Huang u32 crc_val;
189690e9ed1SSandy Huang u32 base2_length;
190690e9ed1SSandy Huang void *base_parameter_addr = (void *)&base_parameter;
191690e9ed1SSandy Huang #ifdef CONFIG_MP_BOOT
192690e9ed1SSandy Huang void *bp_addr = (void *)CONFIG_SPL_VIDEO_BUF;
193690e9ed1SSandy Huang ulong ret;
194690e9ed1SSandy Huang
195690e9ed1SSandy Huang /* make sure the baseparameter is ready */
196690e9ed1SSandy Huang ret = mpb_post(6);
197690e9ed1SSandy Huang printf("SPL read baseparameter %s\n", ret < 0 ? "failed" : "success");
198690e9ed1SSandy Huang memcpy(&base_parameter, bp_addr, sizeof(base_parameter));
199690e9ed1SSandy Huang #endif
200690e9ed1SSandy Huang for (i = 0; i < 8; i++) {
201690e9ed1SSandy Huang disp_header = &base_parameter.disp_header[i];
202690e9ed1SSandy Huang if (disp_header->connector_type == type &&
203690e9ed1SSandy Huang disp_header->connector_id == id) {
204690e9ed1SSandy Huang printf("disp info %d, type:%d, id:%d\n", i, type, id);
205690e9ed1SSandy Huang offset = disp_header->offset;
206690e9ed1SSandy Huang break;
207690e9ed1SSandy Huang }
208690e9ed1SSandy Huang }
209690e9ed1SSandy Huang
210690e9ed1SSandy Huang if (offset < 0)
211690e9ed1SSandy Huang return NULL;
212690e9ed1SSandy Huang disp_info = base_parameter_addr + offset;
213690e9ed1SSandy Huang if (disp_info->screen_info[0].type != type ||
214690e9ed1SSandy Huang disp_info->screen_info[0].id != id) {
215690e9ed1SSandy Huang printf("base2_disp_info couldn't be found, screen_info type[%d] or id[%d] mismatched\n",
216690e9ed1SSandy Huang disp_info->screen_info[0].type,
217690e9ed1SSandy Huang disp_info->screen_info[0].id);
218690e9ed1SSandy Huang return NULL;
219690e9ed1SSandy Huang }
220690e9ed1SSandy Huang
221690e9ed1SSandy Huang if (strncasecmp(disp_info->disp_head_flag, "DISP", 4))
222690e9ed1SSandy Huang return NULL;
223690e9ed1SSandy Huang
224690e9ed1SSandy Huang if (base_parameter.major_version == 3 && base_parameter.minor_version == 0) {
225690e9ed1SSandy Huang crc_val = rockchip_display_crc32c_cal((unsigned char *)disp_info,
226690e9ed1SSandy Huang sizeof(struct base2_disp_info) - 4);
227690e9ed1SSandy Huang if (crc_val != disp_info->crc2) {
228690e9ed1SSandy Huang printf("error: connector type[%d], id[%d] disp info crc2 check error\n",
229690e9ed1SSandy Huang type, id);
230690e9ed1SSandy Huang return NULL;
231690e9ed1SSandy Huang }
232690e9ed1SSandy Huang } else {
233690e9ed1SSandy Huang base2_length = sizeof(struct base2_disp_info) - sizeof(struct csc_info) -
234690e9ed1SSandy Huang sizeof(struct acm_data) - 10 * 1024 - 4;
235690e9ed1SSandy Huang crc_val = rockchip_display_crc32c_cal((unsigned char *)disp_info, base2_length - 4);
236690e9ed1SSandy Huang if (crc_val != disp_info->crc) {
237690e9ed1SSandy Huang printf("error: connector type[%d], id[%d] disp info crc check error\n",
238690e9ed1SSandy Huang type, id);
239690e9ed1SSandy Huang return NULL;
240690e9ed1SSandy Huang }
241690e9ed1SSandy Huang }
242690e9ed1SSandy Huang
243690e9ed1SSandy Huang return disp_info;
244690e9ed1SSandy Huang }
245690e9ed1SSandy Huang
spl_load_baseparamter(struct task_data * data)246690e9ed1SSandy Huang int spl_load_baseparamter(struct task_data *data)
247690e9ed1SSandy Huang {
248690e9ed1SSandy Huang struct spl_load_info *info = &data->info;
249690e9ed1SSandy Huang ulong addr = CONFIG_SPL_VIDEO_BUF;
250690e9ed1SSandy Huang disk_partition_t part;
251690e9ed1SSandy Huang
252690e9ed1SSandy Huang debug("== Baseparam: start\n");
253690e9ed1SSandy Huang
254690e9ed1SSandy Huang if (part_get_info_by_name(info->dev, "baseparameter", &part) < 0) {
255690e9ed1SSandy Huang printf("No baseparameter partition\n");
256690e9ed1SSandy Huang return -ENOENT;
257690e9ed1SSandy Huang } else {
258690e9ed1SSandy Huang if (info->read(info, part.start, part.size, (void *)addr) != part.size)
259690e9ed1SSandy Huang return -EIO;
260690e9ed1SSandy Huang else
261690e9ed1SSandy Huang flush_dcache_range(addr, addr + part.size * info->bl_len);
262690e9ed1SSandy Huang }
263690e9ed1SSandy Huang
264690e9ed1SSandy Huang debug("== Baseparam: load OK\n");
265690e9ed1SSandy Huang
266690e9ed1SSandy Huang return 0;
267690e9ed1SSandy Huang }
268690e9ed1SSandy Huang
269