162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2023 Loongson Technology Corporation Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 762306a36Sopenharmony_ci#include <drm/drm_edid.h> 862306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "lsdc_drv.h" 1162306a36Sopenharmony_ci#include "lsdc_output.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * The display controller in the LS7A1000 exports two DVO interfaces, thus 1562306a36Sopenharmony_ci * external encoder is required, except connected to the DPI panel directly. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * ___________________ _________ 1862306a36Sopenharmony_ci * | -------| | | 1962306a36Sopenharmony_ci * | CRTC0 --> | DVO0 ----> Encoder0 ---> Connector0 ---> | Display | 2062306a36Sopenharmony_ci * | _ _ -------| ^ ^ |_________| 2162306a36Sopenharmony_ci * | | | | | +------+ | | | 2262306a36Sopenharmony_ci * | |_| |_| | i2c6 | <--------+-------------+ 2362306a36Sopenharmony_ci * | +------+ | 2462306a36Sopenharmony_ci * | | 2562306a36Sopenharmony_ci * | DC in LS7A1000 | 2662306a36Sopenharmony_ci * | | 2762306a36Sopenharmony_ci * | _ _ +------+ | 2862306a36Sopenharmony_ci * | | | | | | i2c7 | <--------+-------------+ 2962306a36Sopenharmony_ci * | |_| |_| +------+ | | | _________ 3062306a36Sopenharmony_ci * | -------| | | | | 3162306a36Sopenharmony_ci * | CRTC1 --> | DVO1 ----> Encoder1 ---> Connector1 ---> | Panel | 3262306a36Sopenharmony_ci * | -------| |_________| 3362306a36Sopenharmony_ci * |___________________| 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Currently, we assume the external encoders connected to the DVO are 3662306a36Sopenharmony_ci * transparent. Loongson's DVO interface can directly drive RGB888 panels. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * TODO: Add support for non-transparent encoders 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned int num = 0; 4462306a36Sopenharmony_ci struct edid *edid; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (conn->ddc) { 4762306a36Sopenharmony_ci edid = drm_get_edid(conn, conn->ddc); 4862306a36Sopenharmony_ci if (edid) { 4962306a36Sopenharmony_ci drm_connector_update_edid_property(conn, edid); 5062306a36Sopenharmony_ci num = drm_add_edid_modes(conn, edid); 5162306a36Sopenharmony_ci kfree(edid); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return num; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci num = drm_add_modes_noedid(conn, 1920, 1200); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci drm_set_preferred_mode(conn, 1024, 768); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return num; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct drm_encoder * 6562306a36Sopenharmony_cils7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector, 6662306a36Sopenharmony_ci struct drm_atomic_state *state) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct lsdc_output *output = connector_to_lsdc_output(connector); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return &output->encoder; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs 7462306a36Sopenharmony_cils7a1000_dpi_connector_helpers = { 7562306a36Sopenharmony_ci .atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder, 7662306a36Sopenharmony_ci .get_modes = ls7a1000_dpi_connector_get_modes, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic enum drm_connector_status 8062306a36Sopenharmony_cils7a1000_dpi_connector_detect(struct drm_connector *connector, bool force) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct i2c_adapter *ddc = connector->ddc; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (ddc) { 8562306a36Sopenharmony_ci if (drm_probe_ddc(ddc)) 8662306a36Sopenharmony_ci return connector_status_connected; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return connector_status_disconnected; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return connector_status_unknown; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = { 9562306a36Sopenharmony_ci .detect = ls7a1000_dpi_connector_detect, 9662306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 9762306a36Sopenharmony_ci .destroy = drm_connector_cleanup, 9862306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 9962306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 10062306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct drm_device *ddev = encoder->dev; 10662306a36Sopenharmony_ci struct lsdc_device *ldev = to_lsdc(ddev); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * We need this for S3 support, screen will not lightup if don't set 11062306a36Sopenharmony_ci * this register correctly. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG, 11362306a36Sopenharmony_ci PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct drm_device *ddev = encoder->dev; 11962306a36Sopenharmony_ci struct lsdc_device *ldev = to_lsdc(ddev); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * We need this for S3 support, screen will not lightup if don't set 12362306a36Sopenharmony_ci * this register correctly. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* DVO */ 12762306a36Sopenharmony_ci lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG, 12862306a36Sopenharmony_ci BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = { 13262306a36Sopenharmony_ci { 13362306a36Sopenharmony_ci .reset = ls7a1000_pipe0_encoder_reset, 13462306a36Sopenharmony_ci .destroy = drm_encoder_cleanup, 13562306a36Sopenharmony_ci }, 13662306a36Sopenharmony_ci { 13762306a36Sopenharmony_ci .reset = ls7a1000_pipe1_encoder_reset, 13862306a36Sopenharmony_ci .destroy = drm_encoder_cleanup, 13962306a36Sopenharmony_ci }, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciint ls7a1000_output_init(struct drm_device *ddev, 14362306a36Sopenharmony_ci struct lsdc_display_pipe *dispipe, 14462306a36Sopenharmony_ci struct i2c_adapter *ddc, 14562306a36Sopenharmony_ci unsigned int index) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct lsdc_output *output = &dispipe->output; 14862306a36Sopenharmony_ci struct drm_encoder *encoder = &output->encoder; 14962306a36Sopenharmony_ci struct drm_connector *connector = &output->connector; 15062306a36Sopenharmony_ci int ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index], 15362306a36Sopenharmony_ci DRM_MODE_ENCODER_TMDS, "encoder-%u", index); 15462306a36Sopenharmony_ci if (ret) 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci encoder->possible_crtcs = BIT(index); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = drm_connector_init_with_ddc(ddev, connector, 16062306a36Sopenharmony_ci &ls7a1000_dpi_connector_funcs, 16162306a36Sopenharmony_ci DRM_MODE_CONNECTOR_DPI, ddc); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci drm_info(ddev, "display pipe-%u has a DVO\n", index); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_CONNECT | 17262306a36Sopenharmony_ci DRM_CONNECTOR_POLL_DISCONNECT; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci connector->interlace_allowed = 0; 17562306a36Sopenharmony_ci connector->doublescan_allowed = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 179