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