xref: /dflybsd-src/sys/dev/drm/i915/dvo_ns2501.c (revision aee94f86171368465eaa15d649743f13cea3363a)
1ba55f2f5SFrançois Tigeot /*
2ba55f2f5SFrançois Tigeot  *
3ba55f2f5SFrançois Tigeot  * Copyright (c) 2012 Gilles Dartiguelongue, Thomas Richter
4ba55f2f5SFrançois Tigeot  *
5ba55f2f5SFrançois Tigeot  * All Rights Reserved.
6ba55f2f5SFrançois Tigeot  *
7ba55f2f5SFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
8ba55f2f5SFrançois Tigeot  * copy of this software and associated documentation files (the
9ba55f2f5SFrançois Tigeot  * "Software"), to deal in the Software without restriction, including
10ba55f2f5SFrançois Tigeot  * without limitation the rights to use, copy, modify, merge, publish,
11ba55f2f5SFrançois Tigeot  * distribute, sub license, and/or sell copies of the Software, and to
12ba55f2f5SFrançois Tigeot  * permit persons to whom the Software is furnished to do so, subject to
13ba55f2f5SFrançois Tigeot  * the following conditions:
14ba55f2f5SFrançois Tigeot  *
15ba55f2f5SFrançois Tigeot  * The above copyright notice and this permission notice (including the
16ba55f2f5SFrançois Tigeot  * next paragraph) shall be included in all copies or substantial portions
17ba55f2f5SFrançois Tigeot  * of the Software.
18ba55f2f5SFrançois Tigeot  *
19ba55f2f5SFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20ba55f2f5SFrançois Tigeot  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21ba55f2f5SFrançois Tigeot  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22ba55f2f5SFrançois Tigeot  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
23ba55f2f5SFrançois Tigeot  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24ba55f2f5SFrançois Tigeot  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25ba55f2f5SFrançois Tigeot  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26ba55f2f5SFrançois Tigeot  *
27ba55f2f5SFrançois Tigeot  */
28ba55f2f5SFrançois Tigeot 
29ba55f2f5SFrançois Tigeot #include "dvo.h"
30ba55f2f5SFrançois Tigeot #include "i915_reg.h"
31ba55f2f5SFrançois Tigeot #include "i915_drv.h"
32ba55f2f5SFrançois Tigeot 
33ba55f2f5SFrançois Tigeot #define NS2501_VID 0x1305
34ba55f2f5SFrançois Tigeot #define NS2501_DID 0x6726
35ba55f2f5SFrançois Tigeot 
36ba55f2f5SFrançois Tigeot #define NS2501_VID_LO 0x00
37ba55f2f5SFrançois Tigeot #define NS2501_VID_HI 0x01
38ba55f2f5SFrançois Tigeot #define NS2501_DID_LO 0x02
39ba55f2f5SFrançois Tigeot #define NS2501_DID_HI 0x03
40ba55f2f5SFrançois Tigeot #define NS2501_REV 0x04
41ba55f2f5SFrançois Tigeot #define NS2501_RSVD 0x05
42ba55f2f5SFrançois Tigeot #define NS2501_FREQ_LO 0x06
43ba55f2f5SFrançois Tigeot #define NS2501_FREQ_HI 0x07
44ba55f2f5SFrançois Tigeot 
45ba55f2f5SFrançois Tigeot #define NS2501_REG8 0x08
46ba55f2f5SFrançois Tigeot #define NS2501_8_VEN (1<<5)
47ba55f2f5SFrançois Tigeot #define NS2501_8_HEN (1<<4)
48ba55f2f5SFrançois Tigeot #define NS2501_8_DSEL (1<<3)
49ba55f2f5SFrançois Tigeot #define NS2501_8_BPAS (1<<2)
50ba55f2f5SFrançois Tigeot #define NS2501_8_RSVD (1<<1)
51ba55f2f5SFrançois Tigeot #define NS2501_8_PD (1<<0)
52ba55f2f5SFrançois Tigeot 
53ba55f2f5SFrançois Tigeot #define NS2501_REG9 0x09
54ba55f2f5SFrançois Tigeot #define NS2501_9_VLOW (1<<7)
55ba55f2f5SFrançois Tigeot #define NS2501_9_MSEL_MASK (0x7<<4)
56ba55f2f5SFrançois Tigeot #define NS2501_9_TSEL (1<<3)
57ba55f2f5SFrançois Tigeot #define NS2501_9_RSEN (1<<2)
58ba55f2f5SFrançois Tigeot #define NS2501_9_RSVD (1<<1)
59ba55f2f5SFrançois Tigeot #define NS2501_9_MDI (1<<0)
60ba55f2f5SFrançois Tigeot 
61ba55f2f5SFrançois Tigeot #define NS2501_REGC 0x0c
62ba55f2f5SFrançois Tigeot 
6319c468b4SFrançois Tigeot /*
6419c468b4SFrançois Tigeot  * The following registers are not part of the official datasheet
6519c468b4SFrançois Tigeot  * and are the result of reverse engineering.
6619c468b4SFrançois Tigeot  */
6719c468b4SFrançois Tigeot 
6819c468b4SFrançois Tigeot /*
6919c468b4SFrançois Tigeot  * Register c0 controls how the DVO synchronizes with
7019c468b4SFrançois Tigeot  * its input.
7119c468b4SFrançois Tigeot  */
7219c468b4SFrançois Tigeot #define NS2501_REGC0 0xc0
7319c468b4SFrançois Tigeot #define NS2501_C0_ENABLE (1<<0)	/* enable the DVO sync in general */
7419c468b4SFrançois Tigeot #define NS2501_C0_HSYNC (1<<1)	/* synchronize horizontal with input */
7519c468b4SFrançois Tigeot #define NS2501_C0_VSYNC (1<<2)	/* synchronize vertical with input */
7619c468b4SFrançois Tigeot #define NS2501_C0_RESET (1<<7)	/* reset the synchronization flip/flops */
7719c468b4SFrançois Tigeot 
7819c468b4SFrançois Tigeot /*
7919c468b4SFrançois Tigeot  * Register 41 is somehow related to the sync register and sync
8019c468b4SFrançois Tigeot  * configuration. It should be 0x32 whenever regC0 is 0x05 (hsync off)
8119c468b4SFrançois Tigeot  * and 0x00 otherwise.
8219c468b4SFrançois Tigeot  */
8319c468b4SFrançois Tigeot #define NS2501_REG41 0x41
8419c468b4SFrançois Tigeot 
8519c468b4SFrançois Tigeot /*
8619c468b4SFrançois Tigeot  * this register controls the dithering of the DVO
8719c468b4SFrançois Tigeot  * One bit enables it, the other define the dithering depth.
8819c468b4SFrançois Tigeot  * The higher the value, the lower the dithering depth.
8919c468b4SFrançois Tigeot  */
9019c468b4SFrançois Tigeot #define NS2501_F9_REG 0xf9
9119c468b4SFrançois Tigeot #define NS2501_F9_ENABLE (1<<0)		/* if set, dithering is enabled */
9219c468b4SFrançois Tigeot #define NS2501_F9_DITHER_MASK (0x7f<<1)	/* controls the dither depth */
9319c468b4SFrançois Tigeot #define NS2501_F9_DITHER_SHIFT 1	/* shifts the dither mask */
9419c468b4SFrançois Tigeot 
9519c468b4SFrançois Tigeot /*
9619c468b4SFrançois Tigeot  * PLL configuration register. This is a pair of registers,
9719c468b4SFrançois Tigeot  * one single byte register at 1B, and a pair at 1C,1D.
9819c468b4SFrançois Tigeot  * These registers are counters/dividers.
9919c468b4SFrançois Tigeot  */
10019c468b4SFrançois Tigeot #define NS2501_REG1B 0x1b /* one byte PLL control register */
10119c468b4SFrançois Tigeot #define NS2501_REG1C 0x1c /* low-part of the second register */
10219c468b4SFrançois Tigeot #define NS2501_REG1D 0x1d /* high-part of the second register */
10319c468b4SFrançois Tigeot 
10419c468b4SFrançois Tigeot /*
10519c468b4SFrançois Tigeot  * Scaler control registers. Horizontal at b8,b9,
10619c468b4SFrançois Tigeot  * vertical at 10,11. The scale factor is computed as
10719c468b4SFrançois Tigeot  * 2^16/control-value. The low-byte comes first.
10819c468b4SFrançois Tigeot  */
10919c468b4SFrançois Tigeot #define NS2501_REG10 0x10 /* low-byte vertical scaler */
11019c468b4SFrançois Tigeot #define NS2501_REG11 0x11 /* high-byte vertical scaler */
11119c468b4SFrançois Tigeot #define NS2501_REGB8 0xb8 /* low-byte horizontal scaler */
11219c468b4SFrançois Tigeot #define NS2501_REGB9 0xb9 /* high-byte horizontal scaler */
11319c468b4SFrançois Tigeot 
11419c468b4SFrançois Tigeot /*
11519c468b4SFrançois Tigeot  * Display window definition. This consists of four registers
11619c468b4SFrançois Tigeot  * per dimension. One register pair defines the start of the
11719c468b4SFrançois Tigeot  * display, one the end.
11819c468b4SFrançois Tigeot  * As far as I understand, this defines the window within which
11919c468b4SFrançois Tigeot  * the scaler samples the input.
12019c468b4SFrançois Tigeot  */
12119c468b4SFrançois Tigeot #define NS2501_REGC1 0xc1 /* low-byte horizontal display start */
12219c468b4SFrançois Tigeot #define NS2501_REGC2 0xc2 /* high-byte horizontal display start */
12319c468b4SFrançois Tigeot #define NS2501_REGC3 0xc3 /* low-byte horizontal display stop */
12419c468b4SFrançois Tigeot #define NS2501_REGC4 0xc4 /* high-byte horizontal display stop */
12519c468b4SFrançois Tigeot #define NS2501_REGC5 0xc5 /* low-byte vertical display start */
12619c468b4SFrançois Tigeot #define NS2501_REGC6 0xc6 /* high-byte vertical display start */
12719c468b4SFrançois Tigeot #define NS2501_REGC7 0xc7 /* low-byte vertical display stop */
12819c468b4SFrançois Tigeot #define NS2501_REGC8 0xc8 /* high-byte vertical display stop */
12919c468b4SFrançois Tigeot 
13019c468b4SFrançois Tigeot /*
13119c468b4SFrançois Tigeot  * The following register pair seems to define the start of
13219c468b4SFrançois Tigeot  * the vertical sync. If automatic syncing is enabled, and the
13319c468b4SFrançois Tigeot  * register value defines a sync pulse that is later than the
13419c468b4SFrançois Tigeot  * incoming sync, then the register value is ignored and the
13519c468b4SFrançois Tigeot  * external hsync triggers the synchronization.
13619c468b4SFrançois Tigeot  */
13719c468b4SFrançois Tigeot #define NS2501_REG80 0x80 /* low-byte vsync-start */
13819c468b4SFrançois Tigeot #define NS2501_REG81 0x81 /* high-byte vsync-start */
13919c468b4SFrançois Tigeot 
14019c468b4SFrançois Tigeot /*
14119c468b4SFrançois Tigeot  * The following register pair seems to define the total number
14219c468b4SFrançois Tigeot  * of lines created at the output side of the scaler.
14319c468b4SFrançois Tigeot  * This is again a low-high register pair.
14419c468b4SFrançois Tigeot  */
14519c468b4SFrançois Tigeot #define NS2501_REG82 0x82 /* output display height, low byte */
14619c468b4SFrançois Tigeot #define NS2501_REG83 0x83 /* output display height, high byte */
14719c468b4SFrançois Tigeot 
14819c468b4SFrançois Tigeot /*
14919c468b4SFrançois Tigeot  * The following registers define the end of the front-porch
15019c468b4SFrançois Tigeot  * in horizontal and vertical position and hence allow to shift
15119c468b4SFrançois Tigeot  * the image left/right or up/down.
15219c468b4SFrançois Tigeot  */
15319c468b4SFrançois Tigeot #define NS2501_REG98 0x98 /* horizontal start of display + 256, low */
15419c468b4SFrançois Tigeot #define NS2501_REG99 0x99 /* horizontal start of display + 256, high */
15519c468b4SFrançois Tigeot #define NS2501_REG8E 0x8e /* vertical start of the display, low byte */
15619c468b4SFrançois Tigeot #define NS2501_REG8F 0x8f /* vertical start of the display, high byte */
15719c468b4SFrançois Tigeot 
15819c468b4SFrançois Tigeot /*
15919c468b4SFrançois Tigeot  * The following register pair control the function of the
16019c468b4SFrançois Tigeot  * backlight and the DVO output. To enable the corresponding
16119c468b4SFrançois Tigeot  * function, the corresponding bit must be set in both registers.
16219c468b4SFrançois Tigeot  */
16319c468b4SFrançois Tigeot #define NS2501_REG34 0x34 /* DVO enable functions, first register */
16419c468b4SFrançois Tigeot #define NS2501_REG35 0x35 /* DVO enable functions, second register */
16519c468b4SFrançois Tigeot #define NS2501_34_ENABLE_OUTPUT (1<<0) /* enable DVO output */
16619c468b4SFrançois Tigeot #define NS2501_34_ENABLE_BACKLIGHT (1<<1) /* enable backlight */
16719c468b4SFrançois Tigeot 
16819c468b4SFrançois Tigeot /*
16919c468b4SFrançois Tigeot  * Registers 9C and 9D define the vertical output offset
17019c468b4SFrançois Tigeot  * of the visible region.
17119c468b4SFrançois Tigeot  */
17219c468b4SFrançois Tigeot #define NS2501_REG9C 0x9c
17319c468b4SFrançois Tigeot #define NS2501_REG9D 0x9d
17419c468b4SFrançois Tigeot 
17519c468b4SFrançois Tigeot /*
17619c468b4SFrançois Tigeot  * The register 9F defines the dithering. This requires the
17719c468b4SFrançois Tigeot  * scaler to be ON. Bit 0 enables dithering, the remaining
17819c468b4SFrançois Tigeot  * bits control the depth of the dither. The higher the value,
17919c468b4SFrançois Tigeot  * the LOWER the dithering amplitude. A good value seems to be
18019c468b4SFrançois Tigeot  * 15 (total register value).
18119c468b4SFrançois Tigeot  */
18219c468b4SFrançois Tigeot #define NS2501_REGF9 0xf9
18319c468b4SFrançois Tigeot #define NS2501_F9_ENABLE_DITHER (1<<0) /* enable dithering */
18419c468b4SFrançois Tigeot #define NS2501_F9_DITHER_MASK (0x7f<<1) /* dither masking */
18519c468b4SFrançois Tigeot #define NS2501_F9_DITHER_SHIFT 1	/* upshift of the dither mask */
18619c468b4SFrançois Tigeot 
1871b13d190SFrançois Tigeot enum {
1881b13d190SFrançois Tigeot 	MODE_640x480,
1891b13d190SFrançois Tigeot 	MODE_800x600,
1901b13d190SFrançois Tigeot 	MODE_1024x768,
1911b13d190SFrançois Tigeot };
1921b13d190SFrançois Tigeot 
1931b13d190SFrançois Tigeot struct ns2501_reg {
1941b13d190SFrançois Tigeot 	 uint8_t offset;
1951b13d190SFrançois Tigeot 	 uint8_t value;
1961b13d190SFrançois Tigeot };
1971b13d190SFrançois Tigeot 
1981b13d190SFrançois Tigeot /*
19919c468b4SFrançois Tigeot  * The following structure keeps the complete configuration of
20019c468b4SFrançois Tigeot  * the DVO, given a specific output configuration.
20119c468b4SFrançois Tigeot  * This is pretty much guess-work from reverse-engineering, so
20219c468b4SFrançois Tigeot  * read all this with a grain of salt.
2031b13d190SFrançois Tigeot  */
20419c468b4SFrançois Tigeot struct ns2501_configuration {
20519c468b4SFrançois Tigeot 	uint8_t sync;		/* configuration of the C0 register */
20619c468b4SFrançois Tigeot 	uint8_t conf;		/* configuration register 8 */
20719c468b4SFrançois Tigeot 	uint8_t syncb;		/* configuration register 41 */
20819c468b4SFrançois Tigeot 	uint8_t	dither;		/* configuration of the dithering */
20919c468b4SFrançois Tigeot 	uint8_t pll_a;		/* PLL configuration, register A, 1B */
21019c468b4SFrançois Tigeot 	uint16_t pll_b;		/* PLL configuration, register B, 1C/1D */
21119c468b4SFrançois Tigeot 	uint16_t hstart;	/* horizontal start, registers C1/C2 */
21219c468b4SFrançois Tigeot 	uint16_t hstop;		/* horizontal total, registers C3/C4 */
21319c468b4SFrançois Tigeot 	uint16_t vstart;	/* vertical start, registers C5/C6 */
21419c468b4SFrançois Tigeot 	uint16_t vstop;		/* vertical total, registers C7/C8 */
21519c468b4SFrançois Tigeot 	uint16_t vsync;         /* manual vertical sync start, 80/81 */
21619c468b4SFrançois Tigeot 	uint16_t vtotal;        /* number of lines generated, 82/83 */
21719c468b4SFrançois Tigeot 	uint16_t hpos;		/* horizontal position + 256, 98/99  */
21819c468b4SFrançois Tigeot 	uint16_t vpos;		/* vertical position, 8e/8f */
21919c468b4SFrançois Tigeot 	uint16_t voffs;		/* vertical output offset, 9c/9d */
22019c468b4SFrançois Tigeot 	uint16_t hscale;	/* horizontal scaling factor, b8/b9 */
22119c468b4SFrançois Tigeot 	uint16_t vscale;	/* vertical scaling factor, 10/11 */
22219c468b4SFrançois Tigeot };
22319c468b4SFrançois Tigeot 
22419c468b4SFrançois Tigeot /*
22519c468b4SFrançois Tigeot  * DVO configuration values, partially based on what the BIOS
22619c468b4SFrançois Tigeot  * of the Fujitsu Lifebook S6010 writes into registers,
22719c468b4SFrançois Tigeot  * partially found by manual tweaking. These configurations assume
22819c468b4SFrançois Tigeot  * a 1024x768 panel.
22919c468b4SFrançois Tigeot  */
23019c468b4SFrançois Tigeot static const struct ns2501_configuration ns2501_modes[] = {
2311b13d190SFrançois Tigeot 	[MODE_640x480] = {
23219c468b4SFrançois Tigeot 		.sync	= NS2501_C0_ENABLE | NS2501_C0_VSYNC,
23319c468b4SFrançois Tigeot 		.conf	= NS2501_8_VEN | NS2501_8_HEN | NS2501_8_PD,
23419c468b4SFrançois Tigeot 		.syncb	= 0x32,
23519c468b4SFrançois Tigeot 		.dither	= 0x0f,
23619c468b4SFrançois Tigeot 		.pll_a	= 17,
23719c468b4SFrançois Tigeot 		.pll_b	= 852,
23819c468b4SFrançois Tigeot 		.hstart	= 144,
23919c468b4SFrançois Tigeot 		.hstop	= 783,
24019c468b4SFrançois Tigeot 		.vstart	= 22,
24119c468b4SFrançois Tigeot 		.vstop	= 514,
24219c468b4SFrançois Tigeot 		.vsync	= 2047, /* actually, ignored with this config */
24319c468b4SFrançois Tigeot 		.vtotal	= 1341,
24419c468b4SFrançois Tigeot 		.hpos	= 0,
24519c468b4SFrançois Tigeot 		.vpos	= 16,
24619c468b4SFrançois Tigeot 		.voffs	= 36,
24719c468b4SFrançois Tigeot 		.hscale	= 40960,
24819c468b4SFrançois Tigeot 		.vscale	= 40960
2491b13d190SFrançois Tigeot 	},
2501b13d190SFrançois Tigeot 	[MODE_800x600] = {
25119c468b4SFrançois Tigeot 		.sync	= NS2501_C0_ENABLE |
25219c468b4SFrançois Tigeot 			  NS2501_C0_HSYNC | NS2501_C0_VSYNC,
25319c468b4SFrançois Tigeot 		.conf   = NS2501_8_VEN | NS2501_8_HEN | NS2501_8_PD,
25419c468b4SFrançois Tigeot 		.syncb	= 0x00,
25519c468b4SFrançois Tigeot 		.dither	= 0x0f,
25619c468b4SFrançois Tigeot 		.pll_a	= 25,
25719c468b4SFrançois Tigeot 		.pll_b	= 612,
25819c468b4SFrançois Tigeot 		.hstart	= 215,
25919c468b4SFrançois Tigeot 		.hstop	= 1016,
26019c468b4SFrançois Tigeot 		.vstart	= 26,
26119c468b4SFrançois Tigeot 		.vstop	= 627,
26219c468b4SFrançois Tigeot 		.vsync	= 807,
26319c468b4SFrançois Tigeot 		.vtotal	= 1341,
26419c468b4SFrançois Tigeot 		.hpos	= 0,
26519c468b4SFrançois Tigeot 		.vpos	= 4,
26619c468b4SFrançois Tigeot 		.voffs	= 35,
26719c468b4SFrançois Tigeot 		.hscale	= 51248,
26819c468b4SFrançois Tigeot 		.vscale	= 51232
2691b13d190SFrançois Tigeot 	},
2701b13d190SFrançois Tigeot 	[MODE_1024x768] = {
27119c468b4SFrançois Tigeot 		.sync	= NS2501_C0_ENABLE | NS2501_C0_VSYNC,
27219c468b4SFrançois Tigeot 		.conf   = NS2501_8_VEN | NS2501_8_HEN | NS2501_8_PD,
27319c468b4SFrançois Tigeot 		.syncb	= 0x32,
27419c468b4SFrançois Tigeot 		.dither	= 0x0f,
27519c468b4SFrançois Tigeot 		.pll_a	= 11,
27619c468b4SFrançois Tigeot 		.pll_b	= 1350,
27719c468b4SFrançois Tigeot 		.hstart	= 276,
27819c468b4SFrançois Tigeot 		.hstop	= 1299,
27919c468b4SFrançois Tigeot 		.vstart	= 15,
28019c468b4SFrançois Tigeot 		.vstop	= 1056,
28119c468b4SFrançois Tigeot 		.vsync	= 2047,
28219c468b4SFrançois Tigeot 		.vtotal	= 1341,
28319c468b4SFrançois Tigeot 		.hpos	= 0,
28419c468b4SFrançois Tigeot 		.vpos	= 7,
28519c468b4SFrançois Tigeot 		.voffs	= 27,
28619c468b4SFrançois Tigeot 		.hscale	= 65535,
28719c468b4SFrançois Tigeot 		.vscale	= 65535
28819c468b4SFrançois Tigeot 	}
28919c468b4SFrançois Tigeot };
29019c468b4SFrançois Tigeot 
29119c468b4SFrançois Tigeot /*
29219c468b4SFrançois Tigeot  * Other configuration values left by the BIOS of the
29319c468b4SFrançois Tigeot  * Fujitsu S6010 in the DVO control registers. Their
29419c468b4SFrançois Tigeot  * value does not depend on the BIOS and their meaning
29519c468b4SFrançois Tigeot  * is unknown.
29619c468b4SFrançois Tigeot  */
29719c468b4SFrançois Tigeot 
29819c468b4SFrançois Tigeot static const struct ns2501_reg mode_agnostic_values[] = {
29919c468b4SFrançois Tigeot 	/* 08 is mode specific */
3001b13d190SFrançois Tigeot 	[0] = { .offset = 0x0a, .value = 0x81, },
30119c468b4SFrançois Tigeot 	/* 10,11 are part of the mode specific configuration */
30219c468b4SFrançois Tigeot 	[1] = { .offset = 0x12, .value = 0x02, },
30319c468b4SFrançois Tigeot 	[2] = { .offset = 0x18, .value = 0x07, },
30419c468b4SFrançois Tigeot 	[3] = { .offset = 0x19, .value = 0x00, },
30519c468b4SFrançois Tigeot 	[4] = { .offset = 0x1a, .value = 0x00, }, /* PLL?, ignored */
30619c468b4SFrançois Tigeot 	/* 1b,1c,1d are part of the mode specific configuration */
30719c468b4SFrançois Tigeot 	[5] = { .offset = 0x1e, .value = 0x02, },
30819c468b4SFrançois Tigeot 	[6] = { .offset = 0x1f, .value = 0x40, },
30919c468b4SFrançois Tigeot 	[7] = { .offset = 0x20, .value = 0x00, },
31019c468b4SFrançois Tigeot 	[8] = { .offset = 0x21, .value = 0x00, },
31119c468b4SFrançois Tigeot 	[9] = { .offset = 0x22, .value = 0x00, },
31219c468b4SFrançois Tigeot 	[10] = { .offset = 0x23, .value = 0x00, },
31319c468b4SFrançois Tigeot 	[11] = { .offset = 0x24, .value = 0x00, },
31419c468b4SFrançois Tigeot 	[12] = { .offset = 0x25, .value = 0x00, },
31519c468b4SFrançois Tigeot 	[13] = { .offset = 0x26, .value = 0x00, },
31619c468b4SFrançois Tigeot 	[14] = { .offset = 0x27, .value = 0x00, },
31719c468b4SFrançois Tigeot 	[15] = { .offset = 0x7e, .value = 0x18, },
31819c468b4SFrançois Tigeot 	/* 80-84 are part of the mode-specific configuration */
31919c468b4SFrançois Tigeot 	[16] = { .offset = 0x84, .value = 0x00, },
32019c468b4SFrançois Tigeot 	[17] = { .offset = 0x85, .value = 0x00, },
32119c468b4SFrançois Tigeot 	[18] = { .offset = 0x86, .value = 0x00, },
32219c468b4SFrançois Tigeot 	[19] = { .offset = 0x87, .value = 0x00, },
32319c468b4SFrançois Tigeot 	[20] = { .offset = 0x88, .value = 0x00, },
32419c468b4SFrançois Tigeot 	[21] = { .offset = 0x89, .value = 0x00, },
32519c468b4SFrançois Tigeot 	[22] = { .offset = 0x8a, .value = 0x00, },
32619c468b4SFrançois Tigeot 	[23] = { .offset = 0x8b, .value = 0x00, },
32719c468b4SFrançois Tigeot 	[24] = { .offset = 0x8c, .value = 0x10, },
32819c468b4SFrançois Tigeot 	[25] = { .offset = 0x8d, .value = 0x02, },
32919c468b4SFrançois Tigeot 	/* 8e,8f are part of the mode-specific configuration */
33019c468b4SFrançois Tigeot 	[26] = { .offset = 0x90, .value = 0xff, },
33119c468b4SFrançois Tigeot 	[27] = { .offset = 0x91, .value = 0x07, },
33219c468b4SFrançois Tigeot 	[28] = { .offset = 0x92, .value = 0xa0, },
33319c468b4SFrançois Tigeot 	[29] = { .offset = 0x93, .value = 0x02, },
33419c468b4SFrançois Tigeot 	[30] = { .offset = 0x94, .value = 0x00, },
33519c468b4SFrançois Tigeot 	[31] = { .offset = 0x95, .value = 0x00, },
33619c468b4SFrançois Tigeot 	[32] = { .offset = 0x96, .value = 0x05, },
33719c468b4SFrançois Tigeot 	[33] = { .offset = 0x97, .value = 0x00, },
33819c468b4SFrançois Tigeot 	/* 98,99 are part of the mode-specific configuration */
33919c468b4SFrançois Tigeot 	[34] = { .offset = 0x9a, .value = 0x88, },
34019c468b4SFrançois Tigeot 	[35] = { .offset = 0x9b, .value = 0x00, },
34119c468b4SFrançois Tigeot 	/* 9c,9d are part of the mode-specific configuration */
34219c468b4SFrançois Tigeot 	[36] = { .offset = 0x9e, .value = 0x25, },
34319c468b4SFrançois Tigeot 	[37] = { .offset = 0x9f, .value = 0x03, },
34419c468b4SFrançois Tigeot 	[38] = { .offset = 0xa0, .value = 0x28, },
34519c468b4SFrançois Tigeot 	[39] = { .offset = 0xa1, .value = 0x01, },
34619c468b4SFrançois Tigeot 	[40] = { .offset = 0xa2, .value = 0x28, },
34719c468b4SFrançois Tigeot 	[41] = { .offset = 0xa3, .value = 0x05, },
34819c468b4SFrançois Tigeot 	/* register 0xa4 is mode specific, but 0x80..0x84 works always */
34919c468b4SFrançois Tigeot 	[42] = { .offset = 0xa4, .value = 0x84, },
35019c468b4SFrançois Tigeot 	[43] = { .offset = 0xa5, .value = 0x00, },
35119c468b4SFrançois Tigeot 	[44] = { .offset = 0xa6, .value = 0x00, },
35219c468b4SFrançois Tigeot 	[45] = { .offset = 0xa7, .value = 0x00, },
35319c468b4SFrançois Tigeot 	[46] = { .offset = 0xa8, .value = 0x00, },
35419c468b4SFrançois Tigeot 	/* 0xa9 to 0xab are mode specific, but have no visible effect */
35519c468b4SFrançois Tigeot 	[47] = { .offset = 0xa9, .value = 0x04, },
35619c468b4SFrançois Tigeot 	[48] = { .offset = 0xaa, .value = 0x70, },
35719c468b4SFrançois Tigeot 	[49] = { .offset = 0xab, .value = 0x4f, },
35819c468b4SFrançois Tigeot 	[50] = { .offset = 0xac, .value = 0x00, },
35919c468b4SFrançois Tigeot 	[51] = { .offset = 0xad, .value = 0x00, },
36019c468b4SFrançois Tigeot 	[52] = { .offset = 0xb6, .value = 0x09, },
36119c468b4SFrançois Tigeot 	[53] = { .offset = 0xb7, .value = 0x03, },
36219c468b4SFrançois Tigeot 	/* b8,b9 are part of the mode-specific configuration */
36319c468b4SFrançois Tigeot 	[54] = { .offset = 0xba, .value = 0x00, },
36419c468b4SFrançois Tigeot 	[55] = { .offset = 0xbb, .value = 0x20, },
36519c468b4SFrançois Tigeot 	[56] = { .offset = 0xf3, .value = 0x90, },
36619c468b4SFrançois Tigeot 	[57] = { .offset = 0xf4, .value = 0x00, },
36719c468b4SFrançois Tigeot 	[58] = { .offset = 0xf7, .value = 0x88, },
36819c468b4SFrançois Tigeot 	/* f8 is mode specific, but the value does not matter */
36919c468b4SFrançois Tigeot 	[59] = { .offset = 0xf8, .value = 0x0a, },
37019c468b4SFrançois Tigeot 	[60] = { .offset = 0xf9, .value = 0x00, }
3711b13d190SFrançois Tigeot };
3721b13d190SFrançois Tigeot 
3731b13d190SFrançois Tigeot static const struct ns2501_reg regs_init[] = {
3741b13d190SFrançois Tigeot 	[0] = { .offset = 0x35, .value = 0xff, },
3751b13d190SFrançois Tigeot 	[1] = { .offset = 0x34, .value = 0x00, },
3761b13d190SFrançois Tigeot 	[2] = { .offset = 0x08, .value = 0x30, },
3771b13d190SFrançois Tigeot };
3781b13d190SFrançois Tigeot 
379ba55f2f5SFrançois Tigeot struct ns2501_priv {
380ba55f2f5SFrançois Tigeot 	bool quiet;
38119c468b4SFrançois Tigeot 	const struct ns2501_configuration *conf;
382ba55f2f5SFrançois Tigeot };
383ba55f2f5SFrançois Tigeot 
384ba55f2f5SFrançois Tigeot #define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr))
385ba55f2f5SFrançois Tigeot 
386ba55f2f5SFrançois Tigeot /*
387ba55f2f5SFrançois Tigeot ** Read a register from the ns2501.
388ba55f2f5SFrançois Tigeot ** Returns true if successful, false otherwise.
389ba55f2f5SFrançois Tigeot ** If it returns false, it might be wise to enable the
390ba55f2f5SFrançois Tigeot ** DVO with the above function.
391ba55f2f5SFrançois Tigeot */
ns2501_readb(struct intel_dvo_device * dvo,int addr,uint8_t * ch)392ba55f2f5SFrançois Tigeot static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch)
393ba55f2f5SFrançois Tigeot {
394ba55f2f5SFrançois Tigeot 	struct ns2501_priv *ns = dvo->dev_priv;
395ba55f2f5SFrançois Tigeot 	struct i2c_adapter *adapter = dvo->i2c_bus;
396ba55f2f5SFrançois Tigeot 	u8 out_buf[2];
397ba55f2f5SFrançois Tigeot 	u8 in_buf[2];
398ba55f2f5SFrançois Tigeot 
399ba55f2f5SFrançois Tigeot 	struct i2c_msg msgs[] = {
400ba55f2f5SFrançois Tigeot 		{
4019f4ca867SFrançois Tigeot 		 .addr = dvo->slave_addr,
402ba55f2f5SFrançois Tigeot 		 .flags = 0,
403ba55f2f5SFrançois Tigeot 		 .len = 1,
404ba55f2f5SFrançois Tigeot 		 .buf = out_buf,
405ba55f2f5SFrançois Tigeot 		 },
406ba55f2f5SFrançois Tigeot 		{
4079f4ca867SFrançois Tigeot 		 .addr = dvo->slave_addr,
408ba55f2f5SFrançois Tigeot 		 .flags = I2C_M_RD,
409ba55f2f5SFrançois Tigeot 		 .len = 1,
410ba55f2f5SFrançois Tigeot 		 .buf = in_buf,
411ba55f2f5SFrançois Tigeot 		 }
412ba55f2f5SFrançois Tigeot 	};
413ba55f2f5SFrançois Tigeot 
414ba55f2f5SFrançois Tigeot 	out_buf[0] = addr;
415ba55f2f5SFrançois Tigeot 	out_buf[1] = 0;
416ba55f2f5SFrançois Tigeot 
4179f4ca867SFrançois Tigeot 	if (i2c_transfer(adapter, msgs, 2) == 2) {
418ba55f2f5SFrançois Tigeot 		*ch = in_buf[0];
419ba55f2f5SFrançois Tigeot 		return true;
420ba55f2f5SFrançois Tigeot 	}
421ba55f2f5SFrançois Tigeot 
422ba55f2f5SFrançois Tigeot 	if (!ns->quiet) {
423ba55f2f5SFrançois Tigeot 		DRM_DEBUG_KMS
424ba55f2f5SFrançois Tigeot 		    ("Unable to read register 0x%02x from %s:0x%02x.\n", addr,
4259f4ca867SFrançois Tigeot 		     adapter->name, dvo->slave_addr);
426ba55f2f5SFrançois Tigeot 	}
427ba55f2f5SFrançois Tigeot 
428ba55f2f5SFrançois Tigeot 	return false;
429ba55f2f5SFrançois Tigeot }
430ba55f2f5SFrançois Tigeot 
431ba55f2f5SFrançois Tigeot /*
432ba55f2f5SFrançois Tigeot ** Write a register to the ns2501.
433ba55f2f5SFrançois Tigeot ** Returns true if successful, false otherwise.
434ba55f2f5SFrançois Tigeot ** If it returns false, it might be wise to enable the
435ba55f2f5SFrançois Tigeot ** DVO with the above function.
436ba55f2f5SFrançois Tigeot */
ns2501_writeb(struct intel_dvo_device * dvo,int addr,uint8_t ch)437ba55f2f5SFrançois Tigeot static bool ns2501_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
438ba55f2f5SFrançois Tigeot {
439ba55f2f5SFrançois Tigeot 	struct ns2501_priv *ns = dvo->dev_priv;
440ba55f2f5SFrançois Tigeot 	struct i2c_adapter *adapter = dvo->i2c_bus;
441ba55f2f5SFrançois Tigeot 	uint8_t out_buf[2];
442ba55f2f5SFrançois Tigeot 
443ba55f2f5SFrançois Tigeot 	struct i2c_msg msg = {
4449f4ca867SFrançois Tigeot 		.addr = dvo->slave_addr,
445ba55f2f5SFrançois Tigeot 		.flags = 0,
446ba55f2f5SFrançois Tigeot 		.len = 2,
447ba55f2f5SFrançois Tigeot 		.buf = out_buf,
448ba55f2f5SFrançois Tigeot 	};
449ba55f2f5SFrançois Tigeot 
450ba55f2f5SFrançois Tigeot 	out_buf[0] = addr;
451ba55f2f5SFrançois Tigeot 	out_buf[1] = ch;
452ba55f2f5SFrançois Tigeot 
4539f4ca867SFrançois Tigeot 	if (i2c_transfer(adapter, &msg, 1) == 1) {
454ba55f2f5SFrançois Tigeot 		return true;
455ba55f2f5SFrançois Tigeot 	}
456ba55f2f5SFrançois Tigeot 
457ba55f2f5SFrançois Tigeot 	if (!ns->quiet) {
458ba55f2f5SFrançois Tigeot 		DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d\n",
4599f4ca867SFrançois Tigeot 			      addr, adapter->name, dvo->slave_addr);
460ba55f2f5SFrançois Tigeot 	}
461ba55f2f5SFrançois Tigeot 
462ba55f2f5SFrançois Tigeot 	return false;
463ba55f2f5SFrançois Tigeot }
464ba55f2f5SFrançois Tigeot 
465ba55f2f5SFrançois Tigeot /* National Semiconductor 2501 driver for chip on i2c bus
466ba55f2f5SFrançois Tigeot  * scan for the chip on the bus.
467ba55f2f5SFrançois Tigeot  * Hope the VBIOS initialized the PLL correctly so we can
468ba55f2f5SFrançois Tigeot  * talk to it. If not, it will not be seen and not detected.
469ba55f2f5SFrançois Tigeot  * Bummer!
470ba55f2f5SFrançois Tigeot  */
ns2501_init(struct intel_dvo_device * dvo,struct i2c_adapter * adapter)471ba55f2f5SFrançois Tigeot static bool ns2501_init(struct intel_dvo_device *dvo,
472ba55f2f5SFrançois Tigeot 			struct i2c_adapter *adapter)
473ba55f2f5SFrançois Tigeot {
474ba55f2f5SFrançois Tigeot 	/* this will detect the NS2501 chip on the specified i2c bus */
475ba55f2f5SFrançois Tigeot 	struct ns2501_priv *ns;
476ba55f2f5SFrançois Tigeot 	unsigned char ch;
477ba55f2f5SFrançois Tigeot 
478ba55f2f5SFrançois Tigeot 	ns = kzalloc(sizeof(struct ns2501_priv), GFP_KERNEL);
479ba55f2f5SFrançois Tigeot 	if (ns == NULL)
480ba55f2f5SFrançois Tigeot 		return false;
481ba55f2f5SFrançois Tigeot 
482ba55f2f5SFrançois Tigeot 	dvo->i2c_bus = adapter;
483ba55f2f5SFrançois Tigeot 	dvo->dev_priv = ns;
484ba55f2f5SFrançois Tigeot 	ns->quiet = true;
485ba55f2f5SFrançois Tigeot 
486ba55f2f5SFrançois Tigeot 	if (!ns2501_readb(dvo, NS2501_VID_LO, &ch))
487ba55f2f5SFrançois Tigeot 		goto out;
488ba55f2f5SFrançois Tigeot 
489ba55f2f5SFrançois Tigeot 	if (ch != (NS2501_VID & 0xff)) {
490ba55f2f5SFrançois Tigeot 		DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
4919f4ca867SFrançois Tigeot 			      ch, adapter->name, dvo->slave_addr);
492ba55f2f5SFrançois Tigeot 		goto out;
493ba55f2f5SFrançois Tigeot 	}
494ba55f2f5SFrançois Tigeot 
495ba55f2f5SFrançois Tigeot 	if (!ns2501_readb(dvo, NS2501_DID_LO, &ch))
496ba55f2f5SFrançois Tigeot 		goto out;
497ba55f2f5SFrançois Tigeot 
498ba55f2f5SFrançois Tigeot 	if (ch != (NS2501_DID & 0xff)) {
499ba55f2f5SFrançois Tigeot 		DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
5009f4ca867SFrançois Tigeot 			      ch, adapter->name, dvo->slave_addr);
501ba55f2f5SFrançois Tigeot 		goto out;
502ba55f2f5SFrançois Tigeot 	}
503ba55f2f5SFrançois Tigeot 	ns->quiet = false;
504ba55f2f5SFrançois Tigeot 
505ba55f2f5SFrançois Tigeot 	DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n");
5061b13d190SFrançois Tigeot 
507ba55f2f5SFrançois Tigeot 	return true;
508ba55f2f5SFrançois Tigeot 
509ba55f2f5SFrançois Tigeot out:
510ba55f2f5SFrançois Tigeot 	kfree(ns);
511ba55f2f5SFrançois Tigeot 	return false;
512ba55f2f5SFrançois Tigeot }
513ba55f2f5SFrançois Tigeot 
ns2501_detect(struct intel_dvo_device * dvo)514ba55f2f5SFrançois Tigeot static enum drm_connector_status ns2501_detect(struct intel_dvo_device *dvo)
515ba55f2f5SFrançois Tigeot {
516ba55f2f5SFrançois Tigeot 	/*
517ba55f2f5SFrançois Tigeot 	 * This is a Laptop display, it doesn't have hotplugging.
518ba55f2f5SFrançois Tigeot 	 * Even if not, the detection bit of the 2501 is unreliable as
519ba55f2f5SFrançois Tigeot 	 * it only works for some display types.
520ba55f2f5SFrançois Tigeot 	 * It is even more unreliable as the PLL must be active for
521ba55f2f5SFrançois Tigeot 	 * allowing reading from the chiop.
522ba55f2f5SFrançois Tigeot 	 */
523ba55f2f5SFrançois Tigeot 	return connector_status_connected;
524ba55f2f5SFrançois Tigeot }
525ba55f2f5SFrançois Tigeot 
ns2501_mode_valid(struct intel_dvo_device * dvo,struct drm_display_mode * mode)526ba55f2f5SFrançois Tigeot static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
527ba55f2f5SFrançois Tigeot 					      struct drm_display_mode *mode)
528ba55f2f5SFrançois Tigeot {
529ba55f2f5SFrançois Tigeot 	DRM_DEBUG_KMS
530ba55f2f5SFrançois Tigeot 	    ("is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
531ba55f2f5SFrançois Tigeot 	     mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
532ba55f2f5SFrançois Tigeot 
533ba55f2f5SFrançois Tigeot 	/*
534ba55f2f5SFrançois Tigeot 	 * Currently, these are all the modes I have data from.
535ba55f2f5SFrançois Tigeot 	 * More might exist. Unclear how to find the native resolution
536ba55f2f5SFrançois Tigeot 	 * of the panel in here so we could always accept it
537ba55f2f5SFrançois Tigeot 	 * by disabling the scaler.
538ba55f2f5SFrançois Tigeot 	 */
5391b13d190SFrançois Tigeot 	if ((mode->hdisplay == 640 && mode->vdisplay == 480 && mode->clock == 25175) ||
5401b13d190SFrançois Tigeot 	    (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 40000) ||
5411b13d190SFrançois Tigeot 	    (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 65000)) {
542ba55f2f5SFrançois Tigeot 		return MODE_OK;
543ba55f2f5SFrançois Tigeot 	} else {
544ba55f2f5SFrançois Tigeot 		return MODE_ONE_SIZE;	/* Is this a reasonable error? */
545ba55f2f5SFrançois Tigeot 	}
546ba55f2f5SFrançois Tigeot }
547ba55f2f5SFrançois Tigeot 
ns2501_mode_set(struct intel_dvo_device * dvo,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)548ba55f2f5SFrançois Tigeot static void ns2501_mode_set(struct intel_dvo_device *dvo,
549352ff8bdSFrançois Tigeot 			    const struct drm_display_mode *mode,
550352ff8bdSFrançois Tigeot 			    const struct drm_display_mode *adjusted_mode)
551ba55f2f5SFrançois Tigeot {
55219c468b4SFrançois Tigeot 	const struct ns2501_configuration *conf;
553ba55f2f5SFrançois Tigeot 	struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
5541b13d190SFrançois Tigeot 	int mode_idx, i;
555ba55f2f5SFrançois Tigeot 
556ba55f2f5SFrançois Tigeot 	DRM_DEBUG_KMS
557ba55f2f5SFrançois Tigeot 	    ("set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
558ba55f2f5SFrançois Tigeot 	     mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
559ba55f2f5SFrançois Tigeot 
56019c468b4SFrançois Tigeot 	DRM_DEBUG_KMS("Detailed requested mode settings are:\n"
56119c468b4SFrançois Tigeot 			"clock		: %d kHz\n"
56219c468b4SFrançois Tigeot 			"hdisplay	: %d\n"
56319c468b4SFrançois Tigeot 			"hblank start	: %d\n"
56419c468b4SFrançois Tigeot 			"hblank end	: %d\n"
56519c468b4SFrançois Tigeot 			"hsync start	: %d\n"
56619c468b4SFrançois Tigeot 			"hsync end	: %d\n"
56719c468b4SFrançois Tigeot 			"htotal		: %d\n"
56819c468b4SFrançois Tigeot 			"hskew		: %d\n"
56919c468b4SFrançois Tigeot 			"vdisplay	: %d\n"
57019c468b4SFrançois Tigeot 			"vblank start	: %d\n"
57119c468b4SFrançois Tigeot 			"hblank end	: %d\n"
57219c468b4SFrançois Tigeot 			"vsync start	: %d\n"
57319c468b4SFrançois Tigeot 			"vsync end	: %d\n"
57419c468b4SFrançois Tigeot 			"vtotal		: %d\n",
57519c468b4SFrançois Tigeot 			adjusted_mode->crtc_clock,
57619c468b4SFrançois Tigeot 			adjusted_mode->crtc_hdisplay,
57719c468b4SFrançois Tigeot 			adjusted_mode->crtc_hblank_start,
57819c468b4SFrançois Tigeot 			adjusted_mode->crtc_hblank_end,
57919c468b4SFrançois Tigeot 			adjusted_mode->crtc_hsync_start,
58019c468b4SFrançois Tigeot 			adjusted_mode->crtc_hsync_end,
58119c468b4SFrançois Tigeot 			adjusted_mode->crtc_htotal,
58219c468b4SFrançois Tigeot 			adjusted_mode->crtc_hskew,
58319c468b4SFrançois Tigeot 			adjusted_mode->crtc_vdisplay,
58419c468b4SFrançois Tigeot 			adjusted_mode->crtc_vblank_start,
58519c468b4SFrançois Tigeot 			adjusted_mode->crtc_vblank_end,
58619c468b4SFrançois Tigeot 			adjusted_mode->crtc_vsync_start,
58719c468b4SFrançois Tigeot 			adjusted_mode->crtc_vsync_end,
58819c468b4SFrançois Tigeot 			adjusted_mode->crtc_vtotal);
58919c468b4SFrançois Tigeot 
5901b13d190SFrançois Tigeot 	if (mode->hdisplay == 640 && mode->vdisplay == 480)
5911b13d190SFrançois Tigeot 		mode_idx = MODE_640x480;
5921b13d190SFrançois Tigeot 	else if (mode->hdisplay == 800 && mode->vdisplay == 600)
5931b13d190SFrançois Tigeot 		mode_idx = MODE_800x600;
5941b13d190SFrançois Tigeot 	else if (mode->hdisplay == 1024 && mode->vdisplay == 768)
5951b13d190SFrançois Tigeot 		mode_idx = MODE_1024x768;
5961b13d190SFrançois Tigeot 	else
5971b13d190SFrançois Tigeot 		return;
598ba55f2f5SFrançois Tigeot 
5991b13d190SFrançois Tigeot 	/* Hopefully doing it every time won't hurt... */
6001b13d190SFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(regs_init); i++)
6011b13d190SFrançois Tigeot 		ns2501_writeb(dvo, regs_init[i].offset, regs_init[i].value);
602ba55f2f5SFrançois Tigeot 
60319c468b4SFrançois Tigeot 	/* Write the mode-agnostic values */
60419c468b4SFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(mode_agnostic_values); i++)
60519c468b4SFrançois Tigeot 		ns2501_writeb(dvo, mode_agnostic_values[i].offset,
60619c468b4SFrançois Tigeot 				mode_agnostic_values[i].value);
607ba55f2f5SFrançois Tigeot 
60819c468b4SFrançois Tigeot 	/* Write now the mode-specific configuration */
60919c468b4SFrançois Tigeot 	conf = ns2501_modes + mode_idx;
61019c468b4SFrançois Tigeot 	ns->conf = conf;
61119c468b4SFrançois Tigeot 
61219c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG8, conf->conf);
61319c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG1B, conf->pll_a);
61419c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG1C, conf->pll_b & 0xff);
61519c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG1D, conf->pll_b >> 8);
61619c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC1, conf->hstart & 0xff);
61719c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC2, conf->hstart >> 8);
61819c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC3, conf->hstop & 0xff);
61919c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC4, conf->hstop >> 8);
62019c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC5, conf->vstart & 0xff);
62119c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC6, conf->vstart >> 8);
62219c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC7, conf->vstop & 0xff);
62319c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC8, conf->vstop >> 8);
62419c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG80, conf->vsync & 0xff);
62519c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG81, conf->vsync >> 8);
62619c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG82, conf->vtotal & 0xff);
62719c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG83, conf->vtotal >> 8);
62819c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG98, conf->hpos & 0xff);
62919c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG99, conf->hpos >> 8);
63019c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG8E, conf->vpos & 0xff);
63119c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG8F, conf->vpos >> 8);
63219c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG9C, conf->voffs & 0xff);
63319c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG9D, conf->voffs >> 8);
63419c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGB8, conf->hscale & 0xff);
63519c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGB9, conf->hscale >> 8);
63619c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG10, conf->vscale & 0xff);
63719c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG11, conf->vscale >> 8);
63819c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGF9, conf->dither);
63919c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REG41, conf->syncb);
64019c468b4SFrançois Tigeot 	ns2501_writeb(dvo, NS2501_REGC0, conf->sync);
641ba55f2f5SFrançois Tigeot }
642ba55f2f5SFrançois Tigeot 
643ba55f2f5SFrançois Tigeot /* set the NS2501 power state */
ns2501_get_hw_state(struct intel_dvo_device * dvo)644ba55f2f5SFrançois Tigeot static bool ns2501_get_hw_state(struct intel_dvo_device *dvo)
645ba55f2f5SFrançois Tigeot {
646ba55f2f5SFrançois Tigeot 	unsigned char ch;
647ba55f2f5SFrançois Tigeot 
648ba55f2f5SFrançois Tigeot 	if (!ns2501_readb(dvo, NS2501_REG8, &ch))
649ba55f2f5SFrançois Tigeot 		return false;
650ba55f2f5SFrançois Tigeot 
6511b13d190SFrançois Tigeot 	return ch & NS2501_8_PD;
652ba55f2f5SFrançois Tigeot }
653ba55f2f5SFrançois Tigeot 
654ba55f2f5SFrançois Tigeot /* set the NS2501 power state */
ns2501_dpms(struct intel_dvo_device * dvo,bool enable)655ba55f2f5SFrançois Tigeot static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
656ba55f2f5SFrançois Tigeot {
657ba55f2f5SFrançois Tigeot 	struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
658ba55f2f5SFrançois Tigeot 
659ba55f2f5SFrançois Tigeot 	DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable);
660ba55f2f5SFrançois Tigeot 
6611b13d190SFrançois Tigeot 	if (enable) {
66219c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REGC0, ns->conf->sync | 0x08);
663ba55f2f5SFrançois Tigeot 
66419c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REG41, ns->conf->syncb);
665ba55f2f5SFrançois Tigeot 
66619c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REG34, NS2501_34_ENABLE_OUTPUT);
6671b13d190SFrançois Tigeot 		msleep(15);
6681b13d190SFrançois Tigeot 
66919c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REG8,
67019c468b4SFrançois Tigeot 				ns->conf->conf | NS2501_8_BPAS);
67119c468b4SFrançois Tigeot 		if (!(ns->conf->conf & NS2501_8_BPAS))
67219c468b4SFrançois Tigeot 			ns2501_writeb(dvo, NS2501_REG8, ns->conf->conf);
6731b13d190SFrançois Tigeot 		msleep(200);
6741b13d190SFrançois Tigeot 
67519c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REG34,
67619c468b4SFrançois Tigeot 			NS2501_34_ENABLE_OUTPUT | NS2501_34_ENABLE_BACKLIGHT);
6771b13d190SFrançois Tigeot 
67819c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REGC0, ns->conf->sync);
6791b13d190SFrançois Tigeot 	} else {
68019c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REG34, NS2501_34_ENABLE_OUTPUT);
6811b13d190SFrançois Tigeot 		msleep(200);
6821b13d190SFrançois Tigeot 
68319c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REG8, NS2501_8_VEN | NS2501_8_HEN |
68419c468b4SFrançois Tigeot 				NS2501_8_BPAS);
6851b13d190SFrançois Tigeot 		msleep(15);
6861b13d190SFrançois Tigeot 
68719c468b4SFrançois Tigeot 		ns2501_writeb(dvo, NS2501_REG34, 0x00);
688ba55f2f5SFrançois Tigeot 	}
689ba55f2f5SFrançois Tigeot }
690ba55f2f5SFrançois Tigeot 
ns2501_destroy(struct intel_dvo_device * dvo)691ba55f2f5SFrançois Tigeot static void ns2501_destroy(struct intel_dvo_device *dvo)
692ba55f2f5SFrançois Tigeot {
693ba55f2f5SFrançois Tigeot 	struct ns2501_priv *ns = dvo->dev_priv;
694ba55f2f5SFrançois Tigeot 
695ba55f2f5SFrançois Tigeot 	if (ns) {
696ba55f2f5SFrançois Tigeot 		kfree(ns);
697ba55f2f5SFrançois Tigeot 		dvo->dev_priv = NULL;
698ba55f2f5SFrançois Tigeot 	}
699ba55f2f5SFrançois Tigeot }
700ba55f2f5SFrançois Tigeot 
701*aee94f86SFrançois Tigeot const struct intel_dvo_dev_ops ns2501_ops = {
702ba55f2f5SFrançois Tigeot 	.init = ns2501_init,
703ba55f2f5SFrançois Tigeot 	.detect = ns2501_detect,
704ba55f2f5SFrançois Tigeot 	.mode_valid = ns2501_mode_valid,
705ba55f2f5SFrançois Tigeot 	.mode_set = ns2501_mode_set,
706ba55f2f5SFrançois Tigeot 	.dpms = ns2501_dpms,
707ba55f2f5SFrançois Tigeot 	.get_hw_state = ns2501_get_hw_state,
708ba55f2f5SFrançois Tigeot 	.destroy = ns2501_destroy,
709ba55f2f5SFrançois Tigeot };
710