gdb - Local mods (compile)
[dragonfly.git] / sys / dev / drm / i915 / dvo_ch7xxx.c
1 /**************************************************************************
2
3 Copyright © 2006 Dave Airlie
4
5 All Rights Reserved.
6
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sub license, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14
15 The above copyright notice and this permission notice (including the
16 next paragraph) shall be included in all copies or substantial portions
17 of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
23 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
27 **************************************************************************/
28
29 #include "dvo.h"
30
31 #define CH7xxx_REG_VID          0x4a
32 #define CH7xxx_REG_DID          0x4b
33
34 #define CH7011_VID              0x83 /* 7010 as well */
35 #define CH7010B_VID             0x05
36 #define CH7009A_VID             0x84
37 #define CH7009B_VID             0x85
38 #define CH7301_VID              0x95
39
40 #define CH7xxx_VID              0x84
41 #define CH7xxx_DID              0x17
42 #define CH7010_DID              0x16
43
44 #define CH7xxx_NUM_REGS         0x4c
45
46 #define CH7xxx_CM               0x1c
47 #define CH7xxx_CM_XCM           (1<<0)
48 #define CH7xxx_CM_MCP           (1<<2)
49 #define CH7xxx_INPUT_CLOCK      0x1d
50 #define CH7xxx_GPIO             0x1e
51 #define CH7xxx_GPIO_HPIR        (1<<3)
52 #define CH7xxx_IDF              0x1f
53
54 #define CH7xxx_IDF_HSP          (1<<3)
55 #define CH7xxx_IDF_VSP          (1<<4)
56
57 #define CH7xxx_CONNECTION_DETECT 0x20
58 #define CH7xxx_CDET_DVI         (1<<5)
59
60 #define CH7301_DAC_CNTL         0x21
61 #define CH7301_HOTPLUG          0x23
62 #define CH7xxx_TCTL             0x31
63 #define CH7xxx_TVCO             0x32
64 #define CH7xxx_TPCP             0x33
65 #define CH7xxx_TPD              0x34
66 #define CH7xxx_TPVT             0x35
67 #define CH7xxx_TLPF             0x36
68 #define CH7xxx_TCT              0x37
69 #define CH7301_TEST_PATTERN     0x48
70
71 #define CH7xxx_PM               0x49
72 #define CH7xxx_PM_FPD           (1<<0)
73 #define CH7301_PM_DACPD0        (1<<1)
74 #define CH7301_PM_DACPD1        (1<<2)
75 #define CH7301_PM_DACPD2        (1<<3)
76 #define CH7xxx_PM_DVIL          (1<<6)
77 #define CH7xxx_PM_DVIP          (1<<7)
78
79 #define CH7301_SYNC_POLARITY    0x56
80 #define CH7301_SYNC_RGB_YUV     (1<<0)
81 #define CH7301_SYNC_POL_DVI     (1<<5)
82
83 /** @file
84  * driver for the Chrontel 7xxx DVI chip over DVO.
85  */
86
87 static struct ch7xxx_id_struct {
88         uint8_t vid;
89         char *name;
90 } ch7xxx_ids[] = {
91         { CH7011_VID, "CH7011" },
92         { CH7010B_VID, "CH7010B" },
93         { CH7009A_VID, "CH7009A" },
94         { CH7009B_VID, "CH7009B" },
95         { CH7301_VID, "CH7301" },
96 };
97
98 static struct ch7xxx_did_struct {
99         uint8_t did;
100         char *name;
101 } ch7xxx_dids[] = {
102         { CH7xxx_DID, "CH7XXX" },
103         { CH7010_DID, "CH7010B" },
104 };
105
106 struct ch7xxx_priv {
107         bool quiet;
108 };
109
110 static char *ch7xxx_get_id(uint8_t vid)
111 {
112         int i;
113
114         for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
115                 if (ch7xxx_ids[i].vid == vid)
116                         return ch7xxx_ids[i].name;
117         }
118
119         return NULL;
120 }
121
122 static char *ch7xxx_get_did(uint8_t did)
123 {
124         int i;
125
126         for (i = 0; i < ARRAY_SIZE(ch7xxx_dids); i++) {
127                 if (ch7xxx_dids[i].did == did)
128                         return ch7xxx_dids[i].name;
129         }
130
131         return NULL;
132 }
133
134 /** Reads an 8 bit register */
135 static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
136 {
137         struct intel_iic_softc *sc;
138         struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
139         struct i2c_adapter *adapter = dvo->i2c_bus;
140         u8 out_buf[2];
141         u8 in_buf[2];
142
143         struct i2c_msg msgs[] = {
144                 {
145                         .slave = dvo->slave_addr << 1,
146                         .flags = 0,
147                         .len = 1,
148                         .buf = out_buf,
149                 },
150                 {
151                         .slave = dvo->slave_addr << 1,
152                         .flags = I2C_M_RD,
153                         .len = 1,
154                         .buf = in_buf,
155                 }
156         };
157
158         out_buf[0] = addr;
159         out_buf[1] = 0;
160
161         sc = device_get_softc(adapter);
162
163         if (iicbus_transfer(adapter, msgs, 2) == 0) {
164                 *ch = in_buf[0];
165                 return true;
166         }
167
168         if (!ch7xxx->quiet) {
169                 DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
170                           addr, sc->name, dvo->slave_addr);
171         }
172         return false;
173 }
174
175 /** Writes an 8 bit register */
176 static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
177 {
178         struct intel_iic_softc *sc;
179         struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
180         struct i2c_adapter *adapter = dvo->i2c_bus;
181         uint8_t out_buf[2];
182         struct i2c_msg msg = {
183                 .slave = dvo->slave_addr << 1,
184                 .flags = 0,
185                 .len = 2,
186                 .buf = out_buf,
187         };
188
189         out_buf[0] = addr;
190         out_buf[1] = ch;
191
192         sc = device_get_softc(adapter);
193
194         if (iicbus_transfer(adapter, &msg, 1) == 0)
195                 return true;
196
197         if (!ch7xxx->quiet) {
198                 DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
199                           addr, sc->name, dvo->slave_addr);
200         }
201
202         return false;
203 }
204
205 static bool ch7xxx_init(struct intel_dvo_device *dvo,
206                         struct i2c_adapter *adapter)
207 {
208         struct intel_iic_softc *sc;
209         /* this will detect the CH7xxx chip on the specified i2c bus */
210         struct ch7xxx_priv *ch7xxx;
211         uint8_t vendor, device;
212         char *name, *devid;
213
214         sc = device_get_softc(adapter);
215
216         ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
217         if (ch7xxx == NULL)
218                 return false;
219
220         dvo->i2c_bus = adapter;
221         dvo->dev_priv = ch7xxx;
222         ch7xxx->quiet = true;
223
224         if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
225                 goto out;
226
227         name = ch7xxx_get_id(vendor);
228         if (!name) {
229                 DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
230                                 "slave %d.\n",
231                           vendor, sc->name, dvo->slave_addr);
232                 goto out;
233         }
234
235
236         if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
237                 goto out;
238
239         devid = ch7xxx_get_did(device);
240         if (!devid) {
241                 DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
242                                 "slave %d.\n",
243                           vendor, sc->name, dvo->slave_addr);
244                 goto out;
245         }
246
247         ch7xxx->quiet = false;
248         DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
249                   name, vendor, device);
250         return true;
251 out:
252         kfree(ch7xxx);
253         return false;
254 }
255
256 static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
257 {
258         uint8_t cdet, orig_pm, pm;
259
260         ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
261
262         pm = orig_pm;
263         pm &= ~CH7xxx_PM_FPD;
264         pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
265
266         ch7xxx_writeb(dvo, CH7xxx_PM, pm);
267
268         ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
269
270         ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
271
272         if (cdet & CH7xxx_CDET_DVI)
273                 return connector_status_connected;
274         return connector_status_disconnected;
275 }
276
277 static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
278                                               struct drm_display_mode *mode)
279 {
280         if (mode->clock > 165000)
281                 return MODE_CLOCK_HIGH;
282
283         return MODE_OK;
284 }
285
286 static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
287                             struct drm_display_mode *mode,
288                             struct drm_display_mode *adjusted_mode)
289 {
290         uint8_t tvco, tpcp, tpd, tlpf, idf;
291
292         if (mode->clock <= 65000) {
293                 tvco = 0x23;
294                 tpcp = 0x08;
295                 tpd = 0x16;
296                 tlpf = 0x60;
297         } else {
298                 tvco = 0x2d;
299                 tpcp = 0x06;
300                 tpd = 0x26;
301                 tlpf = 0xa0;
302         }
303
304         ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
305         ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
306         ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
307         ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
308         ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
309         ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
310         ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
311
312         ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
313
314         idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
315         if (mode->flags & DRM_MODE_FLAG_PHSYNC)
316                 idf |= CH7xxx_IDF_HSP;
317
318         if (mode->flags & DRM_MODE_FLAG_PVSYNC)
319                 idf |= CH7xxx_IDF_VSP;
320
321         ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
322 }
323
324 /* set the CH7xxx power state */
325 static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable)
326 {
327         if (enable)
328                 ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
329         else
330                 ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
331 }
332
333 static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo)
334 {
335         u8 val;
336
337         ch7xxx_readb(dvo, CH7xxx_PM, &val);
338
339         if (val & (CH7xxx_PM_DVIL | CH7xxx_PM_DVIP))
340                 return true;
341         else
342                 return false;
343 }
344
345 static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
346 {
347         int i;
348
349         for (i = 0; i < CH7xxx_NUM_REGS; i++) {
350                 uint8_t val;
351                 if ((i % 8) == 0)
352                         DRM_DEBUG_KMS("\n %02X: ", i);
353                 ch7xxx_readb(dvo, i, &val);
354                 DRM_DEBUG_KMS("%02X ", val);
355         }
356 }
357
358 static void ch7xxx_destroy(struct intel_dvo_device *dvo)
359 {
360         struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
361
362         if (ch7xxx) {
363                 kfree(ch7xxx);
364                 dvo->dev_priv = NULL;
365         }
366 }
367
368 struct intel_dvo_dev_ops ch7xxx_ops = {
369         .init = ch7xxx_init,
370         .detect = ch7xxx_detect,
371         .mode_valid = ch7xxx_mode_valid,
372         .mode_set = ch7xxx_mode_set,
373         .dpms = ch7xxx_dpms,
374         .get_hw_state = ch7xxx_get_hw_state,
375         .dump_regs = ch7xxx_dump_regs,
376         .destroy = ch7xxx_destroy,
377 };