18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2018 Loongson Technology Co., Ltd. 38c2ecf20Sopenharmony_ci * Authors: 48c2ecf20Sopenharmony_ci * Chen Zhu <zhuchen@loongson.cn> 58c2ecf20Sopenharmony_ci * Yaling Fang <fangyaling@loongson.cn> 68c2ecf20Sopenharmony_ci * Dandan Zhang <zhangdandan@loongson.cn> 78c2ecf20Sopenharmony_ci * Huacai Chen <chenhc@lemote.com> 88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 98c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 108c2ecf20Sopenharmony_ci * Free Software Foundation; either version 2 of the License, or (at your 118c2ecf20Sopenharmony_ci * option) any later version. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/export.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "loongson_drv.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/** 268c2ecf20Sopenharmony_ci * loongson_connector_best_encoder 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * @connector: point to the drm_connector structure 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Select the best encoder for the given connector. Used by both the helpers in 318c2ecf20Sopenharmony_ci * drm_atomic_helper_check_modeset() and legacy CRTC helpers 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistatic struct drm_encoder *loongson_connector_best_encoder(struct drm_connector 348c2ecf20Sopenharmony_ci *connector) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* There is only one encoder per connector */ 398c2ecf20Sopenharmony_ci drm_connector_for_each_possible_encoder(connector, encoder) 408c2ecf20Sopenharmony_ci return encoder; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return NULL; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** 468c2ecf20Sopenharmony_ci * loongson_get_modes 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * @connetcor: central DRM connector control structure 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Fill in all modes currently valid for the sink into the connector->probed_modes list. 518c2ecf20Sopenharmony_ci * It should also update the EDID property by calling drm_connector_update_edid_property(). 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistatic int loongson_get_modes(struct drm_connector *connector) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int id, ret = 0; 568c2ecf20Sopenharmony_ci enum loongson_edid_method ledid_method; 578c2ecf20Sopenharmony_ci struct edid *edid = NULL; 588c2ecf20Sopenharmony_ci struct loongson_connector *lconnector = to_loongson_connector(connector); 598c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = lconnector->i2c->adapter; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci id = drm_connector_index(connector); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci ledid_method = lconnector->edid_method; 648c2ecf20Sopenharmony_ci switch (ledid_method) { 658c2ecf20Sopenharmony_ci case via_i2c: 668c2ecf20Sopenharmony_ci case via_encoder: 678c2ecf20Sopenharmony_ci default: 688c2ecf20Sopenharmony_ci edid = drm_get_edid(connector, adapter); 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci case via_vbios: 718c2ecf20Sopenharmony_ci edid = kmalloc(EDID_LENGTH * 2, GFP_KERNEL); 728c2ecf20Sopenharmony_ci memcpy(edid, lconnector->vbios_edid, EDID_LENGTH * 2); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (edid) { 768c2ecf20Sopenharmony_ci drm_connector_update_edid_property(connector, edid); 778c2ecf20Sopenharmony_ci ret = drm_add_edid_modes(connector, edid); 788c2ecf20Sopenharmony_ci kfree(edid); 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci ret += drm_add_modes_noedid(connector, 1920, 1080); 818c2ecf20Sopenharmony_ci drm_set_preferred_mode(connector, 1024, 768); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return ret; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic bool is_connected(struct loongson_connector *lconnector) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci unsigned char start = 0x0; 908c2ecf20Sopenharmony_ci struct i2c_adapter *adapter; 918c2ecf20Sopenharmony_ci struct i2c_msg msgs = { 928c2ecf20Sopenharmony_ci .addr = DDC_ADDR, 938c2ecf20Sopenharmony_ci .len = 1, 948c2ecf20Sopenharmony_ci .flags = 0, 958c2ecf20Sopenharmony_ci .buf = &start, 968c2ecf20Sopenharmony_ci }; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (!lconnector->i2c) 998c2ecf20Sopenharmony_ci return false; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci adapter = lconnector->i2c->adapter; 1028c2ecf20Sopenharmony_ci if (i2c_transfer(adapter, &msgs, 1) < 1) { 1038c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("display-%d not connect\n", lconnector->id); 1048c2ecf20Sopenharmony_ci return false; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return true; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/** 1118c2ecf20Sopenharmony_ci * loongson_connector_detect 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * @connector: point to drm_connector 1148c2ecf20Sopenharmony_ci * @force: bool 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * Check to see if anything is attached to the connector. 1178c2ecf20Sopenharmony_ci * The parameter force is set to false whilst polling, 1188c2ecf20Sopenharmony_ci * true when checking the connector due to a user request 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic enum drm_connector_status loongson_connector_detect(struct drm_connector 1218c2ecf20Sopenharmony_ci *connector, bool force) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int r; 1248c2ecf20Sopenharmony_ci enum drm_connector_status ret = connector_status_connected; 1258c2ecf20Sopenharmony_ci struct loongson_connector *lconnector = to_loongson_connector(connector); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci DRM_DEBUG("loongson_connector_detect connector_id=%d, ledid_method=%d\n", 1288c2ecf20Sopenharmony_ci drm_connector_index(connector), lconnector->edid_method); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (lconnector->edid_method != via_vbios) { 1318c2ecf20Sopenharmony_ci r = pm_runtime_get_sync(connector->dev->dev); 1328c2ecf20Sopenharmony_ci if (r < 0) 1338c2ecf20Sopenharmony_ci return connector_status_disconnected; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (is_connected(lconnector)) 1368c2ecf20Sopenharmony_ci ret = connector_status_connected; 1378c2ecf20Sopenharmony_ci else 1388c2ecf20Sopenharmony_ci ret = connector_status_disconnected; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(connector->dev->dev); 1418c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(connector->dev->dev); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/** 1488c2ecf20Sopenharmony_ci * These provide the minimum set of functions required to handle a connector 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * Helper operations for connectors.These functions are used 1518c2ecf20Sopenharmony_ci * by the atomic and legacy modeset helpers and by the probe helpers. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs loongson_connector_helper_funcs = { 1548c2ecf20Sopenharmony_ci .get_modes = loongson_get_modes, 1558c2ecf20Sopenharmony_ci .best_encoder = loongson_connector_best_encoder, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/** 1598c2ecf20Sopenharmony_ci * These provide the minimum set of functions required to handle a connector 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * Control connectors on a given device. 1628c2ecf20Sopenharmony_ci * The functions below allow the core DRM code to control connectors, 1638c2ecf20Sopenharmony_ci * enumerate available modes and so on. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs loongson_connector_funcs = { 1668c2ecf20Sopenharmony_ci .dpms = drm_helper_connector_dpms, 1678c2ecf20Sopenharmony_ci .detect = loongson_connector_detect, 1688c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 1698c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 1708c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 1718c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 1728c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * loongson_connector_init 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * @dev: drm device 1828c2ecf20Sopenharmony_ci * @connector_id: 1838c2ecf20Sopenharmony_ci * 1848c2ecf20Sopenharmony_ci * Vga is the interface between host and monitor 1858c2ecf20Sopenharmony_ci * This function is to init vga 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_cistruct drm_connector *loongson_connector_init(struct drm_device *dev, unsigned int index) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct i2c_adapter *adapter; 1908c2ecf20Sopenharmony_ci struct i2c_client *ddc_client; 1918c2ecf20Sopenharmony_ci struct drm_connector *connector; 1928c2ecf20Sopenharmony_ci struct loongson_encoder *loongson_encoder; 1938c2ecf20Sopenharmony_ci struct loongson_connector *loongson_connector; 1948c2ecf20Sopenharmony_ci struct loongson_drm_device *ldev = (struct loongson_drm_device*)dev->dev_private; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci const struct i2c_board_info ddc_info = { 1978c2ecf20Sopenharmony_ci .type = "ddc-dev", 1988c2ecf20Sopenharmony_ci .addr = DDC_ADDR, 1998c2ecf20Sopenharmony_ci .flags = I2C_CLASS_DDC, 2008c2ecf20Sopenharmony_ci }; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci loongson_encoder = ldev->mode_info[index].encoder; 2038c2ecf20Sopenharmony_ci adapter = loongson_encoder->i2c->adapter; 2048c2ecf20Sopenharmony_ci ddc_client = i2c_new_client_device(adapter, &ddc_info); 2058c2ecf20Sopenharmony_ci if (IS_ERR(ddc_client)) { 2068c2ecf20Sopenharmony_ci i2c_del_adapter(adapter); 2078c2ecf20Sopenharmony_ci DRM_ERROR("Failed to create standard ddc client\n"); 2088c2ecf20Sopenharmony_ci return NULL; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci loongson_connector = kzalloc(sizeof(struct loongson_connector), GFP_KERNEL); 2128c2ecf20Sopenharmony_ci if (!loongson_connector) 2138c2ecf20Sopenharmony_ci return NULL; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ldev->connector_active0 = 0; 2168c2ecf20Sopenharmony_ci ldev->connector_active1 = 0; 2178c2ecf20Sopenharmony_ci loongson_connector->id = index; 2188c2ecf20Sopenharmony_ci loongson_connector->ldev = ldev; 2198c2ecf20Sopenharmony_ci loongson_connector->type = get_connector_type(ldev, index); 2208c2ecf20Sopenharmony_ci loongson_connector->i2c_id = get_connector_i2cid(ldev, index); 2218c2ecf20Sopenharmony_ci loongson_connector->hotplug = get_hotplug_mode(ldev, index); 2228c2ecf20Sopenharmony_ci loongson_connector->edid_method = get_edid_method(ldev, index); 2238c2ecf20Sopenharmony_ci if (loongson_connector->edid_method == via_vbios) 2248c2ecf20Sopenharmony_ci loongson_connector->vbios_edid = get_vbios_edid(ldev, index); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci loongson_connector->i2c = &ldev->i2c_bus[index]; 2278c2ecf20Sopenharmony_ci if (!loongson_connector->i2c) 2288c2ecf20Sopenharmony_ci DRM_INFO("connector-%d match i2c-%d err\n", index, 2298c2ecf20Sopenharmony_ci loongson_connector->i2c_id); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci connector = &loongson_connector->base; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, &loongson_connector_helper_funcs); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci drm_connector_init(dev, connector, 2368c2ecf20Sopenharmony_ci &loongson_connector_funcs, loongson_connector->type); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci drm_connector_register(connector); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return connector; 2418c2ecf20Sopenharmony_ci} 242