Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[linux-2.6] / drivers / gpu / 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 CH7009A_VID             0x84
36 #define CH7009B_VID             0x85
37 #define CH7301_VID              0x95
38
39 #define CH7xxx_VID              0x84
40 #define CH7xxx_DID              0x17
41
42 #define CH7xxx_NUM_REGS         0x4c
43
44 #define CH7xxx_CM               0x1c
45 #define CH7xxx_CM_XCM           (1<<0)
46 #define CH7xxx_CM_MCP           (1<<2)
47 #define CH7xxx_INPUT_CLOCK      0x1d
48 #define CH7xxx_GPIO             0x1e
49 #define CH7xxx_GPIO_HPIR        (1<<3)
50 #define CH7xxx_IDF              0x1f
51
52 #define CH7xxx_IDF_HSP          (1<<3)
53 #define CH7xxx_IDF_VSP          (1<<4)
54
55 #define CH7xxx_CONNECTION_DETECT 0x20
56 #define CH7xxx_CDET_DVI         (1<<5)
57
58 #define CH7301_DAC_CNTL         0x21
59 #define CH7301_HOTPLUG          0x23
60 #define CH7xxx_TCTL             0x31
61 #define CH7xxx_TVCO             0x32
62 #define CH7xxx_TPCP             0x33
63 #define CH7xxx_TPD              0x34
64 #define CH7xxx_TPVT             0x35
65 #define CH7xxx_TLPF             0x36
66 #define CH7xxx_TCT              0x37
67 #define CH7301_TEST_PATTERN     0x48
68
69 #define CH7xxx_PM               0x49
70 #define CH7xxx_PM_FPD           (1<<0)
71 #define CH7301_PM_DACPD0        (1<<1)
72 #define CH7301_PM_DACPD1        (1<<2)
73 #define CH7301_PM_DACPD2        (1<<3)
74 #define CH7xxx_PM_DVIL          (1<<6)
75 #define CH7xxx_PM_DVIP          (1<<7)
76
77 #define CH7301_SYNC_POLARITY    0x56
78 #define CH7301_SYNC_RGB_YUV     (1<<0)
79 #define CH7301_SYNC_POL_DVI     (1<<5)
80
81 /** @file
82  * driver for the Chrontel 7xxx DVI chip over DVO.
83  */
84
85 static struct ch7xxx_id_struct {
86         uint8_t vid;
87         char *name;
88 } ch7xxx_ids[] = {
89         { CH7011_VID, "CH7011" },
90         { CH7009A_VID, "CH7009A" },
91         { CH7009B_VID, "CH7009B" },
92         { CH7301_VID, "CH7301" },
93 };
94
95 struct ch7xxx_reg_state {
96     uint8_t regs[CH7xxx_NUM_REGS];
97 };
98
99 struct ch7xxx_priv {
100         bool quiet;
101
102         struct ch7xxx_reg_state save_reg;
103         struct ch7xxx_reg_state mode_reg;
104         uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT;
105         uint8_t save_TLPF, save_TCT, save_PM, save_IDF;
106 };
107
108 static void ch7xxx_save(struct intel_dvo_device *dvo);
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 /** Reads an 8 bit register */
123 static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
124 {
125         struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
126         struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
127         u8 out_buf[2];
128         u8 in_buf[2];
129
130         struct i2c_msg msgs[] = {
131                 {
132                         .addr = i2cbus->slave_addr,
133                         .flags = 0,
134                         .len = 1,
135                         .buf = out_buf,
136                 },
137                 {
138                         .addr = i2cbus->slave_addr,
139                         .flags = I2C_M_RD,
140                         .len = 1,
141                         .buf = in_buf,
142                 }
143         };
144
145         out_buf[0] = addr;
146         out_buf[1] = 0;
147
148         if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
149                 *ch = in_buf[0];
150                 return true;
151         };
152
153         if (!ch7xxx->quiet) {
154                 DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
155                           addr, i2cbus->adapter.name, i2cbus->slave_addr);
156         }
157         return false;
158 }
159
160 /** Writes an 8 bit register */
161 static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
162 {
163         struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
164         struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
165         uint8_t out_buf[2];
166         struct i2c_msg msg = {
167                 .addr = i2cbus->slave_addr,
168                 .flags = 0,
169                 .len = 2,
170                 .buf = out_buf,
171         };
172
173         out_buf[0] = addr;
174         out_buf[1] = ch;
175
176         if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
177                 return true;
178
179         if (!ch7xxx->quiet) {
180                 DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
181                           addr, i2cbus->adapter.name, i2cbus->slave_addr);
182         }
183
184         return false;
185 }
186
187 static bool ch7xxx_init(struct intel_dvo_device *dvo,
188                         struct intel_i2c_chan *i2cbus)
189 {
190         /* this will detect the CH7xxx chip on the specified i2c bus */
191         struct ch7xxx_priv *ch7xxx;
192         uint8_t vendor, device;
193         char *name;
194
195         ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
196         if (ch7xxx == NULL)
197                 return false;
198
199         dvo->i2c_bus = i2cbus;
200         dvo->i2c_bus->slave_addr = dvo->slave_addr;
201         dvo->dev_priv = ch7xxx;
202         ch7xxx->quiet = true;
203
204         if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
205                 goto out;
206
207         name = ch7xxx_get_id(vendor);
208         if (!name) {
209                 DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
210                           vendor, i2cbus->adapter.name, i2cbus->slave_addr);
211                 goto out;
212         }
213
214
215         if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
216                 goto out;
217
218         if (device != CH7xxx_DID) {
219                 DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
220                           vendor, i2cbus->adapter.name, i2cbus->slave_addr);
221                 goto out;
222         }
223
224         ch7xxx->quiet = false;
225         DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
226                   name, vendor, device);
227         return true;
228 out:
229         kfree(ch7xxx);
230         return false;
231 }
232
233 static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
234 {
235         uint8_t cdet, orig_pm, pm;
236
237         ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
238
239         pm = orig_pm;
240         pm &= ~CH7xxx_PM_FPD;
241         pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
242
243         ch7xxx_writeb(dvo, CH7xxx_PM, pm);
244
245         ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
246
247         ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
248
249         if (cdet & CH7xxx_CDET_DVI)
250                 return connector_status_connected;
251         return connector_status_disconnected;
252 }
253
254 static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
255                                               struct drm_display_mode *mode)
256 {
257         if (mode->clock > 165000)
258                 return MODE_CLOCK_HIGH;
259
260         return MODE_OK;
261 }
262
263 static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
264                             struct drm_display_mode *mode,
265                             struct drm_display_mode *adjusted_mode)
266 {
267         uint8_t tvco, tpcp, tpd, tlpf, idf;
268
269         if (mode->clock <= 65000) {
270                 tvco = 0x23;
271                 tpcp = 0x08;
272                 tpd = 0x16;
273                 tlpf = 0x60;
274         } else {
275                 tvco = 0x2d;
276                 tpcp = 0x06;
277                 tpd = 0x26;
278                 tlpf = 0xa0;
279         }
280
281         ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
282         ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
283         ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
284         ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
285         ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
286         ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
287         ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
288
289         ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
290
291         idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
292         if (mode->flags & DRM_MODE_FLAG_PHSYNC)
293                 idf |= CH7xxx_IDF_HSP;
294
295         if (mode->flags & DRM_MODE_FLAG_PVSYNC)
296                 idf |= CH7xxx_IDF_HSP;
297
298         ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
299 }
300
301 /* set the CH7xxx power state */
302 static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
303 {
304         if (mode == DRM_MODE_DPMS_ON)
305                 ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
306         else
307                 ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
308 }
309
310 static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
311 {
312         struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
313         int i;
314
315         for (i = 0; i < CH7xxx_NUM_REGS; i++) {
316                 if ((i % 8) == 0 )
317                         DRM_DEBUG("\n %02X: ", i);
318                 DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]);
319         }
320 }
321
322 static void ch7xxx_save(struct intel_dvo_device *dvo)
323 {
324         struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
325
326         ch7xxx_readb(dvo, CH7xxx_TCTL, &ch7xxx->save_TCTL);
327         ch7xxx_readb(dvo, CH7xxx_TPCP, &ch7xxx->save_TPCP);
328         ch7xxx_readb(dvo, CH7xxx_TPD, &ch7xxx->save_TPD);
329         ch7xxx_readb(dvo, CH7xxx_TPVT, &ch7xxx->save_TPVT);
330         ch7xxx_readb(dvo, CH7xxx_TLPF, &ch7xxx->save_TLPF);
331         ch7xxx_readb(dvo, CH7xxx_PM, &ch7xxx->save_PM);
332         ch7xxx_readb(dvo, CH7xxx_IDF, &ch7xxx->save_IDF);
333 }
334
335 static void ch7xxx_restore(struct intel_dvo_device *dvo)
336 {
337         struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
338
339         ch7xxx_writeb(dvo, CH7xxx_TCTL, ch7xxx->save_TCTL);
340         ch7xxx_writeb(dvo, CH7xxx_TPCP, ch7xxx->save_TPCP);
341         ch7xxx_writeb(dvo, CH7xxx_TPD, ch7xxx->save_TPD);
342         ch7xxx_writeb(dvo, CH7xxx_TPVT, ch7xxx->save_TPVT);
343         ch7xxx_writeb(dvo, CH7xxx_TLPF, ch7xxx->save_TLPF);
344         ch7xxx_writeb(dvo, CH7xxx_IDF, ch7xxx->save_IDF);
345         ch7xxx_writeb(dvo, CH7xxx_PM, ch7xxx->save_PM);
346 }
347
348 static void ch7xxx_destroy(struct intel_dvo_device *dvo)
349 {
350         struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
351
352         if (ch7xxx) {
353                 kfree(ch7xxx);
354                 dvo->dev_priv = NULL;
355         }
356 }
357
358 struct intel_dvo_dev_ops ch7xxx_ops = {
359         .init = ch7xxx_init,
360         .detect = ch7xxx_detect,
361         .mode_valid = ch7xxx_mode_valid,
362         .mode_set = ch7xxx_mode_set,
363         .dpms = ch7xxx_dpms,
364         .dump_regs = ch7xxx_dump_regs,
365         .save = ch7xxx_save,
366         .restore = ch7xxx_restore,
367         .destroy = ch7xxx_destroy,
368 };