OSDN Git Service

Merge branch 'modesetting-gem' of git+ssh://agd5f@git.freedesktop.org/git/mesa/drm...
[android-x86/external-libdrm.git] / linux-core / nv50_connector.c
1 /*
2  * Copyright (C) 2008 Maarten Maathuis.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial
15  * portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  */
26
27 #include "nv50_connector.h"
28
29 static struct nv50_output *nv50_connector_to_output(struct nv50_connector *connector, bool digital)
30 {
31         struct nv50_display *display = nv50_get_display(connector->dev);
32         struct nv50_output *output = NULL;
33         bool digital_possible = false;
34         bool analog_possible = false;
35
36         switch (connector->type) {
37                 case CONNECTOR_VGA:
38                 case CONNECTOR_TV:
39                         analog_possible = true;
40                         break;
41                 case CONNECTOR_DVI_I:
42                         analog_possible = true;
43                         digital_possible = true;
44                         break;
45                 case CONNECTOR_DVI_D:
46                 case CONNECTOR_LVDS:
47                         digital_possible = true;
48                         break;
49                 default:
50                         break;
51         }
52
53         /* Return early on bad situations. */
54         if (!analog_possible && !digital_possible)
55                 return NULL;
56
57         if (!analog_possible && !digital)
58                 return NULL;
59
60         if (!digital_possible && digital)
61                 return NULL;
62
63         list_for_each_entry(output, &display->outputs, item) {
64                 if (connector->bus != output->bus)
65                         continue;
66                 if (digital && output->type == OUTPUT_TMDS)
67                         return output;
68                 if (digital && output->type == OUTPUT_LVDS)
69                         return output;
70                 if (!digital && output->type == OUTPUT_DAC)
71                         return output;
72                 if (!digital && output->type == OUTPUT_TV)
73                         return output;
74         }
75
76         return NULL;
77 }
78
79 static int nv50_connector_hpd_detect(struct nv50_connector *connector)
80 {
81         struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
82         bool present = 0;
83         uint32_t reg = 0;
84
85         /* Assume connected for the moment. */
86         if (connector->type == CONNECTOR_LVDS) {
87                 NV50_DEBUG("LVDS is defaulting to connected for the moment.\n");
88                 return 1;
89         }
90
91         /* No i2c port, no idea what to do for hotplug. */
92         if (connector->i2c_chan->index == 15) {
93                 DRM_ERROR("You have a non-LVDS SOR with no i2c port, please report\n");
94                 return -EINVAL;
95         }
96
97         if (connector->i2c_chan->index > 3) {
98                 DRM_ERROR("You have an unusual configuration, index is %d\n", connector->i2c_chan->index);
99                 DRM_ERROR("Please report.\n");
100                 return -EINVAL;
101         }
102
103         /* Check hotplug pins. */
104         reg = NV_READ(NV50_PCONNECTOR_HOTPLUG_STATE);
105         if (reg & (NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 << (4 * connector->i2c_chan->index)))
106                 present = 1;
107
108         if (present)
109                 NV50_DEBUG("Hotplug detect returned positive for bus %d\n", connector->bus);
110         else
111                 NV50_DEBUG("Hotplug detect returned negative for bus %d\n", connector->bus);
112
113         return present;
114 }
115
116 static int nv50_connector_i2c_detect(struct nv50_connector *connector)
117 {
118         /* kindly borrrowed from the intel driver, hope it works. */
119         uint8_t out_buf[] = { 0x0, 0x0};
120         uint8_t buf[2];
121         int ret;
122         struct i2c_msg msgs[] = {
123                 {
124                         .addr = 0x50,
125                         .flags = 0,
126                         .len = 1,
127                         .buf = out_buf,
128                 },
129                 {
130                         .addr = 0x50,
131                         .flags = I2C_M_RD,
132                         .len = 1,
133                         .buf = buf,
134                 }
135         };
136
137         if (!connector->i2c_chan)
138                 return -EINVAL;
139
140         ret = i2c_transfer(&connector->i2c_chan->adapter, msgs, 2);
141         NV50_DEBUG("I2C detect returned %d\n", ret);
142
143         if (ret == 2)
144                 return true;
145
146         return false;
147 }
148
149 static int nv50_connector_destroy(struct nv50_connector *connector)
150 {
151         struct drm_device *dev = connector->dev;
152         struct drm_nouveau_private *dev_priv = dev->dev_private;
153         struct nv50_display *display = nv50_get_display(dev);
154
155         NV50_DEBUG("\n");
156
157         if (!display || !connector)
158                 return -EINVAL;
159
160         list_del(&connector->item);
161
162         if (connector->i2c_chan)
163                 nv50_i2c_channel_destroy(connector->i2c_chan);
164
165         if (dev_priv->free_connector)
166                 dev_priv->free_connector(connector);
167
168         return 0;
169 }
170
171 int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type)
172 {
173         struct nv50_connector *connector = NULL;
174         struct drm_nouveau_private *dev_priv = dev->dev_private;
175         struct nv50_display *display = NULL;
176
177         NV50_DEBUG("\n");
178
179         /* This allows the public layer to do it's thing. */
180         if (dev_priv->alloc_connector)
181                 connector = dev_priv->alloc_connector(dev);
182
183         if (!connector)
184                 return -ENOMEM;
185
186         connector->dev = dev;
187
188         display = nv50_get_display(dev);
189         if (!display)
190                 goto out;
191
192         if (type == CONNECTOR_UNKNOWN)
193                 goto out;
194
195         list_add_tail(&connector->item, &display->connectors);
196
197         connector->bus = bus;
198         connector->type = type;
199
200         switch (type) {
201                 case CONNECTOR_VGA:
202                         DRM_INFO("Detected a VGA connector\n");
203                         break;
204                 case CONNECTOR_DVI_D:
205                         DRM_INFO("Detected a DVI-D connector\n");
206                         break;
207                 case CONNECTOR_DVI_I:
208                         DRM_INFO("Detected a DVI-I connector\n");
209                         break;
210                 case CONNECTOR_LVDS:
211                         DRM_INFO("Detected a LVDS connector\n");
212                         break;
213                 case CONNECTOR_TV:
214                         DRM_INFO("Detected a TV connector\n");
215                         break;
216                 default:
217                         DRM_ERROR("Unknown connector, this is not good.\n");
218                         break;
219         }
220
221         /* some reasonable defaults */
222         if (type == CONNECTOR_DVI_D || type == CONNECTOR_DVI_I || type == CONNECTOR_LVDS)
223                 connector->requested_scaling_mode = SCALE_FULLSCREEN;
224         else
225                 connector->requested_scaling_mode = SCALE_NON_GPU;
226
227         connector->use_dithering = false;
228
229         if (i2c_index < 0xf)
230                 connector->i2c_chan = nv50_i2c_channel_create(dev, i2c_index);
231
232         /* set function pointers */
233         connector->hpd_detect = nv50_connector_hpd_detect;
234         connector->i2c_detect = nv50_connector_i2c_detect;
235         connector->destroy = nv50_connector_destroy;
236         connector->to_output = nv50_connector_to_output;
237
238         return 0;
239
240 out:
241         if (dev_priv->free_connector)
242                 dev_priv->free_connector(connector);
243
244         return -EINVAL;
245 }