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