1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2019-2022 Bootlin 4 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 5 */ 6 7 #include <linux/types.h> 8 9 #include <drm/drm_atomic_helper.h> 10 #include <drm/drm_bridge.h> 11 #include <drm/drm_connector.h> 12 #include <drm/drm_crtc_helper.h> 13 #include <drm/drm_drv.h> 14 #include <drm/drm_encoder.h> 15 #include <drm/drm_gem_cma_helper.h> 16 #include <drm/drm_modeset_helper_vtables.h> 17 #include <drm/drm_of.h> 18 #include <drm/drm_panel.h> 19 #include <drm/drm_print.h> 20 #include <drm/drm_probe_helper.h> 21 22 #include "logicvc_crtc.h" 23 #include "logicvc_drm.h" 24 #include "logicvc_interface.h" 25 #include "logicvc_regs.h" 26 27 #define logicvc_interface_from_drm_encoder(c) \ 28 container_of(c, struct logicvc_interface, drm_encoder) 29 #define logicvc_interface_from_drm_connector(c) \ 30 container_of(c, struct logicvc_interface, drm_connector) 31 32 static void logicvc_encoder_enable(struct drm_encoder *drm_encoder) 33 { 34 struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev); 35 struct logicvc_interface *interface = 36 logicvc_interface_from_drm_encoder(drm_encoder); 37 38 regmap_update_bits(logicvc->regmap, LOGICVC_POWER_CTRL_REG, 39 LOGICVC_POWER_CTRL_VIDEO_ENABLE, 40 LOGICVC_POWER_CTRL_VIDEO_ENABLE); 41 42 if (interface->drm_panel) { 43 drm_panel_prepare(interface->drm_panel); 44 drm_panel_enable(interface->drm_panel); 45 } 46 } 47 48 static void logicvc_encoder_disable(struct drm_encoder *drm_encoder) 49 { 50 struct logicvc_interface *interface = 51 logicvc_interface_from_drm_encoder(drm_encoder); 52 53 if (interface->drm_panel) { 54 drm_panel_disable(interface->drm_panel); 55 drm_panel_unprepare(interface->drm_panel); 56 } 57 } 58 59 static const struct drm_encoder_helper_funcs logicvc_encoder_helper_funcs = { 60 .enable = logicvc_encoder_enable, 61 .disable = logicvc_encoder_disable, 62 }; 63 64 static const struct drm_encoder_funcs logicvc_encoder_funcs = { 65 .destroy = drm_encoder_cleanup, 66 }; 67 68 static int logicvc_connector_get_modes(struct drm_connector *drm_connector) 69 { 70 struct logicvc_interface *interface = 71 logicvc_interface_from_drm_connector(drm_connector); 72 73 if (interface->drm_panel) 74 return drm_panel_get_modes(interface->drm_panel, drm_connector); 75 76 WARN_ONCE(1, "Retrieving modes from a native connector is not implemented."); 77 78 return 0; 79 } 80 81 static const struct drm_connector_helper_funcs logicvc_connector_helper_funcs = { 82 .get_modes = logicvc_connector_get_modes, 83 }; 84 85 static const struct drm_connector_funcs logicvc_connector_funcs = { 86 .reset = drm_atomic_helper_connector_reset, 87 .fill_modes = drm_helper_probe_single_connector_modes, 88 .destroy = drm_connector_cleanup, 89 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 90 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 91 }; 92 93 static int logicvc_interface_encoder_type(struct logicvc_drm *logicvc) 94 { 95 switch (logicvc->config.display_interface) { 96 case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS: 97 case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA: 98 case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS: 99 return DRM_MODE_ENCODER_LVDS; 100 case LOGICVC_DISPLAY_INTERFACE_DVI: 101 return DRM_MODE_ENCODER_TMDS; 102 case LOGICVC_DISPLAY_INTERFACE_RGB: 103 return DRM_MODE_ENCODER_DPI; 104 default: 105 return DRM_MODE_ENCODER_NONE; 106 } 107 } 108 109 static int logicvc_interface_connector_type(struct logicvc_drm *logicvc) 110 { 111 switch (logicvc->config.display_interface) { 112 case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS: 113 case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA: 114 case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS: 115 return DRM_MODE_CONNECTOR_LVDS; 116 case LOGICVC_DISPLAY_INTERFACE_DVI: 117 return DRM_MODE_CONNECTOR_DVID; 118 case LOGICVC_DISPLAY_INTERFACE_RGB: 119 return DRM_MODE_CONNECTOR_DPI; 120 default: 121 return DRM_MODE_CONNECTOR_Unknown; 122 } 123 } 124 125 static bool logicvc_interface_native_connector(struct logicvc_drm *logicvc) 126 { 127 switch (logicvc->config.display_interface) { 128 case LOGICVC_DISPLAY_INTERFACE_DVI: 129 return true; 130 default: 131 return false; 132 } 133 } 134 135 void logicvc_interface_attach_crtc(struct logicvc_drm *logicvc) 136 { 137 uint32_t possible_crtcs = drm_crtc_mask(&logicvc->crtc->drm_crtc); 138 139 logicvc->interface->drm_encoder.possible_crtcs = possible_crtcs; 140 } 141 142 int logicvc_interface_init(struct logicvc_drm *logicvc) 143 { 144 struct logicvc_interface *interface; 145 struct drm_device *drm_dev = &logicvc->drm_dev; 146 struct device *dev = drm_dev->dev; 147 struct device_node *of_node = dev->of_node; 148 int encoder_type = logicvc_interface_encoder_type(logicvc); 149 int connector_type = logicvc_interface_connector_type(logicvc); 150 bool native_connector = logicvc_interface_native_connector(logicvc); 151 int ret; 152 153 interface = devm_kzalloc(dev, sizeof(*interface), GFP_KERNEL); 154 if (!interface) { 155 ret = -ENOMEM; 156 goto error_early; 157 } 158 159 ret = drm_of_find_panel_or_bridge(of_node, 0, 0, &interface->drm_panel, 160 &interface->drm_bridge); 161 if (ret == -EPROBE_DEFER) 162 goto error_early; 163 164 ret = drm_encoder_init(drm_dev, &interface->drm_encoder, 165 &logicvc_encoder_funcs, encoder_type, NULL); 166 if (ret) { 167 drm_err(drm_dev, "Failed to initialize encoder\n"); 168 goto error_early; 169 } 170 171 drm_encoder_helper_add(&interface->drm_encoder, 172 &logicvc_encoder_helper_funcs); 173 174 if (native_connector || interface->drm_panel) { 175 ret = drm_connector_init(drm_dev, &interface->drm_connector, 176 &logicvc_connector_funcs, 177 connector_type); 178 if (ret) { 179 drm_err(drm_dev, "Failed to initialize connector\n"); 180 goto error_encoder; 181 } 182 183 drm_connector_helper_add(&interface->drm_connector, 184 &logicvc_connector_helper_funcs); 185 186 ret = drm_connector_attach_encoder(&interface->drm_connector, 187 &interface->drm_encoder); 188 if (ret) { 189 drm_err(drm_dev, 190 "Failed to attach connector to encoder\n"); 191 goto error_encoder; 192 } 193 } 194 195 if (interface->drm_bridge) { 196 ret = drm_bridge_attach(&interface->drm_encoder, 197 interface->drm_bridge, NULL, 0); 198 if (ret) { 199 drm_err(drm_dev, 200 "Failed to attach bridge to encoder\n"); 201 goto error_encoder; 202 } 203 } 204 205 logicvc->interface = interface; 206 207 return 0; 208 209 error_encoder: 210 drm_encoder_cleanup(&interface->drm_encoder); 211 212 error_early: 213 return ret; 214 } 215