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