• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_priv {
96 	bool quiet;
97 };
98 
ch7xxx_get_id(uint8_t vid)99 static char *ch7xxx_get_id(uint8_t vid)
100 {
101 	int i;
102 
103 	for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
104 		if (ch7xxx_ids[i].vid == vid)
105 			return ch7xxx_ids[i].name;
106 	}
107 
108 	return NULL;
109 }
110 
111 /** Reads an 8 bit register */
ch7xxx_readb(struct intel_dvo_device * dvo,int addr,uint8_t * ch)112 static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
113 {
114 	struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
115 	struct i2c_adapter *adapter = dvo->i2c_bus;
116 	u8 out_buf[2];
117 	u8 in_buf[2];
118 
119 	struct i2c_msg msgs[] = {
120 		{
121 			.addr = dvo->slave_addr,
122 			.flags = 0,
123 			.len = 1,
124 			.buf = out_buf,
125 		},
126 		{
127 			.addr = dvo->slave_addr,
128 			.flags = I2C_M_RD,
129 			.len = 1,
130 			.buf = in_buf,
131 		}
132 	};
133 
134 	out_buf[0] = addr;
135 	out_buf[1] = 0;
136 
137 	if (i2c_transfer(adapter, msgs, 2) == 2) {
138 		*ch = in_buf[0];
139 		return true;
140 	};
141 
142 	if (!ch7xxx->quiet) {
143 		DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
144 			  addr, adapter->name, dvo->slave_addr);
145 	}
146 	return false;
147 }
148 
149 /** Writes an 8 bit register */
ch7xxx_writeb(struct intel_dvo_device * dvo,int addr,uint8_t ch)150 static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
151 {
152 	struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
153 	struct i2c_adapter *adapter = dvo->i2c_bus;
154 	uint8_t out_buf[2];
155 	struct i2c_msg msg = {
156 		.addr = dvo->slave_addr,
157 		.flags = 0,
158 		.len = 2,
159 		.buf = out_buf,
160 	};
161 
162 	out_buf[0] = addr;
163 	out_buf[1] = ch;
164 
165 	if (i2c_transfer(adapter, &msg, 1) == 1)
166 		return true;
167 
168 	if (!ch7xxx->quiet) {
169 		DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
170 			  addr, adapter->name, dvo->slave_addr);
171 	}
172 
173 	return false;
174 }
175 
ch7xxx_init(struct intel_dvo_device * dvo,struct i2c_adapter * adapter)176 static bool ch7xxx_init(struct intel_dvo_device *dvo,
177 			struct i2c_adapter *adapter)
178 {
179 	/* this will detect the CH7xxx chip on the specified i2c bus */
180 	struct ch7xxx_priv *ch7xxx;
181 	uint8_t vendor, device;
182 	char *name;
183 
184 	ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
185 	if (ch7xxx == NULL)
186 		return false;
187 
188 	dvo->i2c_bus = adapter;
189 	dvo->dev_priv = ch7xxx;
190 	ch7xxx->quiet = true;
191 
192 	if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
193 		goto out;
194 
195 	name = ch7xxx_get_id(vendor);
196 	if (!name) {
197 		DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
198 				"slave %d.\n",
199 			  vendor, adapter->name, dvo->slave_addr);
200 		goto out;
201 	}
202 
203 
204 	if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
205 		goto out;
206 
207 	if (device != CH7xxx_DID) {
208 		DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
209 				"slave %d.\n",
210 			  vendor, adapter->name, dvo->slave_addr);
211 		goto out;
212 	}
213 
214 	ch7xxx->quiet = false;
215 	DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
216 		  name, vendor, device);
217 	return true;
218 out:
219 	kfree(ch7xxx);
220 	return false;
221 }
222 
ch7xxx_detect(struct intel_dvo_device * dvo)223 static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
224 {
225 	uint8_t cdet, orig_pm, pm;
226 
227 	ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
228 
229 	pm = orig_pm;
230 	pm &= ~CH7xxx_PM_FPD;
231 	pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
232 
233 	ch7xxx_writeb(dvo, CH7xxx_PM, pm);
234 
235 	ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
236 
237 	ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
238 
239 	if (cdet & CH7xxx_CDET_DVI)
240 		return connector_status_connected;
241 	return connector_status_disconnected;
242 }
243 
ch7xxx_mode_valid(struct intel_dvo_device * dvo,struct drm_display_mode * mode)244 static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
245 					      struct drm_display_mode *mode)
246 {
247 	if (mode->clock > 165000)
248 		return MODE_CLOCK_HIGH;
249 
250 	return MODE_OK;
251 }
252 
ch7xxx_mode_set(struct intel_dvo_device * dvo,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)253 static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
254 			    struct drm_display_mode *mode,
255 			    struct drm_display_mode *adjusted_mode)
256 {
257 	uint8_t tvco, tpcp, tpd, tlpf, idf;
258 
259 	if (mode->clock <= 65000) {
260 		tvco = 0x23;
261 		tpcp = 0x08;
262 		tpd = 0x16;
263 		tlpf = 0x60;
264 	} else {
265 		tvco = 0x2d;
266 		tpcp = 0x06;
267 		tpd = 0x26;
268 		tlpf = 0xa0;
269 	}
270 
271 	ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
272 	ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
273 	ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
274 	ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
275 	ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
276 	ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
277 	ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
278 
279 	ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
280 
281 	idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
282 	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
283 		idf |= CH7xxx_IDF_HSP;
284 
285 	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
286 		idf |= CH7xxx_IDF_HSP;
287 
288 	ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
289 }
290 
291 /* set the CH7xxx power state */
ch7xxx_dpms(struct intel_dvo_device * dvo,bool enable)292 static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable)
293 {
294 	if (enable)
295 		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
296 	else
297 		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
298 }
299 
ch7xxx_get_hw_state(struct intel_dvo_device * dvo)300 static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo)
301 {
302 	u8 val;
303 
304 	ch7xxx_readb(dvo, CH7xxx_PM, &val);
305 
306 	if (val & (CH7xxx_PM_DVIL | CH7xxx_PM_DVIP))
307 		return true;
308 	else
309 		return false;
310 }
311 
ch7xxx_dump_regs(struct intel_dvo_device * dvo)312 static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
313 {
314 	int i;
315 
316 	for (i = 0; i < CH7xxx_NUM_REGS; i++) {
317 		uint8_t val;
318 		if ((i % 8) == 0)
319 			DRM_LOG_KMS("\n %02X: ", i);
320 		ch7xxx_readb(dvo, i, &val);
321 		DRM_LOG_KMS("%02X ", val);
322 	}
323 }
324 
ch7xxx_destroy(struct intel_dvo_device * dvo)325 static void ch7xxx_destroy(struct intel_dvo_device *dvo)
326 {
327 	struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
328 
329 	if (ch7xxx) {
330 		kfree(ch7xxx);
331 		dvo->dev_priv = NULL;
332 	}
333 }
334 
335 struct intel_dvo_dev_ops ch7xxx_ops = {
336 	.init = ch7xxx_init,
337 	.detect = ch7xxx_detect,
338 	.mode_valid = ch7xxx_mode_valid,
339 	.mode_set = ch7xxx_mode_set,
340 	.dpms = ch7xxx_dpms,
341 	.get_hw_state = ch7xxx_get_hw_state,
342 	.dump_regs = ch7xxx_dump_regs,
343 	.destroy = ch7xxx_destroy,
344 };
345