diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/ast/ast_mode.c | 204 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_reg.h | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/display/drm_dp_helper.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/display/drm_dp_mst_topology.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/display/drm_dsc_helper.c | 91 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_managed.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_drv.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_mode.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_reg.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c | 1088 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-simple.c | 27 | ||||
-rw-r--r-- | drivers/gpu/drm/panfrost/panfrost_drv.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/panthor/panthor_sched.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 279 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/lvds.c | 1226 | ||||
-rw-r--r-- | drivers/gpu/drm/tests/drm_cmdline_parser_test.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/tests/drm_connector_test.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/tests/drm_modes_test.c | 31 |
20 files changed, 2455 insertions, 559 deletions
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 2fd9c78eab73..dc8f639e82fd 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -303,7 +303,7 @@ static void ast_set_std_reg(struct ast_device *ast, /* Set SEQ; except Screen Disable field */ ast_set_index_reg(ast, AST_IO_VGASRI, 0x00, 0x03); - ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, stdtable->seq[0]); + ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0x20, stdtable->seq[0]); for (i = 1; i < 4; i++) { jreg = stdtable->seq[i]; ast_set_index_reg(ast, AST_IO_VGASRI, (i + 1), jreg); @@ -649,12 +649,12 @@ static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); struct drm_framebuffer *old_fb = old_plane_state->fb; struct ast_plane *ast_plane = to_ast_plane(plane); + struct drm_crtc *crtc = plane_state->crtc; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); struct drm_rect damage; struct drm_atomic_helper_damage_iter iter; - if (!old_fb || (fb->format != old_fb->format)) { - struct drm_crtc *crtc = plane_state->crtc; - struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!old_fb || (fb->format != old_fb->format) || crtc_state->mode_changed) { struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info; @@ -690,15 +690,15 @@ static void ast_primary_plane_helper_atomic_enable(struct drm_plane *plane, * Therefore only reprogram the address after enabling the plane. */ ast_set_start_address_crt1(ast, (u32)ast_plane->offset); - ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x1, 0xdf, 0x00); } static void ast_primary_plane_helper_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *state) { - struct ast_device *ast = to_ast_device(plane->dev); - - ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x1, 0xdf, 0x20); + /* + * Keep this empty function to avoid calling + * atomic_update when disabling the plane. + */ } static int ast_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane, @@ -1019,62 +1019,6 @@ static int ast_cursor_plane_init(struct ast_device *ast) * CRTC */ -static void ast_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct ast_device *ast = to_ast_device(crtc->dev); - u8 ch = AST_DPMS_VSYNC_OFF | AST_DPMS_HSYNC_OFF; - struct ast_crtc_state *ast_state; - const struct drm_format_info *format; - struct ast_vbios_mode_info *vbios_mode_info; - - /* TODO: Maybe control display signal generation with - * Sync Enable (bit CR17.7). - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, 0); - ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, 0); - if (ast->tx_chip_types & AST_TX_DP501_BIT) - ast_set_dp501_video_output(crtc->dev, 1); - - if (ast->tx_chip_types & AST_TX_ASTDP_BIT) { - ast_dp_power_on_off(crtc->dev, AST_DP_POWER_ON); - ast_wait_for_vretrace(ast); - ast_dp_set_on_off(crtc->dev, 1); - } - - ast_state = to_ast_crtc_state(crtc->state); - format = ast_state->format; - - if (format) { - vbios_mode_info = &ast_state->vbios_mode_info; - - ast_set_color_reg(ast, format); - ast_set_vbios_color_reg(ast, format, vbios_mode_info); - if (crtc->state->gamma_lut) - ast_crtc_set_gamma(ast, format, crtc->state->gamma_lut->data); - else - ast_crtc_set_gamma_linear(ast, format); - } - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - ch = mode; - if (ast->tx_chip_types & AST_TX_DP501_BIT) - ast_set_dp501_video_output(crtc->dev, 0); - - if (ast->tx_chip_types & AST_TX_ASTDP_BIT) { - ast_dp_set_on_off(crtc->dev, 0); - ast_dp_power_on_off(crtc->dev, AST_DP_POWER_OFF); - } - - ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, 0x20); - ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, ch); - break; - } -} - static enum drm_mode_status ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) { @@ -1147,6 +1091,33 @@ ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode return status; } +static void ast_crtc_helper_mode_set_nofb(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct ast_device *ast = to_ast_device(dev); + struct drm_crtc_state *crtc_state = crtc->state; + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); + struct ast_vbios_mode_info *vbios_mode_info = + &ast_crtc_state->vbios_mode_info; + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + + /* + * Ensure that no scanout takes place before reprogramming mode + * and format registers. + * + * TODO: Get vblank interrupts working and remove this line. + */ + ast_wait_for_vretrace(ast); + + ast_set_vbios_mode_reg(ast, adjusted_mode, vbios_mode_info); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xa1, 0x06); + ast_set_std_reg(ast, adjusted_mode, vbios_mode_info); + ast_set_crtc_reg(ast, adjusted_mode, vbios_mode_info); + ast_set_dclk_reg(ast, adjusted_mode, vbios_mode_info); + ast_set_crtthd_reg(ast); + ast_set_sync_reg(ast, adjusted_mode, vbios_mode_info); +} + static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { @@ -1206,7 +1177,6 @@ ast_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct ast_device *ast = to_ast_device(dev); struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); - struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info; /* * The gamma LUT has to be reloaded after changing the primary @@ -1220,40 +1190,27 @@ ast_crtc_helper_atomic_flush(struct drm_crtc *crtc, else ast_crtc_set_gamma_linear(ast, ast_crtc_state->format); } - - //Set Aspeed Display-Port - if (ast->tx_chip_types & AST_TX_ASTDP_BIT) - ast_dp_set_mode(crtc, vbios_mode_info); } static void ast_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { - struct drm_device *dev = crtc->dev; - struct ast_device *ast = to_ast_device(dev); - struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); - struct ast_vbios_mode_info *vbios_mode_info = - &ast_crtc_state->vbios_mode_info; - struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; - - ast_set_vbios_mode_reg(ast, adjusted_mode, vbios_mode_info); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa1, 0x06); - ast_set_std_reg(ast, adjusted_mode, vbios_mode_info); - ast_set_crtc_reg(ast, adjusted_mode, vbios_mode_info); - ast_set_dclk_reg(ast, adjusted_mode, vbios_mode_info); - ast_set_crtthd_reg(ast); - ast_set_sync_reg(ast, adjusted_mode, vbios_mode_info); + struct ast_device *ast = to_ast_device(crtc->dev); - ast_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, 0x00); + ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, 0x00); } static void ast_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); - struct drm_device *dev = crtc->dev; - struct ast_device *ast = to_ast_device(dev); + struct ast_device *ast = to_ast_device(crtc->dev); + u8 vgacrb6; + + ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, AST_IO_VGASR1_SD); - ast_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + vgacrb6 = AST_IO_VGACRB6_VSYNC_OFF | + AST_IO_VGACRB6_HSYNC_OFF; + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, vgacrb6); /* * HW cursors require the underlying primary plane and CRTC to @@ -1266,16 +1223,11 @@ static void ast_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_ato * simple pageflips on the planes. */ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); - - /* - * Ensure that no scanout takes place before reprogramming mode - * and format registers. - */ - ast_wait_for_vretrace(ast); } static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = { .mode_valid = ast_crtc_helper_mode_valid, + .mode_set_nofb = ast_crtc_helper_mode_set_nofb, .atomic_check = ast_crtc_helper_atomic_check, .atomic_flush = ast_crtc_helper_atomic_flush, .atomic_enable = ast_crtc_helper_atomic_enable, @@ -1521,6 +1473,27 @@ static const struct drm_encoder_funcs ast_dp501_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static void ast_dp501_encoder_helper_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_device *dev = encoder->dev; + + ast_set_dp501_video_output(dev, 1); +} + +static void ast_dp501_encoder_helper_atomic_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_device *dev = encoder->dev; + + ast_set_dp501_video_output(dev, 0); +} + +static const struct drm_encoder_helper_funcs ast_dp501_encoder_helper_funcs = { + .atomic_enable = ast_dp501_encoder_helper_atomic_enable, + .atomic_disable = ast_dp501_encoder_helper_atomic_disable, +}; + /* * DP501 Connector */ @@ -1607,6 +1580,8 @@ static int ast_dp501_output_init(struct ast_device *ast) DRM_MODE_ENCODER_TMDS, NULL); if (ret) return ret; + drm_encoder_helper_add(encoder, &ast_dp501_encoder_helper_funcs); + encoder->possible_crtcs = drm_crtc_mask(crtc); ret = ast_dp501_connector_init(dev, connector); @@ -1628,6 +1603,43 @@ static const struct drm_encoder_funcs ast_astdp_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static void ast_astdp_encoder_helper_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_crtc *crtc = crtc_state->crtc; + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); + struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info; + + ast_dp_set_mode(crtc, vbios_mode_info); +} + +static void ast_astdp_encoder_helper_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_device *dev = encoder->dev; + struct ast_device *ast = to_ast_device(dev); + + ast_dp_power_on_off(dev, AST_DP_POWER_ON); + ast_wait_for_vretrace(ast); + ast_dp_set_on_off(dev, 1); +} + +static void ast_astdp_encoder_helper_atomic_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_device *dev = encoder->dev; + + ast_dp_set_on_off(dev, 0); + ast_dp_power_on_off(dev, AST_DP_POWER_OFF); +} + +static const struct drm_encoder_helper_funcs ast_astdp_encoder_helper_funcs = { + .atomic_mode_set = ast_astdp_encoder_helper_atomic_mode_set, + .atomic_enable = ast_astdp_encoder_helper_atomic_enable, + .atomic_disable = ast_astdp_encoder_helper_atomic_disable, +}; + /* * ASPEED Display-Port Connector */ @@ -1726,6 +1738,8 @@ static int ast_astdp_output_init(struct ast_device *ast) DRM_MODE_ENCODER_TMDS, NULL); if (ret) return ret; + drm_encoder_helper_add(encoder, &ast_astdp_encoder_helper_funcs); + encoder->possible_crtcs = drm_crtc_mask(crtc); ret = ast_astdp_connector_init(dev, connector); @@ -1851,7 +1865,7 @@ static void ast_mode_config_helper_atomic_commit_tail(struct drm_atomic_state *s * the I/O-register lock. Released in atomic_flush(). */ mutex_lock(&ast->modeset_lock); - drm_atomic_helper_commit_tail_rpm(state); + drm_atomic_helper_commit_tail(state); mutex_unlock(&ast->modeset_lock); } diff --git a/drivers/gpu/drm/ast/ast_reg.h b/drivers/gpu/drm/ast/ast_reg.h index 62dddbf3fe56..75671d345057 100644 --- a/drivers/gpu/drm/ast/ast_reg.h +++ b/drivers/gpu/drm/ast/ast_reg.h @@ -22,6 +22,7 @@ #define AST_IO_VGAER_VGA_ENABLE BIT(0) #define AST_IO_VGASRI (0x44) +#define AST_IO_VGASR1_SD BIT(5) #define AST_IO_VGADRR (0x47) #define AST_IO_VGADWR (0x48) #define AST_IO_VGAPDR (0x49) @@ -31,6 +32,8 @@ #define AST_IO_VGACR80_PASSWORD (0xa8) #define AST_IO_VGACRA1_VGAIO_DISABLED BIT(1) #define AST_IO_VGACRA1_MMIO_ENABLED BIT(2) +#define AST_IO_VGACRB6_HSYNC_OFF BIT(0) +#define AST_IO_VGACRB6_VSYNC_OFF BIT(1) #define AST_IO_VGACRCB_HWC_16BPP BIT(0) /* set: ARGB4444, cleared: 2bpp palette */ #define AST_IO_VGACRCB_HWC_ENABLED BIT(1) @@ -76,13 +79,6 @@ #define ASTDP_HOST_EDID_READ_DONE_MASK GENMASK(0, 0) /* - * CRB8[b1]: Enable VSYNC off - * CRB8[b0]: Enable HSYNC off - */ -#define AST_DPMS_VSYNC_OFF BIT(1) -#define AST_DPMS_HSYNC_OFF BIT(0) - -/* * CRDF[b4]: Mirror of AST_DP_VIDEO_ENABLE * Precondition: A. ~AST_DP_PHY_SLEEP && * B. DP_HPD && diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 79a615667aab..d4c34f364140 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -35,6 +35,7 @@ #include <drm/display/drm_dp_helper.h> #include <drm/display/drm_dp_mst_helper.h> #include <drm/drm_edid.h> +#include <drm/drm_fixed.h> #include <drm/drm_print.h> #include <drm/drm_vblank.h> #include <drm/drm_panel.h> @@ -4151,9 +4152,9 @@ int drm_dp_bw_overhead(int lane_count, int hactive, int symbol_cycles; if (lane_count == 0 || hactive == 0 || bpp_x16 == 0) { - DRM_DEBUG_KMS("Invalid BW overhead params: lane_count %d, hactive %d, bpp_x16 %d.%04d\n", + DRM_DEBUG_KMS("Invalid BW overhead params: lane_count %d, hactive %d, bpp_x16 " FXP_Q4_FMT "\n", lane_count, hactive, - bpp_x16 >> 4, (bpp_x16 & 0xf) * 625); + FXP_Q4_ARGS(bpp_x16)); return 0; } diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 7f8e1cfbe19d..fc2ceae61db2 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -2929,7 +2929,7 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, /* FIXME: Actually do some real error handling here */ ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); - if (ret <= 0) { + if (ret < 0) { drm_err(mgr->dev, "Sending link address failed with %d\n", ret); goto out; } @@ -2981,7 +2981,7 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, mutex_unlock(&mgr->lock); out: - if (ret <= 0) + if (ret < 0) mstb->link_address_sent = false; kfree(txmsg); return ret < 0 ? ret : changed; @@ -4069,6 +4069,7 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) { const struct drm_dp_connection_status_notify *conn_stat = &up_req->msg.u.conn_stat; + bool handle_csn; drm_dbg_kms(mgr->dev, "Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", conn_stat->port_number, @@ -4077,6 +4078,16 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) conn_stat->message_capability_status, conn_stat->input_port, conn_stat->peer_device_type); + + mutex_lock(&mgr->probe_lock); + handle_csn = mgr->mst_primary->link_address_sent; + mutex_unlock(&mgr->probe_lock); + + if (!handle_csn) { + drm_dbg_kms(mgr->dev, "Got CSN before finish topology probing. Skip it."); + kfree(up_req); + goto out; + } } else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) { const struct drm_dp_resource_status_notify *res_stat = &up_req->msg.u.resource_stat; diff --git a/drivers/gpu/drm/display/drm_dsc_helper.c b/drivers/gpu/drm/display/drm_dsc_helper.c index 4424380c6cb6..6900f4dac520 100644 --- a/drivers/gpu/drm/display/drm_dsc_helper.c +++ b/drivers/gpu/drm/display/drm_dsc_helper.c @@ -14,6 +14,7 @@ #include <drm/display/drm_dp_helper.h> #include <drm/display/drm_dsc_helper.h> +#include <drm/drm_fixed.h> #include <drm/drm_print.h> /** @@ -1472,3 +1473,93 @@ u32 drm_dsc_flatness_det_thresh(const struct drm_dsc_config *dsc) return 2 << (dsc->bits_per_component - 8); } EXPORT_SYMBOL(drm_dsc_flatness_det_thresh); + +static void drm_dsc_dump_config_main_params(struct drm_printer *p, int indent, + const struct drm_dsc_config *cfg) +{ + drm_printf_indent(p, indent, + "dsc-cfg: version: %d.%d, picture: w=%d, h=%d, slice: count=%d, w=%d, h=%d, size=%d\n", + cfg->dsc_version_major, cfg->dsc_version_minor, + cfg->pic_width, cfg->pic_height, + cfg->slice_count, cfg->slice_width, cfg->slice_height, cfg->slice_chunk_size); + drm_printf_indent(p, indent, + "dsc-cfg: mode: block-pred=%s, vbr=%s, rgb=%s, simple-422=%s, native-422=%s, native-420=%s\n", + str_yes_no(cfg->block_pred_enable), str_yes_no(cfg->vbr_enable), + str_yes_no(cfg->convert_rgb), + str_yes_no(cfg->simple_422), str_yes_no(cfg->native_422), str_yes_no(cfg->native_420)); + drm_printf_indent(p, indent, + "dsc-cfg: color-depth: uncompressed-bpc=%d, compressed-bpp=" FXP_Q4_FMT " line-buf-bpp=%d\n", + cfg->bits_per_component, FXP_Q4_ARGS(cfg->bits_per_pixel), cfg->line_buf_depth); + drm_printf_indent(p, indent, + "dsc-cfg: rc-model: size=%d, bits=%d, mux-word-size: %d, initial-delays: xmit=%d, dec=%d\n", + cfg->rc_model_size, cfg->rc_bits, cfg->mux_word_size, + cfg->initial_xmit_delay, cfg->initial_dec_delay); + drm_printf_indent(p, indent, + "dsc-cfg: offsets: initial=%d, final=%d, slice-bpg=%d\n", + cfg->initial_offset, cfg->final_offset, cfg->slice_bpg_offset); + drm_printf_indent(p, indent, + "dsc-cfg: line-bpg-offsets: first=%d, non-first=%d, second=%d, non-second=%d, second-adj=%d\n", + cfg->first_line_bpg_offset, cfg->nfl_bpg_offset, + cfg->second_line_bpg_offset, cfg->nsl_bpg_offset, cfg->second_line_offset_adj); + drm_printf_indent(p, indent, + "dsc-cfg: rc-tgt-offsets: low=%d, high=%d, rc-edge-factor: %d, rc-quant-incr-limits: [0]=%d, [1]=%d\n", + cfg->rc_tgt_offset_low, cfg->rc_tgt_offset_high, + cfg->rc_edge_factor, cfg->rc_quant_incr_limit0, cfg->rc_quant_incr_limit1); + drm_printf_indent(p, indent, + "dsc-cfg: initial-scale: %d, scale-intervals: increment=%d, decrement=%d\n", + cfg->initial_scale_value, cfg->scale_increment_interval, cfg->scale_decrement_interval); + drm_printf_indent(p, indent, + "dsc-cfg: flatness: min-qp=%d, max-qp=%d\n", + cfg->flatness_min_qp, cfg->flatness_max_qp); +} + +static void drm_dsc_dump_config_rc_params(struct drm_printer *p, int indent, + const struct drm_dsc_config *cfg) +{ + const u16 *bt = cfg->rc_buf_thresh; + const struct drm_dsc_rc_range_parameters *rp = cfg->rc_range_params; + + BUILD_BUG_ON(ARRAY_SIZE(cfg->rc_buf_thresh) != 14); + BUILD_BUG_ON(ARRAY_SIZE(cfg->rc_range_params) != 15); + + drm_printf_indent(p, indent, + "dsc-cfg: rc-level: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14\n"); + drm_printf_indent(p, indent, + "dsc-cfg: rc-buf-thresh: %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d\n", + bt[0], bt[1], bt[2], bt[3], bt[4], bt[5], bt[6], bt[7], + bt[8], bt[9], bt[10], bt[11], bt[12], bt[13]); + drm_printf_indent(p, indent, + "dsc-cfg: rc-min-qp: %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d\n", + rp[0].range_min_qp, rp[1].range_min_qp, rp[2].range_min_qp, rp[3].range_min_qp, + rp[4].range_min_qp, rp[5].range_min_qp, rp[6].range_min_qp, rp[7].range_min_qp, + rp[8].range_min_qp, rp[9].range_min_qp, rp[10].range_min_qp, rp[11].range_min_qp, + rp[12].range_min_qp, rp[13].range_min_qp, rp[14].range_min_qp); + drm_printf_indent(p, indent, + "dsc-cfg: rc-max-qp: %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d\n", + rp[0].range_max_qp, rp[1].range_max_qp, rp[2].range_max_qp, rp[3].range_max_qp, + rp[4].range_max_qp, rp[5].range_max_qp, rp[6].range_max_qp, rp[7].range_max_qp, + rp[8].range_max_qp, rp[9].range_max_qp, rp[10].range_max_qp, rp[11].range_max_qp, + rp[12].range_max_qp, rp[13].range_max_qp, rp[14].range_max_qp); + drm_printf_indent(p, indent, + "dsc-cfg: rc-bpg-offset: %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d\n", + rp[0].range_bpg_offset, rp[1].range_bpg_offset, rp[2].range_bpg_offset, rp[3].range_bpg_offset, + rp[4].range_bpg_offset, rp[5].range_bpg_offset, rp[6].range_bpg_offset, rp[7].range_bpg_offset, + rp[8].range_bpg_offset, rp[9].range_bpg_offset, rp[10].range_bpg_offset, rp[11].range_bpg_offset, + rp[12].range_bpg_offset, rp[13].range_bpg_offset, rp[14].range_bpg_offset); +} + +/** + * drm_dsc_dump_config - Dump the provided DSC configuration + * @p: The printer used for output + * @indent: Tab indentation level (max 5) + * @cfg: DSC configuration to print + * + * Print the provided DSC configuration in @cfg. + */ +void drm_dsc_dump_config(struct drm_printer *p, int indent, + const struct drm_dsc_config *cfg) +{ + drm_dsc_dump_config_main_params(p, indent, cfg); + drm_dsc_dump_config_rc_params(p, indent, cfg); +} +EXPORT_SYMBOL(drm_dsc_dump_config); diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 7646f67bda4e..79ce86a5bd67 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -197,7 +197,7 @@ void drmm_release_action(struct drm_device *dev, spin_lock_irqsave(&dev->managed.lock, flags); list_for_each_entry_reverse(dr, &dev->managed.resources, node.entry) { if (dr->node.release == action) { - if (!data || (data && *(void **)dr->data == data)) { + if (!data || *(void **)dr->data == data) { dr_match = dr; del_dr(dev, dr_match); break; diff --git a/drivers/gpu/drm/lima/lima_drv.c b/drivers/gpu/drm/lima/lima_drv.c index 739c865b556f..10bce18b7c31 100644 --- a/drivers/gpu/drm/lima/lima_drv.c +++ b/drivers/gpu/drm/lima/lima_drv.c @@ -501,3 +501,4 @@ module_platform_driver(lima_platform_driver); MODULE_AUTHOR("Lima Project Developers"); MODULE_DESCRIPTION("Lima DRM Driver"); MODULE_LICENSE("GPL v2"); +MODULE_SOFTDEP("pre: governor_simpleondemand"); diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index bb6204002cb3..d4550e4b3b01 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -111,12 +111,12 @@ static inline void mga_wait_vsync(struct mga_device *mdev) unsigned int status = 0; do { - status = RREG32(MGAREG_Status); + status = RREG32(MGAREG_STATUS); } while ((status & 0x08) && time_before(jiffies, timeout)); timeout = jiffies + HZ/10; status = 0; do { - status = RREG32(MGAREG_Status); + status = RREG32(MGAREG_STATUS); } while (!(status & 0x08) && time_before(jiffies, timeout)); } @@ -125,7 +125,7 @@ static inline void mga_wait_busy(struct mga_device *mdev) unsigned long timeout = jiffies + HZ; unsigned int status = 0; do { - status = RREG8(MGAREG_Status + 2); + status = RREG8(MGAREG_STATUS + 2); } while ((status & 0x01) && time_before(jiffies, timeout)); } diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h index 1019ffd6c260..aa73463674e4 100644 --- a/drivers/gpu/drm/mgag200/mgag200_reg.h +++ b/drivers/gpu/drm/mgag200/mgag200_reg.h @@ -102,7 +102,7 @@ #define MGAREG_EXEC 0x0100 #define MGAREG_FIFOSTATUS 0x1e10 -#define MGAREG_Status 0x1e14 +#define MGAREG_STATUS 0x1e14 #define MGAREG_CACHEFLUSH 0x1fff #define MGAREG_ICLEAR 0x1e18 #define MGAREG_IEN 0x1e1c diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c index 4879835fe101..c6b669866fed 100644 --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -19,25 +19,30 @@ #include <linux/of.h> #include <linux/regulator/consumer.h> -#define JD9365DA_INIT_CMD_LEN 2 - -struct jadard_init_cmd { - u8 data[JD9365DA_INIT_CMD_LEN]; -}; +struct jadard; struct jadard_panel_desc { const struct drm_display_mode mode; unsigned int lanes; enum mipi_dsi_pixel_format format; - const struct jadard_init_cmd *init_cmds; + int (*init)(struct jadard *jadard); u32 num_init_cmds; + bool lp11_before_reset; + bool reset_before_power_off_vcioo; + unsigned int vcioo_to_lp11_delay_ms; + unsigned int lp11_to_reset_delay_ms; + unsigned int exit_sleep_to_display_on_delay_ms; + unsigned int display_on_delay_ms; + unsigned int backlight_off_to_display_off_delay_ms; + unsigned int display_off_to_enter_sleep_delay_ms; + unsigned int enter_sleep_to_reset_down_delay_ms; }; struct jadard { struct drm_panel panel; struct mipi_dsi_device *dsi; const struct jadard_panel_desc *desc; - + enum drm_panel_orientation orientation; struct regulator *vdd; struct regulator *vccio; struct gpio_desc *reset; @@ -50,51 +55,43 @@ static inline struct jadard *panel_to_jadard(struct drm_panel *panel) static int jadard_enable(struct drm_panel *panel) { - struct device *dev = panel->dev; struct jadard *jadard = panel_to_jadard(panel); - const struct jadard_panel_desc *desc = jadard->desc; - struct mipi_dsi_device *dsi = jadard->dsi; - unsigned int i; - int err; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi }; - msleep(10); - - for (i = 0; i < desc->num_init_cmds; i++) { - const struct jadard_init_cmd *cmd = &desc->init_cmds[i]; + msleep(120); - err = mipi_dsi_dcs_write_buffer(dsi, cmd->data, JD9365DA_INIT_CMD_LEN); - if (err < 0) - return err; - } + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); - msleep(120); + if (jadard->desc->exit_sleep_to_display_on_delay_ms) + mipi_dsi_msleep(&dsi_ctx, jadard->desc->exit_sleep_to_display_on_delay_ms); - err = mipi_dsi_dcs_exit_sleep_mode(dsi); - if (err < 0) - DRM_DEV_ERROR(dev, "failed to exit sleep mode ret = %d\n", err); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); - err = mipi_dsi_dcs_set_display_on(dsi); - if (err < 0) - DRM_DEV_ERROR(dev, "failed to set display on ret = %d\n", err); + if (jadard->desc->display_on_delay_ms) + mipi_dsi_msleep(&dsi_ctx, jadard->desc->display_on_delay_ms); - return 0; + return dsi_ctx.accum_err; } static int jadard_disable(struct drm_panel *panel) { - struct device *dev = panel->dev; struct jadard *jadard = panel_to_jadard(panel); - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi }; - ret = mipi_dsi_dcs_set_display_off(jadard->dsi); - if (ret < 0) - DRM_DEV_ERROR(dev, "failed to set display off: %d\n", ret); + if (jadard->desc->backlight_off_to_display_off_delay_ms) + mipi_dsi_msleep(&dsi_ctx, jadard->desc->backlight_off_to_display_off_delay_ms); - ret = mipi_dsi_dcs_enter_sleep_mode(jadard->dsi); - if (ret < 0) - DRM_DEV_ERROR(dev, "failed to enter sleep mode: %d\n", ret); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); - return 0; + if (jadard->desc->display_off_to_enter_sleep_delay_ms) + mipi_dsi_msleep(&dsi_ctx, jadard->desc->display_off_to_enter_sleep_delay_ms); + + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + + if (jadard->desc->enter_sleep_to_reset_down_delay_ms) + mipi_dsi_msleep(&dsi_ctx, jadard->desc->enter_sleep_to_reset_down_delay_ms); + + return dsi_ctx.accum_err; } static int jadard_prepare(struct drm_panel *panel) @@ -110,6 +107,18 @@ static int jadard_prepare(struct drm_panel *panel) if (ret) return ret; + if (jadard->desc->vcioo_to_lp11_delay_ms) + msleep(jadard->desc->vcioo_to_lp11_delay_ms); + + if (jadard->desc->lp11_before_reset) { + ret = mipi_dsi_dcs_nop(jadard->dsi); + if (ret) + return ret; + } + + if (jadard->desc->lp11_to_reset_delay_ms) + msleep(jadard->desc->lp11_to_reset_delay_ms); + gpiod_set_value(jadard->reset, 1); msleep(5); @@ -117,7 +126,11 @@ static int jadard_prepare(struct drm_panel *panel) msleep(10); gpiod_set_value(jadard->reset, 1); - msleep(120); + msleep(130); + + ret = jadard->desc->init(jadard); + if (ret) + return ret; return 0; } @@ -129,6 +142,12 @@ static int jadard_unprepare(struct drm_panel *panel) gpiod_set_value(jadard->reset, 1); msleep(120); + if (jadard->desc->reset_before_power_off_vcioo) { + gpiod_set_value(jadard->reset, 0); + + usleep_range(1000, 2000); + } + regulator_disable(jadard->vdd); regulator_disable(jadard->vccio); @@ -159,184 +178,197 @@ static int jadard_get_modes(struct drm_panel *panel, return 1; } +static enum drm_panel_orientation jadard_panel_get_orientation(struct drm_panel *panel) +{ + struct jadard *jadard = panel_to_jadard(panel); + + return jadard->orientation; +} + static const struct drm_panel_funcs jadard_funcs = { .disable = jadard_disable, .unprepare = jadard_unprepare, .prepare = jadard_prepare, .enable = jadard_enable, .get_modes = jadard_get_modes, + .get_orientation = jadard_panel_get_orientation, }; -static const struct jadard_init_cmd radxa_display_8hd_ad002_init_cmds[] = { - { .data = { 0xE0, 0x00 } }, - { .data = { 0xE1, 0x93 } }, - { .data = { 0xE2, 0x65 } }, - { .data = { 0xE3, 0xF8 } }, - { .data = { 0x80, 0x03 } }, - { .data = { 0xE0, 0x01 } }, - { .data = { 0x00, 0x00 } }, - { .data = { 0x01, 0x7E } }, - { .data = { 0x03, 0x00 } }, - { .data = { 0x04, 0x65 } }, - { .data = { 0x0C, 0x74 } }, - { .data = { 0x17, 0x00 } }, - { .data = { 0x18, 0xB7 } }, - { .data = { 0x19, 0x00 } }, - { .data = { 0x1A, 0x00 } }, - { .data = { 0x1B, 0xB7 } }, - { .data = { 0x1C, 0x00 } }, - { .data = { 0x24, 0xFE } }, - { .data = { 0x37, 0x19 } }, - { .data = { 0x38, 0x05 } }, - { .data = { 0x39, 0x00 } }, - { .data = { 0x3A, 0x01 } }, - { .data = { 0x3B, 0x01 } }, - { .data = { 0x3C, 0x70 } }, - { .data = { 0x3D, 0xFF } }, - { .data = { 0x3E, 0xFF } }, - { .data = { 0x3F, 0xFF } }, - { .data = { 0x40, 0x06 } }, - { .data = { 0x41, 0xA0 } }, - { .data = { 0x43, 0x1E } }, - { .data = { 0x44, 0x0F } }, - { .data = { 0x45, 0x28 } }, - { .data = { 0x4B, 0x04 } }, - { .data = { 0x55, 0x02 } }, - { .data = { 0x56, 0x01 } }, - { .data = { 0x57, 0xA9 } }, - { .data = { 0x58, 0x0A } }, - { .data = { 0x59, 0x0A } }, - { .data = { 0x5A, 0x37 } }, - { .data = { 0x5B, 0x19 } }, - { .data = { 0x5D, 0x78 } }, - { .data = { 0x5E, 0x63 } }, - { .data = { 0x5F, 0x54 } }, - { .data = { 0x60, 0x49 } }, - { .data = { 0x61, 0x45 } }, - { .data = { 0x62, 0x38 } }, - { .data = { 0x63, 0x3D } }, - { .data = { 0x64, 0x28 } }, - { .data = { 0x65, 0x43 } }, - { .data = { 0x66, 0x41 } }, - { .data = { 0x67, 0x43 } }, - { .data = { 0x68, 0x62 } }, - { .data = { 0x69, 0x50 } }, - { .data = { 0x6A, 0x57 } }, - { .data = { 0x6B, 0x49 } }, - { .data = { 0x6C, 0x44 } }, - { .data = { 0x6D, 0x37 } }, - { .data = { 0x6E, 0x23 } }, - { .data = { 0x6F, 0x10 } }, - { .data = { 0x70, 0x78 } }, - { .data = { 0x71, 0x63 } }, - { .data = { 0x72, 0x54 } }, - { .data = { 0x73, 0x49 } }, - { .data = { 0x74, 0x45 } }, - { .data = { 0x75, 0x38 } }, - { .data = { 0x76, 0x3D } }, - { .data = { 0x77, 0x28 } }, - { .data = { 0x78, 0x43 } }, - { .data = { 0x79, 0x41 } }, - { .data = { 0x7A, 0x43 } }, - { .data = { 0x7B, 0x62 } }, - { .data = { 0x7C, 0x50 } }, - { .data = { 0x7D, 0x57 } }, - { .data = { 0x7E, 0x49 } }, - { .data = { 0x7F, 0x44 } }, - { .data = { 0x80, 0x37 } }, - { .data = { 0x81, 0x23 } }, - { .data = { 0x82, 0x10 } }, - { .data = { 0xE0, 0x02 } }, - { .data = { 0x00, 0x47 } }, - { .data = { 0x01, 0x47 } }, - { .data = { 0x02, 0x45 } }, - { .data = { 0x03, 0x45 } }, - { .data = { 0x04, 0x4B } }, - { .data = { 0x05, 0x4B } }, - { .data = { 0x06, 0x49 } }, - { .data = { 0x07, 0x49 } }, - { .data = { 0x08, 0x41 } }, - { .data = { 0x09, 0x1F } }, - { .data = { 0x0A, 0x1F } }, - { .data = { 0x0B, 0x1F } }, - { .data = { 0x0C, 0x1F } }, - { .data = { 0x0D, 0x1F } }, - { .data = { 0x0E, 0x1F } }, - { .data = { 0x0F, 0x5F } }, - { .data = { 0x10, 0x5F } }, - { .data = { 0x11, 0x57 } }, - { .data = { 0x12, 0x77 } }, - { .data = { 0x13, 0x35 } }, - { .data = { 0x14, 0x1F } }, - { .data = { 0x15, 0x1F } }, - { .data = { 0x16, 0x46 } }, - { .data = { 0x17, 0x46 } }, - { .data = { 0x18, 0x44 } }, - { .data = { 0x19, 0x44 } }, - { .data = { 0x1A, 0x4A } }, - { .data = { 0x1B, 0x4A } }, - { .data = { 0x1C, 0x48 } }, - { .data = { 0x1D, 0x48 } }, - { .data = { 0x1E, 0x40 } }, - { .data = { 0x1F, 0x1F } }, - { .data = { 0x20, 0x1F } }, - { .data = { 0x21, 0x1F } }, - { .data = { 0x22, 0x1F } }, - { .data = { 0x23, 0x1F } }, - { .data = { 0x24, 0x1F } }, - { .data = { 0x25, 0x5F } }, - { .data = { 0x26, 0x5F } }, - { .data = { 0x27, 0x57 } }, - { .data = { 0x28, 0x77 } }, - { .data = { 0x29, 0x35 } }, - { .data = { 0x2A, 0x1F } }, - { .data = { 0x2B, 0x1F } }, - { .data = { 0x58, 0x40 } }, - { .data = { 0x59, 0x00 } }, - { .data = { 0x5A, 0x00 } }, - { .data = { 0x5B, 0x10 } }, - { .data = { 0x5C, 0x06 } }, - { .data = { 0x5D, 0x40 } }, - { .data = { 0x5E, 0x01 } }, - { .data = { 0x5F, 0x02 } }, - { .data = { 0x60, 0x30 } }, - { .data = { 0x61, 0x01 } }, - { .data = { 0x62, 0x02 } }, - { .data = { 0x63, 0x03 } }, - { .data = { 0x64, 0x6B } }, - { .data = { 0x65, 0x05 } }, - { .data = { 0x66, 0x0C } }, - { .data = { 0x67, 0x73 } }, - { .data = { 0x68, 0x09 } }, - { .data = { 0x69, 0x03 } }, - { .data = { 0x6A, 0x56 } }, - { .data = { 0x6B, 0x08 } }, - { .data = { 0x6C, 0x00 } }, - { .data = { 0x6D, 0x04 } }, - { .data = { 0x6E, 0x04 } }, - { .data = { 0x6F, 0x88 } }, - { .data = { 0x70, 0x00 } }, - { .data = { 0x71, 0x00 } }, - { .data = { 0x72, 0x06 } }, - { .data = { 0x73, 0x7B } }, - { .data = { 0x74, 0x00 } }, - { .data = { 0x75, 0xF8 } }, - { .data = { 0x76, 0x00 } }, - { .data = { 0x77, 0xD5 } }, - { .data = { 0x78, 0x2E } }, - { .data = { 0x79, 0x12 } }, - { .data = { 0x7A, 0x03 } }, - { .data = { 0x7B, 0x00 } }, - { .data = { 0x7C, 0x00 } }, - { .data = { 0x7D, 0x03 } }, - { .data = { 0x7E, 0x7B } }, - { .data = { 0xE0, 0x04 } }, - { .data = { 0x00, 0x0E } }, - { .data = { 0x02, 0xB3 } }, - { .data = { 0x09, 0x60 } }, - { .data = { 0x0E, 0x2A } }, - { .data = { 0x36, 0x59 } }, - { .data = { 0xE0, 0x00 } }, +static int radxa_display_8hd_ad002_init_cmds(struct jadard *jadard) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi }; + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE1, 0x93); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE2, 0x65); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE3, 0xF8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x7E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x65); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x74); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xB7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0xB7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xFE); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3B, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x70); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xA0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4B, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0xA9); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x19); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x78); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x63); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x54); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x3D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x41); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x62); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x50); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x23); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x78); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x63); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x54); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x3D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x41); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x62); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x50); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x23); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x47); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x47); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x4B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x4B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x41); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x35); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x4A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0x4A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1D, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1E, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1F, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x35); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2B, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x0C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x56); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x88); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x7B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xF8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0xD5); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x2E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x12); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x7B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xB3); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x60); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x59); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x00); + + return dsi_ctx.accum_err; }; static const struct jadard_panel_desc radxa_display_8hd_ad002_desc = { @@ -359,205 +391,209 @@ static const struct jadard_panel_desc radxa_display_8hd_ad002_desc = { }, .lanes = 4, .format = MIPI_DSI_FMT_RGB888, - .init_cmds = radxa_display_8hd_ad002_init_cmds, - .num_init_cmds = ARRAY_SIZE(radxa_display_8hd_ad002_init_cmds), + .init = radxa_display_8hd_ad002_init_cmds, }; -static const struct jadard_init_cmd cz101b4001_init_cmds[] = { - { .data = { 0xE0, 0x00 } }, - { .data = { 0xE1, 0x93 } }, - { .data = { 0xE2, 0x65 } }, - { .data = { 0xE3, 0xF8 } }, - { .data = { 0x80, 0x03 } }, - { .data = { 0xE0, 0x01 } }, - { .data = { 0x00, 0x00 } }, - { .data = { 0x01, 0x3B } }, - { .data = { 0x0C, 0x74 } }, - { .data = { 0x17, 0x00 } }, - { .data = { 0x18, 0xAF } }, - { .data = { 0x19, 0x00 } }, - { .data = { 0x1A, 0x00 } }, - { .data = { 0x1B, 0xAF } }, - { .data = { 0x1C, 0x00 } }, - { .data = { 0x35, 0x26 } }, - { .data = { 0x37, 0x09 } }, - { .data = { 0x38, 0x04 } }, - { .data = { 0x39, 0x00 } }, - { .data = { 0x3A, 0x01 } }, - { .data = { 0x3C, 0x78 } }, - { .data = { 0x3D, 0xFF } }, - { .data = { 0x3E, 0xFF } }, - { .data = { 0x3F, 0x7F } }, - { .data = { 0x40, 0x06 } }, - { .data = { 0x41, 0xA0 } }, - { .data = { 0x42, 0x81 } }, - { .data = { 0x43, 0x14 } }, - { .data = { 0x44, 0x23 } }, - { .data = { 0x45, 0x28 } }, - { .data = { 0x55, 0x02 } }, - { .data = { 0x57, 0x69 } }, - { .data = { 0x59, 0x0A } }, - { .data = { 0x5A, 0x2A } }, - { .data = { 0x5B, 0x17 } }, - { .data = { 0x5D, 0x7F } }, - { .data = { 0x5E, 0x6B } }, - { .data = { 0x5F, 0x5C } }, - { .data = { 0x60, 0x4F } }, - { .data = { 0x61, 0x4D } }, - { .data = { 0x62, 0x3F } }, - { .data = { 0x63, 0x42 } }, - { .data = { 0x64, 0x2B } }, - { .data = { 0x65, 0x44 } }, - { .data = { 0x66, 0x43 } }, - { .data = { 0x67, 0x43 } }, - { .data = { 0x68, 0x63 } }, - { .data = { 0x69, 0x52 } }, - { .data = { 0x6A, 0x5A } }, - { .data = { 0x6B, 0x4F } }, - { .data = { 0x6C, 0x4E } }, - { .data = { 0x6D, 0x20 } }, - { .data = { 0x6E, 0x0F } }, - { .data = { 0x6F, 0x00 } }, - { .data = { 0x70, 0x7F } }, - { .data = { 0x71, 0x6B } }, - { .data = { 0x72, 0x5C } }, - { .data = { 0x73, 0x4F } }, - { .data = { 0x74, 0x4D } }, - { .data = { 0x75, 0x3F } }, - { .data = { 0x76, 0x42 } }, - { .data = { 0x77, 0x2B } }, - { .data = { 0x78, 0x44 } }, - { .data = { 0x79, 0x43 } }, - { .data = { 0x7A, 0x43 } }, - { .data = { 0x7B, 0x63 } }, - { .data = { 0x7C, 0x52 } }, - { .data = { 0x7D, 0x5A } }, - { .data = { 0x7E, 0x4F } }, - { .data = { 0x7F, 0x4E } }, - { .data = { 0x80, 0x20 } }, - { .data = { 0x81, 0x0F } }, - { .data = { 0x82, 0x00 } }, - { .data = { 0xE0, 0x02 } }, - { .data = { 0x00, 0x02 } }, - { .data = { 0x01, 0x02 } }, - { .data = { 0x02, 0x00 } }, - { .data = { 0x03, 0x00 } }, - { .data = { 0x04, 0x1E } }, - { .data = { 0x05, 0x1E } }, - { .data = { 0x06, 0x1F } }, - { .data = { 0x07, 0x1F } }, - { .data = { 0x08, 0x1F } }, - { .data = { 0x09, 0x17 } }, - { .data = { 0x0A, 0x17 } }, - { .data = { 0x0B, 0x37 } }, - { .data = { 0x0C, 0x37 } }, - { .data = { 0x0D, 0x47 } }, - { .data = { 0x0E, 0x47 } }, - { .data = { 0x0F, 0x45 } }, - { .data = { 0x10, 0x45 } }, - { .data = { 0x11, 0x4B } }, - { .data = { 0x12, 0x4B } }, - { .data = { 0x13, 0x49 } }, - { .data = { 0x14, 0x49 } }, - { .data = { 0x15, 0x1F } }, - { .data = { 0x16, 0x01 } }, - { .data = { 0x17, 0x01 } }, - { .data = { 0x18, 0x00 } }, - { .data = { 0x19, 0x00 } }, - { .data = { 0x1A, 0x1E } }, - { .data = { 0x1B, 0x1E } }, - { .data = { 0x1C, 0x1F } }, - { .data = { 0x1D, 0x1F } }, - { .data = { 0x1E, 0x1F } }, - { .data = { 0x1F, 0x17 } }, - { .data = { 0x20, 0x17 } }, - { .data = { 0x21, 0x37 } }, - { .data = { 0x22, 0x37 } }, - { .data = { 0x23, 0x46 } }, - { .data = { 0x24, 0x46 } }, - { .data = { 0x25, 0x44 } }, - { .data = { 0x26, 0x44 } }, - { .data = { 0x27, 0x4A } }, - { .data = { 0x28, 0x4A } }, - { .data = { 0x29, 0x48 } }, - { .data = { 0x2A, 0x48 } }, - { .data = { 0x2B, 0x1F } }, - { .data = { 0x2C, 0x01 } }, - { .data = { 0x2D, 0x01 } }, - { .data = { 0x2E, 0x00 } }, - { .data = { 0x2F, 0x00 } }, - { .data = { 0x30, 0x1F } }, - { .data = { 0x31, 0x1F } }, - { .data = { 0x32, 0x1E } }, - { .data = { 0x33, 0x1E } }, - { .data = { 0x34, 0x1F } }, - { .data = { 0x35, 0x17 } }, - { .data = { 0x36, 0x17 } }, - { .data = { 0x37, 0x37 } }, - { .data = { 0x38, 0x37 } }, - { .data = { 0x39, 0x08 } }, - { .data = { 0x3A, 0x08 } }, - { .data = { 0x3B, 0x0A } }, - { .data = { 0x3C, 0x0A } }, - { .data = { 0x3D, 0x04 } }, - { .data = { 0x3E, 0x04 } }, - { .data = { 0x3F, 0x06 } }, - { .data = { 0x40, 0x06 } }, - { .data = { 0x41, 0x1F } }, - { .data = { 0x42, 0x02 } }, - { .data = { 0x43, 0x02 } }, - { .data = { 0x44, 0x00 } }, - { .data = { 0x45, 0x00 } }, - { .data = { 0x46, 0x1F } }, - { .data = { 0x47, 0x1F } }, - { .data = { 0x48, 0x1E } }, - { .data = { 0x49, 0x1E } }, - { .data = { 0x4A, 0x1F } }, - { .data = { 0x4B, 0x17 } }, - { .data = { 0x4C, 0x17 } }, - { .data = { 0x4D, 0x37 } }, - { .data = { 0x4E, 0x37 } }, - { .data = { 0x4F, 0x09 } }, - { .data = { 0x50, 0x09 } }, - { .data = { 0x51, 0x0B } }, - { .data = { 0x52, 0x0B } }, - { .data = { 0x53, 0x05 } }, - { .data = { 0x54, 0x05 } }, - { .data = { 0x55, 0x07 } }, - { .data = { 0x56, 0x07 } }, - { .data = { 0x57, 0x1F } }, - { .data = { 0x58, 0x40 } }, - { .data = { 0x5B, 0x30 } }, - { .data = { 0x5C, 0x16 } }, - { .data = { 0x5D, 0x34 } }, - { .data = { 0x5E, 0x05 } }, - { .data = { 0x5F, 0x02 } }, - { .data = { 0x63, 0x00 } }, - { .data = { 0x64, 0x6A } }, - { .data = { 0x67, 0x73 } }, - { .data = { 0x68, 0x1D } }, - { .data = { 0x69, 0x08 } }, - { .data = { 0x6A, 0x6A } }, - { .data = { 0x6B, 0x08 } }, - { .data = { 0x6C, 0x00 } }, - { .data = { 0x6D, 0x00 } }, - { .data = { 0x6E, 0x00 } }, - { .data = { 0x6F, 0x88 } }, - { .data = { 0x75, 0xFF } }, - { .data = { 0x77, 0xDD } }, - { .data = { 0x78, 0x3F } }, - { .data = { 0x79, 0x15 } }, - { .data = { 0x7A, 0x17 } }, - { .data = { 0x7D, 0x14 } }, - { .data = { 0x7E, 0x82 } }, - { .data = { 0xE0, 0x04 } }, - { .data = { 0x00, 0x0E } }, - { .data = { 0x02, 0xB3 } }, - { .data = { 0x09, 0x61 } }, - { .data = { 0x0E, 0x48 } }, - { .data = { 0xE0, 0x00 } }, - { .data = { 0xE6, 0x02 } }, - { .data = { 0xE7, 0x0C } }, +static int cz101b4001_init_cmds(struct jadard *jadard) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi }; + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE1, 0x93); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE2, 0x65); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE3, 0xF8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x3B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x74); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xAF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0xAF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x26); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x78); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x7F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xA0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x81); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x23); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x69); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x7F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x6B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x5C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x4F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x4D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x3F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x42); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x2B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x63); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x5A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x4F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x4E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x6B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x5C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x4F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x4D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x3F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x42); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x2B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x63); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x5A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x4F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x4E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x47); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x47); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x4B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x4B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1D, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1E, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1F, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x4A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x4A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2B, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2C, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2D, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2E, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2F, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3B, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4A, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4B, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4C, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4D, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4E, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4F, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x0B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x0B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x16); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x34); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x1D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x6A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x88); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0xDD); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x3F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x15); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x82); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xB3); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x61); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE6, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE7, 0x0C); + + return dsi_ctx.accum_err; }; static const struct jadard_panel_desc cz101b4001_desc = { @@ -580,8 +616,238 @@ static const struct jadard_panel_desc cz101b4001_desc = { }, .lanes = 4, .format = MIPI_DSI_FMT_RGB888, - .init_cmds = cz101b4001_init_cmds, - .num_init_cmds = ARRAY_SIZE(cz101b4001_init_cmds), + .init = cz101b4001_init_cmds, +}; + +static int kingdisplay_kd101ne3_init_cmds(struct jadard *jadard) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi }; + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0x93); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe2, 0x65); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe3, 0xf8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x74); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xc7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xc7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xfe); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x12); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x7e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x7f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xa0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x6a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x2e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x1a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x15); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x7f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x61); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x50); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x3f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x32); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x35); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x36); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x36); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x54); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x42); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x39); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x34); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x26); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x61); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x50); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x3f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x32); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x35); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x36); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x36); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x54); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x42); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x39); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x34); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x26); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x50); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x4e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x4c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x4a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x53); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x51); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x4f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x4d); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x4b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x47); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x41); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2c, 0x13); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2e, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x0d); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x0f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x0b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x12); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x0c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x0e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x0a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x6c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x75); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xb4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x6c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x6c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x0c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x88); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xbb); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x2a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xb3); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x61); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x00); + + return dsi_ctx.accum_err; +}; + +static const struct jadard_panel_desc kingdisplay_kd101ne3_40ti_desc = { + .mode = { + .clock = (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000, + + .hdisplay = 800, + .hsync_start = 800 + 24, + .hsync_end = 800 + 24 + 24, + .htotal = 800 + 24 + 24 + 24, + + .vdisplay = 1280, + .vsync_start = 1280 + 30, + .vsync_end = 1280 + 30 + 4, + .vtotal = 1280 + 30 + 4 + 8, + + .width_mm = 135, + .height_mm = 216, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .init = kingdisplay_kd101ne3_init_cmds, + .lp11_before_reset = true, + .reset_before_power_off_vcioo = true, + .vcioo_to_lp11_delay_ms = 5, + .lp11_to_reset_delay_ms = 10, + .exit_sleep_to_display_on_delay_ms = 120, + .display_on_delay_ms = 20, + .backlight_off_to_display_off_delay_ms = 100, + .display_off_to_enter_sleep_delay_ms = 50, + .enter_sleep_to_reset_down_delay_ms = 100, }; static int jadard_dsi_probe(struct mipi_dsi_device *dsi) @@ -622,6 +888,10 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi) drm_panel_init(&jadard->panel, dev, &jadard_funcs, DRM_MODE_CONNECTOR_DSI); + ret = of_drm_get_panel_orientation(dev->of_node, &jadard->orientation); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get orientation\n"); + ret = drm_panel_of_backlight(&jadard->panel); if (ret) return ret; @@ -653,6 +923,10 @@ static const struct of_device_id jadard_of_match[] = { .data = &cz101b4001_desc }, { + .compatible = "kingdisplay,kd101ne3-40ti", + .data = &kingdisplay_kd101ne3_40ti_desc + }, + { .compatible = "radxa,display-10hd-ad001", .data = &cz101b4001_desc }, diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index dec3c0cb0bac..bf40057c5cf3 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1067,6 +1067,30 @@ static const struct panel_desc auo_g104sn02 = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct drm_display_mode auo_g104stn01_mode = { + .clock = 40000, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 88, + .htotal = 800 + 40 + 88 + 128, + .vdisplay = 600, + .vsync_start = 600 + 1, + .vsync_end = 600 + 1 + 23, + .vtotal = 600 + 1 + 23 + 4, +}; + +static const struct panel_desc auo_g104stn01 = { + .modes = &auo_g104stn01_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 211, + .height = 158, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct display_timing auo_g121ean01_timing = { .pixelclock = { 60000000, 74400000, 90000000 }, .hactive = { 1280, 1280, 1280 }, @@ -4535,6 +4559,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "auo,g104sn02", .data = &auo_g104sn02, }, { + .compatible = "auo,g104stn01", + .data = &auo_g104stn01, + }, { .compatible = "auo,g121ean01", .data = &auo_g121ean01, }, { diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index b43557b10ae3..671eed4ad890 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -838,3 +838,4 @@ module_platform_driver(panfrost_driver); MODULE_AUTHOR("Panfrost Project Developers"); MODULE_DESCRIPTION("Panfrost DRM Driver"); MODULE_LICENSE("GPL v2"); +MODULE_SOFTDEP("pre: governor_simpleondemand"); diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 79ffcbc41d78..42929e147107 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -2926,6 +2926,7 @@ queue_run_job(struct drm_sched_job *sched_job) pm_runtime_get(ptdev->base.dev); sched->pm.has_ref = true; } + panthor_devfreq_record_busy(sched->ptdev); } done_fence = dma_fence_get(job->done_fence); diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig index 4c906d602825..1cc6b6cbdfa9 100644 --- a/drivers/gpu/drm/stm/Kconfig +++ b/drivers/gpu/drm/stm/Kconfig @@ -20,3 +20,14 @@ config DRM_STM_DSI select DRM_DW_MIPI_DSI help Choose this option for MIPI DSI support on STMicroelectronics SoC. + +config DRM_STM_LVDS + tristate "STMicroelectronics LVDS Display Interface Transmitter DRM driver" + depends on DRM_STM + help + Enable support for LVDS encoders on STMicroelectronics SoC. + The STM LVDS is a bridge which serialize pixel stream onto + a LVDS protocol. + + To compile this driver as a module, choose M here: the module will be + called lvds. diff --git a/drivers/gpu/drm/stm/Makefile b/drivers/gpu/drm/stm/Makefile index 4df5caf01f35..ad740d6175a6 100644 --- a/drivers/gpu/drm/stm/Makefile +++ b/drivers/gpu/drm/stm/Makefile @@ -5,4 +5,6 @@ stm-drm-y := \ obj-$(CONFIG_DRM_STM_DSI) += dw_mipi_dsi-stm.o +obj-$(CONFIG_DRM_STM_LVDS) += lvds.o + obj-$(CONFIG_DRM_STM) += stm-drm.o diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c index d5f8c923d7bc..b20123854c4a 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -7,10 +7,13 @@ */ #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/iopoll.h> +#include <linux/kernel.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <video/mipi_display.h> @@ -76,8 +79,12 @@ enum dsi_color { struct dw_mipi_dsi_stm { void __iomem *base; + struct device *dev; struct clk *pllref_clk; + struct clk *pclk; + struct clk_hw txbyte_clk; struct dw_mipi_dsi *dsi; + struct dw_mipi_dsi_plat_data pdata; u32 hw_version; int lane_min_kbps; int lane_max_kbps; @@ -194,29 +201,198 @@ static int dsi_pll_get_params(struct dw_mipi_dsi_stm *dsi, return 0; } -static int dw_mipi_dsi_phy_init(void *priv_data) +#define clk_to_dw_mipi_dsi_stm(clk) \ + container_of(clk, struct dw_mipi_dsi_stm, txbyte_clk) + +static void dw_mipi_dsi_clk_disable(struct clk_hw *clk) { - struct dw_mipi_dsi_stm *dsi = priv_data; + struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(clk); + + DRM_DEBUG_DRIVER("\n"); + + /* Disable the DSI PLL */ + dsi_clear(dsi, DSI_WRPCR, WRPCR_PLLEN); + + /* Disable the regulator */ + dsi_clear(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN); +} + +static int dw_mipi_dsi_clk_enable(struct clk_hw *clk) +{ + struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(clk); u32 val; int ret; + DRM_DEBUG_DRIVER("\n"); + /* Enable the regulator */ dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN); - ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS, - SLEEP_US, TIMEOUT_US); + ret = readl_poll_timeout_atomic(dsi->base + DSI_WISR, val, val & WISR_RRS, + SLEEP_US, TIMEOUT_US); if (ret) DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n"); /* Enable the DSI PLL & wait for its lock */ dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN); - ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS, - SLEEP_US, TIMEOUT_US); + ret = readl_poll_timeout_atomic(dsi->base + DSI_WISR, val, val & WISR_PLLLS, + SLEEP_US, TIMEOUT_US); if (ret) DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n"); return 0; } +static int dw_mipi_dsi_clk_is_enabled(struct clk_hw *hw) +{ + struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw); + + return dsi_read(dsi, DSI_WRPCR) & WRPCR_PLLEN; +} + +static unsigned long dw_mipi_dsi_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw); + unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + u32 val; + + DRM_DEBUG_DRIVER("\n"); + + pll_in_khz = (unsigned int)(parent_rate / 1000); + + val = dsi_read(dsi, DSI_WRPCR); + + idf = (val & WRPCR_IDF) >> 11; + if (!idf) + idf = 1; + ndiv = (val & WRPCR_NDIV) >> 2; + odf = int_pow(2, (val & WRPCR_ODF) >> 16); + + /* Get the adjusted pll out value */ + pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); + + return (unsigned long)pll_out_khz * 1000; +} + +static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw); + unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + int ret; + + DRM_DEBUG_DRIVER("\n"); + + pll_in_khz = (unsigned int)(*parent_rate / 1000); + + /* Compute best pll parameters */ + idf = 0; + ndiv = 0; + odf = 0; + + ret = dsi_pll_get_params(dsi, pll_in_khz, rate / 1000, + &idf, &ndiv, &odf); + if (ret) + DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); + + /* Get the adjusted pll out value */ + pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); + + return pll_out_khz * 1000; +} + +static int dw_mipi_dsi_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw); + unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + int ret; + u32 val; + + DRM_DEBUG_DRIVER("\n"); + + pll_in_khz = (unsigned int)(parent_rate / 1000); + + /* Compute best pll parameters */ + idf = 0; + ndiv = 0; + odf = 0; + + ret = dsi_pll_get_params(dsi, pll_in_khz, rate / 1000, &idf, &ndiv, &odf); + if (ret) + DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); + + /* Get the adjusted pll out value */ + pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); + + /* Set the PLL division factors */ + dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF, + (ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16)); + + /* Compute uix4 & set the bit period in high-speed mode */ + val = 4000000 / pll_out_khz; + dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val); + + return 0; +} + +static void dw_mipi_dsi_clk_unregister(void *data) +{ + struct dw_mipi_dsi_stm *dsi = data; + + DRM_DEBUG_DRIVER("\n"); + + of_clk_del_provider(dsi->dev->of_node); + clk_hw_unregister(&dsi->txbyte_clk); +} + +static const struct clk_ops dw_mipi_dsi_stm_clk_ops = { + .enable = dw_mipi_dsi_clk_enable, + .disable = dw_mipi_dsi_clk_disable, + .is_enabled = dw_mipi_dsi_clk_is_enabled, + .recalc_rate = dw_mipi_dsi_clk_recalc_rate, + .round_rate = dw_mipi_dsi_clk_round_rate, + .set_rate = dw_mipi_dsi_clk_set_rate, +}; + +static struct clk_init_data cdata_init = { + .name = "ck_dsi_phy", + .ops = &dw_mipi_dsi_stm_clk_ops, + .parent_names = (const char * []) {"ck_hse"}, + .num_parents = 1, +}; + +static int dw_mipi_dsi_clk_register(struct dw_mipi_dsi_stm *dsi, + struct device *dev) +{ + struct device_node *node = dev->of_node; + int ret; + + DRM_DEBUG_DRIVER("Registering clk\n"); + + dsi->txbyte_clk.init = &cdata_init; + + ret = clk_hw_register(dev, &dsi->txbyte_clk); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, + &dsi->txbyte_clk); + if (ret) + clk_hw_unregister(&dsi->txbyte_clk); + + return ret; +} + +static int dw_mipi_dsi_phy_init(void *priv_data) +{ + struct dw_mipi_dsi_stm *dsi = priv_data; + int ret; + + ret = clk_prepare_enable(dsi->txbyte_clk.clk); + return ret; +} + static void dw_mipi_dsi_phy_power_on(void *priv_data) { struct dw_mipi_dsi_stm *dsi = priv_data; @@ -233,6 +409,8 @@ static void dw_mipi_dsi_phy_power_off(void *priv_data) DRM_DEBUG_DRIVER("\n"); + clk_disable_unprepare(dsi->txbyte_clk.clk); + /* Disable the DSI wrapper */ dsi_clear(dsi, DSI_WCR, WCR_DSIEN); } @@ -243,9 +421,8 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, unsigned int *lane_mbps) { struct dw_mipi_dsi_stm *dsi = priv_data; - unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + unsigned int pll_in_khz, pll_out_khz; int ret, bpp; - u32 val; pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000); @@ -266,25 +443,10 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, DRM_WARN("Warning min phy mbps is used\n"); } - /* Compute best pll parameters */ - idf = 0; - ndiv = 0; - odf = 0; - ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz, - &idf, &ndiv, &odf); + ret = clk_set_rate((dsi->txbyte_clk.clk), pll_out_khz * 1000); if (ret) - DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); - - /* Get the adjusted pll out value */ - pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); - - /* Set the PLL division factors */ - dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF, - (ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16)); - - /* Compute uix4 & set the bit period in high-speed mode */ - val = 4000000 / pll_out_khz; - dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val); + DRM_DEBUG_DRIVER("ERROR Could not set rate of %d to %s clk->name", + pll_out_khz, clk_hw_get_name(&dsi->txbyte_clk)); /* Select video mode by resetting DSIM bit */ dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM); @@ -443,7 +605,7 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dw_mipi_dsi_stm *dsi; - struct clk *pclk; + const struct dw_mipi_dsi_plat_data *pdata = of_device_get_match_data(dev); int ret; dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); @@ -483,21 +645,21 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) goto err_clk_get; } - pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(pclk)) { - ret = PTR_ERR(pclk); + dsi->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dsi->pclk)) { + ret = PTR_ERR(dsi->pclk); DRM_ERROR("Unable to get peripheral clock: %d\n", ret); goto err_dsi_probe; } - ret = clk_prepare_enable(pclk); + ret = clk_prepare_enable(dsi->pclk); if (ret) { DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__); goto err_dsi_probe; } dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; - clk_disable_unprepare(pclk); + clk_disable_unprepare(dsi->pclk); if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) { ret = -ENODEV; @@ -513,18 +675,41 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) dsi->lane_max_kbps *= 2; } - dw_mipi_dsi_stm_plat_data.base = dsi->base; - dw_mipi_dsi_stm_plat_data.priv_data = dsi; + dsi->pdata = *pdata; + dsi->pdata.base = dsi->base; + dsi->pdata.priv_data = dsi; + + dsi->pdata.max_data_lanes = 2; + dsi->pdata.phy_ops = &dw_mipi_dsi_stm_phy_ops; platform_set_drvdata(pdev, dsi); - dsi->dsi = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data); + dsi->dsi = dw_mipi_dsi_probe(pdev, &dsi->pdata); if (IS_ERR(dsi->dsi)) { ret = PTR_ERR(dsi->dsi); dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n"); goto err_dsi_probe; } + /* + * We need to wait for the generic bridge to probe before enabling and + * register the internal pixel clock. + */ + ret = clk_prepare_enable(dsi->pclk); + if (ret) { + DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__); + goto err_dsi_probe; + } + + ret = dw_mipi_dsi_clk_register(dsi, dev); + if (ret) { + DRM_ERROR("Failed to register DSI pixel clock: %d\n", ret); + clk_disable_unprepare(dsi->pclk); + goto err_dsi_probe; + } + + clk_disable_unprepare(dsi->pclk); + return 0; err_dsi_probe: @@ -541,24 +726,26 @@ static void dw_mipi_dsi_stm_remove(struct platform_device *pdev) dw_mipi_dsi_remove(dsi->dsi); clk_disable_unprepare(dsi->pllref_clk); + dw_mipi_dsi_clk_unregister(dsi); regulator_disable(dsi->vdd_supply); } -static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev) +static int dw_mipi_dsi_stm_suspend(struct device *dev) { - struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; + struct dw_mipi_dsi_stm *dsi = dev_get_drvdata(dev); DRM_DEBUG_DRIVER("\n"); clk_disable_unprepare(dsi->pllref_clk); + clk_disable_unprepare(dsi->pclk); regulator_disable(dsi->vdd_supply); return 0; } -static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev) +static int dw_mipi_dsi_stm_resume(struct device *dev) { - struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; + struct dw_mipi_dsi_stm *dsi = dev_get_drvdata(dev); int ret; DRM_DEBUG_DRIVER("\n"); @@ -569,8 +756,16 @@ static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev) return ret; } + ret = clk_prepare_enable(dsi->pclk); + if (ret) { + regulator_disable(dsi->vdd_supply); + DRM_ERROR("Failed to enable pclk: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(dsi->pllref_clk); if (ret) { + clk_disable_unprepare(dsi->pclk); regulator_disable(dsi->vdd_supply); DRM_ERROR("Failed to enable pllref_clk: %d\n", ret); return ret; @@ -580,8 +775,10 @@ static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev) } static const struct dev_pm_ops dw_mipi_dsi_stm_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend, - dw_mipi_dsi_stm_resume) + SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend, + dw_mipi_dsi_stm_resume) + RUNTIME_PM_OPS(dw_mipi_dsi_stm_suspend, + dw_mipi_dsi_stm_resume, NULL) }; static struct platform_driver dw_mipi_dsi_stm_driver = { diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c new file mode 100644 index 000000000000..bfc8cb13fbc5 --- /dev/null +++ b/drivers/gpu/drm/stm/lvds.c @@ -0,0 +1,1226 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Author(s): Raphaƫl GALLAIS-POU <raphael.gallais-pou@foss.st.com> for STMicroelectronics. + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_device.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/media-bus-format.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +/* LVDS Host registers */ +#define LVDS_CR 0x0000 /* configuration register */ +#define LVDS_DMLCR0 0x0004 /* data mapping lsb configuration register 0 */ +#define LVDS_DMMCR0 0x0008 /* data mapping msb configuration register 0 */ +#define LVDS_DMLCR1 0x000C /* data mapping lsb configuration register 1 */ +#define LVDS_DMMCR1 0x0010 /* data mapping msb configuration register 1 */ +#define LVDS_DMLCR2 0x0014 /* data mapping lsb configuration register 2 */ +#define LVDS_DMMCR2 0x0018 /* data mapping msb configuration register 2 */ +#define LVDS_DMLCR3 0x001C /* data mapping lsb configuration register 3 */ +#define LVDS_DMMCR3 0x0020 /* data mapping msb configuration register 3 */ +#define LVDS_DMLCR4 0x0024 /* data mapping lsb configuration register 4 */ +#define LVDS_DMMCR4 0x0028 /* data mapping msb configuration register 4 */ +#define LVDS_CDL1CR 0x002C /* channel distrib link 1 configuration register */ +#define LVDS_CDL2CR 0x0030 /* channel distrib link 2 configuration register */ + +#define CDL1CR_DEFAULT 0x04321 /* Default value for CDL1CR */ +#define CDL2CR_DEFAULT 0x59876 /* Default value for CDL2CR */ + +#define LVDS_DMLCR(bit) (LVDS_DMLCR0 + 0x8 * (bit)) +#define LVDS_DMMCR(bit) (LVDS_DMMCR0 + 0x8 * (bit)) + +/* LVDS Wrapper registers */ +#define LVDS_WCLKCR 0x11B0 /* Wrapper clock control register */ + +#define LVDS_HWCFGR 0x1FF0 /* HW configuration register */ +#define LVDS_VERR 0x1FF4 /* Version register */ +#define LVDS_IPIDR 0x1FF8 /* Identification register */ +#define LVDS_SIDR 0x1FFC /* Size Identification register */ + +/* Bitfield description */ +#define CR_LVDSEN BIT(0) /* LVDS PHY Enable */ +#define CR_HSPOL BIT(1) /* Horizontal Synchronization Polarity */ +#define CR_VSPOL BIT(2) /* Vertical Synchronization Polarity */ +#define CR_DEPOL BIT(3) /* Data Enable Polarity */ +#define CR_CI BIT(4) /* Control Internal (software controlled bit) */ +#define CR_LKMOD BIT(5) /* Link Mode, for both Links */ +#define CR_LKPHA BIT(6) /* Link Phase, for both Links */ +#define CR_LK1POL GENMASK(20, 16) /* Link-1 output Polarity */ +#define CR_LK2POL GENMASK(25, 21) /* Link-2 output Polarity */ + +#define DMMCR_MAP0 GENMASK(4, 0) /* Mapping for bit 0 of datalane x */ +#define DMMCR_MAP1 GENMASK(9, 5) /* Mapping for bit 1 of datalane x */ +#define DMMCR_MAP2 GENMASK(14, 10) /* Mapping for bit 2 of datalane x */ +#define DMMCR_MAP3 GENMASK(19, 15) /* Mapping for bit 3 of datalane x */ +#define DMLCR_MAP4 GENMASK(4, 0) /* Mapping for bit 4 of datalane x */ +#define DMLCR_MAP5 GENMASK(9, 5) /* Mapping for bit 5 of datalane x */ +#define DMLCR_MAP6 GENMASK(14, 10) /* Mapping for bit 6 of datalane x */ + +#define CDLCR_DISTR0 GENMASK(3, 0) /* Channel distribution for lane 0 */ +#define CDLCR_DISTR1 GENMASK(7, 4) /* Channel distribution for lane 1 */ +#define CDLCR_DISTR2 GENMASK(11, 8) /* Channel distribution for lane 2 */ +#define CDLCR_DISTR3 GENMASK(15, 12) /* Channel distribution for lane 3 */ +#define CDLCR_DISTR4 GENMASK(19, 16) /* Channel distribution for lane 4 */ + +#define PHY_GCR_BIT_CLK_OUT BIT(0) /* BIT clock enable */ +#define PHY_GCR_LS_CLK_OUT BIT(4) /* LS clock enable */ +#define PHY_GCR_DP_CLK_OUT BIT(8) /* DP clock enable */ +#define PHY_GCR_RSTZ BIT(24) /* LVDS PHY digital reset */ +#define PHY_GCR_DIV_RSTN BIT(25) /* Output divider reset */ +#define PHY_SCR_TX_EN BIT(16) /* Transmission mode enable */ +/* Current mode driver enable */ +#define PHY_CMCR_CM_EN_DL (BIT(28) | BIT(20) | BIT(12) | BIT(4)) +#define PHY_CMCR_CM_EN_DL4 BIT(4) +/* Bias enable */ +#define PHY_BCR1_EN_BIAS_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0)) +#define PHY_BCR2_BIAS_EN BIT(28) +/* Voltage mode driver enable */ +#define PHY_BCR3_VM_EN_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0)) +#define PHY_DCR_POWER_OK BIT(12) +#define PHY_CFGCR_EN_DIG_DL GENMASK(4, 0) /* LVDS PHY digital lane enable */ +#define PHY_PLLCR1_PLL_EN BIT(0) /* LVDS PHY PLL enable */ +#define PHY_PLLCR1_EN_SD BIT(1) /* LVDS PHY PLL sigma-delta signal enable */ +#define PHY_PLLCR1_EN_TWG BIT(2) /* LVDS PHY PLL triangular wave generator enable */ +#define PHY_PLLCR1_DIV_EN BIT(8) /* LVDS PHY PLL dividers enable */ +#define PHY_PLLCR2_NDIV GENMASK(25, 16) /* NDIV mask value */ +#define PHY_PLLCR2_BDIV GENMASK(9, 0) /* BDIV mask value */ +#define PHY_PLLSR_PLL_LOCK BIT(0) /* LVDS PHY PLL lock status */ +#define PHY_PLLSDCR1_MDIV GENMASK(9, 0) /* MDIV mask value */ +#define PHY_PLLTESTCR_TDIV GENMASK(25, 16) /* TDIV mask value */ +#define PHY_PLLTESTCR_CLK_EN BIT(0) /* Test clock enable */ +#define PHY_PLLTESTCR_EN BIT(8) /* Test divider output enable */ + +#define WCLKCR_SECND_CLKPIX_SEL BIT(0) /* Pixel clock selection */ +#define WCLKCR_SRCSEL BIT(8) /* Source selection for the pixel clock */ + +/* Sleep & timeout for pll lock/unlock */ +#define SLEEP_US 1000 +#define TIMEOUT_US 200000 + +/* + * The link phase defines whether an ODD pixel is carried over together with + * the next EVEN pixel or together with the previous EVEN pixel. + * + * LVDS_DUAL_LINK_EVEN_ODD_PIXELS (LKPHA = 0) + * + * ,--------. ,--------. ,--------. ,--------. ,---------. + * | ODD LK \/ PIXEL 3 \/ PIXEL 1 \/ PIXEL' 1 \/ PIXEL' 3 | + * | EVEN LK /\ PIXEL 2 /\ PIXEL' 0 /\ PIXEL' 2 /\ PIXEL' 4 | + * `--------' `--------' `--------' `--------' `---------' + * + * LVDS_DUAL_LINK_ODD_EVEN_PIXELS (LKPHA = 1) + * + * ,--------. ,--------. ,--------. ,--------. ,---------. + * | ODD LK \/ PIXEL 3 \/ PIXEL 1 \/ PIXEL' 1 \/ PIXEL' 3 | + * | EVEN LK /\ PIXEL 4 /\ PIXEL 2 /\ PIXEL' 0 /\ PIXEL' 2 | + * `--------' `--------' `--------' `--------' `---------' + * + */ +enum lvds_link_type { + LVDS_SINGLE_LINK_PRIMARY = 0, + LVDS_SINGLE_LINK_SECONDARY, + LVDS_DUAL_LINK_EVEN_ODD_PIXELS, + LVDS_DUAL_LINK_ODD_EVEN_PIXELS, +}; + +enum lvds_pixel { + PIX_R_0 = 0, + PIX_R_1, + PIX_R_2, + PIX_R_3, + PIX_R_4, + PIX_R_5, + PIX_R_6, + PIX_R_7, + PIX_G_0, + PIX_G_1, + PIX_G_2, + PIX_G_3, + PIX_G_4, + PIX_G_5, + PIX_G_6, + PIX_G_7, + PIX_B_0, + PIX_B_1, + PIX_B_2, + PIX_B_3, + PIX_B_4, + PIX_B_5, + PIX_B_6, + PIX_B_7, + PIX_H_S, + PIX_V_S, + PIX_D_E, + PIX_C_E, + PIX_C_I, + PIX_TOG, + PIX_ONE, + PIX_ZER, +}; + +struct phy_reg_offsets { + u32 GCR; /* Global Control Register */ + u32 CMCR1; /* Current Mode Control Register 1 */ + u32 CMCR2; /* Current Mode Control Register 2 */ + u32 SCR; /* Serial Control Register */ + u32 BCR1; /* Bias Control Register 1 */ + u32 BCR2; /* Bias Control Register 2 */ + u32 BCR3; /* Bias Control Register 3 */ + u32 MPLCR; /* Monitor PLL Lock Control Register */ + u32 DCR; /* Debug Control Register */ + u32 SSR1; /* Spare Status Register 1 */ + u32 CFGCR; /* Configuration Control Register */ + u32 PLLCR1; /* PLL_MODE 1 Control Register */ + u32 PLLCR2; /* PLL_MODE 2 Control Register */ + u32 PLLSR; /* PLL Status Register */ + u32 PLLSDCR1; /* PLL_SD_1 Control Register */ + u32 PLLSDCR2; /* PLL_SD_2 Control Register */ + u32 PLLTWGCR1;/* PLL_TWG_1 Control Register */ + u32 PLLTWGCR2;/* PLL_TWG_2 Control Register */ + u32 PLLCPCR; /* PLL_CP Control Register */ + u32 PLLTESTCR;/* PLL_TEST Control Register */ +}; + +struct lvds_phy_info { + u32 base; + struct phy_reg_offsets ofs; +}; + +static struct lvds_phy_info lvds_phy_16ff_primary = { + .base = 0x1000, + .ofs = { + .GCR = 0x0, + .CMCR1 = 0xC, + .CMCR2 = 0x10, + .SCR = 0x20, + .BCR1 = 0x2C, + .BCR2 = 0x30, + .BCR3 = 0x34, + .MPLCR = 0x64, + .DCR = 0x84, + .SSR1 = 0x88, + .CFGCR = 0xA0, + .PLLCR1 = 0xC0, + .PLLCR2 = 0xC4, + .PLLSR = 0xC8, + .PLLSDCR1 = 0xCC, + .PLLSDCR2 = 0xD0, + .PLLTWGCR1 = 0xD4, + .PLLTWGCR2 = 0xD8, + .PLLCPCR = 0xE0, + .PLLTESTCR = 0xE8, + } +}; + +static struct lvds_phy_info lvds_phy_16ff_secondary = { + .base = 0x1100, + .ofs = { + .GCR = 0x0, + .CMCR1 = 0xC, + .CMCR2 = 0x10, + .SCR = 0x20, + .BCR1 = 0x2C, + .BCR2 = 0x30, + .BCR3 = 0x34, + .MPLCR = 0x64, + .DCR = 0x84, + .SSR1 = 0x88, + .CFGCR = 0xA0, + .PLLCR1 = 0xC0, + .PLLCR2 = 0xC4, + .PLLSR = 0xC8, + .PLLSDCR1 = 0xCC, + .PLLSDCR2 = 0xD0, + .PLLTWGCR1 = 0xD4, + .PLLTWGCR2 = 0xD8, + .PLLCPCR = 0xE0, + .PLLTESTCR = 0xE8, + } +}; + +struct stm_lvds { + void __iomem *base; + struct device *dev; + struct clk *pclk; /* APB peripheral clock */ + struct clk *pllref_clk; /* Reference clock for the internal PLL */ + struct clk_hw lvds_ck_px; /* Pixel clock */ + u32 pixel_clock_rate; /* Pixel clock rate */ + + struct lvds_phy_info *primary; + struct lvds_phy_info *secondary; + + struct drm_bridge lvds_bridge; + struct drm_bridge *next_bridge; + struct drm_connector connector; + struct drm_encoder *encoder; + struct drm_panel *panel; + + u32 hw_version; + u32 link_type; +}; + +#define bridge_to_stm_lvds(b) \ + container_of(b, struct stm_lvds, lvds_bridge) + +#define connector_to_stm_lvds(c) \ + container_of(c, struct stm_lvds, connector) + +#define lvds_is_dual_link(lvds) \ + ({ \ + typeof(lvds) __lvds = (lvds); \ + __lvds == LVDS_DUAL_LINK_EVEN_ODD_PIXELS || \ + __lvds == LVDS_DUAL_LINK_ODD_EVEN_PIXELS; \ + }) + +static inline void lvds_write(struct stm_lvds *lvds, u32 reg, u32 val) +{ + writel(val, lvds->base + reg); +} + +static inline u32 lvds_read(struct stm_lvds *lvds, u32 reg) +{ + return readl(lvds->base + reg); +} + +static inline void lvds_set(struct stm_lvds *lvds, u32 reg, u32 mask) +{ + lvds_write(lvds, reg, lvds_read(lvds, reg) | mask); +} + +static inline void lvds_clear(struct stm_lvds *lvds, u32 reg, u32 mask) +{ + lvds_write(lvds, reg, lvds_read(lvds, reg) & ~mask); +} + +/* + * Expected JEIDA-RGB888 data to be sent in LSB format + * bit6 ............................bit0 + * CHAN0 {ONE, ONE, ZERO, ZERO, ZERO, ONE, ONE} + * CHAN1 {G2, R7, R6, R5, R4, R3, R2} + * CHAN2 {B3, B2, G7, G6, G5, G4, G3} + * CHAN3 {DE, VS, HS, B7, B6, B5, B4} + * CHAN4 {CE, B1, B0, G1, G0, R1, R0} + */ +static enum lvds_pixel lvds_bitmap_jeida_rgb888[5][7] = { + { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE }, + { PIX_G_2, PIX_R_7, PIX_R_6, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2 }, + { PIX_B_3, PIX_B_2, PIX_G_7, PIX_G_6, PIX_G_5, PIX_G_4, PIX_G_3 }, + { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_7, PIX_B_6, PIX_B_5, PIX_B_4 }, + { PIX_C_E, PIX_B_1, PIX_B_0, PIX_G_1, PIX_G_0, PIX_R_1, PIX_R_0 } +}; + +/* + * Expected VESA-RGB888 data to be sent in LSB format + * bit6 ............................bit0 + * CHAN0 {ONE, ONE, ZERO, ZERO, ZERO, ONE, ONE} + * CHAN1 {G0, R5, R4, R3, R2, R1, R0} + * CHAN2 {B1, B0, G5, G4, G3, G2, G1} + * CHAN3 {DE, VS, HS, B5, B4, B3, B2} + * CHAN4 {CE, B7, B6, G7, G6, R7, R6} + */ +static enum lvds_pixel lvds_bitmap_vesa_rgb888[5][7] = { + { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE }, + { PIX_G_0, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2, PIX_R_1, PIX_R_0 }, + { PIX_B_1, PIX_B_0, PIX_G_5, PIX_G_4, PIX_G_3, PIX_G_2, PIX_G_1 }, + { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_5, PIX_B_4, PIX_B_3, PIX_B_2 }, + { PIX_C_E, PIX_B_7, PIX_B_6, PIX_G_7, PIX_G_6, PIX_R_7, PIX_R_6 } +}; + +/* + * Clocks and PHY related functions + */ +static int lvds_pll_enable(struct stm_lvds *lvds, struct lvds_phy_info *phy) +{ + struct drm_device *drm = lvds->lvds_bridge.dev; + u32 lvds_gcr; + int val, ret; + + /* + * PLL lock timing control for the monitor unmask after startup (pll_en) + * Adjusted value so that the masking window is opened at start-up + */ + lvds_write(lvds, phy->base + phy->ofs.MPLCR, (0x200 - 0x160) << 16); + + /* Enable bias */ + lvds_write(lvds, phy->base + phy->ofs.BCR2, PHY_BCR2_BIAS_EN); + + /* Enable DP, LS, BIT clock output */ + lvds_gcr = PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT; + lvds_set(lvds, phy->base + phy->ofs.GCR, lvds_gcr); + + /* Power up all output dividers */ + lvds_set(lvds, phy->base + phy->ofs.PLLTESTCR, PHY_PLLTESTCR_EN); + lvds_set(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_DIV_EN); + + /* Set PHY in serial transmission mode */ + lvds_set(lvds, phy->base + phy->ofs.SCR, PHY_SCR_TX_EN); + + /* Enable the LVDS PLL & wait for its lock */ + lvds_set(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_PLL_EN); + ret = readl_poll_timeout_atomic(lvds->base + phy->base + phy->ofs.PLLSR, + val, val & PHY_PLLSR_PLL_LOCK, + SLEEP_US, TIMEOUT_US); + if (ret) + drm_err(drm, "!TIMEOUT! waiting PLL, let's continue\n"); + + /* WCLKCR_SECND_CLKPIX_SEL is for dual link */ + lvds_write(lvds, LVDS_WCLKCR, WCLKCR_SECND_CLKPIX_SEL); + + lvds_set(lvds, phy->ofs.PLLTESTCR, PHY_PLLTESTCR_CLK_EN); + + return ret; +} + +static int pll_get_clkout_khz(int clkin_khz, int bdiv, int mdiv, int ndiv) +{ + int divisor = ndiv * bdiv; + + /* Prevents from division by 0 */ + if (!divisor) + return 0; + + return clkin_khz * mdiv / divisor; +} + +#define TDIV 70 +#define NDIV_MIN 2 +#define NDIV_MAX 6 +#define BDIV_MIN 2 +#define BDIV_MAX 6 +#define MDIV_MIN 1 +#define MDIV_MAX 1023 + +static int lvds_pll_get_params(struct stm_lvds *lvds, + unsigned int clkin_khz, unsigned int clkout_khz, + unsigned int *bdiv, unsigned int *mdiv, unsigned int *ndiv) +{ + int delta, best_delta; /* all in khz */ + int i, o, n; + + /* Early checks preventing division by 0 & odd results */ + if (clkin_khz <= 0 || clkout_khz <= 0) + return -EINVAL; + + best_delta = 1000000; /* big started value (1000000khz) */ + + for (i = NDIV_MIN; i <= NDIV_MAX; i++) { + for (o = BDIV_MIN; o <= BDIV_MAX; o++) { + n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz); + /* Check ndiv according to vco range */ + if (n < MDIV_MIN || n > MDIV_MAX) + continue; + /* Check if new delta is better & saves parameters */ + delta = pll_get_clkout_khz(clkin_khz, i, n, o) - clkout_khz; + if (delta < 0) + delta = -delta; + if (delta < best_delta) { + *ndiv = i; + *mdiv = n; + *bdiv = o; + best_delta = delta; + } + /* fast return in case of "perfect result" */ + if (!delta) + return 0; + } + } + + return 0; +} + +static void lvds_pll_config(struct stm_lvds *lvds, struct lvds_phy_info *phy) +{ + unsigned int pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0; + struct clk_hw *hwclk; + int multiplier; + + /* + * The LVDS PHY includes a low power low jitter high performance and + * highly configuration Phase Locked Loop supporting integer and + * fractional multiplication ratios and Spread Spectrum Clocking. In + * integer mode, the only software supported feature for now, the PLL is + * made of a pre-divider NDIV, a feedback multiplier MDIV, followed by + * several post-dividers, each one with a specific application. + * + * ,------. ,-----. ,-----. + * Fref --> | NDIV | -Fpdf-> | PFD | --> | VCO | --------> Fvco + * `------' ,-> | | `-----' | + * | `-----' | + * | ,------. | + * `-------- | MDIV | <-----' + * `------' + * + * From the output of the VCO, the clock can be optionally extracted on + * the RCC clock observer, with a divider TDIV, for testing purpose, or + * is passed through a programmable post-divider BDIV. Finally, the + * frequency can be divided further with two fixed dividers. + * + * ,--------. + * ,-----> | DP div | ----------------> Fdp + * ,------. | `--------' + * Fvco --> | BDIV | ------------------------------------> Fbit + * | `------' ,------. | + * `-------------> | TDIV | --.---------------------> ClkObs + * '------' | ,--------. + * `--> | LS div | ------> Fls + * '--------' + * + * The LS and DP clock dividers operate at a fixed ratio of 7 and 3.5 + * respectively with regards to fbit. LS divider converts the bit clock + * to a pixel clock per lane per clock sample (Fls). This is useful + * when used to generate a dot clock for the display unit RGB output, + * and DP divider is. + */ + + hwclk = __clk_get_hw(lvds->pllref_clk); + if (!hwclk) + return; + + pll_in_khz = clk_hw_get_rate(hwclk) / 1000; + + if (lvds_is_dual_link(lvds->link_type)) + multiplier = 2; + else + multiplier = 1; + + lvds_pll_get_params(lvds, pll_in_khz, + lvds->pixel_clock_rate * 7 / 1000 / multiplier, + &bdiv, &mdiv, &ndiv); + + /* Set BDIV, MDIV and NDIV */ + lvds_write(lvds, phy->base + phy->ofs.PLLCR2, ndiv << 16); + lvds_set(lvds, phy->base + phy->ofs.PLLCR2, bdiv); + lvds_write(lvds, phy->base + phy->ofs.PLLSDCR1, mdiv); + + /* Hardcode TDIV as dynamic values are not yet implemented */ + lvds_write(lvds, phy->base + phy->ofs.PLLTESTCR, TDIV << 16); + + /* + * For now, PLL just needs to be in integer mode + * Fractional and spread spectrum clocking are not yet implemented + * + * PLL integer mode: + * - PMRY_PLL_TWG_STEP = PMRY_PLL_SD_INT_RATIO + * - EN_TWG = 0 + * - EN_SD = 0 + * - DOWN_SPREAD = 0 + * + * PLL fractional mode: + * - EN_TWG = 0 + * - EN_SD = 1 + * - DOWN_SPREAD = 0 + * + * Spread Spectrum Clocking + * - EN_TWG = 1 + * - EN_SD = 1 + */ + + /* Disable TWG and SD */ + lvds_clear(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_EN_TWG | PHY_PLLCR1_EN_SD); + + /* Power up bias and PLL dividers */ + lvds_set(lvds, phy->base + phy->ofs.DCR, PHY_DCR_POWER_OK); + lvds_set(lvds, phy->base + phy->ofs.CMCR1, PHY_CMCR_CM_EN_DL); + lvds_set(lvds, phy->base + phy->ofs.CMCR2, PHY_CMCR_CM_EN_DL4); + + /* Set up voltage mode */ + lvds_set(lvds, phy->base + phy->ofs.PLLCPCR, 0x1); + lvds_set(lvds, phy->base + phy->ofs.BCR3, PHY_BCR3_VM_EN_DL); + lvds_set(lvds, phy->base + phy->ofs.BCR1, PHY_BCR1_EN_BIAS_DL); + /* Enable digital datalanes */ + lvds_set(lvds, phy->base + phy->ofs.CFGCR, PHY_CFGCR_EN_DIG_DL); +} + +static int lvds_pixel_clk_enable(struct clk_hw *hw) +{ + struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px); + struct drm_device *drm = lvds->lvds_bridge.dev; + struct lvds_phy_info *phy; + int ret; + + ret = clk_prepare_enable(lvds->pclk); + if (ret) { + drm_err(drm, "Failed to enable lvds peripheral clk\n"); + return ret; + } + + ret = clk_prepare_enable(lvds->pllref_clk); + if (ret) { + drm_err(drm, "Failed to enable lvds reference clk\n"); + clk_disable_unprepare(lvds->pclk); + return ret; + } + + /* In case we are operating in dual link the second PHY is set before the primary PHY. */ + if (lvds->secondary) { + phy = lvds->secondary; + + /* Release LVDS PHY from reset mode */ + lvds_set(lvds, phy->base + phy->ofs.GCR, PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ); + lvds_pll_config(lvds, phy); + + ret = lvds_pll_enable(lvds, phy); + if (ret) { + drm_err(drm, "Failed to enable secondary PHY PLL: %d\n", ret); + return ret; + } + } + + if (lvds->primary) { + phy = lvds->primary; + + /* Release LVDS PHY from reset mode */ + lvds_set(lvds, phy->base + phy->ofs.GCR, PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ); + lvds_pll_config(lvds, phy); + + ret = lvds_pll_enable(lvds, phy); + if (ret) { + drm_err(drm, "Failed to enable primary PHY PLL: %d\n", ret); + return ret; + } + } + + return 0; +} + +static void lvds_pixel_clk_disable(struct clk_hw *hw) +{ + struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px); + + /* + * For each PHY: + * Disable DP, LS, BIT clock outputs + * Shutdown the PLL + * Assert LVDS PHY in reset mode + */ + + if (lvds->primary) { + lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.GCR, + (PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT)); + lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.PLLCR1, + PHY_PLLCR1_PLL_EN); + lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.GCR, + PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ); + } + + if (lvds->secondary) { + lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.GCR, + (PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT)); + lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.PLLCR1, + PHY_PLLCR1_PLL_EN); + lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.GCR, + PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ); + } + + clk_disable_unprepare(lvds->pllref_clk); + clk_disable_unprepare(lvds->pclk); +} + +static unsigned long lvds_pixel_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px); + struct drm_device *drm = lvds->lvds_bridge.dev; + unsigned int pll_in_khz, bdiv, mdiv, ndiv; + int ret, multiplier, pll_out_khz; + u32 val; + + ret = clk_prepare_enable(lvds->pclk); + if (ret) { + drm_err(drm, "Failed to enable lvds peripheral clk\n"); + return 0; + } + + if (lvds_is_dual_link(lvds->link_type)) + multiplier = 2; + else + multiplier = 1; + + val = lvds_read(lvds, lvds->primary->base + lvds->primary->ofs.PLLCR2); + + ndiv = (val & PHY_PLLCR2_NDIV) >> 16; + bdiv = (val & PHY_PLLCR2_BDIV) >> 0; + + mdiv = (unsigned int)lvds_read(lvds, + lvds->primary->base + lvds->primary->ofs.PLLSDCR1); + + pll_in_khz = (unsigned int)(parent_rate / 1000); + + /* Compute values if not yet accessible */ + if (val == 0 || mdiv == 0) { + lvds_pll_get_params(lvds, pll_in_khz, + lvds->pixel_clock_rate * 7 / 1000 / multiplier, + &bdiv, &mdiv, &ndiv); + } + + pll_out_khz = pll_get_clkout_khz(pll_in_khz, bdiv, mdiv, ndiv); + drm_dbg(drm, "ndiv %d , bdiv %d, mdiv %d, pll_out_khz %d\n", + ndiv, bdiv, mdiv, pll_out_khz); + + /* + * 1/7 because for each pixel in 1 lane there is 7 bits + * We want pixclk, not bitclk + */ + lvds->pixel_clock_rate = pll_out_khz * 1000 * multiplier / 7; + + clk_disable_unprepare(lvds->pclk); + + return (unsigned long)lvds->pixel_clock_rate; +} + +static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px); + unsigned int pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0; + const struct drm_connector *connector; + const struct drm_display_mode *mode; + int multiplier; + + connector = &lvds->connector; + if (!connector) + return -EINVAL; + + if (list_empty(&connector->modes)) { + drm_dbg(connector->dev, "connector: empty modes list\n"); + return -EINVAL; + } + + mode = list_first_entry(&connector->modes, + struct drm_display_mode, head); + + pll_in_khz = (unsigned int)(*parent_rate / 1000); + + if (lvds_is_dual_link(lvds->link_type)) + multiplier = 2; + else + multiplier = 1; + + lvds_pll_get_params(lvds, pll_in_khz, mode->clock * 7 / multiplier, &bdiv, &mdiv, &ndiv); + + /* + * 1/7 because for each pixel in 1 lane there is 7 bits + * We want pixclk, not bitclk + */ + lvds->pixel_clock_rate = (unsigned long)pll_get_clkout_khz(pll_in_khz, bdiv, mdiv, ndiv) + * 1000 * multiplier / 7; + + return lvds->pixel_clock_rate; +} + +static const struct clk_ops lvds_pixel_clk_ops = { + .enable = lvds_pixel_clk_enable, + .disable = lvds_pixel_clk_disable, + .recalc_rate = lvds_pixel_clk_recalc_rate, + .round_rate = lvds_pixel_clk_round_rate, +}; + +static const struct clk_init_data clk_data = { + .name = "clk_pix_lvds", + .ops = &lvds_pixel_clk_ops, + .parent_names = (const char * []) {"ck_ker_lvdsphy"}, + .num_parents = 1, + .flags = CLK_IGNORE_UNUSED, +}; + +static void lvds_pixel_clk_unregister(void *data) +{ + struct stm_lvds *lvds = data; + + of_clk_del_provider(lvds->dev->of_node); + clk_hw_unregister(&lvds->lvds_ck_px); +} + +static int lvds_pixel_clk_register(struct stm_lvds *lvds) +{ + struct device_node *node = lvds->dev->of_node; + int ret; + + lvds->lvds_ck_px.init = &clk_data; + + /* set the rate by default at 148500000 */ + lvds->pixel_clock_rate = 148500000; + + ret = clk_hw_register(lvds->dev, &lvds->lvds_ck_px); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, + &lvds->lvds_ck_px); + if (ret) + clk_hw_unregister(&lvds->lvds_ck_px); + + return ret; +} + +/* + * Host configuration related + */ +static void lvds_config_data_mapping(struct stm_lvds *lvds) +{ + struct drm_device *drm = lvds->lvds_bridge.dev; + const struct drm_display_info *info; + enum lvds_pixel (*bitmap)[7]; + u32 lvds_dmlcr, lvds_dmmcr; + int i; + + info = &(&lvds->connector)->display_info; + if (!info->num_bus_formats || !info->bus_formats) { + drm_warn(drm, "No LVDS bus format reported\n"); + return; + } + + switch (info->bus_formats[0]) { + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* VESA-RGB666 */ + drm_warn(drm, "Pixel format with data mapping not yet supported.\n"); + return; + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* VESA-RGB888 */ + bitmap = lvds_bitmap_vesa_rgb888; + break; + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* JEIDA-RGB888 */ + bitmap = lvds_bitmap_jeida_rgb888; + break; + default: + drm_warn(drm, "Unsupported LVDS bus format 0x%04x\n", info->bus_formats[0]); + return; + } + + /* Set bitmap for each lane */ + for (i = 0; i < 5; i++) { + lvds_dmlcr = ((bitmap[i][0]) + + (bitmap[i][1] << 5) + + (bitmap[i][2] << 10) + + (bitmap[i][3] << 15)); + lvds_dmmcr = ((bitmap[i][4]) + + (bitmap[i][5] << 5) + + (bitmap[i][6] << 10)); + + lvds_write(lvds, LVDS_DMLCR(i), lvds_dmlcr); + lvds_write(lvds, LVDS_DMMCR(i), lvds_dmmcr); + } +} + +static void lvds_config_mode(struct stm_lvds *lvds) +{ + u32 bus_flags, lvds_cr = 0, lvds_cdl1cr = 0, lvds_cdl2cr = 0; + const struct drm_display_mode *mode; + const struct drm_connector *connector; + + connector = &lvds->connector; + if (!connector) + return; + + if (list_empty(&connector->modes)) { + drm_dbg(connector->dev, "connector: empty modes list\n"); + return; + } + + bus_flags = connector->display_info.bus_flags; + mode = list_first_entry(&connector->modes, + struct drm_display_mode, head); + + lvds_clear(lvds, LVDS_CR, CR_LKMOD); + lvds_clear(lvds, LVDS_CDL1CR, CDLCR_DISTR0 | CDLCR_DISTR1 | CDLCR_DISTR2 | + CDLCR_DISTR3 | CDLCR_DISTR4); + lvds_clear(lvds, LVDS_CDL2CR, CDLCR_DISTR0 | CDLCR_DISTR1 | CDLCR_DISTR2 | + CDLCR_DISTR3 | CDLCR_DISTR4); + + /* Set channel distribution */ + if (lvds->primary) + lvds_cdl1cr = CDL1CR_DEFAULT; + + if (lvds->secondary) { + lvds_cr |= CR_LKMOD; + lvds_cdl2cr = CDL2CR_DEFAULT; + } + + /* Set signal polarity */ + if (bus_flags & DRM_BUS_FLAG_DE_LOW) + lvds_cr |= CR_DEPOL; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + lvds_cr |= CR_HSPOL; + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + lvds_cr |= CR_VSPOL; + + switch (lvds->link_type) { + case LVDS_DUAL_LINK_EVEN_ODD_PIXELS: /* LKPHA = 0 */ + lvds_cr &= ~CR_LKPHA; + break; + case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: /* LKPHA = 1 */ + lvds_cr |= CR_LKPHA; + break; + default: + drm_notice(lvds->lvds_bridge.dev, "No phase precised, setting default\n"); + lvds_cr &= ~CR_LKPHA; + break; + } + + /* Write config to registers */ + lvds_set(lvds, LVDS_CR, lvds_cr); + lvds_write(lvds, LVDS_CDL1CR, lvds_cdl1cr); + lvds_write(lvds, LVDS_CDL2CR, lvds_cdl2cr); +} + +static int lvds_connector_get_modes(struct drm_connector *connector) +{ + struct stm_lvds *lvds = connector_to_stm_lvds(connector); + + return drm_panel_get_modes(lvds->panel, connector); +} + +static int lvds_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + const struct drm_display_mode *panel_mode; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state) + return -EINVAL; + + if (list_empty(&connector->modes)) { + drm_dbg(connector->dev, "connector: empty modes list\n"); + return -EINVAL; + } + + if (!conn_state->crtc) + return -EINVAL; + + panel_mode = list_first_entry(&connector->modes, + struct drm_display_mode, head); + + /* We're not allowed to modify the resolution. */ + crtc_state = drm_atomic_get_crtc_state(state, conn_state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + if (crtc_state->mode.hdisplay != panel_mode->hdisplay || + crtc_state->mode.vdisplay != panel_mode->vdisplay) + return -EINVAL; + + /* The flat panel mode is fixed, just copy it to the adjusted mode. */ + drm_mode_copy(&crtc_state->adjusted_mode, panel_mode); + + return 0; +} + +static const struct drm_connector_helper_funcs lvds_conn_helper_funcs = { + .get_modes = lvds_connector_get_modes, + .atomic_check = lvds_connector_atomic_check, +}; + +static const struct drm_connector_funcs lvds_conn_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int lvds_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct stm_lvds *lvds = bridge_to_stm_lvds(bridge); + struct drm_connector *connector = &lvds->connector; + struct drm_encoder *encoder = bridge->encoder; + int ret; + + if (!bridge->encoder) { + drm_err(bridge->dev, "Parent encoder object not found\n"); + return -ENODEV; + } + + /* Set the encoder type as caller does not know it */ + bridge->encoder->encoder_type = DRM_MODE_ENCODER_LVDS; + + /* No cloning support */ + bridge->encoder->possible_clones = 0; + + /* If we have a next bridge just attach it. */ + if (lvds->next_bridge) + return drm_bridge_attach(bridge->encoder, lvds->next_bridge, + bridge, flags); + + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + drm_err(bridge->dev, "Fix bridge driver to make connector optional!"); + return -EINVAL; + } + + /* Otherwise if we have a panel, create a connector. */ + if (!lvds->panel) + return 0; + + ret = drm_connector_init(bridge->dev, connector, + &lvds_conn_funcs, DRM_MODE_CONNECTOR_LVDS); + if (ret < 0) + return ret; + + drm_connector_helper_add(connector, &lvds_conn_helper_funcs); + + ret = drm_connector_attach_encoder(connector, encoder); + + return ret; +} + +static void lvds_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct drm_atomic_state *state = old_bridge_state->base.state; + struct stm_lvds *lvds = bridge_to_stm_lvds(bridge); + struct drm_connector_state *conn_state; + struct drm_connector *connector; + int ret; + + ret = clk_prepare_enable(lvds->pclk); + if (ret) { + drm_err(bridge->dev, "Failed to enable lvds peripheral clk\n"); + return; + } + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (!connector) + return; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state) + return; + + lvds_config_mode(lvds); + + /* Set Data Mapping */ + lvds_config_data_mapping(lvds); + + /* Turn the output on. */ + lvds_set(lvds, LVDS_CR, CR_LVDSEN); + + if (lvds->panel) { + drm_panel_prepare(lvds->panel); + drm_panel_enable(lvds->panel); + } +} + +static void lvds_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct stm_lvds *lvds = bridge_to_stm_lvds(bridge); + + if (lvds->panel) { + drm_panel_disable(lvds->panel); + drm_panel_unprepare(lvds->panel); + } + + /* Disable LVDS module */ + lvds_clear(lvds, LVDS_CR, CR_LVDSEN); + + clk_disable_unprepare(lvds->pclk); +} + +static const struct drm_bridge_funcs lvds_bridge_funcs = { + .attach = lvds_attach, + .atomic_enable = lvds_atomic_enable, + .atomic_disable = lvds_atomic_disable, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, +}; + +static int lvds_probe(struct platform_device *pdev) +{ + struct device_node *port1, *port2, *remote; + struct device *dev = &pdev->dev; + struct reset_control *rstc; + struct stm_lvds *lvds; + int ret, dual_link; + + dev_dbg(dev, "Probing LVDS driver...\n"); + + lvds = devm_kzalloc(dev, sizeof(*lvds), GFP_KERNEL); + if (!lvds) + return -ENOMEM; + + lvds->dev = dev; + + ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, + &lvds->panel, &lvds->next_bridge); + if (ret) { + dev_err_probe(dev, ret, "Panel not found\n"); + return ret; + } + + lvds->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(lvds->base)) { + ret = PTR_ERR(lvds->base); + dev_err(dev, "Unable to get regs %d\n", ret); + return ret; + } + + lvds->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(lvds->pclk)) { + ret = PTR_ERR(lvds->pclk); + dev_err(dev, "Unable to get peripheral clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(lvds->pclk); + if (ret) { + dev_err(dev, "%s: Failed to enable peripheral clk\n", __func__); + return ret; + } + + rstc = devm_reset_control_get_exclusive(dev, NULL); + + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + goto err_lvds_probe; + } + + reset_control_assert(rstc); + usleep_range(10, 20); + reset_control_deassert(rstc); + + port1 = of_graph_get_port_by_id(dev->of_node, 1); + port2 = of_graph_get_port_by_id(dev->of_node, 2); + dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2); + + switch (dual_link) { + case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: + lvds->link_type = LVDS_DUAL_LINK_ODD_EVEN_PIXELS; + lvds->primary = &lvds_phy_16ff_primary; + lvds->secondary = &lvds_phy_16ff_secondary; + break; + case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: + lvds->link_type = LVDS_DUAL_LINK_EVEN_ODD_PIXELS; + lvds->primary = &lvds_phy_16ff_primary; + lvds->secondary = &lvds_phy_16ff_secondary; + break; + case -EINVAL: + /* + * drm_of_lvds_get_dual_pixel_order returns 4 possible values. + * In the case where the returned value is an error, it can be + * either ENODEV or EINVAL. Seeing the structure of this + * function, EINVAL means that either port1 or port2 is not + * present in the device tree. + * In that case, the lvds panel can be a single link panel, or + * there is a semantical error in the device tree code. + */ + remote = of_get_next_available_child(port1, NULL); + if (remote) { + if (of_graph_get_remote_endpoint(remote)) { + lvds->link_type = LVDS_SINGLE_LINK_PRIMARY; + lvds->primary = &lvds_phy_16ff_primary; + lvds->secondary = NULL; + } else { + ret = -EINVAL; + } + + of_node_put(remote); + } + + remote = of_get_next_available_child(port2, NULL); + if (remote) { + if (of_graph_get_remote_endpoint(remote)) { + lvds->link_type = LVDS_SINGLE_LINK_SECONDARY; + lvds->primary = NULL; + lvds->secondary = &lvds_phy_16ff_secondary; + } else { + ret = (ret == -EINVAL) ? -EINVAL : 0; + } + + of_node_put(remote); + } + break; + default: + ret = -EINVAL; + goto err_lvds_probe; + } + of_node_put(port1); + of_node_put(port2); + + lvds->pllref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(lvds->pllref_clk)) { + ret = PTR_ERR(lvds->pllref_clk); + dev_err(dev, "Unable to get reference clock: %d\n", ret); + goto err_lvds_probe; + } + + ret = lvds_pixel_clk_register(lvds); + if (ret) { + dev_err(dev, "Failed to register LVDS pixel clock: %d\n", ret); + goto err_lvds_probe; + } + + lvds->lvds_bridge.funcs = &lvds_bridge_funcs; + lvds->lvds_bridge.of_node = dev->of_node; + lvds->hw_version = lvds_read(lvds, LVDS_VERR); + + dev_info(dev, "version 0x%02x initialized\n", lvds->hw_version); + + drm_bridge_add(&lvds->lvds_bridge); + + platform_set_drvdata(pdev, lvds); + + clk_disable_unprepare(lvds->pclk); + + return 0; + +err_lvds_probe: + clk_disable_unprepare(lvds->pclk); + + return ret; +} + +static int lvds_remove(struct platform_device *pdev) +{ + struct stm_lvds *lvds = platform_get_drvdata(pdev); + + lvds_pixel_clk_unregister(lvds); + + drm_bridge_remove(&lvds->lvds_bridge); + + return 0; +} + +static const struct of_device_id lvds_dt_ids[] = { + { + .compatible = "st,stm32mp25-lvds", + .data = NULL + }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, lvds_dt_ids); + +static struct platform_driver lvds_platform_driver = { + .probe = lvds_probe, + .remove = lvds_remove, + .driver = { + .name = "stm32-display-lvds", + .owner = THIS_MODULE, + .of_match_table = lvds_dt_ids, + }, +}; + +module_platform_driver(lvds_platform_driver); + +MODULE_AUTHOR("Raphaƫl Gallais-Pou <raphael.gallais-pou@foss.st.com>"); +MODULE_AUTHOR("Philippe Cornu <philippe.cornu@foss.st.com>"); +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@foss.st.com>"); +MODULE_DESCRIPTION("STMicroelectronics LVDS Display Interface Transmitter DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c index 6f1457bd21d9..59c8408c453c 100644 --- a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c +++ b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c @@ -992,6 +992,17 @@ static const struct drm_cmdline_tv_option_test drm_cmdline_tv_option_tests[] = { TV_OPT_TEST(PAL_M, "720x480i,tv_mode=PAL-M", drm_mode_analog_ntsc_480i), TV_OPT_TEST(PAL_N, "720x576i,tv_mode=PAL-N", drm_mode_analog_pal_576i), TV_OPT_TEST(SECAM, "720x576i,tv_mode=SECAM", drm_mode_analog_pal_576i), + { + .name = "MONO_525", + .cmdline = "720x480i,tv_mode=Mono", + .mode_fn = drm_mode_analog_ntsc_480i, + .tv_mode = DRM_MODE_TV_MODE_MONOCHROME, + }, { + .name = "MONO_625", + .cmdline = "720x576i,tv_mode=Mono", + .mode_fn = drm_mode_analog_pal_576i, + .tv_mode = DRM_MODE_TV_MODE_MONOCHROME, + }, }; static void drm_cmdline_tv_option_desc(const struct drm_cmdline_tv_option_test *t, diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 2812b123a79c..15e36a8db685 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -777,6 +777,7 @@ struct drm_get_tv_mode_from_name_test drm_get_tv_mode_from_name_valid_tests[] = TV_MODE_NAME("PAL-M", DRM_MODE_TV_MODE_PAL_M), TV_MODE_NAME("PAL-N", DRM_MODE_TV_MODE_PAL_N), TV_MODE_NAME("SECAM", DRM_MODE_TV_MODE_SECAM), + TV_MODE_NAME("Mono", DRM_MODE_TV_MODE_MONOCHROME), }; static void diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index 7029f7a2eb4d..6ed51f99e133 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -130,7 +130,38 @@ static void drm_test_modes_analog_tv_pal_576i_inlined(struct kunit *test) KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected, mode)); } +static void drm_test_modes_analog_tv_mono_576i(struct kunit *test) +{ + struct drm_test_modes_priv *priv = test->priv; + struct drm_display_mode *mode; + + mode = drm_analog_tv_mode(priv->drm, + DRM_MODE_TV_MODE_MONOCHROME, + 13500 * HZ_PER_KHZ, 720, 576, + true); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_EXPECT_EQ(test, drm_mode_vrefresh(mode), 50); + KUNIT_EXPECT_EQ(test, mode->hdisplay, 720); + + /* BT.601 defines hsync_start at 732 for 576i */ + KUNIT_EXPECT_EQ(test, mode->hsync_start, 732); + + /* + * The PAL standard expects a line to take 64us. With a pixel + * clock of 13.5 MHz, a pixel takes around 74ns, so we need to + * have 64000ns / 74ns = 864. + * + * This is also mandated by BT.601. + */ + KUNIT_EXPECT_EQ(test, mode->htotal, 864); + + KUNIT_EXPECT_EQ(test, mode->vdisplay, 576); + KUNIT_EXPECT_EQ(test, mode->vtotal, 625); +} + static struct kunit_case drm_modes_analog_tv_tests[] = { + KUNIT_CASE(drm_test_modes_analog_tv_mono_576i), KUNIT_CASE(drm_test_modes_analog_tv_ntsc_480i), KUNIT_CASE(drm_test_modes_analog_tv_ntsc_480i_inlined), KUNIT_CASE(drm_test_modes_analog_tv_pal_576i), |