1/* 2 * Copyright (c) 2018 Loongson Technology Co., Ltd. 3 * Authors: 4 * Chen Zhu <zhuchen@loongson.cn> 5 * Yaling Fang <fangyaling@loongson.cn> 6 * Dandan Zhang <zhangdandan@loongson.cn> 7 * Huacai Chen <chenhc@lemote.com> 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 */ 13 14#include <linux/export.h> 15#include <linux/i2c.h> 16#include <linux/i2c-algo-bit.h> 17#include <linux/pm_runtime.h> 18#include <drm/drm_atomic_helper.h> 19#include <drm/drm_crtc_helper.h> 20#include <drm/drm_probe_helper.h> 21#include <drm/drm_edid.h> 22 23#include "loongson_drv.h" 24 25/** 26 * loongson_connector_best_encoder 27 * 28 * @connector: point to the drm_connector structure 29 * 30 * Select the best encoder for the given connector. Used by both the helpers in 31 * drm_atomic_helper_check_modeset() and legacy CRTC helpers 32 */ 33static struct drm_encoder *loongson_connector_best_encoder(struct drm_connector 34 *connector) 35{ 36 struct drm_encoder *encoder; 37 38 /* There is only one encoder per connector */ 39 drm_connector_for_each_possible_encoder(connector, encoder) 40 return encoder; 41 42 return NULL; 43} 44 45/** 46 * loongson_get_modes 47 * 48 * @connetcor: central DRM connector control structure 49 * 50 * Fill in all modes currently valid for the sink into the connector->probed_modes list. 51 * It should also update the EDID property by calling drm_connector_update_edid_property(). 52 */ 53static int loongson_get_modes(struct drm_connector *connector) 54{ 55 int id, ret = 0; 56 enum loongson_edid_method ledid_method; 57 struct edid *edid = NULL; 58 struct loongson_connector *lconnector = to_loongson_connector(connector); 59 struct i2c_adapter *adapter = lconnector->i2c->adapter; 60 61 id = drm_connector_index(connector); 62 63 ledid_method = lconnector->edid_method; 64 switch (ledid_method) { 65 case via_i2c: 66 case via_encoder: 67 default: 68 edid = drm_get_edid(connector, adapter); 69 break; 70 case via_vbios: 71 edid = kmalloc(EDID_LENGTH * 2, GFP_KERNEL); 72 memcpy(edid, lconnector->vbios_edid, EDID_LENGTH * 2); 73 } 74 75 if (edid) { 76 drm_connector_update_edid_property(connector, edid); 77 ret = drm_add_edid_modes(connector, edid); 78 kfree(edid); 79 } else { 80 ret += drm_add_modes_noedid(connector, 1920, 1080); 81 drm_set_preferred_mode(connector, 1024, 768); 82 } 83 84 return ret; 85} 86 87static bool is_connected(struct loongson_connector *lconnector) 88{ 89 unsigned char start = 0x0; 90 struct i2c_adapter *adapter; 91 struct i2c_msg msgs = { 92 .addr = DDC_ADDR, 93 .len = 1, 94 .flags = 0, 95 .buf = &start, 96 }; 97 98 if (!lconnector->i2c) 99 return false; 100 101 adapter = lconnector->i2c->adapter; 102 if (i2c_transfer(adapter, &msgs, 1) < 1) { 103 DRM_DEBUG_KMS("display-%d not connect\n", lconnector->id); 104 return false; 105 } 106 107 return true; 108} 109 110/** 111 * loongson_connector_detect 112 * 113 * @connector: point to drm_connector 114 * @force: bool 115 * 116 * Check to see if anything is attached to the connector. 117 * The parameter force is set to false whilst polling, 118 * true when checking the connector due to a user request 119 */ 120static enum drm_connector_status loongson_connector_detect(struct drm_connector 121 *connector, bool force) 122{ 123 int r; 124 enum drm_connector_status ret = connector_status_connected; 125 struct loongson_connector *lconnector = to_loongson_connector(connector); 126 127 DRM_DEBUG("loongson_connector_detect connector_id=%d, ledid_method=%d\n", 128 drm_connector_index(connector), lconnector->edid_method); 129 130 if (lconnector->edid_method != via_vbios) { 131 r = pm_runtime_get_sync(connector->dev->dev); 132 if (r < 0) 133 return connector_status_disconnected; 134 135 if (is_connected(lconnector)) 136 ret = connector_status_connected; 137 else 138 ret = connector_status_disconnected; 139 140 pm_runtime_mark_last_busy(connector->dev->dev); 141 pm_runtime_put_autosuspend(connector->dev->dev); 142 } 143 144 return ret; 145} 146 147/** 148 * These provide the minimum set of functions required to handle a connector 149 * 150 * Helper operations for connectors.These functions are used 151 * by the atomic and legacy modeset helpers and by the probe helpers. 152 */ 153static const struct drm_connector_helper_funcs loongson_connector_helper_funcs = { 154 .get_modes = loongson_get_modes, 155 .best_encoder = loongson_connector_best_encoder, 156}; 157 158/** 159 * These provide the minimum set of functions required to handle a connector 160 * 161 * Control connectors on a given device. 162 * The functions below allow the core DRM code to control connectors, 163 * enumerate available modes and so on. 164 */ 165static const struct drm_connector_funcs loongson_connector_funcs = { 166 .dpms = drm_helper_connector_dpms, 167 .detect = loongson_connector_detect, 168 .fill_modes = drm_helper_probe_single_connector_modes, 169 .destroy = drm_connector_cleanup, 170 .reset = drm_atomic_helper_connector_reset, 171 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 172 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 173}; 174 175static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; 176 177 178/** 179 * loongson_connector_init 180 * 181 * @dev: drm device 182 * @connector_id: 183 * 184 * Vga is the interface between host and monitor 185 * This function is to init vga 186 */ 187struct drm_connector *loongson_connector_init(struct drm_device *dev, unsigned int index) 188{ 189 struct i2c_adapter *adapter; 190 struct i2c_client *ddc_client; 191 struct drm_connector *connector; 192 struct loongson_encoder *loongson_encoder; 193 struct loongson_connector *loongson_connector; 194 struct loongson_drm_device *ldev = (struct loongson_drm_device*)dev->dev_private; 195 196 const struct i2c_board_info ddc_info = { 197 .type = "ddc-dev", 198 .addr = DDC_ADDR, 199 .flags = I2C_CLASS_DDC, 200 }; 201 202 loongson_encoder = ldev->mode_info[index].encoder; 203 adapter = loongson_encoder->i2c->adapter; 204 ddc_client = i2c_new_client_device(adapter, &ddc_info); 205 if (IS_ERR(ddc_client)) { 206 i2c_del_adapter(adapter); 207 DRM_ERROR("Failed to create standard ddc client\n"); 208 return NULL; 209 } 210 211 loongson_connector = kzalloc(sizeof(struct loongson_connector), GFP_KERNEL); 212 if (!loongson_connector) 213 return NULL; 214 215 ldev->connector_active0 = 0; 216 ldev->connector_active1 = 0; 217 loongson_connector->id = index; 218 loongson_connector->ldev = ldev; 219 loongson_connector->type = get_connector_type(ldev, index); 220 loongson_connector->i2c_id = get_connector_i2cid(ldev, index); 221 loongson_connector->hotplug = get_hotplug_mode(ldev, index); 222 loongson_connector->edid_method = get_edid_method(ldev, index); 223 if (loongson_connector->edid_method == via_vbios) 224 loongson_connector->vbios_edid = get_vbios_edid(ldev, index); 225 226 loongson_connector->i2c = &ldev->i2c_bus[index]; 227 if (!loongson_connector->i2c) 228 DRM_INFO("connector-%d match i2c-%d err\n", index, 229 loongson_connector->i2c_id); 230 231 connector = &loongson_connector->base; 232 233 drm_connector_helper_add(connector, &loongson_connector_helper_funcs); 234 235 drm_connector_init(dev, connector, 236 &loongson_connector_funcs, loongson_connector->type); 237 238 drm_connector_register(connector); 239 240 return connector; 241} 242