Commit | Line | Data |
---|---|---|
5320918b DA |
1 | /* |
2 | * Copyright (C) 2012 Red Hat | |
3 | * based in parts on udlfb.c: | |
4 | * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> | |
5 | * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> | |
6 | * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> | |
7 | * | |
8 | * This file is subject to the terms and conditions of the GNU General Public | |
9 | * License v2. See the file COPYING in the main directory of this archive for | |
10 | * more details. | |
11 | */ | |
12 | ||
760285e7 DH |
13 | #include <drm/drmP.h> |
14 | #include <drm/drm_crtc.h> | |
15 | #include <drm/drm_edid.h> | |
16 | #include <drm/drm_crtc_helper.h> | |
5320918b DA |
17 | #include "udl_drv.h" |
18 | ||
19 | /* dummy connector to just get EDID, | |
20 | all UDL appear to have a DVI-D */ | |
21 | ||
22 | static u8 *udl_get_edid(struct udl_device *udl) | |
23 | { | |
24 | u8 *block; | |
242187b3 | 25 | char *rbuf; |
5320918b DA |
26 | int ret, i; |
27 | ||
28 | block = kmalloc(EDID_LENGTH, GFP_KERNEL); | |
29 | if (block == NULL) | |
30 | return NULL; | |
31 | ||
242187b3 HG |
32 | rbuf = kmalloc(2, GFP_KERNEL); |
33 | if (rbuf == NULL) | |
34 | goto error; | |
35 | ||
5320918b | 36 | for (i = 0; i < EDID_LENGTH; i++) { |
d4f68a75 DH |
37 | ret = usb_control_msg(udl->udev, |
38 | usb_rcvctrlpipe(udl->udev, 0), (0x02), | |
5320918b DA |
39 | (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, |
40 | HZ); | |
41 | if (ret < 1) { | |
42 | DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); | |
5320918b DA |
43 | goto error; |
44 | } | |
45 | block[i] = rbuf[1]; | |
46 | } | |
47 | ||
242187b3 | 48 | kfree(rbuf); |
5320918b DA |
49 | return block; |
50 | ||
51 | error: | |
52 | kfree(block); | |
242187b3 | 53 | kfree(rbuf); |
5320918b DA |
54 | return NULL; |
55 | } | |
56 | ||
57 | static int udl_get_modes(struct drm_connector *connector) | |
58 | { | |
59 | struct udl_device *udl = connector->dev->dev_private; | |
60 | struct edid *edid; | |
61 | int ret; | |
62 | ||
63 | edid = (struct edid *)udl_get_edid(udl); | |
1baee586 DA |
64 | if (!edid) { |
65 | drm_mode_connector_update_edid_property(connector, NULL); | |
66 | return 0; | |
67 | } | |
5320918b | 68 | |
c930812f HG |
69 | /* |
70 | * We only read the main block, but if the monitor reports extension | |
71 | * blocks then the drm edid code expects them to be present, so patch | |
72 | * the extension count to 0. | |
73 | */ | |
74 | edid->checksum += edid->extensions; | |
75 | edid->extensions = 0; | |
76 | ||
5320918b DA |
77 | drm_mode_connector_update_edid_property(connector, edid); |
78 | ret = drm_add_edid_modes(connector, edid); | |
5320918b DA |
79 | kfree(edid); |
80 | return ret; | |
81 | } | |
82 | ||
83 | static int udl_mode_valid(struct drm_connector *connector, | |
84 | struct drm_display_mode *mode) | |
85 | { | |
3a758858 DA |
86 | struct udl_device *udl = connector->dev->dev_private; |
87 | if (!udl->sku_pixel_limit) | |
88 | return 0; | |
89 | ||
90 | if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit) | |
91 | return MODE_VIRTUAL_Y; | |
92 | ||
5320918b DA |
93 | return 0; |
94 | } | |
95 | ||
96 | static enum drm_connector_status | |
97 | udl_detect(struct drm_connector *connector, bool force) | |
98 | { | |
99 | if (drm_device_is_unplugged(connector->dev)) | |
100 | return connector_status_disconnected; | |
101 | return connector_status_connected; | |
102 | } | |
103 | ||
b27b6d32 SK |
104 | static struct drm_encoder* |
105 | udl_best_single_encoder(struct drm_connector *connector) | |
5320918b DA |
106 | { |
107 | int enc_id = connector->encoder_ids[0]; | |
bf02d87f | 108 | return drm_encoder_find(connector->dev, enc_id); |
5320918b DA |
109 | } |
110 | ||
b27b6d32 SK |
111 | static int udl_connector_set_property(struct drm_connector *connector, |
112 | struct drm_property *property, | |
113 | uint64_t val) | |
5320918b DA |
114 | { |
115 | return 0; | |
116 | } | |
117 | ||
118 | static void udl_connector_destroy(struct drm_connector *connector) | |
119 | { | |
34ea3d38 | 120 | drm_connector_unregister(connector); |
5320918b DA |
121 | drm_connector_cleanup(connector); |
122 | kfree(connector); | |
123 | } | |
124 | ||
a942d739 | 125 | static const struct drm_connector_helper_funcs udl_connector_helper_funcs = { |
5320918b DA |
126 | .get_modes = udl_get_modes, |
127 | .mode_valid = udl_mode_valid, | |
128 | .best_encoder = udl_best_single_encoder, | |
129 | }; | |
130 | ||
a942d739 | 131 | static const struct drm_connector_funcs udl_connector_funcs = { |
5320918b DA |
132 | .dpms = drm_helper_connector_dpms, |
133 | .detect = udl_detect, | |
134 | .fill_modes = drm_helper_probe_single_connector_modes, | |
135 | .destroy = udl_connector_destroy, | |
136 | .set_property = udl_connector_set_property, | |
137 | }; | |
138 | ||
139 | int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) | |
140 | { | |
141 | struct drm_connector *connector; | |
142 | ||
143 | connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL); | |
144 | if (!connector) | |
145 | return -ENOMEM; | |
146 | ||
147 | drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII); | |
148 | drm_connector_helper_add(connector, &udl_connector_helper_funcs); | |
149 | ||
34ea3d38 | 150 | drm_connector_register(connector); |
5320918b DA |
151 | drm_mode_connector_attach_encoder(connector, encoder); |
152 | ||
99d1dba6 | 153 | drm_object_attach_property(&connector->base, |
5320918b DA |
154 | dev->mode_config.dirty_info_property, |
155 | 1); | |
156 | return 0; | |
157 | } |