diff options
author | Alan Cox <alan@linux.intel.com> | 2011-11-03 14:22:26 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-11-16 06:27:12 -0500 |
commit | 1b082ccf5901108d3acd860a73d8c0442556c0bb (patch) | |
tree | d5b362913e21881a790490fe50228a13cb556be0 | |
parent | 89c78134cc54dff016c83367912eb055637fa50c (diff) |
gma500: Add Oaktrail support
Oaktrail (GMA600) is found on some tablet/slate PC type systems. It's a bit
different to the GMA500 but similar enough it makes sense to plug it into
the same driver.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail.h | 252 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_crtc.c | 610 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_device.c | 489 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_hdmi.c | 852 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c | 327 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_lvds.c | 406 |
6 files changed, 2936 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/oaktrail.h b/drivers/gpu/drm/gma500/oaktrail.h new file mode 100644 index 000000000000..2da1f368f14e --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail.h | |||
@@ -0,0 +1,252 @@ | |||
1 | /************************************************************************** | ||
2 | * Copyright (c) 2007-2011, Intel Corporation. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | * | ||
18 | **************************************************************************/ | ||
19 | |||
20 | /* MID device specific descriptors */ | ||
21 | |||
22 | struct oaktrail_vbt { | ||
23 | s8 signature[4]; /*4 bytes,"$GCT" */ | ||
24 | u8 revision; | ||
25 | u8 size; | ||
26 | u8 checksum; | ||
27 | void *oaktrail_gct; | ||
28 | } __packed; | ||
29 | |||
30 | struct oaktrail_timing_info { | ||
31 | u16 pixel_clock; | ||
32 | u8 hactive_lo; | ||
33 | u8 hblank_lo; | ||
34 | u8 hblank_hi:4; | ||
35 | u8 hactive_hi:4; | ||
36 | u8 vactive_lo; | ||
37 | u8 vblank_lo; | ||
38 | u8 vblank_hi:4; | ||
39 | u8 vactive_hi:4; | ||
40 | u8 hsync_offset_lo; | ||
41 | u8 hsync_pulse_width_lo; | ||
42 | u8 vsync_pulse_width_lo:4; | ||
43 | u8 vsync_offset_lo:4; | ||
44 | u8 vsync_pulse_width_hi:2; | ||
45 | u8 vsync_offset_hi:2; | ||
46 | u8 hsync_pulse_width_hi:2; | ||
47 | u8 hsync_offset_hi:2; | ||
48 | u8 width_mm_lo; | ||
49 | u8 height_mm_lo; | ||
50 | u8 height_mm_hi:4; | ||
51 | u8 width_mm_hi:4; | ||
52 | u8 hborder; | ||
53 | u8 vborder; | ||
54 | u8 unknown0:1; | ||
55 | u8 hsync_positive:1; | ||
56 | u8 vsync_positive:1; | ||
57 | u8 separate_sync:2; | ||
58 | u8 stereo:1; | ||
59 | u8 unknown6:1; | ||
60 | u8 interlaced:1; | ||
61 | } __packed; | ||
62 | |||
63 | struct gct_r10_timing_info { | ||
64 | u16 pixel_clock; | ||
65 | u32 hactive_lo:8; | ||
66 | u32 hactive_hi:4; | ||
67 | u32 hblank_lo:8; | ||
68 | u32 hblank_hi:4; | ||
69 | u32 hsync_offset_lo:8; | ||
70 | u16 hsync_offset_hi:2; | ||
71 | u16 hsync_pulse_width_lo:8; | ||
72 | u16 hsync_pulse_width_hi:2; | ||
73 | u16 hsync_positive:1; | ||
74 | u16 rsvd_1:3; | ||
75 | u8 vactive_lo:8; | ||
76 | u16 vactive_hi:4; | ||
77 | u16 vblank_lo:8; | ||
78 | u16 vblank_hi:4; | ||
79 | u16 vsync_offset_lo:4; | ||
80 | u16 vsync_offset_hi:2; | ||
81 | u16 vsync_pulse_width_lo:4; | ||
82 | u16 vsync_pulse_width_hi:2; | ||
83 | u16 vsync_positive:1; | ||
84 | u16 rsvd_2:3; | ||
85 | } __packed; | ||
86 | |||
87 | struct oaktrail_panel_descriptor_v1 { | ||
88 | u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */ | ||
89 | /* 0x61190 if MIPI */ | ||
90 | u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/ | ||
91 | u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/ | ||
92 | u32 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 dword */ | ||
93 | /* Register 0x61210 */ | ||
94 | struct oaktrail_timing_info DTD;/*18 bytes, Standard definition */ | ||
95 | u16 Panel_Backlight_Inverter_Descriptor;/* 16 bits, as follows */ | ||
96 | /* Bit 0, Frequency, 15 bits,0 - 32767Hz */ | ||
97 | /* Bit 15, Polarity, 1 bit, 0: Normal, 1: Inverted */ | ||
98 | u16 Panel_MIPI_Display_Descriptor; | ||
99 | /*16 bits, Defined as follows: */ | ||
100 | /* if MIPI, 0x0000 if LVDS */ | ||
101 | /* Bit 0, Type, 2 bits, */ | ||
102 | /* 0: Type-1, */ | ||
103 | /* 1: Type-2, */ | ||
104 | /* 2: Type-3, */ | ||
105 | /* 3: Type-4 */ | ||
106 | /* Bit 2, Pixel Format, 4 bits */ | ||
107 | /* Bit0: 16bpp (not supported in LNC), */ | ||
108 | /* Bit1: 18bpp loosely packed, */ | ||
109 | /* Bit2: 18bpp packed, */ | ||
110 | /* Bit3: 24bpp */ | ||
111 | /* Bit 6, Reserved, 2 bits, 00b */ | ||
112 | /* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */ | ||
113 | /* Bit 14, Reserved, 2 bits, 00b */ | ||
114 | } __packed; | ||
115 | |||
116 | struct oaktrail_panel_descriptor_v2 { | ||
117 | u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */ | ||
118 | /* 0x61190 if MIPI */ | ||
119 | u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/ | ||
120 | u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/ | ||
121 | u8 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 byte */ | ||
122 | /* Register 0x61210 */ | ||
123 | struct oaktrail_timing_info DTD;/*18 bytes, Standard definition */ | ||
124 | u16 Panel_Backlight_Inverter_Descriptor;/*16 bits, as follows*/ | ||
125 | /*Bit 0, Frequency, 16 bits, 0 - 32767Hz*/ | ||
126 | u8 Panel_Initial_Brightness;/* [7:0] 0 - 100% */ | ||
127 | /*Bit 7, Polarity, 1 bit,0: Normal, 1: Inverted*/ | ||
128 | u16 Panel_MIPI_Display_Descriptor; | ||
129 | /*16 bits, Defined as follows: */ | ||
130 | /* if MIPI, 0x0000 if LVDS */ | ||
131 | /* Bit 0, Type, 2 bits, */ | ||
132 | /* 0: Type-1, */ | ||
133 | /* 1: Type-2, */ | ||
134 | /* 2: Type-3, */ | ||
135 | /* 3: Type-4 */ | ||
136 | /* Bit 2, Pixel Format, 4 bits */ | ||
137 | /* Bit0: 16bpp (not supported in LNC), */ | ||
138 | /* Bit1: 18bpp loosely packed, */ | ||
139 | /* Bit2: 18bpp packed, */ | ||
140 | /* Bit3: 24bpp */ | ||
141 | /* Bit 6, Reserved, 2 bits, 00b */ | ||
142 | /* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */ | ||
143 | /* Bit 14, Reserved, 2 bits, 00b */ | ||
144 | } __packed; | ||
145 | |||
146 | union oaktrail_panel_rx { | ||
147 | struct { | ||
148 | u16 NumberOfLanes:2; /*Num of Lanes, 2 bits,0 = 1 lane,*/ | ||
149 | /* 1 = 2 lanes, 2 = 3 lanes, 3 = 4 lanes. */ | ||
150 | u16 MaxLaneFreq:3; /* 0: 100MHz, 1: 200MHz, 2: 300MHz, */ | ||
151 | /*3: 400MHz, 4: 500MHz, 5: 600MHz, 6: 700MHz, 7: 800MHz.*/ | ||
152 | u16 SupportedVideoTransferMode:2; /*0: Non-burst only */ | ||
153 | /* 1: Burst and non-burst */ | ||
154 | /* 2/3: Reserved */ | ||
155 | u16 HSClkBehavior:1; /*0: Continuous, 1: Non-continuous*/ | ||
156 | u16 DuoDisplaySupport:1; /*1 bit,0: No, 1: Yes*/ | ||
157 | u16 ECC_ChecksumCapabilities:1;/*1 bit,0: No, 1: Yes*/ | ||
158 | u16 BidirectionalCommunication:1;/*1 bit,0: No, 1: Yes */ | ||
159 | u16 Rsvd:5;/*5 bits,00000b */ | ||
160 | } panelrx; | ||
161 | u16 panel_receiver; | ||
162 | } __packed; | ||
163 | |||
164 | struct oaktrail_gct_v1 { | ||
165 | union { /*8 bits,Defined as follows: */ | ||
166 | struct { | ||
167 | u8 PanelType:4; /*4 bits, Bit field for panels*/ | ||
168 | /* 0 - 3: 0 = LVDS, 1 = MIPI*/ | ||
169 | /*2 bits,Specifies which of the*/ | ||
170 | u8 BootPanelIndex:2; | ||
171 | /* 4 panels to use by default*/ | ||
172 | u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/ | ||
173 | /* the 4 MIPI DSI receivers to use*/ | ||
174 | } PD; | ||
175 | u8 PanelDescriptor; | ||
176 | }; | ||
177 | struct oaktrail_panel_descriptor_v1 panel[4];/*panel descrs,38 bytes each*/ | ||
178 | union oaktrail_panel_rx panelrx[4]; /* panel receivers*/ | ||
179 | } __packed; | ||
180 | |||
181 | struct oaktrail_gct_v2 { | ||
182 | union { /*8 bits,Defined as follows: */ | ||
183 | struct { | ||
184 | u8 PanelType:4; /*4 bits, Bit field for panels*/ | ||
185 | /* 0 - 3: 0 = LVDS, 1 = MIPI*/ | ||
186 | /*2 bits,Specifies which of the*/ | ||
187 | u8 BootPanelIndex:2; | ||
188 | /* 4 panels to use by default*/ | ||
189 | u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/ | ||
190 | /* the 4 MIPI DSI receivers to use*/ | ||
191 | } PD; | ||
192 | u8 PanelDescriptor; | ||
193 | }; | ||
194 | struct oaktrail_panel_descriptor_v2 panel[4];/*panel descrs,38 bytes each*/ | ||
195 | union oaktrail_panel_rx panelrx[4]; /* panel receivers*/ | ||
196 | } __packed; | ||
197 | |||
198 | struct oaktrail_gct_data { | ||
199 | u8 bpi; /* boot panel index, number of panel used during boot */ | ||
200 | u8 pt; /* panel type, 4 bit field, 0=lvds, 1=mipi */ | ||
201 | struct oaktrail_timing_info DTD; /* timing info for the selected panel */ | ||
202 | u32 Panel_Port_Control; | ||
203 | u32 PP_On_Sequencing;/*1 dword,Register 0x61208,*/ | ||
204 | u32 PP_Off_Sequencing;/*1 dword,Register 0x6120C,*/ | ||
205 | u32 PP_Cycle_Delay; | ||
206 | u16 Panel_Backlight_Inverter_Descriptor; | ||
207 | u16 Panel_MIPI_Display_Descriptor; | ||
208 | } __packed; | ||
209 | |||
210 | #define MODE_SETTING_IN_CRTC 0x1 | ||
211 | #define MODE_SETTING_IN_ENCODER 0x2 | ||
212 | #define MODE_SETTING_ON_GOING 0x3 | ||
213 | #define MODE_SETTING_IN_DSR 0x4 | ||
214 | #define MODE_SETTING_ENCODER_DONE 0x8 | ||
215 | |||
216 | #define GCT_R10_HEADER_SIZE 16 | ||
217 | #define GCT_R10_DISPLAY_DESC_SIZE 28 | ||
218 | |||
219 | /* | ||
220 | * Moorestown HDMI interfaces | ||
221 | */ | ||
222 | |||
223 | struct oaktrail_hdmi_dev { | ||
224 | struct pci_dev *dev; | ||
225 | void __iomem *regs; | ||
226 | unsigned int mmio, mmio_len; | ||
227 | int dpms_mode; | ||
228 | struct hdmi_i2c_dev *i2c_dev; | ||
229 | |||
230 | /* register state */ | ||
231 | u32 saveDPLL_CTRL; | ||
232 | u32 saveDPLL_DIV_CTRL; | ||
233 | u32 saveDPLL_ADJUST; | ||
234 | u32 saveDPLL_UPDATE; | ||
235 | u32 saveDPLL_CLK_ENABLE; | ||
236 | u32 savePCH_HTOTAL_B; | ||
237 | u32 savePCH_HBLANK_B; | ||
238 | u32 savePCH_HSYNC_B; | ||
239 | u32 savePCH_VTOTAL_B; | ||
240 | u32 savePCH_VBLANK_B; | ||
241 | u32 savePCH_VSYNC_B; | ||
242 | u32 savePCH_PIPEBCONF; | ||
243 | u32 savePCH_PIPEBSRC; | ||
244 | }; | ||
245 | |||
246 | extern void oaktrail_hdmi_setup(struct drm_device *dev); | ||
247 | extern void oaktrail_hdmi_teardown(struct drm_device *dev); | ||
248 | extern int oaktrail_hdmi_i2c_init(struct pci_dev *dev); | ||
249 | extern void oaktrail_hdmi_i2c_exit(struct pci_dev *dev); | ||
250 | extern void oaktrail_hdmi_save(struct drm_device *dev); | ||
251 | extern void oaktrail_hdmi_restore(struct drm_device *dev); | ||
252 | extern void oaktrail_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); | ||
diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c new file mode 100644 index 000000000000..7f9c791fbe78 --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c | |||
@@ -0,0 +1,610 @@ | |||
1 | /* | ||
2 | * Copyright © 2009 Intel Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
16 | */ | ||
17 | |||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/pm_runtime.h> | ||
20 | |||
21 | #include <drm/drmP.h> | ||
22 | #include "framebuffer.h" | ||
23 | #include "psb_drv.h" | ||
24 | #include "psb_intel_drv.h" | ||
25 | #include "psb_intel_reg.h" | ||
26 | #include "psb_intel_display.h" | ||
27 | #include "power.h" | ||
28 | |||
29 | struct psb_intel_range_t { | ||
30 | int min, max; | ||
31 | }; | ||
32 | |||
33 | struct oaktrail_limit_t { | ||
34 | struct psb_intel_range_t dot, m, p1; | ||
35 | }; | ||
36 | |||
37 | struct oaktrail_clock_t { | ||
38 | /* derived values */ | ||
39 | int dot; | ||
40 | int m; | ||
41 | int p1; | ||
42 | }; | ||
43 | |||
44 | #define MRST_LIMIT_LVDS_100L 0 | ||
45 | #define MRST_LIMIT_LVDS_83 1 | ||
46 | #define MRST_LIMIT_LVDS_100 2 | ||
47 | |||
48 | #define MRST_DOT_MIN 19750 | ||
49 | #define MRST_DOT_MAX 120000 | ||
50 | #define MRST_M_MIN_100L 20 | ||
51 | #define MRST_M_MIN_100 10 | ||
52 | #define MRST_M_MIN_83 12 | ||
53 | #define MRST_M_MAX_100L 34 | ||
54 | #define MRST_M_MAX_100 17 | ||
55 | #define MRST_M_MAX_83 20 | ||
56 | #define MRST_P1_MIN 2 | ||
57 | #define MRST_P1_MAX_0 7 | ||
58 | #define MRST_P1_MAX_1 8 | ||
59 | |||
60 | static const struct oaktrail_limit_t oaktrail_limits[] = { | ||
61 | { /* MRST_LIMIT_LVDS_100L */ | ||
62 | .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, | ||
63 | .m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L}, | ||
64 | .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, | ||
65 | }, | ||
66 | { /* MRST_LIMIT_LVDS_83L */ | ||
67 | .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, | ||
68 | .m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83}, | ||
69 | .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0}, | ||
70 | }, | ||
71 | { /* MRST_LIMIT_LVDS_100 */ | ||
72 | .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, | ||
73 | .m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100}, | ||
74 | .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, | ||
75 | }, | ||
76 | }; | ||
77 | |||
78 | #define MRST_M_MIN 10 | ||
79 | static const u32 oaktrail_m_converts[] = { | ||
80 | 0x2B, 0x15, 0x2A, 0x35, 0x1A, 0x0D, 0x26, 0x33, 0x19, 0x2C, | ||
81 | 0x36, 0x3B, 0x1D, 0x2E, 0x37, 0x1B, 0x2D, 0x16, 0x0B, 0x25, | ||
82 | 0x12, 0x09, 0x24, 0x32, 0x39, 0x1c, | ||
83 | }; | ||
84 | |||
85 | static const struct oaktrail_limit_t *oaktrail_limit(struct drm_crtc *crtc) | ||
86 | { | ||
87 | const struct oaktrail_limit_t *limit = NULL; | ||
88 | struct drm_device *dev = crtc->dev; | ||
89 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
90 | |||
91 | if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) | ||
92 | || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) { | ||
93 | switch (dev_priv->core_freq) { | ||
94 | case 100: | ||
95 | limit = &oaktrail_limits[MRST_LIMIT_LVDS_100L]; | ||
96 | break; | ||
97 | case 166: | ||
98 | limit = &oaktrail_limits[MRST_LIMIT_LVDS_83]; | ||
99 | break; | ||
100 | case 200: | ||
101 | limit = &oaktrail_limits[MRST_LIMIT_LVDS_100]; | ||
102 | break; | ||
103 | } | ||
104 | } else { | ||
105 | limit = NULL; | ||
106 | dev_err(dev->dev, "oaktrail_limit Wrong display type.\n"); | ||
107 | } | ||
108 | |||
109 | return limit; | ||
110 | } | ||
111 | |||
112 | /** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ | ||
113 | static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock) | ||
114 | { | ||
115 | clock->dot = (refclk * clock->m) / (14 * clock->p1); | ||
116 | } | ||
117 | |||
118 | void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock) | ||
119 | { | ||
120 | pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n", | ||
121 | prefix, clock->dot, clock->m, clock->p1); | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Returns a set of divisors for the desired target clock with the given refclk, | ||
126 | * or FALSE. Divisor values are the actual divisors for | ||
127 | */ | ||
128 | static bool | ||
129 | mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, | ||
130 | struct oaktrail_clock_t *best_clock) | ||
131 | { | ||
132 | struct oaktrail_clock_t clock; | ||
133 | const struct oaktrail_limit_t *limit = oaktrail_limit(crtc); | ||
134 | int err = target; | ||
135 | |||
136 | memset(best_clock, 0, sizeof(*best_clock)); | ||
137 | |||
138 | for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { | ||
139 | for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; | ||
140 | clock.p1++) { | ||
141 | int this_err; | ||
142 | |||
143 | oaktrail_clock(refclk, &clock); | ||
144 | |||
145 | this_err = abs(clock.dot - target); | ||
146 | if (this_err < err) { | ||
147 | *best_clock = clock; | ||
148 | err = this_err; | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", err); | ||
153 | return err != target; | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * Sets the power management mode of the pipe and plane. | ||
158 | * | ||
159 | * This code should probably grow support for turning the cursor off and back | ||
160 | * on appropriately at the same time as we're turning the pipe off/on. | ||
161 | */ | ||
162 | static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
163 | { | ||
164 | struct drm_device *dev = crtc->dev; | ||
165 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | ||
166 | int pipe = psb_intel_crtc->pipe; | ||
167 | int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; | ||
168 | int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; | ||
169 | int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE; | ||
170 | int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; | ||
171 | u32 temp; | ||
172 | bool enabled; | ||
173 | |||
174 | if (!gma_power_begin(dev, true)) | ||
175 | return; | ||
176 | |||
177 | /* XXX: When our outputs are all unaware of DPMS modes other than off | ||
178 | * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. | ||
179 | */ | ||
180 | switch (mode) { | ||
181 | case DRM_MODE_DPMS_ON: | ||
182 | case DRM_MODE_DPMS_STANDBY: | ||
183 | case DRM_MODE_DPMS_SUSPEND: | ||
184 | /* Enable the DPLL */ | ||
185 | temp = REG_READ(dpll_reg); | ||
186 | if ((temp & DPLL_VCO_ENABLE) == 0) { | ||
187 | REG_WRITE(dpll_reg, temp); | ||
188 | REG_READ(dpll_reg); | ||
189 | /* Wait for the clocks to stabilize. */ | ||
190 | udelay(150); | ||
191 | REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); | ||
192 | REG_READ(dpll_reg); | ||
193 | /* Wait for the clocks to stabilize. */ | ||
194 | udelay(150); | ||
195 | REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); | ||
196 | REG_READ(dpll_reg); | ||
197 | /* Wait for the clocks to stabilize. */ | ||
198 | udelay(150); | ||
199 | } | ||
200 | /* Enable the pipe */ | ||
201 | temp = REG_READ(pipeconf_reg); | ||
202 | if ((temp & PIPEACONF_ENABLE) == 0) | ||
203 | REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); | ||
204 | /* Enable the plane */ | ||
205 | temp = REG_READ(dspcntr_reg); | ||
206 | if ((temp & DISPLAY_PLANE_ENABLE) == 0) { | ||
207 | REG_WRITE(dspcntr_reg, | ||
208 | temp | DISPLAY_PLANE_ENABLE); | ||
209 | /* Flush the plane changes */ | ||
210 | REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); | ||
211 | } | ||
212 | |||
213 | psb_intel_crtc_load_lut(crtc); | ||
214 | |||
215 | /* Give the overlay scaler a chance to enable | ||
216 | if it's on this pipe */ | ||
217 | /* psb_intel_crtc_dpms_video(crtc, true); TODO */ | ||
218 | break; | ||
219 | case DRM_MODE_DPMS_OFF: | ||
220 | /* Give the overlay scaler a chance to disable | ||
221 | * if it's on this pipe */ | ||
222 | /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ | ||
223 | |||
224 | /* Disable the VGA plane that we never use */ | ||
225 | REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); | ||
226 | /* Disable display plane */ | ||
227 | temp = REG_READ(dspcntr_reg); | ||
228 | if ((temp & DISPLAY_PLANE_ENABLE) != 0) { | ||
229 | REG_WRITE(dspcntr_reg, | ||
230 | temp & ~DISPLAY_PLANE_ENABLE); | ||
231 | /* Flush the plane changes */ | ||
232 | REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); | ||
233 | REG_READ(dspbase_reg); | ||
234 | } | ||
235 | |||
236 | /* Next, disable display pipes */ | ||
237 | temp = REG_READ(pipeconf_reg); | ||
238 | if ((temp & PIPEACONF_ENABLE) != 0) { | ||
239 | REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); | ||
240 | REG_READ(pipeconf_reg); | ||
241 | } | ||
242 | /* Wait for for the pipe disable to take effect. */ | ||
243 | psb_intel_wait_for_vblank(dev); | ||
244 | |||
245 | temp = REG_READ(dpll_reg); | ||
246 | if ((temp & DPLL_VCO_ENABLE) != 0) { | ||
247 | REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); | ||
248 | REG_READ(dpll_reg); | ||
249 | } | ||
250 | |||
251 | /* Wait for the clocks to turn off. */ | ||
252 | udelay(150); | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; | ||
257 | |||
258 | /*Set FIFO Watermarks*/ | ||
259 | REG_WRITE(DSPARB, 0x3FFF); | ||
260 | REG_WRITE(DSPFW1, 0x3F88080A); | ||
261 | REG_WRITE(DSPFW2, 0x0b060808); | ||
262 | REG_WRITE(DSPFW3, 0x0); | ||
263 | REG_WRITE(DSPFW4, 0x08030404); | ||
264 | REG_WRITE(DSPFW5, 0x04040404); | ||
265 | REG_WRITE(DSPFW6, 0x78); | ||
266 | REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000); | ||
267 | /* Must write Bit 14 of the Chicken Bit Register */ | ||
268 | |||
269 | gma_power_end(dev); | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * Return the pipe currently connected to the panel fitter, | ||
274 | * or -1 if the panel fitter is not present or not in use | ||
275 | */ | ||
276 | static int oaktrail_panel_fitter_pipe(struct drm_device *dev) | ||
277 | { | ||
278 | u32 pfit_control; | ||
279 | |||
280 | pfit_control = REG_READ(PFIT_CONTROL); | ||
281 | |||
282 | /* See if the panel fitter is in use */ | ||
283 | if ((pfit_control & PFIT_ENABLE) == 0) | ||
284 | return -1; | ||
285 | return (pfit_control >> 29) & 3; | ||
286 | } | ||
287 | |||
288 | static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, | ||
289 | struct drm_display_mode *mode, | ||
290 | struct drm_display_mode *adjusted_mode, | ||
291 | int x, int y, | ||
292 | struct drm_framebuffer *old_fb) | ||
293 | { | ||
294 | struct drm_device *dev = crtc->dev; | ||
295 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | ||
296 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
297 | int pipe = psb_intel_crtc->pipe; | ||
298 | int fp_reg = (pipe == 0) ? MRST_FPA0 : FPB0; | ||
299 | int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; | ||
300 | int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; | ||
301 | int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; | ||
302 | int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; | ||
303 | int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; | ||
304 | int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; | ||
305 | int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; | ||
306 | int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; | ||
307 | int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; | ||
308 | int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; | ||
309 | int refclk = 0; | ||
310 | struct oaktrail_clock_t clock; | ||
311 | u32 dpll = 0, fp = 0, dspcntr, pipeconf; | ||
312 | bool ok, is_sdvo = false; | ||
313 | bool is_crt = false, is_lvds = false, is_tv = false; | ||
314 | bool is_mipi = false; | ||
315 | struct drm_mode_config *mode_config = &dev->mode_config; | ||
316 | struct psb_intel_output *psb_intel_output = NULL; | ||
317 | uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; | ||
318 | struct drm_encoder *encoder; | ||
319 | |||
320 | if (!gma_power_begin(dev, true)) | ||
321 | return 0; | ||
322 | |||
323 | memcpy(&psb_intel_crtc->saved_mode, | ||
324 | mode, | ||
325 | sizeof(struct drm_display_mode)); | ||
326 | memcpy(&psb_intel_crtc->saved_adjusted_mode, | ||
327 | adjusted_mode, | ||
328 | sizeof(struct drm_display_mode)); | ||
329 | |||
330 | list_for_each_entry(encoder, &mode_config->encoder_list, head) { | ||
331 | |||
332 | if (encoder->crtc != crtc) | ||
333 | continue; | ||
334 | |||
335 | psb_intel_output = enc_to_psb_intel_output(encoder); | ||
336 | switch (psb_intel_output->type) { | ||
337 | case INTEL_OUTPUT_LVDS: | ||
338 | is_lvds = true; | ||
339 | break; | ||
340 | case INTEL_OUTPUT_SDVO: | ||
341 | is_sdvo = true; | ||
342 | break; | ||
343 | case INTEL_OUTPUT_TVOUT: | ||
344 | is_tv = true; | ||
345 | break; | ||
346 | case INTEL_OUTPUT_ANALOG: | ||
347 | is_crt = true; | ||
348 | break; | ||
349 | case INTEL_OUTPUT_MIPI: | ||
350 | is_mipi = true; | ||
351 | break; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | /* Disable the VGA plane that we never use */ | ||
356 | REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); | ||
357 | |||
358 | /* Disable the panel fitter if it was on our pipe */ | ||
359 | if (oaktrail_panel_fitter_pipe(dev) == pipe) | ||
360 | REG_WRITE(PFIT_CONTROL, 0); | ||
361 | |||
362 | REG_WRITE(pipesrc_reg, | ||
363 | ((mode->crtc_hdisplay - 1) << 16) | | ||
364 | (mode->crtc_vdisplay - 1)); | ||
365 | |||
366 | if (psb_intel_output) | ||
367 | drm_connector_property_get_value(&psb_intel_output->base, | ||
368 | dev->mode_config.scaling_mode_property, &scalingType); | ||
369 | |||
370 | if (scalingType == DRM_MODE_SCALE_NO_SCALE) { | ||
371 | /* Moorestown doesn't have register support for centering so | ||
372 | * we need to mess with the h/vblank and h/vsync start and | ||
373 | * ends to get centering */ | ||
374 | int offsetX = 0, offsetY = 0; | ||
375 | |||
376 | offsetX = (adjusted_mode->crtc_hdisplay - | ||
377 | mode->crtc_hdisplay) / 2; | ||
378 | offsetY = (adjusted_mode->crtc_vdisplay - | ||
379 | mode->crtc_vdisplay) / 2; | ||
380 | |||
381 | REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) | | ||
382 | ((adjusted_mode->crtc_htotal - 1) << 16)); | ||
383 | REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) | | ||
384 | ((adjusted_mode->crtc_vtotal - 1) << 16)); | ||
385 | REG_WRITE(hblank_reg, | ||
386 | (adjusted_mode->crtc_hblank_start - offsetX - 1) | | ||
387 | ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); | ||
388 | REG_WRITE(hsync_reg, | ||
389 | (adjusted_mode->crtc_hsync_start - offsetX - 1) | | ||
390 | ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); | ||
391 | REG_WRITE(vblank_reg, | ||
392 | (adjusted_mode->crtc_vblank_start - offsetY - 1) | | ||
393 | ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); | ||
394 | REG_WRITE(vsync_reg, | ||
395 | (adjusted_mode->crtc_vsync_start - offsetY - 1) | | ||
396 | ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); | ||
397 | } else { | ||
398 | REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | | ||
399 | ((adjusted_mode->crtc_htotal - 1) << 16)); | ||
400 | REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | | ||
401 | ((adjusted_mode->crtc_vtotal - 1) << 16)); | ||
402 | REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | | ||
403 | ((adjusted_mode->crtc_hblank_end - 1) << 16)); | ||
404 | REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | | ||
405 | ((adjusted_mode->crtc_hsync_end - 1) << 16)); | ||
406 | REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | | ||
407 | ((adjusted_mode->crtc_vblank_end - 1) << 16)); | ||
408 | REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | | ||
409 | ((adjusted_mode->crtc_vsync_end - 1) << 16)); | ||
410 | } | ||
411 | |||
412 | /* Flush the plane changes */ | ||
413 | { | ||
414 | struct drm_crtc_helper_funcs *crtc_funcs = | ||
415 | crtc->helper_private; | ||
416 | crtc_funcs->mode_set_base(crtc, x, y, old_fb); | ||
417 | } | ||
418 | |||
419 | /* setup pipeconf */ | ||
420 | pipeconf = REG_READ(pipeconf_reg); | ||
421 | |||
422 | /* Set up the display plane register */ | ||
423 | dspcntr = REG_READ(dspcntr_reg); | ||
424 | dspcntr |= DISPPLANE_GAMMA_ENABLE; | ||
425 | |||
426 | if (pipe == 0) | ||
427 | dspcntr |= DISPPLANE_SEL_PIPE_A; | ||
428 | else | ||
429 | dspcntr |= DISPPLANE_SEL_PIPE_B; | ||
430 | |||
431 | dev_priv->dspcntr = dspcntr |= DISPLAY_PLANE_ENABLE; | ||
432 | dev_priv->pipeconf = pipeconf |= PIPEACONF_ENABLE; | ||
433 | |||
434 | if (is_mipi) | ||
435 | goto oaktrail_crtc_mode_set_exit; | ||
436 | |||
437 | refclk = dev_priv->core_freq * 1000; | ||
438 | |||
439 | dpll = 0; /*BIT16 = 0 for 100MHz reference */ | ||
440 | |||
441 | ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock); | ||
442 | |||
443 | if (!ok) { | ||
444 | dev_dbg(dev->dev, "mrstFindBestPLL fail in oaktrail_crtc_mode_set.\n"); | ||
445 | } else { | ||
446 | dev_dbg(dev->dev, "oaktrail_crtc_mode_set pixel clock = %d," | ||
447 | "m = %x, p1 = %x.\n", clock.dot, clock.m, | ||
448 | clock.p1); | ||
449 | } | ||
450 | |||
451 | fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8; | ||
452 | |||
453 | dpll |= DPLL_VGA_MODE_DIS; | ||
454 | |||
455 | |||
456 | dpll |= DPLL_VCO_ENABLE; | ||
457 | |||
458 | if (is_lvds) | ||
459 | dpll |= DPLLA_MODE_LVDS; | ||
460 | else | ||
461 | dpll |= DPLLB_MODE_DAC_SERIAL; | ||
462 | |||
463 | if (is_sdvo) { | ||
464 | int sdvo_pixel_multiply = | ||
465 | adjusted_mode->clock / mode->clock; | ||
466 | |||
467 | dpll |= DPLL_DVO_HIGH_SPEED; | ||
468 | dpll |= | ||
469 | (sdvo_pixel_multiply - | ||
470 | 1) << SDVO_MULTIPLIER_SHIFT_HIRES; | ||
471 | } | ||
472 | |||
473 | |||
474 | /* compute bitmask from p1 value */ | ||
475 | dpll |= (1 << (clock.p1 - 2)) << 17; | ||
476 | |||
477 | dpll |= DPLL_VCO_ENABLE; | ||
478 | |||
479 | mrstPrintPll("chosen", &clock); | ||
480 | |||
481 | if (dpll & DPLL_VCO_ENABLE) { | ||
482 | REG_WRITE(fp_reg, fp); | ||
483 | REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); | ||
484 | REG_READ(dpll_reg); | ||
485 | /* Check the DPLLA lock bit PIPEACONF[29] */ | ||
486 | udelay(150); | ||
487 | } | ||
488 | |||
489 | REG_WRITE(fp_reg, fp); | ||
490 | REG_WRITE(dpll_reg, dpll); | ||
491 | REG_READ(dpll_reg); | ||
492 | /* Wait for the clocks to stabilize. */ | ||
493 | udelay(150); | ||
494 | |||
495 | /* write it again -- the BIOS does, after all */ | ||
496 | REG_WRITE(dpll_reg, dpll); | ||
497 | REG_READ(dpll_reg); | ||
498 | /* Wait for the clocks to stabilize. */ | ||
499 | udelay(150); | ||
500 | |||
501 | REG_WRITE(pipeconf_reg, pipeconf); | ||
502 | REG_READ(pipeconf_reg); | ||
503 | psb_intel_wait_for_vblank(dev); | ||
504 | |||
505 | REG_WRITE(dspcntr_reg, dspcntr); | ||
506 | psb_intel_wait_for_vblank(dev); | ||
507 | |||
508 | oaktrail_crtc_mode_set_exit: | ||
509 | gma_power_end(dev); | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static bool oaktrail_crtc_mode_fixup(struct drm_crtc *crtc, | ||
514 | struct drm_display_mode *mode, | ||
515 | struct drm_display_mode *adjusted_mode) | ||
516 | { | ||
517 | return true; | ||
518 | } | ||
519 | |||
520 | int oaktrail_pipe_set_base(struct drm_crtc *crtc, | ||
521 | int x, int y, struct drm_framebuffer *old_fb) | ||
522 | { | ||
523 | struct drm_device *dev = crtc->dev; | ||
524 | /* struct drm_i915_master_private *master_priv; */ | ||
525 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | ||
526 | struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); | ||
527 | int pipe = psb_intel_crtc->pipe; | ||
528 | unsigned long start, offset; | ||
529 | /* FIXME: check if we need this surely MRST is pipe 0 only */ | ||
530 | int dspbase = (pipe == 0 ? DSPALINOFF : DSPBBASE); | ||
531 | int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); | ||
532 | int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; | ||
533 | int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; | ||
534 | u32 dspcntr; | ||
535 | int ret = 0; | ||
536 | |||
537 | /* no fb bound */ | ||
538 | if (!crtc->fb) { | ||
539 | dev_dbg(dev->dev, "No FB bound\n"); | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | if (!gma_power_begin(dev, true)) | ||
544 | return 0; | ||
545 | |||
546 | start = psbfb->gtt->offset; | ||
547 | offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8); | ||
548 | |||
549 | REG_WRITE(dspstride, crtc->fb->pitch); | ||
550 | |||
551 | dspcntr = REG_READ(dspcntr_reg); | ||
552 | dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; | ||
553 | |||
554 | switch (crtc->fb->bits_per_pixel) { | ||
555 | case 8: | ||
556 | dspcntr |= DISPPLANE_8BPP; | ||
557 | break; | ||
558 | case 16: | ||
559 | if (crtc->fb->depth == 15) | ||
560 | dspcntr |= DISPPLANE_15_16BPP; | ||
561 | else | ||
562 | dspcntr |= DISPPLANE_16BPP; | ||
563 | break; | ||
564 | case 24: | ||
565 | case 32: | ||
566 | dspcntr |= DISPPLANE_32BPP_NO_ALPHA; | ||
567 | break; | ||
568 | default: | ||
569 | dev_err(dev->dev, "Unknown color depth\n"); | ||
570 | ret = -EINVAL; | ||
571 | goto pipe_set_base_exit; | ||
572 | } | ||
573 | REG_WRITE(dspcntr_reg, dspcntr); | ||
574 | |||
575 | if (0 /* FIXMEAC - check what PSB needs */) { | ||
576 | REG_WRITE(dspbase, offset); | ||
577 | REG_READ(dspbase); | ||
578 | REG_WRITE(dspsurf, start); | ||
579 | REG_READ(dspsurf); | ||
580 | } else { | ||
581 | REG_WRITE(dspbase, start + offset); | ||
582 | REG_READ(dspbase); | ||
583 | } | ||
584 | |||
585 | pipe_set_base_exit: | ||
586 | gma_power_end(dev); | ||
587 | return ret; | ||
588 | } | ||
589 | |||
590 | static void oaktrail_crtc_prepare(struct drm_crtc *crtc) | ||
591 | { | ||
592 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
593 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); | ||
594 | } | ||
595 | |||
596 | static void oaktrail_crtc_commit(struct drm_crtc *crtc) | ||
597 | { | ||
598 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
599 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); | ||
600 | } | ||
601 | |||
602 | const struct drm_crtc_helper_funcs oaktrail_helper_funcs = { | ||
603 | .dpms = oaktrail_crtc_dpms, | ||
604 | .mode_fixup = oaktrail_crtc_mode_fixup, | ||
605 | .mode_set = oaktrail_crtc_mode_set, | ||
606 | .mode_set_base = oaktrail_pipe_set_base, | ||
607 | .prepare = oaktrail_crtc_prepare, | ||
608 | .commit = oaktrail_crtc_commit, | ||
609 | }; | ||
610 | |||
diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c new file mode 100644 index 000000000000..41c418fa9aee --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_device.c | |||
@@ -0,0 +1,489 @@ | |||
1 | /************************************************************************** | ||
2 | * Copyright (c) 2011, Intel Corporation. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | * | ||
18 | **************************************************************************/ | ||
19 | |||
20 | #include <linux/backlight.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/dmi.h> | ||
23 | #include <drm/drmP.h> | ||
24 | #include <drm/drm.h> | ||
25 | #include "psb_drm.h" | ||
26 | #include "psb_drv.h" | ||
27 | #include "psb_reg.h" | ||
28 | #include "psb_intel_reg.h" | ||
29 | #include <asm/mrst.h> | ||
30 | #include <asm/intel_scu_ipc.h> | ||
31 | #include "mid_bios.h" | ||
32 | |||
33 | static int oaktrail_output_init(struct drm_device *dev) | ||
34 | { | ||
35 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
36 | if (dev_priv->iLVDS_enable) | ||
37 | oaktrail_lvds_init(dev, &dev_priv->mode_dev); | ||
38 | else | ||
39 | dev_err(dev->dev, "DSI is not supported\n"); | ||
40 | if (dev_priv->hdmi_priv) | ||
41 | oaktrail_hdmi_init(dev, &dev_priv->mode_dev); | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * Provide the low level interfaces for the Moorestown backlight | ||
47 | */ | ||
48 | |||
49 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE | ||
50 | |||
51 | #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF | ||
52 | #define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ | ||
53 | #define BLC_PWM_FREQ_CALC_CONSTANT 32 | ||
54 | #define MHz 1000000 | ||
55 | #define BLC_ADJUSTMENT_MAX 100 | ||
56 | |||
57 | static struct backlight_device *oaktrail_backlight_device; | ||
58 | static int oaktrail_brightness; | ||
59 | |||
60 | static int oaktrail_set_brightness(struct backlight_device *bd) | ||
61 | { | ||
62 | struct drm_device *dev = bl_get_data(oaktrail_backlight_device); | ||
63 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
64 | int level = bd->props.brightness; | ||
65 | u32 blc_pwm_ctl; | ||
66 | u32 max_pwm_blc; | ||
67 | |||
68 | /* Percentage 1-100% being valid */ | ||
69 | if (level < 1) | ||
70 | level = 1; | ||
71 | |||
72 | if (gma_power_begin(dev, 0)) { | ||
73 | /* Calculate and set the brightness value */ | ||
74 | max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16; | ||
75 | blc_pwm_ctl = level * max_pwm_blc / 100; | ||
76 | |||
77 | /* Adjust the backlight level with the percent in | ||
78 | * dev_priv->blc_adj1; | ||
79 | */ | ||
80 | blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1; | ||
81 | blc_pwm_ctl = blc_pwm_ctl / 100; | ||
82 | |||
83 | /* Adjust the backlight level with the percent in | ||
84 | * dev_priv->blc_adj2; | ||
85 | */ | ||
86 | blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2; | ||
87 | blc_pwm_ctl = blc_pwm_ctl / 100; | ||
88 | |||
89 | /* force PWM bit on */ | ||
90 | REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); | ||
91 | REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl); | ||
92 | gma_power_end(dev); | ||
93 | } | ||
94 | oaktrail_brightness = level; | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int oaktrail_get_brightness(struct backlight_device *bd) | ||
99 | { | ||
100 | /* return locally cached var instead of HW read (due to DPST etc.) */ | ||
101 | /* FIXME: ideally return actual value in case firmware fiddled with | ||
102 | it */ | ||
103 | return oaktrail_brightness; | ||
104 | } | ||
105 | |||
106 | static int device_backlight_init(struct drm_device *dev) | ||
107 | { | ||
108 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
109 | unsigned long core_clock; | ||
110 | u16 bl_max_freq; | ||
111 | uint32_t value; | ||
112 | uint32_t blc_pwm_precision_factor; | ||
113 | |||
114 | dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; | ||
115 | dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; | ||
116 | bl_max_freq = 256; | ||
117 | /* this needs to be set elsewhere */ | ||
118 | blc_pwm_precision_factor = BLC_PWM_PRECISION_FACTOR; | ||
119 | |||
120 | core_clock = dev_priv->core_freq; | ||
121 | |||
122 | value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; | ||
123 | value *= blc_pwm_precision_factor; | ||
124 | value /= bl_max_freq; | ||
125 | value /= blc_pwm_precision_factor; | ||
126 | |||
127 | if (value > (unsigned long long)MRST_BLC_MAX_PWM_REG_FREQ) | ||
128 | return -ERANGE; | ||
129 | |||
130 | if (gma_power_begin(dev, false)) { | ||
131 | REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); | ||
132 | REG_WRITE(BLC_PWM_CTL, value | (value << 16)); | ||
133 | gma_power_end(dev); | ||
134 | } | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static const struct backlight_ops oaktrail_ops = { | ||
139 | .get_brightness = oaktrail_get_brightness, | ||
140 | .update_status = oaktrail_set_brightness, | ||
141 | }; | ||
142 | |||
143 | int oaktrail_backlight_init(struct drm_device *dev) | ||
144 | { | ||
145 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
146 | int ret; | ||
147 | struct backlight_properties props; | ||
148 | |||
149 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
150 | props.max_brightness = 100; | ||
151 | props.type = BACKLIGHT_PLATFORM; | ||
152 | |||
153 | oaktrail_backlight_device = backlight_device_register("oaktrail-bl", | ||
154 | NULL, (void *)dev, &oaktrail_ops, &props); | ||
155 | |||
156 | if (IS_ERR(oaktrail_backlight_device)) | ||
157 | return PTR_ERR(oaktrail_backlight_device); | ||
158 | |||
159 | ret = device_backlight_init(dev); | ||
160 | if (ret < 0) { | ||
161 | backlight_device_unregister(oaktrail_backlight_device); | ||
162 | return ret; | ||
163 | } | ||
164 | oaktrail_backlight_device->props.brightness = 100; | ||
165 | oaktrail_backlight_device->props.max_brightness = 100; | ||
166 | backlight_update_status(oaktrail_backlight_device); | ||
167 | dev_priv->backlight_device = oaktrail_backlight_device; | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | #endif | ||
172 | |||
173 | /* | ||
174 | * Provide the Moorestown specific chip logic and low level methods | ||
175 | * for power management | ||
176 | */ | ||
177 | |||
178 | static void oaktrail_init_pm(struct drm_device *dev) | ||
179 | { | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * oaktrail_save_display_registers - save registers lost on suspend | ||
184 | * @dev: our DRM device | ||
185 | * | ||
186 | * Save the state we need in order to be able to restore the interface | ||
187 | * upon resume from suspend | ||
188 | */ | ||
189 | static int oaktrail_save_display_registers(struct drm_device *dev) | ||
190 | { | ||
191 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
192 | int i; | ||
193 | u32 pp_stat; | ||
194 | |||
195 | /* Display arbitration control + watermarks */ | ||
196 | dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); | ||
197 | dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1); | ||
198 | dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2); | ||
199 | dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3); | ||
200 | dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4); | ||
201 | dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5); | ||
202 | dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); | ||
203 | dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); | ||
204 | |||
205 | /* Pipe & plane A info */ | ||
206 | dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF); | ||
207 | dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC); | ||
208 | dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0); | ||
209 | dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1); | ||
210 | dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A); | ||
211 | dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A); | ||
212 | dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A); | ||
213 | dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A); | ||
214 | dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A); | ||
215 | dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A); | ||
216 | dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A); | ||
217 | dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); | ||
218 | dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR); | ||
219 | dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE); | ||
220 | dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE); | ||
221 | dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF); | ||
222 | dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF); | ||
223 | dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF); | ||
224 | |||
225 | /* Save cursor regs */ | ||
226 | dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); | ||
227 | dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); | ||
228 | dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); | ||
229 | |||
230 | /* Save palette (gamma) */ | ||
231 | for (i = 0; i < 256; i++) | ||
232 | dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2)); | ||
233 | |||
234 | if (dev_priv->hdmi_priv) | ||
235 | oaktrail_hdmi_save(dev); | ||
236 | |||
237 | /* Save performance state */ | ||
238 | dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); | ||
239 | |||
240 | /* LVDS state */ | ||
241 | dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL); | ||
242 | dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); | ||
243 | dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); | ||
244 | dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); | ||
245 | dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); | ||
246 | dev_priv->saveLVDS = PSB_RVDC32(LVDS); | ||
247 | dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); | ||
248 | dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); | ||
249 | dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); | ||
250 | dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); | ||
251 | |||
252 | /* HW overlay */ | ||
253 | dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD); | ||
254 | dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); | ||
255 | dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); | ||
256 | dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); | ||
257 | dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); | ||
258 | dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); | ||
259 | dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); | ||
260 | |||
261 | /* DPST registers */ | ||
262 | dev_priv->saveHISTOGRAM_INT_CONTROL_REG = | ||
263 | PSB_RVDC32(HISTOGRAM_INT_CONTROL); | ||
264 | dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG = | ||
265 | PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); | ||
266 | dev_priv->savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); | ||
267 | |||
268 | if (dev_priv->iLVDS_enable) { | ||
269 | /* Shut down the panel */ | ||
270 | PSB_WVDC32(0, PP_CONTROL); | ||
271 | |||
272 | do { | ||
273 | pp_stat = PSB_RVDC32(PP_STATUS); | ||
274 | } while (pp_stat & 0x80000000); | ||
275 | |||
276 | /* Turn off the plane */ | ||
277 | PSB_WVDC32(0x58000000, DSPACNTR); | ||
278 | /* Trigger the plane disable */ | ||
279 | PSB_WVDC32(0, DSPASURF); | ||
280 | |||
281 | /* Wait ~4 ticks */ | ||
282 | msleep(4); | ||
283 | |||
284 | /* Turn off pipe */ | ||
285 | PSB_WVDC32(0x0, PIPEACONF); | ||
286 | /* Wait ~8 ticks */ | ||
287 | msleep(8); | ||
288 | |||
289 | /* Turn off PLLs */ | ||
290 | PSB_WVDC32(0, MRST_DPLL_A); | ||
291 | } | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * oaktrail_restore_display_registers - restore lost register state | ||
297 | * @dev: our DRM device | ||
298 | * | ||
299 | * Restore register state that was lost during suspend and resume. | ||
300 | */ | ||
301 | static int oaktrail_restore_display_registers(struct drm_device *dev) | ||
302 | { | ||
303 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
304 | u32 pp_stat; | ||
305 | int i; | ||
306 | |||
307 | /* Display arbitration + watermarks */ | ||
308 | PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); | ||
309 | PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1); | ||
310 | PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2); | ||
311 | PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3); | ||
312 | PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4); | ||
313 | PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5); | ||
314 | PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); | ||
315 | PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); | ||
316 | |||
317 | /* Make sure VGA plane is off. it initializes to on after reset!*/ | ||
318 | PSB_WVDC32(0x80000000, VGACNTRL); | ||
319 | |||
320 | /* set the plls */ | ||
321 | PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0); | ||
322 | PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1); | ||
323 | |||
324 | /* Actually enable it */ | ||
325 | PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A); | ||
326 | DRM_UDELAY(150); | ||
327 | |||
328 | /* Restore mode */ | ||
329 | PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A); | ||
330 | PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A); | ||
331 | PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A); | ||
332 | PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A); | ||
333 | PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A); | ||
334 | PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A); | ||
335 | PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC); | ||
336 | PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A); | ||
337 | |||
338 | /* Restore performance mode*/ | ||
339 | PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE); | ||
340 | |||
341 | /* Enable the pipe*/ | ||
342 | if (dev_priv->iLVDS_enable) | ||
343 | PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF); | ||
344 | |||
345 | /* Set up the plane*/ | ||
346 | PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF); | ||
347 | PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE); | ||
348 | PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF); | ||
349 | |||
350 | /* Enable the plane */ | ||
351 | PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR); | ||
352 | PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF); | ||
353 | |||
354 | /* Enable Cursor A */ | ||
355 | PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR); | ||
356 | PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS); | ||
357 | PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE); | ||
358 | |||
359 | /* Restore palette (gamma) */ | ||
360 | for (i = 0; i < 256; i++) | ||
361 | PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2)); | ||
362 | |||
363 | if (dev_priv->hdmi_priv) | ||
364 | oaktrail_hdmi_restore(dev); | ||
365 | |||
366 | if (dev_priv->iLVDS_enable) { | ||
367 | PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2); | ||
368 | PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/ | ||
369 | PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL); | ||
370 | PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); | ||
371 | PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); | ||
372 | PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL); | ||
373 | PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON); | ||
374 | PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF); | ||
375 | PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE); | ||
376 | PSB_WVDC32(dev_priv->savePP_CONTROL, PP_CONTROL); | ||
377 | } | ||
378 | |||
379 | /* Wait for cycle delay */ | ||
380 | do { | ||
381 | pp_stat = PSB_RVDC32(PP_STATUS); | ||
382 | } while (pp_stat & 0x08000000); | ||
383 | |||
384 | /* Wait for panel power up */ | ||
385 | do { | ||
386 | pp_stat = PSB_RVDC32(PP_STATUS); | ||
387 | } while (pp_stat & 0x10000000); | ||
388 | |||
389 | /* Restore HW overlay */ | ||
390 | PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD); | ||
391 | PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0); | ||
392 | PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1); | ||
393 | PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2); | ||
394 | PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3); | ||
395 | PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4); | ||
396 | PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5); | ||
397 | |||
398 | /* DPST registers */ | ||
399 | PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG, | ||
400 | HISTOGRAM_INT_CONTROL); | ||
401 | PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG, | ||
402 | HISTOGRAM_LOGIC_CONTROL); | ||
403 | PSB_WVDC32(dev_priv->savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | /** | ||
409 | * oaktrail_power_down - power down the display island | ||
410 | * @dev: our DRM device | ||
411 | * | ||
412 | * Power down the display interface of our device | ||
413 | */ | ||
414 | static int oaktrail_power_down(struct drm_device *dev) | ||
415 | { | ||
416 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
417 | u32 pwr_mask ; | ||
418 | u32 pwr_sts; | ||
419 | |||
420 | pwr_mask = PSB_PWRGT_DISPLAY_MASK; | ||
421 | outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC); | ||
422 | |||
423 | while (true) { | ||
424 | pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); | ||
425 | if ((pwr_sts & pwr_mask) == pwr_mask) | ||
426 | break; | ||
427 | else | ||
428 | udelay(10); | ||
429 | } | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * oaktrail_power_up | ||
435 | * | ||
436 | * Restore power to the specified island(s) (powergating) | ||
437 | */ | ||
438 | static int oaktrail_power_up(struct drm_device *dev) | ||
439 | { | ||
440 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
441 | u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK; | ||
442 | u32 pwr_sts, pwr_cnt; | ||
443 | |||
444 | pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); | ||
445 | pwr_cnt &= ~pwr_mask; | ||
446 | outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC)); | ||
447 | |||
448 | while (true) { | ||
449 | pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); | ||
450 | if ((pwr_sts & pwr_mask) == 0) | ||
451 | break; | ||
452 | else | ||
453 | udelay(10); | ||
454 | } | ||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | |||
459 | static void oaktrail_teardown(struct drm_device *dev) | ||
460 | { | ||
461 | oaktrail_hdmi_teardown(dev); | ||
462 | } | ||
463 | |||
464 | const struct psb_ops oaktrail_chip_ops = { | ||
465 | .name = "Oaktrail", | ||
466 | .accel_2d = 1, | ||
467 | .pipes = 2, | ||
468 | .crtcs = 2, | ||
469 | .sgx_offset = MRST_SGX_OFFSET, | ||
470 | |||
471 | .chip_setup = mid_chip_setup, | ||
472 | .chip_teardown = oaktrail_teardown, | ||
473 | .crtc_helper = &oaktrail_helper_funcs, | ||
474 | .crtc_funcs = &psb_intel_crtc_funcs, | ||
475 | |||
476 | .output_init = oaktrail_output_init, | ||
477 | |||
478 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE | ||
479 | .backlight_init = oaktrail_backlight_init, | ||
480 | #endif | ||
481 | |||
482 | .init_pm = oaktrail_init_pm, | ||
483 | .save_regs = oaktrail_save_display_registers, | ||
484 | .restore_regs = oaktrail_restore_display_registers, | ||
485 | .power_down = oaktrail_power_down, | ||
486 | .power_up = oaktrail_power_up, | ||
487 | |||
488 | .i2c_bus = 1, | ||
489 | }; | ||
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c new file mode 100644 index 000000000000..6f423c01c209 --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c | |||
@@ -0,0 +1,852 @@ | |||
1 | /* | ||
2 | * Copyright © 2010 Intel Corporation | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice (including the next | ||
12 | * paragraph) shall be included in all copies or substantial portions of the | ||
13 | * Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
21 | * DEALINGS IN THE SOFTWARE. | ||
22 | * | ||
23 | * Authors: | ||
24 | * Li Peng <peng.li@intel.com> | ||
25 | */ | ||
26 | |||
27 | #include <drm/drmP.h> | ||
28 | #include <drm/drm.h> | ||
29 | #include "psb_intel_drv.h" | ||
30 | #include "psb_intel_reg.h" | ||
31 | #include "psb_drv.h" | ||
32 | |||
33 | #define HDMI_READ(reg) readl(hdmi_dev->regs + (reg)) | ||
34 | #define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg)) | ||
35 | |||
36 | #define HDMI_HCR 0x1000 | ||
37 | #define HCR_ENABLE_HDCP (1 << 5) | ||
38 | #define HCR_ENABLE_AUDIO (1 << 2) | ||
39 | #define HCR_ENABLE_PIXEL (1 << 1) | ||
40 | #define HCR_ENABLE_TMDS (1 << 0) | ||
41 | |||
42 | #define HDMI_HICR 0x1004 | ||
43 | #define HDMI_HSR 0x1008 | ||
44 | #define HDMI_HISR 0x100C | ||
45 | #define HDMI_DETECT_HDP (1 << 0) | ||
46 | |||
47 | #define HDMI_VIDEO_REG 0x3000 | ||
48 | #define HDMI_UNIT_EN (1 << 7) | ||
49 | #define HDMI_MODE_OUTPUT (1 << 0) | ||
50 | #define HDMI_HBLANK_A 0x3100 | ||
51 | |||
52 | #define HDMI_AUDIO_CTRL 0x4000 | ||
53 | #define HDMI_ENABLE_AUDIO (1 << 0) | ||
54 | |||
55 | #define PCH_HTOTAL_B 0x3100 | ||
56 | #define PCH_HBLANK_B 0x3104 | ||
57 | #define PCH_HSYNC_B 0x3108 | ||
58 | #define PCH_VTOTAL_B 0x310C | ||
59 | #define PCH_VBLANK_B 0x3110 | ||
60 | #define PCH_VSYNC_B 0x3114 | ||
61 | #define PCH_PIPEBSRC 0x311C | ||
62 | |||
63 | #define PCH_PIPEB_DSL 0x3800 | ||
64 | #define PCH_PIPEB_SLC 0x3804 | ||
65 | #define PCH_PIPEBCONF 0x3808 | ||
66 | #define PCH_PIPEBSTAT 0x3824 | ||
67 | |||
68 | #define CDVO_DFT 0x5000 | ||
69 | #define CDVO_SLEWRATE 0x5004 | ||
70 | #define CDVO_STRENGTH 0x5008 | ||
71 | #define CDVO_RCOMP 0x500C | ||
72 | |||
73 | #define DPLL_CTRL 0x6000 | ||
74 | #define DPLL_PDIV_SHIFT 16 | ||
75 | #define DPLL_PDIV_MASK (0xf << 16) | ||
76 | #define DPLL_PWRDN (1 << 4) | ||
77 | #define DPLL_RESET (1 << 3) | ||
78 | #define DPLL_FASTEN (1 << 2) | ||
79 | #define DPLL_ENSTAT (1 << 1) | ||
80 | #define DPLL_DITHEN (1 << 0) | ||
81 | |||
82 | #define DPLL_DIV_CTRL 0x6004 | ||
83 | #define DPLL_CLKF_MASK 0xffffffc0 | ||
84 | #define DPLL_CLKR_MASK (0x3f) | ||
85 | |||
86 | #define DPLL_CLK_ENABLE 0x6008 | ||
87 | #define DPLL_EN_DISP (1 << 31) | ||
88 | #define DPLL_SEL_HDMI (1 << 8) | ||
89 | #define DPLL_EN_HDMI (1 << 1) | ||
90 | #define DPLL_EN_VGA (1 << 0) | ||
91 | |||
92 | #define DPLL_ADJUST 0x600C | ||
93 | #define DPLL_STATUS 0x6010 | ||
94 | #define DPLL_UPDATE 0x6014 | ||
95 | #define DPLL_DFT 0x6020 | ||
96 | |||
97 | struct intel_range { | ||
98 | int min, max; | ||
99 | }; | ||
100 | |||
101 | struct oaktrail_hdmi_limit { | ||
102 | struct intel_range vco, np, nr, nf; | ||
103 | }; | ||
104 | |||
105 | struct oaktrail_hdmi_clock { | ||
106 | int np; | ||
107 | int nr; | ||
108 | int nf; | ||
109 | int dot; | ||
110 | }; | ||
111 | |||
112 | #define VCO_MIN 320000 | ||
113 | #define VCO_MAX 1650000 | ||
114 | #define NP_MIN 1 | ||
115 | #define NP_MAX 15 | ||
116 | #define NR_MIN 1 | ||
117 | #define NR_MAX 64 | ||
118 | #define NF_MIN 2 | ||
119 | #define NF_MAX 4095 | ||
120 | |||
121 | static const struct oaktrail_hdmi_limit oaktrail_hdmi_limit = { | ||
122 | .vco = { .min = VCO_MIN, .max = VCO_MAX }, | ||
123 | .np = { .min = NP_MIN, .max = NP_MAX }, | ||
124 | .nr = { .min = NR_MIN, .max = NR_MAX }, | ||
125 | .nf = { .min = NF_MIN, .max = NF_MAX }, | ||
126 | }; | ||
127 | |||
128 | static void wait_for_vblank(struct drm_device *dev) | ||
129 | { | ||
130 | /* FIXME: Can we do this as a sleep ? */ | ||
131 | /* Wait for 20ms, i.e. one cycle at 50hz. */ | ||
132 | mdelay(20); | ||
133 | } | ||
134 | |||
135 | static void scu_busy_loop(void *scu_base) | ||
136 | { | ||
137 | u32 status = 0; | ||
138 | u32 loop_count = 0; | ||
139 | |||
140 | status = readl(scu_base + 0x04); | ||
141 | while (status & 1) { | ||
142 | udelay(1); /* scu processing time is in few u secods */ | ||
143 | status = readl(scu_base + 0x04); | ||
144 | loop_count++; | ||
145 | /* break if scu doesn't reset busy bit after huge retry */ | ||
146 | if (loop_count > 1000) { | ||
147 | DRM_DEBUG_KMS("SCU IPC timed out"); | ||
148 | return; | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | static void oaktrail_hdmi_reset(struct drm_device *dev) | ||
154 | { | ||
155 | void *base; | ||
156 | /* FIXME: at least make these defines */ | ||
157 | unsigned int scu_ipc_mmio = 0xff11c000; | ||
158 | int scu_len = 1024; | ||
159 | |||
160 | base = ioremap((resource_size_t)scu_ipc_mmio, scu_len); | ||
161 | if (base == NULL) { | ||
162 | DRM_ERROR("failed to map SCU mmio\n"); | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | /* scu ipc: assert hdmi controller reset */ | ||
167 | writel(0xff11d118, base + 0x0c); | ||
168 | writel(0x7fffffdf, base + 0x80); | ||
169 | writel(0x42005, base + 0x0); | ||
170 | scu_busy_loop(base); | ||
171 | |||
172 | /* scu ipc: de-assert hdmi controller reset */ | ||
173 | writel(0xff11d118, base + 0x0c); | ||
174 | writel(0x7fffffff, base + 0x80); | ||
175 | writel(0x42005, base + 0x0); | ||
176 | scu_busy_loop(base); | ||
177 | |||
178 | iounmap(base); | ||
179 | } | ||
180 | |||
181 | static void oaktrail_hdmi_audio_enable(struct drm_device *dev) | ||
182 | { | ||
183 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
184 | struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
185 | |||
186 | HDMI_WRITE(HDMI_HCR, 0x67); | ||
187 | HDMI_READ(HDMI_HCR); | ||
188 | |||
189 | HDMI_WRITE(0x51a8, 0x10); | ||
190 | HDMI_READ(0x51a8); | ||
191 | |||
192 | HDMI_WRITE(HDMI_AUDIO_CTRL, 0x1); | ||
193 | HDMI_READ(HDMI_AUDIO_CTRL); | ||
194 | } | ||
195 | |||
196 | static void oaktrail_hdmi_audio_disable(struct drm_device *dev) | ||
197 | { | ||
198 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
199 | struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
200 | |||
201 | HDMI_WRITE(0x51a8, 0x0); | ||
202 | HDMI_READ(0x51a8); | ||
203 | |||
204 | HDMI_WRITE(HDMI_AUDIO_CTRL, 0x0); | ||
205 | HDMI_READ(HDMI_AUDIO_CTRL); | ||
206 | |||
207 | HDMI_WRITE(HDMI_HCR, 0x47); | ||
208 | HDMI_READ(HDMI_HCR); | ||
209 | } | ||
210 | |||
211 | void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode) | ||
212 | { | ||
213 | struct drm_device *dev = crtc->dev; | ||
214 | u32 temp; | ||
215 | |||
216 | switch (mode) { | ||
217 | case DRM_MODE_DPMS_OFF: | ||
218 | /* Disable VGACNTRL */ | ||
219 | REG_WRITE(VGACNTRL, 0x80000000); | ||
220 | |||
221 | /* Disable plane */ | ||
222 | temp = REG_READ(DSPBCNTR); | ||
223 | if ((temp & DISPLAY_PLANE_ENABLE) != 0) { | ||
224 | REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE); | ||
225 | REG_READ(DSPBCNTR); | ||
226 | /* Flush the plane changes */ | ||
227 | REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); | ||
228 | REG_READ(DSPBSURF); | ||
229 | } | ||
230 | |||
231 | /* Disable pipe B */ | ||
232 | temp = REG_READ(PIPEBCONF); | ||
233 | if ((temp & PIPEACONF_ENABLE) != 0) { | ||
234 | REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE); | ||
235 | REG_READ(PIPEBCONF); | ||
236 | } | ||
237 | |||
238 | /* Disable LNW Pipes, etc */ | ||
239 | temp = REG_READ(PCH_PIPEBCONF); | ||
240 | if ((temp & PIPEACONF_ENABLE) != 0) { | ||
241 | REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE); | ||
242 | REG_READ(PCH_PIPEBCONF); | ||
243 | } | ||
244 | /* wait for pipe off */ | ||
245 | udelay(150); | ||
246 | /* Disable dpll */ | ||
247 | temp = REG_READ(DPLL_CTRL); | ||
248 | if ((temp & DPLL_PWRDN) == 0) { | ||
249 | REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET)); | ||
250 | REG_WRITE(DPLL_STATUS, 0x1); | ||
251 | } | ||
252 | /* wait for dpll off */ | ||
253 | udelay(150); | ||
254 | break; | ||
255 | case DRM_MODE_DPMS_ON: | ||
256 | case DRM_MODE_DPMS_STANDBY: | ||
257 | case DRM_MODE_DPMS_SUSPEND: | ||
258 | /* Enable dpll */ | ||
259 | temp = REG_READ(DPLL_CTRL); | ||
260 | if ((temp & DPLL_PWRDN) != 0) { | ||
261 | REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET)); | ||
262 | temp = REG_READ(DPLL_CLK_ENABLE); | ||
263 | REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI); | ||
264 | REG_READ(DPLL_CLK_ENABLE); | ||
265 | } | ||
266 | /* wait for dpll warm up */ | ||
267 | udelay(150); | ||
268 | |||
269 | /* Enable pipe B */ | ||
270 | temp = REG_READ(PIPEBCONF); | ||
271 | if ((temp & PIPEACONF_ENABLE) == 0) { | ||
272 | REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE); | ||
273 | REG_READ(PIPEBCONF); | ||
274 | } | ||
275 | |||
276 | /* Enable LNW Pipe B */ | ||
277 | temp = REG_READ(PCH_PIPEBCONF); | ||
278 | if ((temp & PIPEACONF_ENABLE) == 0) { | ||
279 | REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE); | ||
280 | REG_READ(PCH_PIPEBCONF); | ||
281 | } | ||
282 | wait_for_vblank(dev); | ||
283 | |||
284 | /* Enable plane */ | ||
285 | temp = REG_READ(DSPBCNTR); | ||
286 | if ((temp & DISPLAY_PLANE_ENABLE) == 0) { | ||
287 | REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE); | ||
288 | /* Flush the plane changes */ | ||
289 | REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); | ||
290 | REG_READ(DSPBSURF); | ||
291 | } | ||
292 | psb_intel_crtc_load_lut(crtc); | ||
293 | } | ||
294 | /* DSPARB */ | ||
295 | REG_WRITE(DSPARB, 0x00003fbf); | ||
296 | /* FW1 */ | ||
297 | REG_WRITE(0x70034, 0x3f880a0a); | ||
298 | /* FW2 */ | ||
299 | REG_WRITE(0x70038, 0x0b060808); | ||
300 | /* FW4 */ | ||
301 | REG_WRITE(0x70050, 0x08030404); | ||
302 | /* FW5 */ | ||
303 | REG_WRITE(0x70054, 0x04040404); | ||
304 | /* LNC Chicken Bits */ | ||
305 | REG_WRITE(0x70400, 0x4000); | ||
306 | } | ||
307 | |||
308 | |||
309 | static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode) | ||
310 | { | ||
311 | static int dpms_mode = -1; | ||
312 | |||
313 | struct drm_device *dev = encoder->dev; | ||
314 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
315 | struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
316 | u32 temp; | ||
317 | |||
318 | if (dpms_mode == mode) | ||
319 | return; | ||
320 | |||
321 | if (mode != DRM_MODE_DPMS_ON) | ||
322 | temp = 0x0; | ||
323 | else | ||
324 | temp = 0x99; | ||
325 | |||
326 | dpms_mode = mode; | ||
327 | HDMI_WRITE(HDMI_VIDEO_REG, temp); | ||
328 | } | ||
329 | |||
330 | static unsigned int htotal_calculate(struct drm_display_mode *mode) | ||
331 | { | ||
332 | u32 htotal, new_crtc_htotal; | ||
333 | |||
334 | htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16); | ||
335 | |||
336 | /* | ||
337 | * 1024 x 768 new_crtc_htotal = 0x1024; | ||
338 | * 1280 x 1024 new_crtc_htotal = 0x0c34; | ||
339 | */ | ||
340 | new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock; | ||
341 | |||
342 | return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16); | ||
343 | } | ||
344 | |||
345 | static void oaktrail_hdmi_find_dpll(struct drm_crtc *crtc, int target, | ||
346 | int refclk, struct oaktrail_hdmi_clock *best_clock) | ||
347 | { | ||
348 | int np_min, np_max, nr_min, nr_max; | ||
349 | int np, nr, nf; | ||
350 | |||
351 | np_min = DIV_ROUND_UP(oaktrail_hdmi_limit.vco.min, target * 10); | ||
352 | np_max = oaktrail_hdmi_limit.vco.max / (target * 10); | ||
353 | if (np_min < oaktrail_hdmi_limit.np.min) | ||
354 | np_min = oaktrail_hdmi_limit.np.min; | ||
355 | if (np_max > oaktrail_hdmi_limit.np.max) | ||
356 | np_max = oaktrail_hdmi_limit.np.max; | ||
357 | |||
358 | nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max)); | ||
359 | nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min)); | ||
360 | if (nr_min < oaktrail_hdmi_limit.nr.min) | ||
361 | nr_min = oaktrail_hdmi_limit.nr.min; | ||
362 | if (nr_max > oaktrail_hdmi_limit.nr.max) | ||
363 | nr_max = oaktrail_hdmi_limit.nr.max; | ||
364 | |||
365 | np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max)); | ||
366 | nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np)); | ||
367 | nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk); | ||
368 | DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf); | ||
369 | |||
370 | /* | ||
371 | * 1024 x 768 np = 1; nr = 0x26; nf = 0x0fd8000; | ||
372 | * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000; | ||
373 | */ | ||
374 | best_clock->np = np; | ||
375 | best_clock->nr = nr - 1; | ||
376 | best_clock->nf = (nf << 14); | ||
377 | } | ||
378 | |||
379 | int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, | ||
380 | struct drm_display_mode *mode, | ||
381 | struct drm_display_mode *adjusted_mode, | ||
382 | int x, int y, | ||
383 | struct drm_framebuffer *old_fb) | ||
384 | { | ||
385 | struct drm_device *dev = crtc->dev; | ||
386 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
387 | struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
388 | int pipe = 1; | ||
389 | int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; | ||
390 | int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; | ||
391 | int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; | ||
392 | int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; | ||
393 | int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; | ||
394 | int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; | ||
395 | int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; | ||
396 | int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; | ||
397 | int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; | ||
398 | int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; | ||
399 | int refclk; | ||
400 | struct oaktrail_hdmi_clock clock; | ||
401 | u32 dspcntr, pipeconf, dpll, temp; | ||
402 | int dspcntr_reg = DSPBCNTR; | ||
403 | |||
404 | /* Disable the VGA plane that we never use */ | ||
405 | REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); | ||
406 | |||
407 | /* XXX: Disable the panel fitter if it was on our pipe */ | ||
408 | |||
409 | /* Disable dpll if necessary */ | ||
410 | dpll = REG_READ(DPLL_CTRL); | ||
411 | if ((dpll & DPLL_PWRDN) == 0) { | ||
412 | REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET)); | ||
413 | REG_WRITE(DPLL_DIV_CTRL, 0x00000000); | ||
414 | REG_WRITE(DPLL_STATUS, 0x1); | ||
415 | } | ||
416 | udelay(150); | ||
417 | |||
418 | /* reset controller: FIXME - can we sort out the ioremap mess ? */ | ||
419 | iounmap(hdmi_dev->regs); | ||
420 | oaktrail_hdmi_reset(dev); | ||
421 | |||
422 | /* program and enable dpll */ | ||
423 | refclk = 25000; | ||
424 | oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock); | ||
425 | |||
426 | /* Setting DPLL */ | ||
427 | dpll = REG_READ(DPLL_CTRL); | ||
428 | dpll &= ~DPLL_PDIV_MASK; | ||
429 | dpll &= ~(DPLL_PWRDN | DPLL_RESET); | ||
430 | REG_WRITE(DPLL_CTRL, 0x00000008); | ||
431 | REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr)); | ||
432 | REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1)); | ||
433 | REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN)); | ||
434 | REG_WRITE(DPLL_UPDATE, 0x80000000); | ||
435 | REG_WRITE(DPLL_CLK_ENABLE, 0x80050102); | ||
436 | udelay(150); | ||
437 | |||
438 | hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); | ||
439 | if (hdmi_dev->regs == NULL) { | ||
440 | DRM_ERROR("failed to do hdmi mmio mapping\n"); | ||
441 | return -ENOMEM; | ||
442 | } | ||
443 | |||
444 | /* configure HDMI */ | ||
445 | HDMI_WRITE(0x1004, 0x1fd); | ||
446 | HDMI_WRITE(0x2000, 0x1); | ||
447 | HDMI_WRITE(0x2008, 0x0); | ||
448 | HDMI_WRITE(0x3130, 0x8); | ||
449 | HDMI_WRITE(0x101c, 0x1800810); | ||
450 | |||
451 | temp = htotal_calculate(adjusted_mode); | ||
452 | REG_WRITE(htot_reg, temp); | ||
453 | REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); | ||
454 | REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); | ||
455 | REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); | ||
456 | REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); | ||
457 | REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); | ||
458 | REG_WRITE(pipesrc_reg, | ||
459 | ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); | ||
460 | |||
461 | REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); | ||
462 | REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); | ||
463 | REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); | ||
464 | REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); | ||
465 | REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); | ||
466 | REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); | ||
467 | REG_WRITE(PCH_PIPEBSRC, | ||
468 | ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); | ||
469 | |||
470 | temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start; | ||
471 | HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) | temp); | ||
472 | |||
473 | REG_WRITE(dspsize_reg, | ||
474 | ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); | ||
475 | REG_WRITE(dsppos_reg, 0); | ||
476 | |||
477 | /* Flush the plane changes */ | ||
478 | { | ||
479 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
480 | crtc_funcs->mode_set_base(crtc, x, y, old_fb); | ||
481 | } | ||
482 | |||
483 | /* Set up the display plane register */ | ||
484 | dspcntr = REG_READ(dspcntr_reg); | ||
485 | dspcntr |= DISPPLANE_GAMMA_ENABLE; | ||
486 | dspcntr |= DISPPLANE_SEL_PIPE_B; | ||
487 | dspcntr |= DISPLAY_PLANE_ENABLE; | ||
488 | |||
489 | /* setup pipeconf */ | ||
490 | pipeconf = REG_READ(pipeconf_reg); | ||
491 | pipeconf |= PIPEACONF_ENABLE; | ||
492 | |||
493 | REG_WRITE(pipeconf_reg, pipeconf); | ||
494 | REG_READ(pipeconf_reg); | ||
495 | |||
496 | REG_WRITE(PCH_PIPEBCONF, pipeconf); | ||
497 | REG_READ(PCH_PIPEBCONF); | ||
498 | wait_for_vblank(dev); | ||
499 | |||
500 | REG_WRITE(dspcntr_reg, dspcntr); | ||
501 | wait_for_vblank(dev); | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int oaktrail_hdmi_mode_valid(struct drm_connector *connector, | ||
507 | struct drm_display_mode *mode) | ||
508 | { | ||
509 | if (mode->clock > 165000) | ||
510 | return MODE_CLOCK_HIGH; | ||
511 | if (mode->clock < 20000) | ||
512 | return MODE_CLOCK_LOW; | ||
513 | |||
514 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
515 | return MODE_NO_DBLESCAN; | ||
516 | |||
517 | return MODE_OK; | ||
518 | } | ||
519 | |||
520 | static bool oaktrail_hdmi_mode_fixup(struct drm_encoder *encoder, | ||
521 | struct drm_display_mode *mode, | ||
522 | struct drm_display_mode *adjusted_mode) | ||
523 | { | ||
524 | return true; | ||
525 | } | ||
526 | |||
527 | static enum drm_connector_status | ||
528 | oaktrail_hdmi_detect(struct drm_connector *connector, bool force) | ||
529 | { | ||
530 | enum drm_connector_status status; | ||
531 | struct drm_device *dev = connector->dev; | ||
532 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
533 | struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
534 | u32 temp; | ||
535 | |||
536 | temp = HDMI_READ(HDMI_HSR); | ||
537 | DRM_DEBUG_KMS("HDMI_HSR %x\n", temp); | ||
538 | |||
539 | if ((temp & HDMI_DETECT_HDP) != 0) | ||
540 | status = connector_status_connected; | ||
541 | else | ||
542 | status = connector_status_disconnected; | ||
543 | |||
544 | return status; | ||
545 | } | ||
546 | |||
547 | static const unsigned char raw_edid[] = { | ||
548 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x2f, 0xa0, | ||
549 | 0x53, 0x55, 0x33, 0x30, 0x16, 0x13, 0x01, 0x03, 0x0e, 0x3a, 0x24, 0x78, | ||
550 | 0xea, 0xe9, 0xf5, 0xac, 0x51, 0x30, 0xb4, 0x25, 0x11, 0x50, 0x54, 0xa5, | ||
551 | 0x4b, 0x00, 0x81, 0x80, 0xa9, 0x40, 0x71, 0x4f, 0xb3, 0x00, 0x01, 0x01, | ||
552 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, | ||
553 | 0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x46, 0x6c, 0x21, 0x00, 0x00, 0x1a, | ||
554 | 0x00, 0x00, 0x00, 0xff, 0x00, 0x47, 0x4e, 0x37, 0x32, 0x31, 0x39, 0x35, | ||
555 | 0x52, 0x30, 0x33, 0x55, 0x53, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44, | ||
556 | 0x45, 0x4c, 0x4c, 0x20, 0x32, 0x37, 0x30, 0x39, 0x57, 0x0a, 0x20, 0x20, | ||
557 | 0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4c, 0x1e, 0x53, 0x11, 0x00, 0x0a, | ||
558 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x8d | ||
559 | }; | ||
560 | |||
561 | static int oaktrail_hdmi_get_modes(struct drm_connector *connector) | ||
562 | { | ||
563 | struct drm_device *dev = connector->dev; | ||
564 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
565 | struct i2c_adapter *i2c_adap; | ||
566 | struct edid *edid; | ||
567 | struct drm_display_mode *mode, *t; | ||
568 | int i = 0, ret = 0; | ||
569 | |||
570 | i2c_adap = i2c_get_adapter(3); | ||
571 | if (i2c_adap == NULL) { | ||
572 | DRM_ERROR("No ddc adapter available!\n"); | ||
573 | edid = (struct edid *)raw_edid; | ||
574 | } else { | ||
575 | edid = (struct edid *)raw_edid; | ||
576 | /* FIXME ? edid = drm_get_edid(connector, i2c_adap); */ | ||
577 | } | ||
578 | |||
579 | if (edid) { | ||
580 | drm_mode_connector_update_edid_property(connector, edid); | ||
581 | ret = drm_add_edid_modes(connector, edid); | ||
582 | connector->display_info.raw_edid = NULL; | ||
583 | } | ||
584 | |||
585 | /* | ||
586 | * prune modes that require frame buffer bigger than stolen mem | ||
587 | */ | ||
588 | list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { | ||
589 | if ((mode->hdisplay * mode->vdisplay * 4) >= dev_priv->vram_stolen_size) { | ||
590 | i++; | ||
591 | drm_mode_remove(connector, mode); | ||
592 | } | ||
593 | } | ||
594 | return ret - i; | ||
595 | } | ||
596 | |||
597 | static void oaktrail_hdmi_mode_set(struct drm_encoder *encoder, | ||
598 | struct drm_display_mode *mode, | ||
599 | struct drm_display_mode *adjusted_mode) | ||
600 | { | ||
601 | struct drm_device *dev = encoder->dev; | ||
602 | |||
603 | oaktrail_hdmi_audio_enable(dev); | ||
604 | return; | ||
605 | } | ||
606 | |||
607 | static void oaktrail_hdmi_destroy(struct drm_connector *connector) | ||
608 | { | ||
609 | return; | ||
610 | } | ||
611 | |||
612 | static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = { | ||
613 | .dpms = oaktrail_hdmi_dpms, | ||
614 | .mode_fixup = oaktrail_hdmi_mode_fixup, | ||
615 | .prepare = psb_intel_encoder_prepare, | ||
616 | .mode_set = oaktrail_hdmi_mode_set, | ||
617 | .commit = psb_intel_encoder_commit, | ||
618 | }; | ||
619 | |||
620 | static const struct drm_connector_helper_funcs | ||
621 | oaktrail_hdmi_connector_helper_funcs = { | ||
622 | .get_modes = oaktrail_hdmi_get_modes, | ||
623 | .mode_valid = oaktrail_hdmi_mode_valid, | ||
624 | .best_encoder = psb_intel_best_encoder, | ||
625 | }; | ||
626 | |||
627 | static const struct drm_connector_funcs oaktrail_hdmi_connector_funcs = { | ||
628 | .dpms = drm_helper_connector_dpms, | ||
629 | .detect = oaktrail_hdmi_detect, | ||
630 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
631 | .destroy = oaktrail_hdmi_destroy, | ||
632 | }; | ||
633 | |||
634 | static void oaktrail_hdmi_enc_destroy(struct drm_encoder *encoder) | ||
635 | { | ||
636 | drm_encoder_cleanup(encoder); | ||
637 | } | ||
638 | |||
639 | static const struct drm_encoder_funcs oaktrail_hdmi_enc_funcs = { | ||
640 | .destroy = oaktrail_hdmi_enc_destroy, | ||
641 | }; | ||
642 | |||
643 | void oaktrail_hdmi_init(struct drm_device *dev, | ||
644 | struct psb_intel_mode_device *mode_dev) | ||
645 | { | ||
646 | struct psb_intel_output *psb_intel_output; | ||
647 | struct drm_connector *connector; | ||
648 | struct drm_encoder *encoder; | ||
649 | |||
650 | psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); | ||
651 | if (!psb_intel_output) | ||
652 | return; | ||
653 | |||
654 | psb_intel_output->mode_dev = mode_dev; | ||
655 | connector = &psb_intel_output->base; | ||
656 | encoder = &psb_intel_output->enc; | ||
657 | drm_connector_init(dev, &psb_intel_output->base, | ||
658 | &oaktrail_hdmi_connector_funcs, | ||
659 | DRM_MODE_CONNECTOR_DVID); | ||
660 | |||
661 | drm_encoder_init(dev, &psb_intel_output->enc, | ||
662 | &oaktrail_hdmi_enc_funcs, | ||
663 | DRM_MODE_ENCODER_TMDS); | ||
664 | |||
665 | drm_mode_connector_attach_encoder(&psb_intel_output->base, | ||
666 | &psb_intel_output->enc); | ||
667 | |||
668 | psb_intel_output->type = INTEL_OUTPUT_HDMI; | ||
669 | drm_encoder_helper_add(encoder, &oaktrail_hdmi_helper_funcs); | ||
670 | drm_connector_helper_add(connector, &oaktrail_hdmi_connector_helper_funcs); | ||
671 | |||
672 | connector->display_info.subpixel_order = SubPixelHorizontalRGB; | ||
673 | connector->interlace_allowed = false; | ||
674 | connector->doublescan_allowed = false; | ||
675 | drm_sysfs_connector_add(connector); | ||
676 | |||
677 | return; | ||
678 | } | ||
679 | |||
680 | static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = { | ||
681 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) }, | ||
682 | {} | ||
683 | }; | ||
684 | |||
685 | void oaktrail_hdmi_setup(struct drm_device *dev) | ||
686 | { | ||
687 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
688 | struct pci_dev *pdev; | ||
689 | struct oaktrail_hdmi_dev *hdmi_dev; | ||
690 | int ret; | ||
691 | |||
692 | pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x080d, NULL); | ||
693 | if (!pdev) | ||
694 | return; | ||
695 | |||
696 | hdmi_dev = kzalloc(sizeof(struct oaktrail_hdmi_dev), GFP_KERNEL); | ||
697 | if (!hdmi_dev) { | ||
698 | dev_err(dev->dev, "failed to allocate memory\n"); | ||
699 | goto out; | ||
700 | } | ||
701 | |||
702 | |||
703 | ret = pci_enable_device(pdev); | ||
704 | if (ret) { | ||
705 | dev_err(dev->dev, "failed to enable hdmi controller\n"); | ||
706 | goto free; | ||
707 | } | ||
708 | |||
709 | hdmi_dev->mmio = pci_resource_start(pdev, 0); | ||
710 | hdmi_dev->mmio_len = pci_resource_len(pdev, 0); | ||
711 | hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); | ||
712 | if (!hdmi_dev->regs) { | ||
713 | dev_err(dev->dev, "failed to map hdmi mmio\n"); | ||
714 | goto free; | ||
715 | } | ||
716 | |||
717 | hdmi_dev->dev = pdev; | ||
718 | pci_set_drvdata(pdev, hdmi_dev); | ||
719 | |||
720 | /* Initialize i2c controller */ | ||
721 | ret = oaktrail_hdmi_i2c_init(hdmi_dev->dev); | ||
722 | if (ret) | ||
723 | dev_err(dev->dev, "HDMI I2C initialization failed\n"); | ||
724 | |||
725 | dev_priv->hdmi_priv = hdmi_dev; | ||
726 | oaktrail_hdmi_audio_disable(dev); | ||
727 | return; | ||
728 | |||
729 | free: | ||
730 | kfree(hdmi_dev); | ||
731 | out: | ||
732 | return; | ||
733 | } | ||
734 | |||
735 | void oaktrail_hdmi_teardown(struct drm_device *dev) | ||
736 | { | ||
737 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
738 | struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
739 | struct pci_dev *pdev; | ||
740 | |||
741 | if (hdmi_dev) { | ||
742 | pdev = hdmi_dev->dev; | ||
743 | pci_set_drvdata(pdev, NULL); | ||
744 | oaktrail_hdmi_i2c_exit(pdev); | ||
745 | iounmap(hdmi_dev->regs); | ||
746 | kfree(hdmi_dev); | ||
747 | pci_dev_put(pdev); | ||
748 | } | ||
749 | } | ||
750 | |||
751 | /* save HDMI register state */ | ||
752 | void oaktrail_hdmi_save(struct drm_device *dev) | ||
753 | { | ||
754 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
755 | struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
756 | int i; | ||
757 | |||
758 | /* dpll */ | ||
759 | hdmi_dev->saveDPLL_CTRL = PSB_RVDC32(DPLL_CTRL); | ||
760 | hdmi_dev->saveDPLL_DIV_CTRL = PSB_RVDC32(DPLL_DIV_CTRL); | ||
761 | hdmi_dev->saveDPLL_ADJUST = PSB_RVDC32(DPLL_ADJUST); | ||
762 | hdmi_dev->saveDPLL_UPDATE = PSB_RVDC32(DPLL_UPDATE); | ||
763 | hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE); | ||
764 | |||
765 | /* pipe B */ | ||
766 | dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF); | ||
767 | dev_priv->savePIPEBSRC = PSB_RVDC32(PIPEBSRC); | ||
768 | dev_priv->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B); | ||
769 | dev_priv->saveHBLANK_B = PSB_RVDC32(HBLANK_B); | ||
770 | dev_priv->saveHSYNC_B = PSB_RVDC32(HSYNC_B); | ||
771 | dev_priv->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B); | ||
772 | dev_priv->saveVBLANK_B = PSB_RVDC32(VBLANK_B); | ||
773 | dev_priv->saveVSYNC_B = PSB_RVDC32(VSYNC_B); | ||
774 | |||
775 | hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF); | ||
776 | hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC); | ||
777 | hdmi_dev->savePCH_HTOTAL_B = PSB_RVDC32(PCH_HTOTAL_B); | ||
778 | hdmi_dev->savePCH_HBLANK_B = PSB_RVDC32(PCH_HBLANK_B); | ||
779 | hdmi_dev->savePCH_HSYNC_B = PSB_RVDC32(PCH_HSYNC_B); | ||
780 | hdmi_dev->savePCH_VTOTAL_B = PSB_RVDC32(PCH_VTOTAL_B); | ||
781 | hdmi_dev->savePCH_VBLANK_B = PSB_RVDC32(PCH_VBLANK_B); | ||
782 | hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B); | ||
783 | |||
784 | /* plane */ | ||
785 | dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR); | ||
786 | dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE); | ||
787 | dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE); | ||
788 | dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF); | ||
789 | dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF); | ||
790 | dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF); | ||
791 | |||
792 | /* cursor B */ | ||
793 | dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); | ||
794 | dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); | ||
795 | dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); | ||
796 | |||
797 | /* save palette */ | ||
798 | for (i = 0; i < 256; i++) | ||
799 | dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2)); | ||
800 | } | ||
801 | |||
802 | /* restore HDMI register state */ | ||
803 | void oaktrail_hdmi_restore(struct drm_device *dev) | ||
804 | { | ||
805 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
806 | struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
807 | int i; | ||
808 | |||
809 | /* dpll */ | ||
810 | PSB_WVDC32(hdmi_dev->saveDPLL_CTRL, DPLL_CTRL); | ||
811 | PSB_WVDC32(hdmi_dev->saveDPLL_DIV_CTRL, DPLL_DIV_CTRL); | ||
812 | PSB_WVDC32(hdmi_dev->saveDPLL_ADJUST, DPLL_ADJUST); | ||
813 | PSB_WVDC32(hdmi_dev->saveDPLL_UPDATE, DPLL_UPDATE); | ||
814 | PSB_WVDC32(hdmi_dev->saveDPLL_CLK_ENABLE, DPLL_CLK_ENABLE); | ||
815 | DRM_UDELAY(150); | ||
816 | |||
817 | /* pipe */ | ||
818 | PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC); | ||
819 | PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B); | ||
820 | PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B); | ||
821 | PSB_WVDC32(dev_priv->saveHSYNC_B, HSYNC_B); | ||
822 | PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B); | ||
823 | PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B); | ||
824 | PSB_WVDC32(dev_priv->saveVSYNC_B, VSYNC_B); | ||
825 | |||
826 | PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC); | ||
827 | PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B); | ||
828 | PSB_WVDC32(hdmi_dev->savePCH_HBLANK_B, PCH_HBLANK_B); | ||
829 | PSB_WVDC32(hdmi_dev->savePCH_HSYNC_B, PCH_HSYNC_B); | ||
830 | PSB_WVDC32(hdmi_dev->savePCH_VTOTAL_B, PCH_VTOTAL_B); | ||
831 | PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B); | ||
832 | PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B); | ||
833 | |||
834 | PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF); | ||
835 | PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF); | ||
836 | |||
837 | /* plane */ | ||
838 | PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF); | ||
839 | PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE); | ||
840 | PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF); | ||
841 | PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR); | ||
842 | PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF); | ||
843 | |||
844 | /* cursor B */ | ||
845 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR); | ||
846 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS); | ||
847 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE); | ||
848 | |||
849 | /* restore palette */ | ||
850 | for (i = 0; i < 256; i++) | ||
851 | PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2)); | ||
852 | } | ||
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c new file mode 100644 index 000000000000..d454e6f615a8 --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c | |||
@@ -0,0 +1,327 @@ | |||
1 | /* | ||
2 | * Copyright © 2010 Intel Corporation | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice (including the next | ||
12 | * paragraph) shall be included in all copies or substantial portions of the | ||
13 | * Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
21 | * DEALINGS IN THE SOFTWARE. | ||
22 | * | ||
23 | * Authors: | ||
24 | * Li Peng <peng.li@intel.com> | ||
25 | */ | ||
26 | |||
27 | #include <linux/mutex.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/i2c.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include "psb_drv.h" | ||
33 | |||
34 | #define HDMI_READ(reg) readl(hdmi_dev->regs + (reg)) | ||
35 | #define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg)) | ||
36 | |||
37 | #define HDMI_HCR 0x1000 | ||
38 | #define HCR_DETECT_HDP (1 << 6) | ||
39 | #define HCR_ENABLE_HDCP (1 << 5) | ||
40 | #define HCR_ENABLE_AUDIO (1 << 2) | ||
41 | #define HCR_ENABLE_PIXEL (1 << 1) | ||
42 | #define HCR_ENABLE_TMDS (1 << 0) | ||
43 | #define HDMI_HICR 0x1004 | ||
44 | #define HDMI_INTR_I2C_ERROR (1 << 4) | ||
45 | #define HDMI_INTR_I2C_FULL (1 << 3) | ||
46 | #define HDMI_INTR_I2C_DONE (1 << 2) | ||
47 | #define HDMI_INTR_HPD (1 << 0) | ||
48 | #define HDMI_HSR 0x1008 | ||
49 | #define HDMI_HISR 0x100C | ||
50 | #define HDMI_HI2CRDB0 0x1200 | ||
51 | #define HDMI_HI2CHCR 0x1240 | ||
52 | #define HI2C_HDCP_WRITE (0 << 2) | ||
53 | #define HI2C_HDCP_RI_READ (1 << 2) | ||
54 | #define HI2C_HDCP_READ (2 << 2) | ||
55 | #define HI2C_EDID_READ (3 << 2) | ||
56 | #define HI2C_READ_CONTINUE (1 << 1) | ||
57 | #define HI2C_ENABLE_TRANSACTION (1 << 0) | ||
58 | |||
59 | #define HDMI_ICRH 0x1100 | ||
60 | #define HDMI_HI2CTDR0 0x1244 | ||
61 | #define HDMI_HI2CTDR1 0x1248 | ||
62 | |||
63 | #define I2C_STAT_INIT 0 | ||
64 | #define I2C_READ_DONE 1 | ||
65 | #define I2C_TRANSACTION_DONE 2 | ||
66 | |||
67 | struct hdmi_i2c_dev { | ||
68 | struct i2c_adapter *adap; | ||
69 | struct mutex i2c_lock; | ||
70 | struct completion complete; | ||
71 | int status; | ||
72 | struct i2c_msg *msg; | ||
73 | int buf_offset; | ||
74 | }; | ||
75 | |||
76 | static void hdmi_i2c_irq_enable(struct oaktrail_hdmi_dev *hdmi_dev) | ||
77 | { | ||
78 | u32 temp; | ||
79 | |||
80 | temp = HDMI_READ(HDMI_HICR); | ||
81 | temp |= (HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE); | ||
82 | HDMI_WRITE(HDMI_HICR, temp); | ||
83 | HDMI_READ(HDMI_HICR); | ||
84 | } | ||
85 | |||
86 | static void hdmi_i2c_irq_disable(struct oaktrail_hdmi_dev *hdmi_dev) | ||
87 | { | ||
88 | HDMI_WRITE(HDMI_HICR, 0x0); | ||
89 | HDMI_READ(HDMI_HICR); | ||
90 | } | ||
91 | |||
92 | static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) | ||
93 | { | ||
94 | struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); | ||
95 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
96 | u32 temp; | ||
97 | |||
98 | i2c_dev->status = I2C_STAT_INIT; | ||
99 | i2c_dev->msg = pmsg; | ||
100 | i2c_dev->buf_offset = 0; | ||
101 | INIT_COMPLETION(i2c_dev->complete); | ||
102 | |||
103 | /* Enable I2C transaction */ | ||
104 | temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION; | ||
105 | HDMI_WRITE(HDMI_HI2CHCR, temp); | ||
106 | HDMI_READ(HDMI_HI2CHCR); | ||
107 | |||
108 | while (i2c_dev->status != I2C_TRANSACTION_DONE) | ||
109 | wait_for_completion_interruptible_timeout(&i2c_dev->complete, | ||
110 | 10 * HZ); | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int xfer_write(struct i2c_adapter *adap, struct i2c_msg *pmsg) | ||
116 | { | ||
117 | /* | ||
118 | * XXX: i2c write seems isn't useful for EDID probe, don't do anything | ||
119 | */ | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, | ||
124 | struct i2c_msg *pmsg, | ||
125 | int num) | ||
126 | { | ||
127 | struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); | ||
128 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
129 | int i, err = 0; | ||
130 | |||
131 | mutex_lock(&i2c_dev->i2c_lock); | ||
132 | |||
133 | /* Enable i2c unit */ | ||
134 | HDMI_WRITE(HDMI_ICRH, 0x00008760); | ||
135 | |||
136 | /* Enable irq */ | ||
137 | hdmi_i2c_irq_enable(hdmi_dev); | ||
138 | for (i = 0; i < num; i++) { | ||
139 | if (pmsg->len && pmsg->buf) { | ||
140 | if (pmsg->flags & I2C_M_RD) | ||
141 | err = xfer_read(adap, pmsg); | ||
142 | else | ||
143 | err = xfer_write(adap, pmsg); | ||
144 | } | ||
145 | pmsg++; /* next message */ | ||
146 | } | ||
147 | |||
148 | /* Disable irq */ | ||
149 | hdmi_i2c_irq_disable(hdmi_dev); | ||
150 | |||
151 | mutex_unlock(&i2c_dev->i2c_lock); | ||
152 | |||
153 | return i; | ||
154 | } | ||
155 | |||
156 | static u32 oaktrail_hdmi_i2c_func(struct i2c_adapter *adapter) | ||
157 | { | ||
158 | return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; | ||
159 | } | ||
160 | |||
161 | static const struct i2c_algorithm oaktrail_hdmi_i2c_algorithm = { | ||
162 | .master_xfer = oaktrail_hdmi_i2c_access, | ||
163 | .functionality = oaktrail_hdmi_i2c_func, | ||
164 | }; | ||
165 | |||
166 | static struct i2c_adapter oaktrail_hdmi_i2c_adapter = { | ||
167 | .name = "oaktrail_hdmi_i2c", | ||
168 | .nr = 3, | ||
169 | .owner = THIS_MODULE, | ||
170 | .class = I2C_CLASS_DDC, | ||
171 | .algo = &oaktrail_hdmi_i2c_algorithm, | ||
172 | }; | ||
173 | |||
174 | static void hdmi_i2c_read(struct oaktrail_hdmi_dev *hdmi_dev) | ||
175 | { | ||
176 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
177 | struct i2c_msg *msg = i2c_dev->msg; | ||
178 | u8 *buf = msg->buf; | ||
179 | u32 temp; | ||
180 | int i, offset; | ||
181 | |||
182 | offset = i2c_dev->buf_offset; | ||
183 | for (i = 0; i < 0x10; i++) { | ||
184 | temp = HDMI_READ(HDMI_HI2CRDB0 + (i * 4)); | ||
185 | memcpy(buf + (offset + i * 4), &temp, 4); | ||
186 | } | ||
187 | i2c_dev->buf_offset += (0x10 * 4); | ||
188 | |||
189 | /* clearing read buffer full intr */ | ||
190 | temp = HDMI_READ(HDMI_HISR); | ||
191 | HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_FULL); | ||
192 | HDMI_READ(HDMI_HISR); | ||
193 | |||
194 | /* continue read transaction */ | ||
195 | temp = HDMI_READ(HDMI_HI2CHCR); | ||
196 | HDMI_WRITE(HDMI_HI2CHCR, temp | HI2C_READ_CONTINUE); | ||
197 | HDMI_READ(HDMI_HI2CHCR); | ||
198 | |||
199 | i2c_dev->status = I2C_READ_DONE; | ||
200 | return; | ||
201 | } | ||
202 | |||
203 | static void hdmi_i2c_transaction_done(struct oaktrail_hdmi_dev *hdmi_dev) | ||
204 | { | ||
205 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
206 | u32 temp; | ||
207 | |||
208 | /* clear transaction done intr */ | ||
209 | temp = HDMI_READ(HDMI_HISR); | ||
210 | HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_DONE); | ||
211 | HDMI_READ(HDMI_HISR); | ||
212 | |||
213 | |||
214 | temp = HDMI_READ(HDMI_HI2CHCR); | ||
215 | HDMI_WRITE(HDMI_HI2CHCR, temp & ~HI2C_ENABLE_TRANSACTION); | ||
216 | HDMI_READ(HDMI_HI2CHCR); | ||
217 | |||
218 | i2c_dev->status = I2C_TRANSACTION_DONE; | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | static irqreturn_t oaktrail_hdmi_i2c_handler(int this_irq, void *dev) | ||
223 | { | ||
224 | struct oaktrail_hdmi_dev *hdmi_dev = dev; | ||
225 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
226 | u32 stat; | ||
227 | |||
228 | stat = HDMI_READ(HDMI_HISR); | ||
229 | |||
230 | if (stat & HDMI_INTR_HPD) { | ||
231 | HDMI_WRITE(HDMI_HISR, stat | HDMI_INTR_HPD); | ||
232 | HDMI_READ(HDMI_HISR); | ||
233 | } | ||
234 | |||
235 | if (stat & HDMI_INTR_I2C_FULL) | ||
236 | hdmi_i2c_read(hdmi_dev); | ||
237 | |||
238 | if (stat & HDMI_INTR_I2C_DONE) | ||
239 | hdmi_i2c_transaction_done(hdmi_dev); | ||
240 | |||
241 | complete(&i2c_dev->complete); | ||
242 | |||
243 | return IRQ_HANDLED; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * choose alternate function 2 of GPIO pin 52, 53, | ||
248 | * which is used by HDMI I2C logic | ||
249 | */ | ||
250 | static void oaktrail_hdmi_i2c_gpio_fix(void) | ||
251 | { | ||
252 | void *base; | ||
253 | unsigned int gpio_base = 0xff12c000; | ||
254 | int gpio_len = 0x1000; | ||
255 | u32 temp; | ||
256 | |||
257 | base = ioremap((resource_size_t)gpio_base, gpio_len); | ||
258 | if (base == NULL) { | ||
259 | DRM_ERROR("gpio ioremap fail\n"); | ||
260 | return; | ||
261 | } | ||
262 | |||
263 | temp = readl(base + 0x44); | ||
264 | DRM_DEBUG_DRIVER("old gpio val %x\n", temp); | ||
265 | writel((temp | 0x00000a00), (base + 0x44)); | ||
266 | temp = readl(base + 0x44); | ||
267 | DRM_DEBUG_DRIVER("new gpio val %x\n", temp); | ||
268 | |||
269 | iounmap(base); | ||
270 | } | ||
271 | |||
272 | int oaktrail_hdmi_i2c_init(struct pci_dev *dev) | ||
273 | { | ||
274 | struct oaktrail_hdmi_dev *hdmi_dev; | ||
275 | struct hdmi_i2c_dev *i2c_dev; | ||
276 | int ret; | ||
277 | |||
278 | hdmi_dev = pci_get_drvdata(dev); | ||
279 | |||
280 | i2c_dev = kzalloc(sizeof(struct hdmi_i2c_dev), GFP_KERNEL); | ||
281 | if (i2c_dev == NULL) { | ||
282 | DRM_ERROR("Can't allocate interface\n"); | ||
283 | ret = -ENOMEM; | ||
284 | goto exit; | ||
285 | } | ||
286 | |||
287 | i2c_dev->adap = &oaktrail_hdmi_i2c_adapter; | ||
288 | i2c_dev->status = I2C_STAT_INIT; | ||
289 | init_completion(&i2c_dev->complete); | ||
290 | mutex_init(&i2c_dev->i2c_lock); | ||
291 | i2c_set_adapdata(&oaktrail_hdmi_i2c_adapter, hdmi_dev); | ||
292 | hdmi_dev->i2c_dev = i2c_dev; | ||
293 | |||
294 | /* Enable HDMI I2C function on gpio */ | ||
295 | oaktrail_hdmi_i2c_gpio_fix(); | ||
296 | |||
297 | /* request irq */ | ||
298 | ret = request_irq(dev->irq, oaktrail_hdmi_i2c_handler, IRQF_SHARED, | ||
299 | oaktrail_hdmi_i2c_adapter.name, hdmi_dev); | ||
300 | if (ret) { | ||
301 | DRM_ERROR("Failed to request IRQ for I2C controller\n"); | ||
302 | goto err; | ||
303 | } | ||
304 | |||
305 | /* Adapter registration */ | ||
306 | ret = i2c_add_numbered_adapter(&oaktrail_hdmi_i2c_adapter); | ||
307 | return ret; | ||
308 | |||
309 | err: | ||
310 | kfree(i2c_dev); | ||
311 | exit: | ||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | void oaktrail_hdmi_i2c_exit(struct pci_dev *dev) | ||
316 | { | ||
317 | struct oaktrail_hdmi_dev *hdmi_dev; | ||
318 | struct hdmi_i2c_dev *i2c_dev; | ||
319 | |||
320 | hdmi_dev = pci_get_drvdata(dev); | ||
321 | if (i2c_del_adapter(&oaktrail_hdmi_i2c_adapter)) | ||
322 | DRM_DEBUG_DRIVER("Failed to delete hdmi-i2c adapter\n"); | ||
323 | |||
324 | i2c_dev = hdmi_dev->i2c_dev; | ||
325 | kfree(i2c_dev); | ||
326 | free_irq(dev->irq, hdmi_dev); | ||
327 | } | ||
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c new file mode 100644 index 000000000000..a552226a08ff --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c | |||
@@ -0,0 +1,406 @@ | |||
1 | /* | ||
2 | * Copyright © 2006-2009 Intel Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
16 | * | ||
17 | * Authors: | ||
18 | * Eric Anholt <eric@anholt.net> | ||
19 | * Dave Airlie <airlied@linux.ie> | ||
20 | * Jesse Barnes <jesse.barnes@intel.com> | ||
21 | */ | ||
22 | |||
23 | #include <linux/i2c.h> | ||
24 | #include <drm/drmP.h> | ||
25 | #include <asm/mrst.h> | ||
26 | |||
27 | #include "intel_bios.h" | ||
28 | #include "psb_drv.h" | ||
29 | #include "psb_intel_drv.h" | ||
30 | #include "psb_intel_reg.h" | ||
31 | #include "power.h" | ||
32 | #include <linux/pm_runtime.h> | ||
33 | |||
34 | /* The max/min PWM frequency in BPCR[31:17] - */ | ||
35 | /* The smallest number is 1 (not 0) that can fit in the | ||
36 | * 15-bit field of the and then*/ | ||
37 | /* shifts to the left by one bit to get the actual 16-bit | ||
38 | * value that the 15-bits correspond to.*/ | ||
39 | #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF | ||
40 | #define BRIGHTNESS_MAX_LEVEL 100 | ||
41 | |||
42 | /** | ||
43 | * Sets the power state for the panel. | ||
44 | */ | ||
45 | static void oaktrail_lvds_set_power(struct drm_device *dev, | ||
46 | struct psb_intel_output *output, bool on) | ||
47 | { | ||
48 | u32 pp_status; | ||
49 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
50 | |||
51 | if (!gma_power_begin(dev, true)) | ||
52 | return; | ||
53 | |||
54 | if (on) { | ||
55 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | | ||
56 | POWER_TARGET_ON); | ||
57 | do { | ||
58 | pp_status = REG_READ(PP_STATUS); | ||
59 | } while ((pp_status & (PP_ON | PP_READY)) == PP_READY); | ||
60 | dev_priv->is_lvds_on = true; | ||
61 | if (dev_priv->ops->lvds_bl_power) | ||
62 | dev_priv->ops->lvds_bl_power(dev, true); | ||
63 | } else { | ||
64 | if (dev_priv->ops->lvds_bl_power) | ||
65 | dev_priv->ops->lvds_bl_power(dev, false); | ||
66 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & | ||
67 | ~POWER_TARGET_ON); | ||
68 | do { | ||
69 | pp_status = REG_READ(PP_STATUS); | ||
70 | } while (pp_status & PP_ON); | ||
71 | dev_priv->is_lvds_on = false; | ||
72 | pm_request_idle(&dev->pdev->dev); | ||
73 | } | ||
74 | gma_power_end(dev); | ||
75 | } | ||
76 | |||
77 | static void oaktrail_lvds_dpms(struct drm_encoder *encoder, int mode) | ||
78 | { | ||
79 | struct drm_device *dev = encoder->dev; | ||
80 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
81 | |||
82 | if (mode == DRM_MODE_DPMS_ON) | ||
83 | oaktrail_lvds_set_power(dev, output, true); | ||
84 | else | ||
85 | oaktrail_lvds_set_power(dev, output, false); | ||
86 | |||
87 | /* XXX: We never power down the LVDS pairs. */ | ||
88 | } | ||
89 | |||
90 | static void oaktrail_lvds_mode_set(struct drm_encoder *encoder, | ||
91 | struct drm_display_mode *mode, | ||
92 | struct drm_display_mode *adjusted_mode) | ||
93 | { | ||
94 | struct psb_intel_mode_device *mode_dev = | ||
95 | enc_to_psb_intel_output(encoder)->mode_dev; | ||
96 | struct drm_device *dev = encoder->dev; | ||
97 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
98 | u32 lvds_port; | ||
99 | uint64_t v = DRM_MODE_SCALE_FULLSCREEN; | ||
100 | |||
101 | if (!gma_power_begin(dev, true)) | ||
102 | return; | ||
103 | |||
104 | /* | ||
105 | * The LVDS pin pair will already have been turned on in the | ||
106 | * psb_intel_crtc_mode_set since it has a large impact on the DPLL | ||
107 | * settings. | ||
108 | */ | ||
109 | lvds_port = (REG_READ(LVDS) & | ||
110 | (~LVDS_PIPEB_SELECT)) | | ||
111 | LVDS_PORT_EN | | ||
112 | LVDS_BORDER_EN; | ||
113 | |||
114 | /* If the firmware says dither on Moorestown, or the BIOS does | ||
115 | on Oaktrail then enable dithering */ | ||
116 | if (mode_dev->panel_wants_dither || dev_priv->lvds_dither) | ||
117 | lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE; | ||
118 | |||
119 | REG_WRITE(LVDS, lvds_port); | ||
120 | |||
121 | drm_connector_property_get_value( | ||
122 | &enc_to_psb_intel_output(encoder)->base, | ||
123 | dev->mode_config.scaling_mode_property, | ||
124 | &v); | ||
125 | |||
126 | if (v == DRM_MODE_SCALE_NO_SCALE) | ||
127 | REG_WRITE(PFIT_CONTROL, 0); | ||
128 | else if (v == DRM_MODE_SCALE_ASPECT) { | ||
129 | if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) || | ||
130 | (mode->hdisplay != adjusted_mode->crtc_hdisplay)) { | ||
131 | if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) == | ||
132 | (mode->hdisplay * adjusted_mode->crtc_vdisplay)) | ||
133 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | ||
134 | else if ((adjusted_mode->crtc_hdisplay * | ||
135 | mode->vdisplay) > (mode->hdisplay * | ||
136 | adjusted_mode->crtc_vdisplay)) | ||
137 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | | ||
138 | PFIT_SCALING_MODE_PILLARBOX); | ||
139 | else | ||
140 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | | ||
141 | PFIT_SCALING_MODE_LETTERBOX); | ||
142 | } else | ||
143 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | ||
144 | } else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/ | ||
145 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | ||
146 | |||
147 | gma_power_end(dev); | ||
148 | } | ||
149 | |||
150 | static void oaktrail_lvds_prepare(struct drm_encoder *encoder) | ||
151 | { | ||
152 | struct drm_device *dev = encoder->dev; | ||
153 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
154 | struct psb_intel_mode_device *mode_dev = output->mode_dev; | ||
155 | |||
156 | if (!gma_power_begin(dev, true)) | ||
157 | return; | ||
158 | |||
159 | mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); | ||
160 | mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & | ||
161 | BACKLIGHT_DUTY_CYCLE_MASK); | ||
162 | oaktrail_lvds_set_power(dev, output, false); | ||
163 | gma_power_end(dev); | ||
164 | } | ||
165 | |||
166 | static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev) | ||
167 | { | ||
168 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
169 | u32 ret; | ||
170 | |||
171 | if (gma_power_begin(dev, false)) { | ||
172 | ret = ((REG_READ(BLC_PWM_CTL) & | ||
173 | BACKLIGHT_MODULATION_FREQ_MASK) >> | ||
174 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; | ||
175 | |||
176 | gma_power_end(dev); | ||
177 | } else | ||
178 | ret = ((dev_priv->saveBLC_PWM_CTL & | ||
179 | BACKLIGHT_MODULATION_FREQ_MASK) >> | ||
180 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; | ||
181 | |||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static void oaktrail_lvds_commit(struct drm_encoder *encoder) | ||
186 | { | ||
187 | struct drm_device *dev = encoder->dev; | ||
188 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
189 | struct psb_intel_mode_device *mode_dev = output->mode_dev; | ||
190 | |||
191 | if (mode_dev->backlight_duty_cycle == 0) | ||
192 | mode_dev->backlight_duty_cycle = | ||
193 | oaktrail_lvds_get_max_backlight(dev); | ||
194 | oaktrail_lvds_set_power(dev, output, true); | ||
195 | } | ||
196 | |||
197 | static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = { | ||
198 | .dpms = oaktrail_lvds_dpms, | ||
199 | .mode_fixup = psb_intel_lvds_mode_fixup, | ||
200 | .prepare = oaktrail_lvds_prepare, | ||
201 | .mode_set = oaktrail_lvds_mode_set, | ||
202 | .commit = oaktrail_lvds_commit, | ||
203 | }; | ||
204 | |||
205 | static struct drm_display_mode lvds_configuration_modes[] = { | ||
206 | /* hard coded fixed mode for TPO LTPS LPJ040K001A */ | ||
207 | { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836, | ||
208 | 846, 1056, 0, 480, 489, 491, 525, 0, 0) }, | ||
209 | /* hard coded fixed mode for LVDS 800x480 */ | ||
210 | { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801, | ||
211 | 802, 1024, 0, 480, 481, 482, 525, 0, 0) }, | ||
212 | /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ | ||
213 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072, | ||
214 | 1104, 1184, 0, 600, 603, 604, 608, 0, 0) }, | ||
215 | /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ | ||
216 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104, | ||
217 | 1136, 1184, 0, 600, 603, 604, 608, 0, 0) }, | ||
218 | /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */ | ||
219 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124, | ||
220 | 1204, 1312, 0, 600, 607, 610, 621, 0, 0) }, | ||
221 | /* hard coded fixed mode for LVDS 1024x768 */ | ||
222 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, | ||
223 | 1184, 1344, 0, 768, 771, 777, 806, 0, 0) }, | ||
224 | /* hard coded fixed mode for LVDS 1366x768 */ | ||
225 | { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430, | ||
226 | 1558, 1664, 0, 768, 769, 770, 776, 0, 0) }, | ||
227 | }; | ||
228 | |||
229 | /* Returns the panel fixed mode from configuration. */ | ||
230 | |||
231 | static struct drm_display_mode * | ||
232 | oaktrail_lvds_get_configuration_mode(struct drm_device *dev) | ||
233 | { | ||
234 | struct drm_display_mode *mode = NULL; | ||
235 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
236 | struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD; | ||
237 | |||
238 | if (dev_priv->vbt_data.size != 0x00) { /*if non-zero, then use vbt*/ | ||
239 | mode = kzalloc(sizeof(*mode), GFP_KERNEL); | ||
240 | if (!mode) | ||
241 | return NULL; | ||
242 | |||
243 | mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; | ||
244 | mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; | ||
245 | mode->hsync_start = mode->hdisplay + \ | ||
246 | ((ti->hsync_offset_hi << 8) | \ | ||
247 | ti->hsync_offset_lo); | ||
248 | mode->hsync_end = mode->hsync_start + \ | ||
249 | ((ti->hsync_pulse_width_hi << 8) | \ | ||
250 | ti->hsync_pulse_width_lo); | ||
251 | mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ | ||
252 | ti->hblank_lo); | ||
253 | mode->vsync_start = \ | ||
254 | mode->vdisplay + ((ti->vsync_offset_hi << 4) | \ | ||
255 | ti->vsync_offset_lo); | ||
256 | mode->vsync_end = \ | ||
257 | mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \ | ||
258 | ti->vsync_pulse_width_lo); | ||
259 | mode->vtotal = mode->vdisplay + \ | ||
260 | ((ti->vblank_hi << 8) | ti->vblank_lo); | ||
261 | mode->clock = ti->pixel_clock * 10; | ||
262 | #if 0 | ||
263 | printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay); | ||
264 | printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay); | ||
265 | printk(KERN_INFO "HSS is %d\n", mode->hsync_start); | ||
266 | printk(KERN_INFO "HSE is %d\n", mode->hsync_end); | ||
267 | printk(KERN_INFO "htotal is %d\n", mode->htotal); | ||
268 | printk(KERN_INFO "VSS is %d\n", mode->vsync_start); | ||
269 | printk(KERN_INFO "VSE is %d\n", mode->vsync_end); | ||
270 | printk(KERN_INFO "vtotal is %d\n", mode->vtotal); | ||
271 | printk(KERN_INFO "clock is %d\n", mode->clock); | ||
272 | #endif | ||
273 | } else | ||
274 | mode = drm_mode_duplicate(dev, &lvds_configuration_modes[2]); | ||
275 | |||
276 | drm_mode_set_name(mode); | ||
277 | drm_mode_set_crtcinfo(mode, 0); | ||
278 | |||
279 | return mode; | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * oaktrail_lvds_init - setup LVDS connectors on this device | ||
284 | * @dev: drm device | ||
285 | * | ||
286 | * Create the connector, register the LVDS DDC bus, and try to figure out what | ||
287 | * modes we can display on the LVDS panel (if present). | ||
288 | */ | ||
289 | void oaktrail_lvds_init(struct drm_device *dev, | ||
290 | struct psb_intel_mode_device *mode_dev) | ||
291 | { | ||
292 | struct psb_intel_output *psb_intel_output; | ||
293 | struct drm_connector *connector; | ||
294 | struct drm_encoder *encoder; | ||
295 | struct drm_psb_private *dev_priv = | ||
296 | (struct drm_psb_private *) dev->dev_private; | ||
297 | struct edid *edid; | ||
298 | int ret = 0; | ||
299 | struct i2c_adapter *i2c_adap; | ||
300 | struct drm_display_mode *scan; /* *modes, *bios_mode; */ | ||
301 | |||
302 | psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); | ||
303 | if (!psb_intel_output) | ||
304 | return; | ||
305 | |||
306 | psb_intel_output->mode_dev = mode_dev; | ||
307 | connector = &psb_intel_output->base; | ||
308 | encoder = &psb_intel_output->enc; | ||
309 | dev_priv->is_lvds_on = true; | ||
310 | drm_connector_init(dev, &psb_intel_output->base, | ||
311 | &psb_intel_lvds_connector_funcs, | ||
312 | DRM_MODE_CONNECTOR_LVDS); | ||
313 | |||
314 | drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_lvds_enc_funcs, | ||
315 | DRM_MODE_ENCODER_LVDS); | ||
316 | |||
317 | drm_mode_connector_attach_encoder(&psb_intel_output->base, | ||
318 | &psb_intel_output->enc); | ||
319 | psb_intel_output->type = INTEL_OUTPUT_LVDS; | ||
320 | |||
321 | drm_encoder_helper_add(encoder, &oaktrail_lvds_helper_funcs); | ||
322 | drm_connector_helper_add(connector, | ||
323 | &psb_intel_lvds_connector_helper_funcs); | ||
324 | connector->display_info.subpixel_order = SubPixelHorizontalRGB; | ||
325 | connector->interlace_allowed = false; | ||
326 | connector->doublescan_allowed = false; | ||
327 | |||
328 | drm_connector_attach_property(connector, | ||
329 | dev->mode_config.scaling_mode_property, | ||
330 | DRM_MODE_SCALE_FULLSCREEN); | ||
331 | drm_connector_attach_property(connector, | ||
332 | dev_priv->backlight_property, | ||
333 | BRIGHTNESS_MAX_LEVEL); | ||
334 | |||
335 | mode_dev->panel_wants_dither = false; | ||
336 | if (dev_priv->vbt_data.size != 0x00) | ||
337 | mode_dev->panel_wants_dither = (dev_priv->gct_data. | ||
338 | Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE); | ||
339 | |||
340 | /* | ||
341 | * LVDS discovery: | ||
342 | * 1) check for EDID on DDC | ||
343 | * 2) check for VBT data | ||
344 | * 3) check to see if LVDS is already on | ||
345 | * if none of the above, no panel | ||
346 | * 4) make sure lid is open | ||
347 | * if closed, act like it's not there for now | ||
348 | */ | ||
349 | |||
350 | i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); | ||
351 | if (i2c_adap == NULL) | ||
352 | dev_err(dev->dev, "No ddc adapter available!\n"); | ||
353 | /* | ||
354 | * Attempt to get the fixed panel mode from DDC. Assume that the | ||
355 | * preferred mode is the right one. | ||
356 | */ | ||
357 | if (i2c_adap) { | ||
358 | edid = drm_get_edid(connector, i2c_adap); | ||
359 | if (edid) { | ||
360 | drm_mode_connector_update_edid_property(connector, | ||
361 | edid); | ||
362 | ret = drm_add_edid_modes(connector, edid); | ||
363 | kfree(edid); | ||
364 | } | ||
365 | |||
366 | list_for_each_entry(scan, &connector->probed_modes, head) { | ||
367 | if (scan->type & DRM_MODE_TYPE_PREFERRED) { | ||
368 | mode_dev->panel_fixed_mode = | ||
369 | drm_mode_duplicate(dev, scan); | ||
370 | goto out; /* FIXME: check for quirks */ | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | /* | ||
375 | * If we didn't get EDID, try geting panel timing | ||
376 | * from configuration data | ||
377 | */ | ||
378 | mode_dev->panel_fixed_mode = oaktrail_lvds_get_configuration_mode(dev); | ||
379 | |||
380 | if (mode_dev->panel_fixed_mode) { | ||
381 | mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; | ||
382 | goto out; /* FIXME: check for quirks */ | ||
383 | } | ||
384 | |||
385 | /* If we still don't have a mode after all that, give up. */ | ||
386 | if (!mode_dev->panel_fixed_mode) { | ||
387 | dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); | ||
388 | goto failed_find; | ||
389 | } | ||
390 | |||
391 | out: | ||
392 | drm_sysfs_connector_add(connector); | ||
393 | return; | ||
394 | |||
395 | failed_find: | ||
396 | dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); | ||
397 | if (psb_intel_output->ddc_bus) | ||
398 | psb_intel_i2c_destroy(psb_intel_output->ddc_bus); | ||
399 | |||
400 | /* failed_ddc: */ | ||
401 | |||
402 | drm_encoder_cleanup(encoder); | ||
403 | drm_connector_cleanup(connector); | ||
404 | kfree(connector); | ||
405 | } | ||
406 | |||