1/*
2 *
3 * Copyright (c) 2012 Gilles Dartiguelongue, Thomas Richter
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#include "i915_reg.h"
31#include "i915_drv.h"
32
33#define NS2501_VID 0x1305
34#define NS2501_DID 0x6726
35
36#define NS2501_VID_LO 0x00
37#define NS2501_VID_HI 0x01
38#define NS2501_DID_LO 0x02
39#define NS2501_DID_HI 0x03
40#define NS2501_REV 0x04
41#define NS2501_RSVD 0x05
42#define NS2501_FREQ_LO 0x06
43#define NS2501_FREQ_HI 0x07
44
45#define NS2501_REG8 0x08
46#define NS2501_8_VEN (1<<5)
47#define NS2501_8_HEN (1<<4)
48#define NS2501_8_DSEL (1<<3)
49#define NS2501_8_BPAS (1<<2)
50#define NS2501_8_RSVD (1<<1)
51#define NS2501_8_PD (1<<0)
52
53#define NS2501_REG9 0x09
54#define NS2501_9_VLOW (1<<7)
55#define NS2501_9_MSEL_MASK (0x7<<4)
56#define NS2501_9_TSEL (1<<3)
57#define NS2501_9_RSEN (1<<2)
58#define NS2501_9_RSVD (1<<1)
59#define NS2501_9_MDI (1<<0)
60
61#define NS2501_REGC 0x0c
62
63enum {
64	MODE_640x480,
65	MODE_800x600,
66	MODE_1024x768,
67};
68
69struct ns2501_reg {
70	 uint8_t offset;
71	 uint8_t value;
72};
73
74/*
75 * Magic values based on what the BIOS on
76 * Fujitsu-Siemens Lifebook S6010 programs (1024x768 panel).
77 */
78static const struct ns2501_reg regs_1024x768[][86] = {
79	[MODE_640x480] = {
80		[0] = { .offset = 0x0a, .value = 0x81, },
81		[1] = { .offset = 0x18, .value = 0x07, },
82		[2] = { .offset = 0x19, .value = 0x00, },
83		[3] = { .offset = 0x1a, .value = 0x00, },
84		[4] = { .offset = 0x1b, .value = 0x11, },
85		[5] = { .offset = 0x1c, .value = 0x54, },
86		[6] = { .offset = 0x1d, .value = 0x03, },
87		[7] = { .offset = 0x1e, .value = 0x02, },
88		[8] = { .offset = 0xf3, .value = 0x90, },
89		[9] = { .offset = 0xf9, .value = 0x00, },
90		[10] = { .offset = 0xc1, .value = 0x90, },
91		[11] = { .offset = 0xc2, .value = 0x00, },
92		[12] = { .offset = 0xc3, .value = 0x0f, },
93		[13] = { .offset = 0xc4, .value = 0x03, },
94		[14] = { .offset = 0xc5, .value = 0x16, },
95		[15] = { .offset = 0xc6, .value = 0x00, },
96		[16] = { .offset = 0xc7, .value = 0x02, },
97		[17] = { .offset = 0xc8, .value = 0x02, },
98		[18] = { .offset = 0xf4, .value = 0x00, },
99		[19] = { .offset = 0x80, .value = 0xff, },
100		[20] = { .offset = 0x81, .value = 0x07, },
101		[21] = { .offset = 0x82, .value = 0x3d, },
102		[22] = { .offset = 0x83, .value = 0x05, },
103		[23] = { .offset = 0x94, .value = 0x00, },
104		[24] = { .offset = 0x95, .value = 0x00, },
105		[25] = { .offset = 0x96, .value = 0x05, },
106		[26] = { .offset = 0x97, .value = 0x00, },
107		[27] = { .offset = 0x9a, .value = 0x88, },
108		[28] = { .offset = 0x9b, .value = 0x00, },
109		[29] = { .offset = 0x98, .value = 0x00, },
110		[30] = { .offset = 0x99, .value = 0x00, },
111		[31] = { .offset = 0xf7, .value = 0x88, },
112		[32] = { .offset = 0xf8, .value = 0x0a, },
113		[33] = { .offset = 0x9c, .value = 0x24, },
114		[34] = { .offset = 0x9d, .value = 0x00, },
115		[35] = { .offset = 0x9e, .value = 0x25, },
116		[36] = { .offset = 0x9f, .value = 0x03, },
117		[37] = { .offset = 0xa0, .value = 0x28, },
118		[38] = { .offset = 0xa1, .value = 0x01, },
119		[39] = { .offset = 0xa2, .value = 0x28, },
120		[40] = { .offset = 0xa3, .value = 0x05, },
121		[41] = { .offset = 0xb6, .value = 0x09, },
122		[42] = { .offset = 0xb8, .value = 0x00, },
123		[43] = { .offset = 0xb9, .value = 0xa0, },
124		[44] = { .offset = 0xba, .value = 0x00, },
125		[45] = { .offset = 0xbb, .value = 0x20, },
126		[46] = { .offset = 0x10, .value = 0x00, },
127		[47] = { .offset = 0x11, .value = 0xa0, },
128		[48] = { .offset = 0x12, .value = 0x02, },
129		[49] = { .offset = 0x20, .value = 0x00, },
130		[50] = { .offset = 0x22, .value = 0x00, },
131		[51] = { .offset = 0x23, .value = 0x00, },
132		[52] = { .offset = 0x24, .value = 0x00, },
133		[53] = { .offset = 0x25, .value = 0x00, },
134		[54] = { .offset = 0x8c, .value = 0x10, },
135		[55] = { .offset = 0x8d, .value = 0x02, },
136		[56] = { .offset = 0x8e, .value = 0x10, },
137		[57] = { .offset = 0x8f, .value = 0x00, },
138		[58] = { .offset = 0x90, .value = 0xff, },
139		[59] = { .offset = 0x91, .value = 0x07, },
140		[60] = { .offset = 0x92, .value = 0xa0, },
141		[61] = { .offset = 0x93, .value = 0x02, },
142		[62] = { .offset = 0xa5, .value = 0x00, },
143		[63] = { .offset = 0xa6, .value = 0x00, },
144		[64] = { .offset = 0xa7, .value = 0x00, },
145		[65] = { .offset = 0xa8, .value = 0x00, },
146		[66] = { .offset = 0xa9, .value = 0x04, },
147		[67] = { .offset = 0xaa, .value = 0x70, },
148		[68] = { .offset = 0xab, .value = 0x4f, },
149		[69] = { .offset = 0xac, .value = 0x00, },
150		[70] = { .offset = 0xa4, .value = 0x84, },
151		[71] = { .offset = 0x7e, .value = 0x18, },
152		[72] = { .offset = 0x84, .value = 0x00, },
153		[73] = { .offset = 0x85, .value = 0x00, },
154		[74] = { .offset = 0x86, .value = 0x00, },
155		[75] = { .offset = 0x87, .value = 0x00, },
156		[76] = { .offset = 0x88, .value = 0x00, },
157		[77] = { .offset = 0x89, .value = 0x00, },
158		[78] = { .offset = 0x8a, .value = 0x00, },
159		[79] = { .offset = 0x8b, .value = 0x00, },
160		[80] = { .offset = 0x26, .value = 0x00, },
161		[81] = { .offset = 0x27, .value = 0x00, },
162		[82] = { .offset = 0xad, .value = 0x00, },
163		[83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */
164		[84] = { .offset = 0x41, .value = 0x00, },
165		[85] = { .offset = 0xc0, .value = 0x05, },
166	},
167	[MODE_800x600] = {
168		[0] = { .offset = 0x0a, .value = 0x81, },
169		[1] = { .offset = 0x18, .value = 0x07, },
170		[2] = { .offset = 0x19, .value = 0x00, },
171		[3] = { .offset = 0x1a, .value = 0x00, },
172		[4] = { .offset = 0x1b, .value = 0x19, },
173		[5] = { .offset = 0x1c, .value = 0x64, },
174		[6] = { .offset = 0x1d, .value = 0x02, },
175		[7] = { .offset = 0x1e, .value = 0x02, },
176		[8] = { .offset = 0xf3, .value = 0x90, },
177		[9] = { .offset = 0xf9, .value = 0x00, },
178		[10] = { .offset = 0xc1, .value = 0xd7, },
179		[11] = { .offset = 0xc2, .value = 0x00, },
180		[12] = { .offset = 0xc3, .value = 0xf8, },
181		[13] = { .offset = 0xc4, .value = 0x03, },
182		[14] = { .offset = 0xc5, .value = 0x1a, },
183		[15] = { .offset = 0xc6, .value = 0x00, },
184		[16] = { .offset = 0xc7, .value = 0x73, },
185		[17] = { .offset = 0xc8, .value = 0x02, },
186		[18] = { .offset = 0xf4, .value = 0x00, },
187		[19] = { .offset = 0x80, .value = 0x27, },
188		[20] = { .offset = 0x81, .value = 0x03, },
189		[21] = { .offset = 0x82, .value = 0x41, },
190		[22] = { .offset = 0x83, .value = 0x05, },
191		[23] = { .offset = 0x94, .value = 0x00, },
192		[24] = { .offset = 0x95, .value = 0x00, },
193		[25] = { .offset = 0x96, .value = 0x05, },
194		[26] = { .offset = 0x97, .value = 0x00, },
195		[27] = { .offset = 0x9a, .value = 0x88, },
196		[28] = { .offset = 0x9b, .value = 0x00, },
197		[29] = { .offset = 0x98, .value = 0x00, },
198		[30] = { .offset = 0x99, .value = 0x00, },
199		[31] = { .offset = 0xf7, .value = 0x88, },
200		[32] = { .offset = 0xf8, .value = 0x06, },
201		[33] = { .offset = 0x9c, .value = 0x23, },
202		[34] = { .offset = 0x9d, .value = 0x00, },
203		[35] = { .offset = 0x9e, .value = 0x25, },
204		[36] = { .offset = 0x9f, .value = 0x03, },
205		[37] = { .offset = 0xa0, .value = 0x28, },
206		[38] = { .offset = 0xa1, .value = 0x01, },
207		[39] = { .offset = 0xa2, .value = 0x28, },
208		[40] = { .offset = 0xa3, .value = 0x05, },
209		[41] = { .offset = 0xb6, .value = 0x09, },
210		[42] = { .offset = 0xb8, .value = 0x30, },
211		[43] = { .offset = 0xb9, .value = 0xc8, },
212		[44] = { .offset = 0xba, .value = 0x00, },
213		[45] = { .offset = 0xbb, .value = 0x20, },
214		[46] = { .offset = 0x10, .value = 0x20, },
215		[47] = { .offset = 0x11, .value = 0xc8, },
216		[48] = { .offset = 0x12, .value = 0x02, },
217		[49] = { .offset = 0x20, .value = 0x00, },
218		[50] = { .offset = 0x22, .value = 0x00, },
219		[51] = { .offset = 0x23, .value = 0x00, },
220		[52] = { .offset = 0x24, .value = 0x00, },
221		[53] = { .offset = 0x25, .value = 0x00, },
222		[54] = { .offset = 0x8c, .value = 0x10, },
223		[55] = { .offset = 0x8d, .value = 0x02, },
224		[56] = { .offset = 0x8e, .value = 0x04, },
225		[57] = { .offset = 0x8f, .value = 0x00, },
226		[58] = { .offset = 0x90, .value = 0xff, },
227		[59] = { .offset = 0x91, .value = 0x07, },
228		[60] = { .offset = 0x92, .value = 0xa0, },
229		[61] = { .offset = 0x93, .value = 0x02, },
230		[62] = { .offset = 0xa5, .value = 0x00, },
231		[63] = { .offset = 0xa6, .value = 0x00, },
232		[64] = { .offset = 0xa7, .value = 0x00, },
233		[65] = { .offset = 0xa8, .value = 0x00, },
234		[66] = { .offset = 0xa9, .value = 0x83, },
235		[67] = { .offset = 0xaa, .value = 0x40, },
236		[68] = { .offset = 0xab, .value = 0x32, },
237		[69] = { .offset = 0xac, .value = 0x00, },
238		[70] = { .offset = 0xa4, .value = 0x80, },
239		[71] = { .offset = 0x7e, .value = 0x18, },
240		[72] = { .offset = 0x84, .value = 0x00, },
241		[73] = { .offset = 0x85, .value = 0x00, },
242		[74] = { .offset = 0x86, .value = 0x00, },
243		[75] = { .offset = 0x87, .value = 0x00, },
244		[76] = { .offset = 0x88, .value = 0x00, },
245		[77] = { .offset = 0x89, .value = 0x00, },
246		[78] = { .offset = 0x8a, .value = 0x00, },
247		[79] = { .offset = 0x8b, .value = 0x00, },
248		[80] = { .offset = 0x26, .value = 0x00, },
249		[81] = { .offset = 0x27, .value = 0x00, },
250		[82] = { .offset = 0xad, .value = 0x00, },
251		[83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */
252		[84] = { .offset = 0x41, .value = 0x00, },
253		[85] = { .offset = 0xc0, .value = 0x07, },
254	},
255	[MODE_1024x768] = {
256		[0] = { .offset = 0x0a, .value = 0x81, },
257		[1] = { .offset = 0x18, .value = 0x07, },
258		[2] = { .offset = 0x19, .value = 0x00, },
259		[3] = { .offset = 0x1a, .value = 0x00, },
260		[4] = { .offset = 0x1b, .value = 0x11, },
261		[5] = { .offset = 0x1c, .value = 0x54, },
262		[6] = { .offset = 0x1d, .value = 0x03, },
263		[7] = { .offset = 0x1e, .value = 0x02, },
264		[8] = { .offset = 0xf3, .value = 0x90, },
265		[9] = { .offset = 0xf9, .value = 0x00, },
266		[10] = { .offset = 0xc1, .value = 0x90, },
267		[11] = { .offset = 0xc2, .value = 0x00, },
268		[12] = { .offset = 0xc3, .value = 0x0f, },
269		[13] = { .offset = 0xc4, .value = 0x03, },
270		[14] = { .offset = 0xc5, .value = 0x16, },
271		[15] = { .offset = 0xc6, .value = 0x00, },
272		[16] = { .offset = 0xc7, .value = 0x02, },
273		[17] = { .offset = 0xc8, .value = 0x02, },
274		[18] = { .offset = 0xf4, .value = 0x00, },
275		[19] = { .offset = 0x80, .value = 0xff, },
276		[20] = { .offset = 0x81, .value = 0x07, },
277		[21] = { .offset = 0x82, .value = 0x3d, },
278		[22] = { .offset = 0x83, .value = 0x05, },
279		[23] = { .offset = 0x94, .value = 0x00, },
280		[24] = { .offset = 0x95, .value = 0x00, },
281		[25] = { .offset = 0x96, .value = 0x05, },
282		[26] = { .offset = 0x97, .value = 0x00, },
283		[27] = { .offset = 0x9a, .value = 0x88, },
284		[28] = { .offset = 0x9b, .value = 0x00, },
285		[29] = { .offset = 0x98, .value = 0x00, },
286		[30] = { .offset = 0x99, .value = 0x00, },
287		[31] = { .offset = 0xf7, .value = 0x88, },
288		[32] = { .offset = 0xf8, .value = 0x0a, },
289		[33] = { .offset = 0x9c, .value = 0x24, },
290		[34] = { .offset = 0x9d, .value = 0x00, },
291		[35] = { .offset = 0x9e, .value = 0x25, },
292		[36] = { .offset = 0x9f, .value = 0x03, },
293		[37] = { .offset = 0xa0, .value = 0x28, },
294		[38] = { .offset = 0xa1, .value = 0x01, },
295		[39] = { .offset = 0xa2, .value = 0x28, },
296		[40] = { .offset = 0xa3, .value = 0x05, },
297		[41] = { .offset = 0xb6, .value = 0x09, },
298		[42] = { .offset = 0xb8, .value = 0x00, },
299		[43] = { .offset = 0xb9, .value = 0xa0, },
300		[44] = { .offset = 0xba, .value = 0x00, },
301		[45] = { .offset = 0xbb, .value = 0x20, },
302		[46] = { .offset = 0x10, .value = 0x00, },
303		[47] = { .offset = 0x11, .value = 0xa0, },
304		[48] = { .offset = 0x12, .value = 0x02, },
305		[49] = { .offset = 0x20, .value = 0x00, },
306		[50] = { .offset = 0x22, .value = 0x00, },
307		[51] = { .offset = 0x23, .value = 0x00, },
308		[52] = { .offset = 0x24, .value = 0x00, },
309		[53] = { .offset = 0x25, .value = 0x00, },
310		[54] = { .offset = 0x8c, .value = 0x10, },
311		[55] = { .offset = 0x8d, .value = 0x02, },
312		[56] = { .offset = 0x8e, .value = 0x10, },
313		[57] = { .offset = 0x8f, .value = 0x00, },
314		[58] = { .offset = 0x90, .value = 0xff, },
315		[59] = { .offset = 0x91, .value = 0x07, },
316		[60] = { .offset = 0x92, .value = 0xa0, },
317		[61] = { .offset = 0x93, .value = 0x02, },
318		[62] = { .offset = 0xa5, .value = 0x00, },
319		[63] = { .offset = 0xa6, .value = 0x00, },
320		[64] = { .offset = 0xa7, .value = 0x00, },
321		[65] = { .offset = 0xa8, .value = 0x00, },
322		[66] = { .offset = 0xa9, .value = 0x04, },
323		[67] = { .offset = 0xaa, .value = 0x70, },
324		[68] = { .offset = 0xab, .value = 0x4f, },
325		[69] = { .offset = 0xac, .value = 0x00, },
326		[70] = { .offset = 0xa4, .value = 0x84, },
327		[71] = { .offset = 0x7e, .value = 0x18, },
328		[72] = { .offset = 0x84, .value = 0x00, },
329		[73] = { .offset = 0x85, .value = 0x00, },
330		[74] = { .offset = 0x86, .value = 0x00, },
331		[75] = { .offset = 0x87, .value = 0x00, },
332		[76] = { .offset = 0x88, .value = 0x00, },
333		[77] = { .offset = 0x89, .value = 0x00, },
334		[78] = { .offset = 0x8a, .value = 0x00, },
335		[79] = { .offset = 0x8b, .value = 0x00, },
336		[80] = { .offset = 0x26, .value = 0x00, },
337		[81] = { .offset = 0x27, .value = 0x00, },
338		[82] = { .offset = 0xad, .value = 0x00, },
339		[83] = { .offset = 0x08, .value = 0x34, }, /* 0x35 */
340		[84] = { .offset = 0x41, .value = 0x00, },
341		[85] = { .offset = 0xc0, .value = 0x01, },
342	},
343};
344
345static const struct ns2501_reg regs_init[] = {
346	[0] = { .offset = 0x35, .value = 0xff, },
347	[1] = { .offset = 0x34, .value = 0x00, },
348	[2] = { .offset = 0x08, .value = 0x30, },
349};
350
351struct ns2501_priv {
352	bool quiet;
353	const struct ns2501_reg *regs;
354};
355
356#define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr))
357
358/*
359 * For reasons unclear to me, the ns2501 at least on the Fujitsu/Siemens
360 * laptops does not react on the i2c bus unless
361 * both the PLL is running and the display is configured in its native
362 * resolution.
363 * This function forces the DVO on, and stores the registers it touches.
364 * Afterwards, registers are restored to regular values.
365 *
366 * This is pretty much a hack, though it works.
367 * Without that, ns2501_readb and ns2501_writeb fail
368 * when switching the resolution.
369 */
370
371/*
372** Read a register from the ns2501.
373** Returns true if successful, false otherwise.
374** If it returns false, it might be wise to enable the
375** DVO with the above function.
376*/
377static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch)
378{
379	struct ns2501_priv *ns = dvo->dev_priv;
380	struct i2c_adapter *adapter = dvo->i2c_bus;
381	u8 out_buf[2];
382	u8 in_buf[2];
383
384	struct i2c_msg msgs[] = {
385		{
386		 .addr = dvo->slave_addr,
387		 .flags = 0,
388		 .len = 1,
389		 .buf = out_buf,
390		 },
391		{
392		 .addr = dvo->slave_addr,
393		 .flags = I2C_M_RD,
394		 .len = 1,
395		 .buf = in_buf,
396		 }
397	};
398
399	out_buf[0] = addr;
400	out_buf[1] = 0;
401
402	if (i2c_transfer(adapter, msgs, 2) == 2) {
403		*ch = in_buf[0];
404		return true;
405	}
406
407	if (!ns->quiet) {
408		DRM_DEBUG_KMS
409		    ("Unable to read register 0x%02x from %s:0x%02x.\n", addr,
410		     adapter->name, dvo->slave_addr);
411	}
412
413	return false;
414}
415
416/*
417** Write a register to the ns2501.
418** Returns true if successful, false otherwise.
419** If it returns false, it might be wise to enable the
420** DVO with the above function.
421*/
422static bool ns2501_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
423{
424	struct ns2501_priv *ns = dvo->dev_priv;
425	struct i2c_adapter *adapter = dvo->i2c_bus;
426	uint8_t out_buf[2];
427
428	struct i2c_msg msg = {
429		.addr = dvo->slave_addr,
430		.flags = 0,
431		.len = 2,
432		.buf = out_buf,
433	};
434
435	out_buf[0] = addr;
436	out_buf[1] = ch;
437
438	if (i2c_transfer(adapter, &msg, 1) == 1) {
439		return true;
440	}
441
442	if (!ns->quiet) {
443		DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d\n",
444			      addr, adapter->name, dvo->slave_addr);
445	}
446
447	return false;
448}
449
450/* National Semiconductor 2501 driver for chip on i2c bus
451 * scan for the chip on the bus.
452 * Hope the VBIOS initialized the PLL correctly so we can
453 * talk to it. If not, it will not be seen and not detected.
454 * Bummer!
455 */
456static bool ns2501_init(struct intel_dvo_device *dvo,
457			struct i2c_adapter *adapter)
458{
459	/* this will detect the NS2501 chip on the specified i2c bus */
460	struct ns2501_priv *ns;
461	unsigned char ch;
462
463	ns = kzalloc(sizeof(struct ns2501_priv), GFP_KERNEL);
464	if (ns == NULL)
465		return false;
466
467	dvo->i2c_bus = adapter;
468	dvo->dev_priv = ns;
469	ns->quiet = true;
470
471	if (!ns2501_readb(dvo, NS2501_VID_LO, &ch))
472		goto out;
473
474	if (ch != (NS2501_VID & 0xff)) {
475		DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
476			      ch, adapter->name, dvo->slave_addr);
477		goto out;
478	}
479
480	if (!ns2501_readb(dvo, NS2501_DID_LO, &ch))
481		goto out;
482
483	if (ch != (NS2501_DID & 0xff)) {
484		DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
485			      ch, adapter->name, dvo->slave_addr);
486		goto out;
487	}
488	ns->quiet = false;
489
490	DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n");
491
492	return true;
493
494out:
495	kfree(ns);
496	return false;
497}
498
499static enum drm_connector_status ns2501_detect(struct intel_dvo_device *dvo)
500{
501	/*
502	 * This is a Laptop display, it doesn't have hotplugging.
503	 * Even if not, the detection bit of the 2501 is unreliable as
504	 * it only works for some display types.
505	 * It is even more unreliable as the PLL must be active for
506	 * allowing reading from the chiop.
507	 */
508	return connector_status_connected;
509}
510
511static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
512					      struct drm_display_mode *mode)
513{
514	DRM_DEBUG_KMS
515	    ("is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
516	     mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
517
518	/*
519	 * Currently, these are all the modes I have data from.
520	 * More might exist. Unclear how to find the native resolution
521	 * of the panel in here so we could always accept it
522	 * by disabling the scaler.
523	 */
524	if ((mode->hdisplay == 640 && mode->vdisplay == 480 && mode->clock == 25175) ||
525	    (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 40000) ||
526	    (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 65000)) {
527		return MODE_OK;
528	} else {
529		return MODE_ONE_SIZE;	/* Is this a reasonable error? */
530	}
531}
532
533static void ns2501_mode_set(struct intel_dvo_device *dvo,
534			    struct drm_display_mode *mode,
535			    struct drm_display_mode *adjusted_mode)
536{
537	struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
538	int mode_idx, i;
539
540	DRM_DEBUG_KMS
541	    ("set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
542	     mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
543
544	if (mode->hdisplay == 640 && mode->vdisplay == 480)
545		mode_idx = MODE_640x480;
546	else if (mode->hdisplay == 800 && mode->vdisplay == 600)
547		mode_idx = MODE_800x600;
548	else if (mode->hdisplay == 1024 && mode->vdisplay == 768)
549		mode_idx = MODE_1024x768;
550	else
551		return;
552
553	/* Hopefully doing it every time won't hurt... */
554	for (i = 0; i < ARRAY_SIZE(regs_init); i++)
555		ns2501_writeb(dvo, regs_init[i].offset, regs_init[i].value);
556
557	ns->regs = regs_1024x768[mode_idx];
558
559	for (i = 0; i < 84; i++)
560		ns2501_writeb(dvo, ns->regs[i].offset, ns->regs[i].value);
561}
562
563/* set the NS2501 power state */
564static bool ns2501_get_hw_state(struct intel_dvo_device *dvo)
565{
566	unsigned char ch;
567
568	if (!ns2501_readb(dvo, NS2501_REG8, &ch))
569		return false;
570
571	return ch & NS2501_8_PD;
572}
573
574/* set the NS2501 power state */
575static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
576{
577	struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
578
579	DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable);
580
581	if (enable) {
582		if (WARN_ON(ns->regs[83].offset != 0x08 ||
583			    ns->regs[84].offset != 0x41 ||
584			    ns->regs[85].offset != 0xc0))
585			return;
586
587		ns2501_writeb(dvo, 0xc0, ns->regs[85].value | 0x08);
588
589		ns2501_writeb(dvo, 0x41, ns->regs[84].value);
590
591		ns2501_writeb(dvo, 0x34, 0x01);
592		msleep(15);
593
594		ns2501_writeb(dvo, 0x08, 0x35);
595		if (!(ns->regs[83].value & NS2501_8_BPAS))
596			ns2501_writeb(dvo, 0x08, 0x31);
597		msleep(200);
598
599		ns2501_writeb(dvo, 0x34, 0x03);
600
601		ns2501_writeb(dvo, 0xc0, ns->regs[85].value);
602	} else {
603		ns2501_writeb(dvo, 0x34, 0x01);
604		msleep(200);
605
606		ns2501_writeb(dvo, 0x08, 0x34);
607		msleep(15);
608
609		ns2501_writeb(dvo, 0x34, 0x00);
610	}
611}
612
613static void ns2501_destroy(struct intel_dvo_device *dvo)
614{
615	struct ns2501_priv *ns = dvo->dev_priv;
616
617	if (ns) {
618		kfree(ns);
619		dvo->dev_priv = NULL;
620	}
621}
622
623struct intel_dvo_dev_ops ns2501_ops = {
624	.init = ns2501_init,
625	.detect = ns2501_detect,
626	.mode_valid = ns2501_mode_valid,
627	.mode_set = ns2501_mode_set,
628	.dpms = ns2501_dpms,
629	.get_hw_state = ns2501_get_hw_state,
630	.destroy = ns2501_destroy,
631};
632