xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/gma500/oaktrail_hdmi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright © 2010 Intel Corporation
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Permission is hereby granted, free of charge, to any person obtaining a
5*4882a593Smuzhiyun  * copy of this software and associated documentation files (the "Software"),
6*4882a593Smuzhiyun  * to deal in the Software without restriction, including without limitation
7*4882a593Smuzhiyun  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*4882a593Smuzhiyun  * and/or sell copies of the Software, and to permit persons to whom the
9*4882a593Smuzhiyun  * Software is furnished to do so, subject to the following conditions:
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * The above copyright notice and this permission notice (including the next
12*4882a593Smuzhiyun  * paragraph) shall be included in all copies or substantial portions of the
13*4882a593Smuzhiyun  * Software.
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*4882a593Smuzhiyun  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*4882a593Smuzhiyun  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18*4882a593Smuzhiyun  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*4882a593Smuzhiyun  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20*4882a593Smuzhiyun  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21*4882a593Smuzhiyun  * DEALINGS IN THE SOFTWARE.
22*4882a593Smuzhiyun  *
23*4882a593Smuzhiyun  * Authors:
24*4882a593Smuzhiyun  *	Li Peng <peng.li@intel.com>
25*4882a593Smuzhiyun  */
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include <linux/delay.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #include <drm/drm.h>
30*4882a593Smuzhiyun #include <drm/drm_simple_kms_helper.h>
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #include "psb_drv.h"
33*4882a593Smuzhiyun #include "psb_intel_drv.h"
34*4882a593Smuzhiyun #include "psb_intel_reg.h"
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define HDMI_READ(reg)		readl(hdmi_dev->regs + (reg))
37*4882a593Smuzhiyun #define HDMI_WRITE(reg, val)	writel(val, hdmi_dev->regs + (reg))
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #define HDMI_HCR	0x1000
40*4882a593Smuzhiyun #define HCR_ENABLE_HDCP		(1 << 5)
41*4882a593Smuzhiyun #define HCR_ENABLE_AUDIO	(1 << 2)
42*4882a593Smuzhiyun #define HCR_ENABLE_PIXEL	(1 << 1)
43*4882a593Smuzhiyun #define HCR_ENABLE_TMDS		(1 << 0)
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define HDMI_HICR	0x1004
46*4882a593Smuzhiyun #define HDMI_HSR	0x1008
47*4882a593Smuzhiyun #define HDMI_HISR	0x100C
48*4882a593Smuzhiyun #define HDMI_DETECT_HDP		(1 << 0)
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun #define HDMI_VIDEO_REG	0x3000
51*4882a593Smuzhiyun #define HDMI_UNIT_EN		(1 << 7)
52*4882a593Smuzhiyun #define HDMI_MODE_OUTPUT	(1 << 0)
53*4882a593Smuzhiyun #define HDMI_HBLANK_A	0x3100
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun #define HDMI_AUDIO_CTRL	0x4000
56*4882a593Smuzhiyun #define HDMI_ENABLE_AUDIO	(1 << 0)
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun #define PCH_HTOTAL_B	0x3100
59*4882a593Smuzhiyun #define PCH_HBLANK_B	0x3104
60*4882a593Smuzhiyun #define PCH_HSYNC_B	0x3108
61*4882a593Smuzhiyun #define PCH_VTOTAL_B	0x310C
62*4882a593Smuzhiyun #define PCH_VBLANK_B	0x3110
63*4882a593Smuzhiyun #define PCH_VSYNC_B	0x3114
64*4882a593Smuzhiyun #define PCH_PIPEBSRC	0x311C
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun #define PCH_PIPEB_DSL	0x3800
67*4882a593Smuzhiyun #define PCH_PIPEB_SLC	0x3804
68*4882a593Smuzhiyun #define PCH_PIPEBCONF	0x3808
69*4882a593Smuzhiyun #define PCH_PIPEBSTAT	0x3824
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun #define CDVO_DFT	0x5000
72*4882a593Smuzhiyun #define CDVO_SLEWRATE	0x5004
73*4882a593Smuzhiyun #define CDVO_STRENGTH	0x5008
74*4882a593Smuzhiyun #define CDVO_RCOMP	0x500C
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun #define DPLL_CTRL       0x6000
77*4882a593Smuzhiyun #define DPLL_PDIV_SHIFT		16
78*4882a593Smuzhiyun #define DPLL_PDIV_MASK		(0xf << 16)
79*4882a593Smuzhiyun #define DPLL_PWRDN		(1 << 4)
80*4882a593Smuzhiyun #define DPLL_RESET		(1 << 3)
81*4882a593Smuzhiyun #define DPLL_FASTEN		(1 << 2)
82*4882a593Smuzhiyun #define DPLL_ENSTAT		(1 << 1)
83*4882a593Smuzhiyun #define DPLL_DITHEN		(1 << 0)
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun #define DPLL_DIV_CTRL   0x6004
86*4882a593Smuzhiyun #define DPLL_CLKF_MASK		0xffffffc0
87*4882a593Smuzhiyun #define DPLL_CLKR_MASK		(0x3f)
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun #define DPLL_CLK_ENABLE 0x6008
90*4882a593Smuzhiyun #define DPLL_EN_DISP		(1 << 31)
91*4882a593Smuzhiyun #define DPLL_SEL_HDMI		(1 << 8)
92*4882a593Smuzhiyun #define DPLL_EN_HDMI		(1 << 1)
93*4882a593Smuzhiyun #define DPLL_EN_VGA		(1 << 0)
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun #define DPLL_ADJUST     0x600C
96*4882a593Smuzhiyun #define DPLL_STATUS     0x6010
97*4882a593Smuzhiyun #define DPLL_UPDATE     0x6014
98*4882a593Smuzhiyun #define DPLL_DFT        0x6020
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun struct intel_range {
101*4882a593Smuzhiyun 	int	min, max;
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun struct oaktrail_hdmi_limit {
105*4882a593Smuzhiyun 	struct intel_range vco, np, nr, nf;
106*4882a593Smuzhiyun };
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun struct oaktrail_hdmi_clock {
109*4882a593Smuzhiyun 	int np;
110*4882a593Smuzhiyun 	int nr;
111*4882a593Smuzhiyun 	int nf;
112*4882a593Smuzhiyun 	int dot;
113*4882a593Smuzhiyun };
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun #define VCO_MIN		320000
116*4882a593Smuzhiyun #define VCO_MAX		1650000
117*4882a593Smuzhiyun #define	NP_MIN		1
118*4882a593Smuzhiyun #define	NP_MAX		15
119*4882a593Smuzhiyun #define	NR_MIN		1
120*4882a593Smuzhiyun #define	NR_MAX		64
121*4882a593Smuzhiyun #define NF_MIN		2
122*4882a593Smuzhiyun #define NF_MAX		4095
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun static const struct oaktrail_hdmi_limit oaktrail_hdmi_limit = {
125*4882a593Smuzhiyun 	.vco = { .min = VCO_MIN,		.max = VCO_MAX },
126*4882a593Smuzhiyun 	.np  = { .min = NP_MIN,			.max = NP_MAX  },
127*4882a593Smuzhiyun 	.nr  = { .min = NR_MIN,			.max = NR_MAX  },
128*4882a593Smuzhiyun 	.nf  = { .min = NF_MIN,			.max = NF_MAX  },
129*4882a593Smuzhiyun };
130*4882a593Smuzhiyun 
oaktrail_hdmi_audio_enable(struct drm_device * dev)131*4882a593Smuzhiyun static void oaktrail_hdmi_audio_enable(struct drm_device *dev)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
134*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	HDMI_WRITE(HDMI_HCR, 0x67);
137*4882a593Smuzhiyun 	HDMI_READ(HDMI_HCR);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	HDMI_WRITE(0x51a8, 0x10);
140*4882a593Smuzhiyun 	HDMI_READ(0x51a8);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	HDMI_WRITE(HDMI_AUDIO_CTRL, 0x1);
143*4882a593Smuzhiyun 	HDMI_READ(HDMI_AUDIO_CTRL);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
oaktrail_hdmi_audio_disable(struct drm_device * dev)146*4882a593Smuzhiyun static void oaktrail_hdmi_audio_disable(struct drm_device *dev)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
149*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	HDMI_WRITE(0x51a8, 0x0);
152*4882a593Smuzhiyun 	HDMI_READ(0x51a8);
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	HDMI_WRITE(HDMI_AUDIO_CTRL, 0x0);
155*4882a593Smuzhiyun 	HDMI_READ(HDMI_AUDIO_CTRL);
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	HDMI_WRITE(HDMI_HCR, 0x47);
158*4882a593Smuzhiyun 	HDMI_READ(HDMI_HCR);
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
htotal_calculate(struct drm_display_mode * mode)161*4882a593Smuzhiyun static unsigned int htotal_calculate(struct drm_display_mode *mode)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	u32 new_crtc_htotal;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	/*
166*4882a593Smuzhiyun 	 * 1024 x 768  new_crtc_htotal = 0x1024;
167*4882a593Smuzhiyun 	 * 1280 x 1024 new_crtc_htotal = 0x0c34;
168*4882a593Smuzhiyun 	 */
169*4882a593Smuzhiyun 	new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	DRM_DEBUG_KMS("new crtc htotal 0x%4x\n", new_crtc_htotal);
172*4882a593Smuzhiyun 	return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16);
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun 
oaktrail_hdmi_find_dpll(struct drm_crtc * crtc,int target,int refclk,struct oaktrail_hdmi_clock * best_clock)175*4882a593Smuzhiyun static void oaktrail_hdmi_find_dpll(struct drm_crtc *crtc, int target,
176*4882a593Smuzhiyun 				int refclk, struct oaktrail_hdmi_clock *best_clock)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	int np_min, np_max, nr_min, nr_max;
179*4882a593Smuzhiyun 	int np, nr, nf;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	np_min = DIV_ROUND_UP(oaktrail_hdmi_limit.vco.min, target * 10);
182*4882a593Smuzhiyun 	np_max = oaktrail_hdmi_limit.vco.max / (target * 10);
183*4882a593Smuzhiyun 	if (np_min < oaktrail_hdmi_limit.np.min)
184*4882a593Smuzhiyun 		np_min = oaktrail_hdmi_limit.np.min;
185*4882a593Smuzhiyun 	if (np_max > oaktrail_hdmi_limit.np.max)
186*4882a593Smuzhiyun 		np_max = oaktrail_hdmi_limit.np.max;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max));
189*4882a593Smuzhiyun 	nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min));
190*4882a593Smuzhiyun 	if (nr_min < oaktrail_hdmi_limit.nr.min)
191*4882a593Smuzhiyun 		nr_min = oaktrail_hdmi_limit.nr.min;
192*4882a593Smuzhiyun 	if (nr_max > oaktrail_hdmi_limit.nr.max)
193*4882a593Smuzhiyun 		nr_max = oaktrail_hdmi_limit.nr.max;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max));
196*4882a593Smuzhiyun 	nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np));
197*4882a593Smuzhiyun 	nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk);
198*4882a593Smuzhiyun 	DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	/*
201*4882a593Smuzhiyun 	 * 1024 x 768  np = 1; nr = 0x26; nf = 0x0fd8000;
202*4882a593Smuzhiyun 	 * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000;
203*4882a593Smuzhiyun 	 */
204*4882a593Smuzhiyun 	best_clock->np = np;
205*4882a593Smuzhiyun 	best_clock->nr = nr - 1;
206*4882a593Smuzhiyun 	best_clock->nf = (nf << 14);
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
scu_busy_loop(void __iomem * scu_base)209*4882a593Smuzhiyun static void scu_busy_loop(void __iomem *scu_base)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	u32 status = 0;
212*4882a593Smuzhiyun 	u32 loop_count = 0;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	status = readl(scu_base + 0x04);
215*4882a593Smuzhiyun 	while (status & 1) {
216*4882a593Smuzhiyun 		udelay(1); /* scu processing time is in few u secods */
217*4882a593Smuzhiyun 		status = readl(scu_base + 0x04);
218*4882a593Smuzhiyun 		loop_count++;
219*4882a593Smuzhiyun 		/* break if scu doesn't reset busy bit after huge retry */
220*4882a593Smuzhiyun 		if (loop_count > 1000) {
221*4882a593Smuzhiyun 			DRM_DEBUG_KMS("SCU IPC timed out");
222*4882a593Smuzhiyun 			return;
223*4882a593Smuzhiyun 		}
224*4882a593Smuzhiyun 	}
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun /*
228*4882a593Smuzhiyun  *	You don't want to know, you really really don't want to know....
229*4882a593Smuzhiyun  *
230*4882a593Smuzhiyun  *	This is magic. However it's safe magic because of the way the platform
231*4882a593Smuzhiyun  *	works and it is necessary magic.
232*4882a593Smuzhiyun  */
oaktrail_hdmi_reset(struct drm_device * dev)233*4882a593Smuzhiyun static void oaktrail_hdmi_reset(struct drm_device *dev)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	void __iomem *base;
236*4882a593Smuzhiyun 	unsigned long scu_ipc_mmio = 0xff11c000UL;
237*4882a593Smuzhiyun 	int scu_len = 1024;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	base = ioremap((resource_size_t)scu_ipc_mmio, scu_len);
240*4882a593Smuzhiyun 	if (base == NULL) {
241*4882a593Smuzhiyun 		DRM_ERROR("failed to map scu mmio\n");
242*4882a593Smuzhiyun 		return;
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	/* scu ipc: assert hdmi controller reset */
246*4882a593Smuzhiyun 	writel(0xff11d118, base + 0x0c);
247*4882a593Smuzhiyun 	writel(0x7fffffdf, base + 0x80);
248*4882a593Smuzhiyun 	writel(0x42005, base + 0x0);
249*4882a593Smuzhiyun 	scu_busy_loop(base);
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	/* scu ipc: de-assert hdmi controller reset */
252*4882a593Smuzhiyun 	writel(0xff11d118, base + 0x0c);
253*4882a593Smuzhiyun 	writel(0x7fffffff, base + 0x80);
254*4882a593Smuzhiyun 	writel(0x42005, base + 0x0);
255*4882a593Smuzhiyun 	scu_busy_loop(base);
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	iounmap(base);
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun 
oaktrail_crtc_hdmi_mode_set(struct drm_crtc * crtc,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode,int x,int y,struct drm_framebuffer * old_fb)260*4882a593Smuzhiyun int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc,
261*4882a593Smuzhiyun 			    struct drm_display_mode *mode,
262*4882a593Smuzhiyun 			    struct drm_display_mode *adjusted_mode,
263*4882a593Smuzhiyun 			    int x, int y,
264*4882a593Smuzhiyun 			    struct drm_framebuffer *old_fb)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun 	struct drm_device *dev = crtc->dev;
267*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
268*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
269*4882a593Smuzhiyun 	int pipe = 1;
270*4882a593Smuzhiyun 	int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
271*4882a593Smuzhiyun 	int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
272*4882a593Smuzhiyun 	int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
273*4882a593Smuzhiyun 	int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
274*4882a593Smuzhiyun 	int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
275*4882a593Smuzhiyun 	int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
276*4882a593Smuzhiyun 	int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
277*4882a593Smuzhiyun 	int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
278*4882a593Smuzhiyun 	int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
279*4882a593Smuzhiyun 	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
280*4882a593Smuzhiyun 	int refclk;
281*4882a593Smuzhiyun 	struct oaktrail_hdmi_clock clock;
282*4882a593Smuzhiyun 	u32 dspcntr, pipeconf, dpll, temp;
283*4882a593Smuzhiyun 	int dspcntr_reg = DSPBCNTR;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	if (!gma_power_begin(dev, true))
286*4882a593Smuzhiyun 		return 0;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	/* Disable the VGA plane that we never use */
289*4882a593Smuzhiyun 	REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	/* Disable dpll if necessary */
292*4882a593Smuzhiyun 	dpll = REG_READ(DPLL_CTRL);
293*4882a593Smuzhiyun 	if ((dpll & DPLL_PWRDN) == 0) {
294*4882a593Smuzhiyun 		REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET));
295*4882a593Smuzhiyun 		REG_WRITE(DPLL_DIV_CTRL, 0x00000000);
296*4882a593Smuzhiyun 		REG_WRITE(DPLL_STATUS, 0x1);
297*4882a593Smuzhiyun 	}
298*4882a593Smuzhiyun 	udelay(150);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	/* Reset controller */
301*4882a593Smuzhiyun 	oaktrail_hdmi_reset(dev);
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	/* program and enable dpll */
304*4882a593Smuzhiyun 	refclk = 25000;
305*4882a593Smuzhiyun 	oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	/* Set the DPLL */
308*4882a593Smuzhiyun 	dpll = REG_READ(DPLL_CTRL);
309*4882a593Smuzhiyun 	dpll &= ~DPLL_PDIV_MASK;
310*4882a593Smuzhiyun 	dpll &= ~(DPLL_PWRDN | DPLL_RESET);
311*4882a593Smuzhiyun 	REG_WRITE(DPLL_CTRL, 0x00000008);
312*4882a593Smuzhiyun 	REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr));
313*4882a593Smuzhiyun 	REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1));
314*4882a593Smuzhiyun 	REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN));
315*4882a593Smuzhiyun 	REG_WRITE(DPLL_UPDATE, 0x80000000);
316*4882a593Smuzhiyun 	REG_WRITE(DPLL_CLK_ENABLE, 0x80050102);
317*4882a593Smuzhiyun 	udelay(150);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	/* configure HDMI */
320*4882a593Smuzhiyun 	HDMI_WRITE(0x1004, 0x1fd);
321*4882a593Smuzhiyun 	HDMI_WRITE(0x2000, 0x1);
322*4882a593Smuzhiyun 	HDMI_WRITE(0x2008, 0x0);
323*4882a593Smuzhiyun 	HDMI_WRITE(0x3130, 0x8);
324*4882a593Smuzhiyun 	HDMI_WRITE(0x101c, 0x1800810);
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	temp = htotal_calculate(adjusted_mode);
327*4882a593Smuzhiyun 	REG_WRITE(htot_reg, temp);
328*4882a593Smuzhiyun 	REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16));
329*4882a593Smuzhiyun 	REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16));
330*4882a593Smuzhiyun 	REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16));
331*4882a593Smuzhiyun 	REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16));
332*4882a593Smuzhiyun 	REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16));
333*4882a593Smuzhiyun 	REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16) |  (mode->crtc_vdisplay - 1));
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16));
336*4882a593Smuzhiyun 	REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16));
337*4882a593Smuzhiyun 	REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16));
338*4882a593Smuzhiyun 	REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16));
339*4882a593Smuzhiyun 	REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16));
340*4882a593Smuzhiyun 	REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16));
341*4882a593Smuzhiyun 	REG_WRITE(PCH_PIPEBSRC, ((mode->crtc_hdisplay - 1) << 16) |  (mode->crtc_vdisplay - 1));
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
344*4882a593Smuzhiyun 	HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) |  temp);
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	REG_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
347*4882a593Smuzhiyun 	REG_WRITE(dsppos_reg, 0);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	/* Flush the plane changes */
350*4882a593Smuzhiyun 	{
351*4882a593Smuzhiyun 		const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
352*4882a593Smuzhiyun 		crtc_funcs->mode_set_base(crtc, x, y, old_fb);
353*4882a593Smuzhiyun 	}
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	/* Set up the display plane register */
356*4882a593Smuzhiyun 	dspcntr = REG_READ(dspcntr_reg);
357*4882a593Smuzhiyun 	dspcntr |= DISPPLANE_GAMMA_ENABLE;
358*4882a593Smuzhiyun 	dspcntr |= DISPPLANE_SEL_PIPE_B;
359*4882a593Smuzhiyun 	dspcntr |= DISPLAY_PLANE_ENABLE;
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	/* setup pipeconf */
362*4882a593Smuzhiyun 	pipeconf = REG_READ(pipeconf_reg);
363*4882a593Smuzhiyun 	pipeconf |= PIPEACONF_ENABLE;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	REG_WRITE(pipeconf_reg, pipeconf);
366*4882a593Smuzhiyun 	REG_READ(pipeconf_reg);
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	REG_WRITE(PCH_PIPEBCONF, pipeconf);
369*4882a593Smuzhiyun 	REG_READ(PCH_PIPEBCONF);
370*4882a593Smuzhiyun 	gma_wait_for_vblank(dev);
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	REG_WRITE(dspcntr_reg, dspcntr);
373*4882a593Smuzhiyun 	gma_wait_for_vblank(dev);
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	gma_power_end(dev);
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	return 0;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun 
oaktrail_crtc_hdmi_dpms(struct drm_crtc * crtc,int mode)380*4882a593Smuzhiyun void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun 	struct drm_device *dev = crtc->dev;
383*4882a593Smuzhiyun 	u32 temp;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	DRM_DEBUG_KMS("%s %d\n", __func__, mode);
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	switch (mode) {
388*4882a593Smuzhiyun 	case DRM_MODE_DPMS_OFF:
389*4882a593Smuzhiyun 		REG_WRITE(VGACNTRL, 0x80000000);
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 		/* Disable plane */
392*4882a593Smuzhiyun 		temp = REG_READ(DSPBCNTR);
393*4882a593Smuzhiyun 		if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
394*4882a593Smuzhiyun 			REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE);
395*4882a593Smuzhiyun 			REG_READ(DSPBCNTR);
396*4882a593Smuzhiyun 			/* Flush the plane changes */
397*4882a593Smuzhiyun 			REG_WRITE(DSPBSURF, REG_READ(DSPBSURF));
398*4882a593Smuzhiyun 			REG_READ(DSPBSURF);
399*4882a593Smuzhiyun 		}
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 		/* Disable pipe B */
402*4882a593Smuzhiyun 		temp = REG_READ(PIPEBCONF);
403*4882a593Smuzhiyun 		if ((temp & PIPEACONF_ENABLE) != 0) {
404*4882a593Smuzhiyun 			REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE);
405*4882a593Smuzhiyun 			REG_READ(PIPEBCONF);
406*4882a593Smuzhiyun 		}
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 		/* Disable LNW Pipes, etc */
409*4882a593Smuzhiyun 		temp = REG_READ(PCH_PIPEBCONF);
410*4882a593Smuzhiyun 		if ((temp & PIPEACONF_ENABLE) != 0) {
411*4882a593Smuzhiyun 			REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE);
412*4882a593Smuzhiyun 			REG_READ(PCH_PIPEBCONF);
413*4882a593Smuzhiyun 		}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 		/* wait for pipe off */
416*4882a593Smuzhiyun 		udelay(150);
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 		/* Disable dpll */
419*4882a593Smuzhiyun 		temp = REG_READ(DPLL_CTRL);
420*4882a593Smuzhiyun 		if ((temp & DPLL_PWRDN) == 0) {
421*4882a593Smuzhiyun 			REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET));
422*4882a593Smuzhiyun 			REG_WRITE(DPLL_STATUS, 0x1);
423*4882a593Smuzhiyun 		}
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 		/* wait for dpll off */
426*4882a593Smuzhiyun 		udelay(150);
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 		break;
429*4882a593Smuzhiyun 	case DRM_MODE_DPMS_ON:
430*4882a593Smuzhiyun 	case DRM_MODE_DPMS_STANDBY:
431*4882a593Smuzhiyun 	case DRM_MODE_DPMS_SUSPEND:
432*4882a593Smuzhiyun 		/* Enable dpll */
433*4882a593Smuzhiyun 		temp = REG_READ(DPLL_CTRL);
434*4882a593Smuzhiyun 		if ((temp & DPLL_PWRDN) != 0) {
435*4882a593Smuzhiyun 			REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET));
436*4882a593Smuzhiyun 			temp = REG_READ(DPLL_CLK_ENABLE);
437*4882a593Smuzhiyun 			REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI);
438*4882a593Smuzhiyun 			REG_READ(DPLL_CLK_ENABLE);
439*4882a593Smuzhiyun 		}
440*4882a593Smuzhiyun 		/* wait for dpll warm up */
441*4882a593Smuzhiyun 		udelay(150);
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 		/* Enable pipe B */
444*4882a593Smuzhiyun 		temp = REG_READ(PIPEBCONF);
445*4882a593Smuzhiyun 		if ((temp & PIPEACONF_ENABLE) == 0) {
446*4882a593Smuzhiyun 			REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE);
447*4882a593Smuzhiyun 			REG_READ(PIPEBCONF);
448*4882a593Smuzhiyun 		}
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 		/* Enable LNW Pipe B */
451*4882a593Smuzhiyun 		temp = REG_READ(PCH_PIPEBCONF);
452*4882a593Smuzhiyun 		if ((temp & PIPEACONF_ENABLE) == 0) {
453*4882a593Smuzhiyun 			REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE);
454*4882a593Smuzhiyun 			REG_READ(PCH_PIPEBCONF);
455*4882a593Smuzhiyun 		}
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 		gma_wait_for_vblank(dev);
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 		/* Enable plane */
460*4882a593Smuzhiyun 		temp = REG_READ(DSPBCNTR);
461*4882a593Smuzhiyun 		if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
462*4882a593Smuzhiyun 			REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE);
463*4882a593Smuzhiyun 			/* Flush the plane changes */
464*4882a593Smuzhiyun 			REG_WRITE(DSPBSURF, REG_READ(DSPBSURF));
465*4882a593Smuzhiyun 			REG_READ(DSPBSURF);
466*4882a593Smuzhiyun 		}
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 		gma_crtc_load_lut(crtc);
469*4882a593Smuzhiyun 	}
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	/* DSPARB */
472*4882a593Smuzhiyun 	REG_WRITE(DSPARB, 0x00003fbf);
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	/* FW1 */
475*4882a593Smuzhiyun 	REG_WRITE(0x70034, 0x3f880a0a);
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	/* FW2 */
478*4882a593Smuzhiyun 	REG_WRITE(0x70038, 0x0b060808);
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	/* FW4 */
481*4882a593Smuzhiyun 	REG_WRITE(0x70050, 0x08030404);
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	/* FW5 */
484*4882a593Smuzhiyun 	REG_WRITE(0x70054, 0x04040404);
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	/* LNC Chicken Bits - Squawk! */
487*4882a593Smuzhiyun 	REG_WRITE(0x70400, 0x4000);
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	return;
490*4882a593Smuzhiyun }
491*4882a593Smuzhiyun 
oaktrail_hdmi_dpms(struct drm_encoder * encoder,int mode)492*4882a593Smuzhiyun static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode)
493*4882a593Smuzhiyun {
494*4882a593Smuzhiyun 	static int dpms_mode = -1;
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	struct drm_device *dev = encoder->dev;
497*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
498*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
499*4882a593Smuzhiyun 	u32 temp;
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	if (dpms_mode == mode)
502*4882a593Smuzhiyun 		return;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	if (mode != DRM_MODE_DPMS_ON)
505*4882a593Smuzhiyun 		temp = 0x0;
506*4882a593Smuzhiyun 	else
507*4882a593Smuzhiyun 		temp = 0x99;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	dpms_mode = mode;
510*4882a593Smuzhiyun 	HDMI_WRITE(HDMI_VIDEO_REG, temp);
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun 
oaktrail_hdmi_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)513*4882a593Smuzhiyun static enum drm_mode_status oaktrail_hdmi_mode_valid(struct drm_connector *connector,
514*4882a593Smuzhiyun 				struct drm_display_mode *mode)
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun 	if (mode->clock > 165000)
517*4882a593Smuzhiyun 		return MODE_CLOCK_HIGH;
518*4882a593Smuzhiyun 	if (mode->clock < 20000)
519*4882a593Smuzhiyun 		return MODE_CLOCK_LOW;
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
522*4882a593Smuzhiyun 		return MODE_NO_DBLESCAN;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	return MODE_OK;
525*4882a593Smuzhiyun }
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun static enum drm_connector_status
oaktrail_hdmi_detect(struct drm_connector * connector,bool force)528*4882a593Smuzhiyun oaktrail_hdmi_detect(struct drm_connector *connector, bool force)
529*4882a593Smuzhiyun {
530*4882a593Smuzhiyun 	enum drm_connector_status status;
531*4882a593Smuzhiyun 	struct drm_device *dev = connector->dev;
532*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
533*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
534*4882a593Smuzhiyun 	u32 temp;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	temp = HDMI_READ(HDMI_HSR);
537*4882a593Smuzhiyun 	DRM_DEBUG_KMS("HDMI_HSR %x\n", temp);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	if ((temp & HDMI_DETECT_HDP) != 0)
540*4882a593Smuzhiyun 		status = connector_status_connected;
541*4882a593Smuzhiyun 	else
542*4882a593Smuzhiyun 		status = connector_status_disconnected;
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	return status;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun static const unsigned char raw_edid[] = {
548*4882a593Smuzhiyun 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x2f, 0xa0,
549*4882a593Smuzhiyun 	0x53, 0x55, 0x33, 0x30, 0x16, 0x13, 0x01, 0x03, 0x0e, 0x3a, 0x24, 0x78,
550*4882a593Smuzhiyun 	0xea, 0xe9, 0xf5, 0xac, 0x51, 0x30, 0xb4, 0x25, 0x11, 0x50, 0x54, 0xa5,
551*4882a593Smuzhiyun 	0x4b, 0x00, 0x81, 0x80, 0xa9, 0x40, 0x71, 0x4f, 0xb3, 0x00, 0x01, 0x01,
552*4882a593Smuzhiyun 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0,
553*4882a593Smuzhiyun 	0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x46, 0x6c, 0x21, 0x00, 0x00, 0x1a,
554*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0xff, 0x00, 0x47, 0x4e, 0x37, 0x32, 0x31, 0x39, 0x35,
555*4882a593Smuzhiyun 	0x52, 0x30, 0x33, 0x55, 0x53, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44,
556*4882a593Smuzhiyun 	0x45, 0x4c, 0x4c, 0x20, 0x32, 0x37, 0x30, 0x39, 0x57, 0x0a, 0x20, 0x20,
557*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4c, 0x1e, 0x53, 0x11, 0x00, 0x0a,
558*4882a593Smuzhiyun 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x8d
559*4882a593Smuzhiyun };
560*4882a593Smuzhiyun 
oaktrail_hdmi_get_modes(struct drm_connector * connector)561*4882a593Smuzhiyun static int oaktrail_hdmi_get_modes(struct drm_connector *connector)
562*4882a593Smuzhiyun {
563*4882a593Smuzhiyun 	struct i2c_adapter *i2c_adap;
564*4882a593Smuzhiyun 	struct edid *edid;
565*4882a593Smuzhiyun 	int ret = 0;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	/*
568*4882a593Smuzhiyun 	 *	FIXME: We need to figure this lot out. In theory we can
569*4882a593Smuzhiyun 	 *	read the EDID somehow but I've yet to find working reference
570*4882a593Smuzhiyun 	 *	code.
571*4882a593Smuzhiyun 	 */
572*4882a593Smuzhiyun 	i2c_adap = i2c_get_adapter(3);
573*4882a593Smuzhiyun 	if (i2c_adap == NULL) {
574*4882a593Smuzhiyun 		DRM_ERROR("No ddc adapter available!\n");
575*4882a593Smuzhiyun 		edid = (struct edid *)raw_edid;
576*4882a593Smuzhiyun 	} else {
577*4882a593Smuzhiyun 		edid = (struct edid *)raw_edid;
578*4882a593Smuzhiyun 		/* FIXME ? edid = drm_get_edid(connector, i2c_adap); */
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	if (edid) {
582*4882a593Smuzhiyun 		drm_connector_update_edid_property(connector, edid);
583*4882a593Smuzhiyun 		ret = drm_add_edid_modes(connector, edid);
584*4882a593Smuzhiyun 	}
585*4882a593Smuzhiyun 	return ret;
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun 
oaktrail_hdmi_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)588*4882a593Smuzhiyun static void oaktrail_hdmi_mode_set(struct drm_encoder *encoder,
589*4882a593Smuzhiyun 			       struct drm_display_mode *mode,
590*4882a593Smuzhiyun 			       struct drm_display_mode *adjusted_mode)
591*4882a593Smuzhiyun {
592*4882a593Smuzhiyun 	struct drm_device *dev = encoder->dev;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	oaktrail_hdmi_audio_enable(dev);
595*4882a593Smuzhiyun 	return;
596*4882a593Smuzhiyun }
597*4882a593Smuzhiyun 
oaktrail_hdmi_destroy(struct drm_connector * connector)598*4882a593Smuzhiyun static void oaktrail_hdmi_destroy(struct drm_connector *connector)
599*4882a593Smuzhiyun {
600*4882a593Smuzhiyun 	return;
601*4882a593Smuzhiyun }
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = {
604*4882a593Smuzhiyun 	.dpms = oaktrail_hdmi_dpms,
605*4882a593Smuzhiyun 	.prepare = gma_encoder_prepare,
606*4882a593Smuzhiyun 	.mode_set = oaktrail_hdmi_mode_set,
607*4882a593Smuzhiyun 	.commit = gma_encoder_commit,
608*4882a593Smuzhiyun };
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun static const struct drm_connector_helper_funcs
611*4882a593Smuzhiyun 					oaktrail_hdmi_connector_helper_funcs = {
612*4882a593Smuzhiyun 	.get_modes = oaktrail_hdmi_get_modes,
613*4882a593Smuzhiyun 	.mode_valid = oaktrail_hdmi_mode_valid,
614*4882a593Smuzhiyun 	.best_encoder = gma_best_encoder,
615*4882a593Smuzhiyun };
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun static const struct drm_connector_funcs oaktrail_hdmi_connector_funcs = {
618*4882a593Smuzhiyun 	.dpms = drm_helper_connector_dpms,
619*4882a593Smuzhiyun 	.detect = oaktrail_hdmi_detect,
620*4882a593Smuzhiyun 	.fill_modes = drm_helper_probe_single_connector_modes,
621*4882a593Smuzhiyun 	.destroy = oaktrail_hdmi_destroy,
622*4882a593Smuzhiyun };
623*4882a593Smuzhiyun 
oaktrail_hdmi_init(struct drm_device * dev,struct psb_intel_mode_device * mode_dev)624*4882a593Smuzhiyun void oaktrail_hdmi_init(struct drm_device *dev,
625*4882a593Smuzhiyun 					struct psb_intel_mode_device *mode_dev)
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun 	struct gma_encoder *gma_encoder;
628*4882a593Smuzhiyun 	struct gma_connector *gma_connector;
629*4882a593Smuzhiyun 	struct drm_connector *connector;
630*4882a593Smuzhiyun 	struct drm_encoder *encoder;
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL);
633*4882a593Smuzhiyun 	if (!gma_encoder)
634*4882a593Smuzhiyun 		return;
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL);
637*4882a593Smuzhiyun 	if (!gma_connector)
638*4882a593Smuzhiyun 		goto failed_connector;
639*4882a593Smuzhiyun 
640*4882a593Smuzhiyun 	connector = &gma_connector->base;
641*4882a593Smuzhiyun 	encoder = &gma_encoder->base;
642*4882a593Smuzhiyun 	drm_connector_init(dev, connector,
643*4882a593Smuzhiyun 			   &oaktrail_hdmi_connector_funcs,
644*4882a593Smuzhiyun 			   DRM_MODE_CONNECTOR_DVID);
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	gma_connector_attach_encoder(gma_connector, gma_encoder);
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	gma_encoder->type = INTEL_OUTPUT_HDMI;
651*4882a593Smuzhiyun 	drm_encoder_helper_add(encoder, &oaktrail_hdmi_helper_funcs);
652*4882a593Smuzhiyun 	drm_connector_helper_add(connector, &oaktrail_hdmi_connector_helper_funcs);
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
655*4882a593Smuzhiyun 	connector->interlace_allowed = false;
656*4882a593Smuzhiyun 	connector->doublescan_allowed = false;
657*4882a593Smuzhiyun 	drm_connector_register(connector);
658*4882a593Smuzhiyun 	dev_info(dev->dev, "HDMI initialised.\n");
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	return;
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun failed_connector:
663*4882a593Smuzhiyun 	kfree(gma_encoder);
664*4882a593Smuzhiyun }
665*4882a593Smuzhiyun 
oaktrail_hdmi_setup(struct drm_device * dev)666*4882a593Smuzhiyun void oaktrail_hdmi_setup(struct drm_device *dev)
667*4882a593Smuzhiyun {
668*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
669*4882a593Smuzhiyun 	struct pci_dev *pdev;
670*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev;
671*4882a593Smuzhiyun 	int ret;
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x080d, NULL);
674*4882a593Smuzhiyun 	if (!pdev)
675*4882a593Smuzhiyun 		return;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	hdmi_dev = kzalloc(sizeof(struct oaktrail_hdmi_dev), GFP_KERNEL);
678*4882a593Smuzhiyun 	if (!hdmi_dev) {
679*4882a593Smuzhiyun 		dev_err(dev->dev, "failed to allocate memory\n");
680*4882a593Smuzhiyun 		goto out;
681*4882a593Smuzhiyun 	}
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	ret = pci_enable_device(pdev);
685*4882a593Smuzhiyun 	if (ret) {
686*4882a593Smuzhiyun 		dev_err(dev->dev, "failed to enable hdmi controller\n");
687*4882a593Smuzhiyun 		goto free;
688*4882a593Smuzhiyun 	}
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 	hdmi_dev->mmio = pci_resource_start(pdev, 0);
691*4882a593Smuzhiyun 	hdmi_dev->mmio_len = pci_resource_len(pdev, 0);
692*4882a593Smuzhiyun 	hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len);
693*4882a593Smuzhiyun 	if (!hdmi_dev->regs) {
694*4882a593Smuzhiyun 		dev_err(dev->dev, "failed to map hdmi mmio\n");
695*4882a593Smuzhiyun 		goto free;
696*4882a593Smuzhiyun 	}
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	hdmi_dev->dev = pdev;
699*4882a593Smuzhiyun 	pci_set_drvdata(pdev, hdmi_dev);
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 	/* Initialize i2c controller */
702*4882a593Smuzhiyun 	ret = oaktrail_hdmi_i2c_init(hdmi_dev->dev);
703*4882a593Smuzhiyun 	if (ret)
704*4882a593Smuzhiyun 		dev_err(dev->dev, "HDMI I2C initialization failed\n");
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun 	dev_priv->hdmi_priv = hdmi_dev;
707*4882a593Smuzhiyun 	oaktrail_hdmi_audio_disable(dev);
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun 	dev_info(dev->dev, "HDMI hardware present.\n");
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 	return;
712*4882a593Smuzhiyun 
713*4882a593Smuzhiyun free:
714*4882a593Smuzhiyun 	kfree(hdmi_dev);
715*4882a593Smuzhiyun out:
716*4882a593Smuzhiyun 	return;
717*4882a593Smuzhiyun }
718*4882a593Smuzhiyun 
oaktrail_hdmi_teardown(struct drm_device * dev)719*4882a593Smuzhiyun void oaktrail_hdmi_teardown(struct drm_device *dev)
720*4882a593Smuzhiyun {
721*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
722*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
723*4882a593Smuzhiyun 	struct pci_dev *pdev;
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun 	if (hdmi_dev) {
726*4882a593Smuzhiyun 		pdev = hdmi_dev->dev;
727*4882a593Smuzhiyun 		pci_set_drvdata(pdev, NULL);
728*4882a593Smuzhiyun 		oaktrail_hdmi_i2c_exit(pdev);
729*4882a593Smuzhiyun 		iounmap(hdmi_dev->regs);
730*4882a593Smuzhiyun 		kfree(hdmi_dev);
731*4882a593Smuzhiyun 		pci_dev_put(pdev);
732*4882a593Smuzhiyun 	}
733*4882a593Smuzhiyun }
734*4882a593Smuzhiyun 
735*4882a593Smuzhiyun /* save HDMI register state */
oaktrail_hdmi_save(struct drm_device * dev)736*4882a593Smuzhiyun void oaktrail_hdmi_save(struct drm_device *dev)
737*4882a593Smuzhiyun {
738*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
739*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
740*4882a593Smuzhiyun 	struct psb_state *regs = &dev_priv->regs.psb;
741*4882a593Smuzhiyun 	struct psb_pipe *pipeb = &dev_priv->regs.pipe[1];
742*4882a593Smuzhiyun 	int i;
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	/* dpll */
745*4882a593Smuzhiyun 	hdmi_dev->saveDPLL_CTRL = PSB_RVDC32(DPLL_CTRL);
746*4882a593Smuzhiyun 	hdmi_dev->saveDPLL_DIV_CTRL = PSB_RVDC32(DPLL_DIV_CTRL);
747*4882a593Smuzhiyun 	hdmi_dev->saveDPLL_ADJUST = PSB_RVDC32(DPLL_ADJUST);
748*4882a593Smuzhiyun 	hdmi_dev->saveDPLL_UPDATE = PSB_RVDC32(DPLL_UPDATE);
749*4882a593Smuzhiyun 	hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE);
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	/* pipe B */
752*4882a593Smuzhiyun 	pipeb->conf = PSB_RVDC32(PIPEBCONF);
753*4882a593Smuzhiyun 	pipeb->src = PSB_RVDC32(PIPEBSRC);
754*4882a593Smuzhiyun 	pipeb->htotal = PSB_RVDC32(HTOTAL_B);
755*4882a593Smuzhiyun 	pipeb->hblank = PSB_RVDC32(HBLANK_B);
756*4882a593Smuzhiyun 	pipeb->hsync = PSB_RVDC32(HSYNC_B);
757*4882a593Smuzhiyun 	pipeb->vtotal = PSB_RVDC32(VTOTAL_B);
758*4882a593Smuzhiyun 	pipeb->vblank = PSB_RVDC32(VBLANK_B);
759*4882a593Smuzhiyun 	pipeb->vsync = PSB_RVDC32(VSYNC_B);
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun 	hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF);
762*4882a593Smuzhiyun 	hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC);
763*4882a593Smuzhiyun 	hdmi_dev->savePCH_HTOTAL_B = PSB_RVDC32(PCH_HTOTAL_B);
764*4882a593Smuzhiyun 	hdmi_dev->savePCH_HBLANK_B = PSB_RVDC32(PCH_HBLANK_B);
765*4882a593Smuzhiyun 	hdmi_dev->savePCH_HSYNC_B  = PSB_RVDC32(PCH_HSYNC_B);
766*4882a593Smuzhiyun 	hdmi_dev->savePCH_VTOTAL_B = PSB_RVDC32(PCH_VTOTAL_B);
767*4882a593Smuzhiyun 	hdmi_dev->savePCH_VBLANK_B = PSB_RVDC32(PCH_VBLANK_B);
768*4882a593Smuzhiyun 	hdmi_dev->savePCH_VSYNC_B  = PSB_RVDC32(PCH_VSYNC_B);
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 	/* plane */
771*4882a593Smuzhiyun 	pipeb->cntr = PSB_RVDC32(DSPBCNTR);
772*4882a593Smuzhiyun 	pipeb->stride = PSB_RVDC32(DSPBSTRIDE);
773*4882a593Smuzhiyun 	pipeb->addr = PSB_RVDC32(DSPBBASE);
774*4882a593Smuzhiyun 	pipeb->surf = PSB_RVDC32(DSPBSURF);
775*4882a593Smuzhiyun 	pipeb->linoff = PSB_RVDC32(DSPBLINOFF);
776*4882a593Smuzhiyun 	pipeb->tileoff = PSB_RVDC32(DSPBTILEOFF);
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun 	/* cursor B */
779*4882a593Smuzhiyun 	regs->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR);
780*4882a593Smuzhiyun 	regs->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE);
781*4882a593Smuzhiyun 	regs->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS);
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	/* save palette */
784*4882a593Smuzhiyun 	for (i = 0; i < 256; i++)
785*4882a593Smuzhiyun 		pipeb->palette[i] = PSB_RVDC32(PALETTE_B + (i << 2));
786*4882a593Smuzhiyun }
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun /* restore HDMI register state */
oaktrail_hdmi_restore(struct drm_device * dev)789*4882a593Smuzhiyun void oaktrail_hdmi_restore(struct drm_device *dev)
790*4882a593Smuzhiyun {
791*4882a593Smuzhiyun 	struct drm_psb_private *dev_priv = dev->dev_private;
792*4882a593Smuzhiyun 	struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
793*4882a593Smuzhiyun 	struct psb_state *regs = &dev_priv->regs.psb;
794*4882a593Smuzhiyun 	struct psb_pipe *pipeb = &dev_priv->regs.pipe[1];
795*4882a593Smuzhiyun 	int i;
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun 	/* dpll */
798*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->saveDPLL_CTRL, DPLL_CTRL);
799*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->saveDPLL_DIV_CTRL, DPLL_DIV_CTRL);
800*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->saveDPLL_ADJUST, DPLL_ADJUST);
801*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->saveDPLL_UPDATE, DPLL_UPDATE);
802*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->saveDPLL_CLK_ENABLE, DPLL_CLK_ENABLE);
803*4882a593Smuzhiyun 	udelay(150);
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	/* pipe */
806*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->src, PIPEBSRC);
807*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->htotal, HTOTAL_B);
808*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->hblank, HBLANK_B);
809*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->hsync,  HSYNC_B);
810*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->vtotal, VTOTAL_B);
811*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->vblank, VBLANK_B);
812*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->vsync,  VSYNC_B);
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC);
815*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B);
816*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->savePCH_HBLANK_B, PCH_HBLANK_B);
817*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->savePCH_HSYNC_B,  PCH_HSYNC_B);
818*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->savePCH_VTOTAL_B, PCH_VTOTAL_B);
819*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B);
820*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B,  PCH_VSYNC_B);
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->conf, PIPEBCONF);
823*4882a593Smuzhiyun 	PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF);
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	/* plane */
826*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->linoff, DSPBLINOFF);
827*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->stride, DSPBSTRIDE);
828*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->tileoff, DSPBTILEOFF);
829*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->cntr, DSPBCNTR);
830*4882a593Smuzhiyun 	PSB_WVDC32(pipeb->surf, DSPBSURF);
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 	/* cursor B */
833*4882a593Smuzhiyun 	PSB_WVDC32(regs->saveDSPBCURSOR_CTRL, CURBCNTR);
834*4882a593Smuzhiyun 	PSB_WVDC32(regs->saveDSPBCURSOR_POS, CURBPOS);
835*4882a593Smuzhiyun 	PSB_WVDC32(regs->saveDSPBCURSOR_BASE, CURBBASE);
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	/* restore palette */
838*4882a593Smuzhiyun 	for (i = 0; i < 256; i++)
839*4882a593Smuzhiyun 		PSB_WVDC32(pipeb->palette[i], PALETTE_B + (i << 2));
840*4882a593Smuzhiyun }
841