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; | |
25 | char rbuf[3]; | |
26 | int ret, i; | |
27 | ||
28 | block = kmalloc(EDID_LENGTH, GFP_KERNEL); | |
29 | if (block == NULL) | |
30 | return NULL; | |
31 | ||
32 | for (i = 0; i < EDID_LENGTH; i++) { | |
33 | ret = usb_control_msg(udl->ddev->usbdev, | |
34 | usb_rcvctrlpipe(udl->ddev->usbdev, 0), (0x02), | |
35 | (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, | |
36 | HZ); | |
37 | if (ret < 1) { | |
38 | DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); | |
39 | i--; | |
40 | goto error; | |
41 | } | |
42 | block[i] = rbuf[1]; | |
43 | } | |
44 | ||
45 | return block; | |
46 | ||
47 | error: | |
48 | kfree(block); | |
49 | return NULL; | |
50 | } | |
51 | ||
52 | static int udl_get_modes(struct drm_connector *connector) | |
53 | { | |
54 | struct udl_device *udl = connector->dev->dev_private; | |
55 | struct edid *edid; | |
56 | int ret; | |
57 | ||
58 | edid = (struct edid *)udl_get_edid(udl); | |
59 | ||
60 | connector->display_info.raw_edid = (char *)edid; | |
61 | ||
62 | drm_mode_connector_update_edid_property(connector, edid); | |
63 | ret = drm_add_edid_modes(connector, edid); | |
64 | connector->display_info.raw_edid = NULL; | |
65 | kfree(edid); | |
66 | return ret; | |
67 | } | |
68 | ||
69 | static int udl_mode_valid(struct drm_connector *connector, | |
70 | struct drm_display_mode *mode) | |
71 | { | |
3a758858 DA |
72 | struct udl_device *udl = connector->dev->dev_private; |
73 | if (!udl->sku_pixel_limit) | |
74 | return 0; | |
75 | ||
76 | if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit) | |
77 | return MODE_VIRTUAL_Y; | |
78 | ||
5320918b DA |
79 | return 0; |
80 | } | |
81 | ||
82 | static enum drm_connector_status | |
83 | udl_detect(struct drm_connector *connector, bool force) | |
84 | { | |
85 | if (drm_device_is_unplugged(connector->dev)) | |
86 | return connector_status_disconnected; | |
87 | return connector_status_connected; | |
88 | } | |
89 | ||
90 | struct drm_encoder *udl_best_single_encoder(struct drm_connector *connector) | |
91 | { | |
92 | int enc_id = connector->encoder_ids[0]; | |
93 | struct drm_mode_object *obj; | |
94 | struct drm_encoder *encoder; | |
95 | ||
96 | obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); | |
97 | if (!obj) | |
98 | return NULL; | |
99 | encoder = obj_to_encoder(obj); | |
100 | return encoder; | |
101 | } | |
102 | ||
103 | int udl_connector_set_property(struct drm_connector *connector, struct drm_property *property, | |
104 | uint64_t val) | |
105 | { | |
106 | return 0; | |
107 | } | |
108 | ||
109 | static void udl_connector_destroy(struct drm_connector *connector) | |
110 | { | |
111 | drm_sysfs_connector_remove(connector); | |
112 | drm_connector_cleanup(connector); | |
113 | kfree(connector); | |
114 | } | |
115 | ||
116 | struct drm_connector_helper_funcs udl_connector_helper_funcs = { | |
117 | .get_modes = udl_get_modes, | |
118 | .mode_valid = udl_mode_valid, | |
119 | .best_encoder = udl_best_single_encoder, | |
120 | }; | |
121 | ||
122 | struct drm_connector_funcs udl_connector_funcs = { | |
123 | .dpms = drm_helper_connector_dpms, | |
124 | .detect = udl_detect, | |
125 | .fill_modes = drm_helper_probe_single_connector_modes, | |
126 | .destroy = udl_connector_destroy, | |
127 | .set_property = udl_connector_set_property, | |
128 | }; | |
129 | ||
130 | int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) | |
131 | { | |
132 | struct drm_connector *connector; | |
133 | ||
134 | connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL); | |
135 | if (!connector) | |
136 | return -ENOMEM; | |
137 | ||
138 | drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII); | |
139 | drm_connector_helper_add(connector, &udl_connector_helper_funcs); | |
140 | ||
141 | drm_sysfs_connector_add(connector); | |
142 | drm_mode_connector_attach_encoder(connector, encoder); | |
143 | ||
144 | drm_connector_attach_property(connector, | |
145 | dev->mode_config.dirty_info_property, | |
146 | 1); | |
147 | return 0; | |
148 | } |