1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2023 Loongson Technology Corporation Limited 4 */ 5 6 #include <drm/drm_atomic_helper.h> 7 #include <drm/drm_edid.h> 8 #include <drm/drm_probe_helper.h> 9 10 #include "lsdc_drv.h" 11 #include "lsdc_output.h" 12 13 /* 14 * The display controller in the LS7A1000 exports two DVO interfaces, thus 15 * external encoder is required, except connected to the DPI panel directly. 16 * 17 * ___________________ _________ 18 * | -------| | | 19 * | CRTC0 --> | DVO0 ----> Encoder0 ---> Connector0 ---> | Display | 20 * | _ _ -------| ^ ^ |_________| 21 * | | | | | +------+ | | | 22 * | |_| |_| | i2c6 | <--------+-------------+ 23 * | +------+ | 24 * | | 25 * | DC in LS7A1000 | 26 * | | 27 * | _ _ +------+ | 28 * | | | | | | i2c7 | <--------+-------------+ 29 * | |_| |_| +------+ | | | _________ 30 * | -------| | | | | 31 * | CRTC1 --> | DVO1 ----> Encoder1 ---> Connector1 ---> | Panel | 32 * | -------| |_________| 33 * |___________________| 34 * 35 * Currently, we assume the external encoders connected to the DVO are 36 * transparent. Loongson's DVO interface can directly drive RGB888 panels. 37 * 38 * TODO: Add support for non-transparent encoders 39 */ 40 41 static int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn) 42 { 43 int num; 44 45 if (conn->ddc) { 46 const struct drm_edid *drm_edid; 47 48 drm_edid = drm_edid_read(conn); 49 drm_edid_connector_update(conn, drm_edid); 50 num = drm_edid_connector_add_modes(conn); 51 drm_edid_free(drm_edid); 52 53 return num; 54 } 55 56 num = drm_add_modes_noedid(conn, 1920, 1200); 57 58 drm_set_preferred_mode(conn, 1024, 768); 59 60 return num; 61 } 62 63 static struct drm_encoder * 64 ls7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector, 65 struct drm_atomic_state *state) 66 { 67 struct lsdc_output *output = connector_to_lsdc_output(connector); 68 69 return &output->encoder; 70 } 71 72 static const struct drm_connector_helper_funcs 73 ls7a1000_dpi_connector_helpers = { 74 .atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder, 75 .get_modes = ls7a1000_dpi_connector_get_modes, 76 }; 77 78 static enum drm_connector_status 79 ls7a1000_dpi_connector_detect(struct drm_connector *connector, bool force) 80 { 81 struct i2c_adapter *ddc = connector->ddc; 82 83 if (ddc) { 84 if (drm_probe_ddc(ddc)) 85 return connector_status_connected; 86 87 return connector_status_disconnected; 88 } 89 90 return connector_status_unknown; 91 } 92 93 static const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = { 94 .detect = ls7a1000_dpi_connector_detect, 95 .fill_modes = drm_helper_probe_single_connector_modes, 96 .destroy = drm_connector_cleanup, 97 .reset = drm_atomic_helper_connector_reset, 98 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 99 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state 100 }; 101 102 static void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder) 103 { 104 struct drm_device *ddev = encoder->dev; 105 struct lsdc_device *ldev = to_lsdc(ddev); 106 107 /* 108 * We need this for S3 support, screen will not lightup if don't set 109 * this register correctly. 110 */ 111 lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG, 112 PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN); 113 } 114 115 static void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder) 116 { 117 struct drm_device *ddev = encoder->dev; 118 struct lsdc_device *ldev = to_lsdc(ddev); 119 120 /* 121 * We need this for S3 support, screen will not lightup if don't set 122 * this register correctly. 123 */ 124 125 /* DVO */ 126 lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG, 127 BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN); 128 } 129 130 static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = { 131 { 132 .reset = ls7a1000_pipe0_encoder_reset, 133 .destroy = drm_encoder_cleanup, 134 }, 135 { 136 .reset = ls7a1000_pipe1_encoder_reset, 137 .destroy = drm_encoder_cleanup, 138 }, 139 }; 140 141 int ls7a1000_output_init(struct drm_device *ddev, 142 struct lsdc_display_pipe *dispipe, 143 struct i2c_adapter *ddc, 144 unsigned int index) 145 { 146 struct lsdc_output *output = &dispipe->output; 147 struct drm_encoder *encoder = &output->encoder; 148 struct drm_connector *connector = &output->connector; 149 int ret; 150 151 ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index], 152 DRM_MODE_ENCODER_TMDS, "encoder-%u", index); 153 if (ret) 154 return ret; 155 156 encoder->possible_crtcs = BIT(index); 157 158 ret = drm_connector_init_with_ddc(ddev, connector, 159 &ls7a1000_dpi_connector_funcs, 160 DRM_MODE_CONNECTOR_DPI, ddc); 161 if (ret) 162 return ret; 163 164 drm_info(ddev, "display pipe-%u has a DVO\n", index); 165 166 drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers); 167 168 drm_connector_attach_encoder(connector, encoder); 169 170 connector->polled = DRM_CONNECTOR_POLL_CONNECT | 171 DRM_CONNECTOR_POLL_DISCONNECT; 172 173 connector->interlace_allowed = 0; 174 connector->doublescan_allowed = 0; 175 176 return 0; 177 } 178