18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012 Red Hat 48c2ecf20Sopenharmony_ci * based in parts on udlfb.c: 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> 68c2ecf20Sopenharmony_ci * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <drm/drm_atomic_state_helper.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "udl_connector.h" 158c2ecf20Sopenharmony_ci#include "udl_drv.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic int udl_get_edid_block(void *data, u8 *buf, unsigned int block, 188c2ecf20Sopenharmony_ci size_t len) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci int ret, i; 218c2ecf20Sopenharmony_ci u8 *read_buff; 228c2ecf20Sopenharmony_ci struct udl_device *udl = data; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci read_buff = kmalloc(2, GFP_KERNEL); 258c2ecf20Sopenharmony_ci if (!read_buff) 268c2ecf20Sopenharmony_ci return -1; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 298c2ecf20Sopenharmony_ci int bval = (i + block * EDID_LENGTH) << 8; 308c2ecf20Sopenharmony_ci ret = usb_control_msg(udl->udev, 318c2ecf20Sopenharmony_ci usb_rcvctrlpipe(udl->udev, 0), 328c2ecf20Sopenharmony_ci (0x02), (0x80 | (0x02 << 5)), bval, 338c2ecf20Sopenharmony_ci 0xA1, read_buff, 2, 1000); 348c2ecf20Sopenharmony_ci if (ret < 1) { 358c2ecf20Sopenharmony_ci DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); 368c2ecf20Sopenharmony_ci kfree(read_buff); 378c2ecf20Sopenharmony_ci return -1; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci buf[i] = read_buff[1]; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci kfree(read_buff); 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int udl_get_modes(struct drm_connector *connector) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct udl_drm_connector *udl_connector = 498c2ecf20Sopenharmony_ci container_of(connector, 508c2ecf20Sopenharmony_ci struct udl_drm_connector, 518c2ecf20Sopenharmony_ci connector); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci drm_connector_update_edid_property(connector, udl_connector->edid); 548c2ecf20Sopenharmony_ci if (udl_connector->edid) 558c2ecf20Sopenharmony_ci return drm_add_edid_modes(connector, udl_connector->edid); 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic enum drm_mode_status udl_mode_valid(struct drm_connector *connector, 608c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct udl_device *udl = to_udl(connector->dev); 638c2ecf20Sopenharmony_ci if (!udl->sku_pixel_limit) 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit) 678c2ecf20Sopenharmony_ci return MODE_VIRTUAL_Y; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic enum drm_connector_status 738c2ecf20Sopenharmony_ciudl_detect(struct drm_connector *connector, bool force) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct udl_device *udl = to_udl(connector->dev); 768c2ecf20Sopenharmony_ci struct udl_drm_connector *udl_connector = 778c2ecf20Sopenharmony_ci container_of(connector, 788c2ecf20Sopenharmony_ci struct udl_drm_connector, 798c2ecf20Sopenharmony_ci connector); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* cleanup previous edid */ 828c2ecf20Sopenharmony_ci if (udl_connector->edid != NULL) { 838c2ecf20Sopenharmony_ci kfree(udl_connector->edid); 848c2ecf20Sopenharmony_ci udl_connector->edid = NULL; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci udl_connector->edid = drm_do_get_edid(connector, udl_get_edid_block, udl); 888c2ecf20Sopenharmony_ci if (!udl_connector->edid) 898c2ecf20Sopenharmony_ci return connector_status_disconnected; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return connector_status_connected; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void udl_connector_destroy(struct drm_connector *connector) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct udl_drm_connector *udl_connector = 978c2ecf20Sopenharmony_ci container_of(connector, 988c2ecf20Sopenharmony_ci struct udl_drm_connector, 998c2ecf20Sopenharmony_ci connector); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 1028c2ecf20Sopenharmony_ci kfree(udl_connector->edid); 1038c2ecf20Sopenharmony_ci kfree(connector); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs udl_connector_helper_funcs = { 1078c2ecf20Sopenharmony_ci .get_modes = udl_get_modes, 1088c2ecf20Sopenharmony_ci .mode_valid = udl_mode_valid, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs udl_connector_funcs = { 1128c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 1138c2ecf20Sopenharmony_ci .detect = udl_detect, 1148c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 1158c2ecf20Sopenharmony_ci .destroy = udl_connector_destroy, 1168c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 1178c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct drm_connector *udl_connector_init(struct drm_device *dev) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct udl_drm_connector *udl_connector; 1238c2ecf20Sopenharmony_ci struct drm_connector *connector; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci udl_connector = kzalloc(sizeof(struct udl_drm_connector), GFP_KERNEL); 1268c2ecf20Sopenharmony_ci if (!udl_connector) 1278c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci connector = &udl_connector->connector; 1308c2ecf20Sopenharmony_ci drm_connector_init(dev, connector, &udl_connector_funcs, 1318c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DVII); 1328c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, &udl_connector_helper_funcs); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD | 1358c2ecf20Sopenharmony_ci DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return connector; 1388c2ecf20Sopenharmony_ci} 139