1/**************************************************************************
2
3Copyright © 2006 Dave Airlie
4
5All Rights Reserved.
6
7Permission is hereby granted, free of charge, to any person obtaining a
8copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sub license, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice (including the
16next paragraph) shall be included in all copies or substantial portions
17of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
23ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25SOFTWARE 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
101static 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
112static struct ch7xxx_did_struct {
113 u8 did;
114 char *name;
115} ch7xxx_dids[] = {
116 { CH7xxx_DID, "CH7XXX" },
117 { CH7010_DID, "CH7010B" },
118};
119
120struct ch7xxx_priv {
121 bool quiet;
122};
123
124static 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
136static 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 */
149static 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(adap: adapter, msgs, num: 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 */
187static 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(adap: adapter, msgs: &msg, num: 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
213static 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, ch: &vendor))
230 goto out;
231
232 name = ch7xxx_get_id(vid: 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, ch: &device))
241 goto out;
242
243 devid = ch7xxx_get_did(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;
254out:
255 kfree(objp: ch7xxx);
256 return false;
257}
258
259static 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, ch: &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, ch: pm);
270
271 ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, ch: &cdet);
272
273 ch7xxx_writeb(dvo, CH7xxx_PM, ch: orig_pm);
274
275 if (cdet & CH7xxx_CDET_DVI)
276 return connector_status_connected;
277 return connector_status_disconnected;
278}
279
280static 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
289static 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, ch: 0x00);
308 ch7xxx_writeb(dvo, CH7xxx_TVCO, ch: tvco);
309 ch7xxx_writeb(dvo, CH7xxx_TPCP, ch: tpcp);
310 ch7xxx_writeb(dvo, CH7xxx_TPD, ch: tpd);
311 ch7xxx_writeb(dvo, CH7xxx_TPVT, ch: 0x30);
312 ch7xxx_writeb(dvo, CH7xxx_TLPF, ch: tlpf);
313 ch7xxx_writeb(dvo, CH7xxx_TCT, ch: 0x00);
314
315 ch7xxx_readb(dvo, CH7xxx_IDF, ch: &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, ch: 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 */
335static 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
343static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo)
344{
345 u8 val;
346
347 ch7xxx_readb(dvo, CH7xxx_PM, ch: &val);
348
349 if (val & (CH7xxx_PM_DVIL | CH7xxx_PM_DVIP))
350 return true;
351 else
352 return false;
353}
354
355static 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, addr: i, ch: &val);
364 DRM_DEBUG_KMS("%02X ", val);
365 }
366}
367
368static void ch7xxx_destroy(struct intel_dvo_device *dvo)
369{
370 struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
371
372 if (ch7xxx) {
373 kfree(objp: ch7xxx);
374 dvo->dev_priv = NULL;
375 }
376}
377
378const 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