1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /**************************************************************************
3*4882a593Smuzhiyun * Copyright (c) 2011, Intel Corporation.
4*4882a593Smuzhiyun * All Rights Reserved.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun **************************************************************************/
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/backlight.h>
9*4882a593Smuzhiyun #include <linux/delay.h>
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <drm/drm.h>
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include "cdv_device.h"
14*4882a593Smuzhiyun #include "gma_device.h"
15*4882a593Smuzhiyun #include "intel_bios.h"
16*4882a593Smuzhiyun #include "psb_drv.h"
17*4882a593Smuzhiyun #include "psb_intel_reg.h"
18*4882a593Smuzhiyun #include "psb_reg.h"
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define VGA_SR_INDEX 0x3c4
21*4882a593Smuzhiyun #define VGA_SR_DATA 0x3c5
22*4882a593Smuzhiyun
cdv_disable_vga(struct drm_device * dev)23*4882a593Smuzhiyun static void cdv_disable_vga(struct drm_device *dev)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun u8 sr1;
26*4882a593Smuzhiyun u32 vga_reg;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun vga_reg = VGACNTRL;
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun outb(1, VGA_SR_INDEX);
31*4882a593Smuzhiyun sr1 = inb(VGA_SR_DATA);
32*4882a593Smuzhiyun outb(sr1 | 1<<5, VGA_SR_DATA);
33*4882a593Smuzhiyun udelay(300);
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun REG_WRITE(vga_reg, VGA_DISP_DISABLE);
36*4882a593Smuzhiyun REG_READ(vga_reg);
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun
cdv_output_init(struct drm_device * dev)39*4882a593Smuzhiyun static int cdv_output_init(struct drm_device *dev)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun drm_mode_create_scaling_mode_property(dev);
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun cdv_disable_vga(dev);
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun cdv_intel_crt_init(dev, &dev_priv->mode_dev);
48*4882a593Smuzhiyun cdv_intel_lvds_init(dev, &dev_priv->mode_dev);
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun /* These bits indicate HDMI not SDVO on CDV */
51*4882a593Smuzhiyun if (REG_READ(SDVOB) & SDVO_DETECTED) {
52*4882a593Smuzhiyun cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB);
53*4882a593Smuzhiyun if (REG_READ(DP_B) & DP_DETECTED)
54*4882a593Smuzhiyun cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_B);
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun if (REG_READ(SDVOC) & SDVO_DETECTED) {
58*4882a593Smuzhiyun cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC);
59*4882a593Smuzhiyun if (REG_READ(DP_C) & DP_DETECTED)
60*4882a593Smuzhiyun cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_C);
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun return 0;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun /*
68*4882a593Smuzhiyun * Cedartrail Backlght Interfaces
69*4882a593Smuzhiyun */
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun static struct backlight_device *cdv_backlight_device;
72*4882a593Smuzhiyun
cdv_backlight_combination_mode(struct drm_device * dev)73*4882a593Smuzhiyun static int cdv_backlight_combination_mode(struct drm_device *dev)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun return REG_READ(BLC_PWM_CTL2) & PWM_LEGACY_MODE;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
cdv_get_max_backlight(struct drm_device * dev)78*4882a593Smuzhiyun static u32 cdv_get_max_backlight(struct drm_device *dev)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun u32 max = REG_READ(BLC_PWM_CTL);
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun if (max == 0) {
83*4882a593Smuzhiyun DRM_DEBUG_KMS("LVDS Panel PWM value is 0!\n");
84*4882a593Smuzhiyun /* i915 does this, I believe which means that we should not
85*4882a593Smuzhiyun * smash PWM control as firmware will take control of it. */
86*4882a593Smuzhiyun return 1;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun max >>= 16;
90*4882a593Smuzhiyun if (cdv_backlight_combination_mode(dev))
91*4882a593Smuzhiyun max *= 0xff;
92*4882a593Smuzhiyun return max;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
cdv_get_brightness(struct backlight_device * bd)95*4882a593Smuzhiyun static int cdv_get_brightness(struct backlight_device *bd)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun struct drm_device *dev = bl_get_data(bd);
98*4882a593Smuzhiyun u32 val = REG_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun if (cdv_backlight_combination_mode(dev)) {
101*4882a593Smuzhiyun u8 lbpc;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun val &= ~1;
104*4882a593Smuzhiyun pci_read_config_byte(dev->pdev, 0xF4, &lbpc);
105*4882a593Smuzhiyun val *= lbpc;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun return (val * 100)/cdv_get_max_backlight(dev);
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
cdv_set_brightness(struct backlight_device * bd)111*4882a593Smuzhiyun static int cdv_set_brightness(struct backlight_device *bd)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun struct drm_device *dev = bl_get_data(bd);
114*4882a593Smuzhiyun int level = bd->props.brightness;
115*4882a593Smuzhiyun u32 blc_pwm_ctl;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun /* Percentage 1-100% being valid */
118*4882a593Smuzhiyun if (level < 1)
119*4882a593Smuzhiyun level = 1;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun level *= cdv_get_max_backlight(dev);
122*4882a593Smuzhiyun level /= 100;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun if (cdv_backlight_combination_mode(dev)) {
125*4882a593Smuzhiyun u32 max = cdv_get_max_backlight(dev);
126*4882a593Smuzhiyun u8 lbpc;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun lbpc = level * 0xfe / max + 1;
129*4882a593Smuzhiyun level /= lbpc;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun pci_write_config_byte(dev->pdev, 0xF4, lbpc);
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun blc_pwm_ctl = REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
135*4882a593Smuzhiyun REG_WRITE(BLC_PWM_CTL, (blc_pwm_ctl |
136*4882a593Smuzhiyun (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
137*4882a593Smuzhiyun return 0;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun static const struct backlight_ops cdv_ops = {
141*4882a593Smuzhiyun .get_brightness = cdv_get_brightness,
142*4882a593Smuzhiyun .update_status = cdv_set_brightness,
143*4882a593Smuzhiyun };
144*4882a593Smuzhiyun
cdv_backlight_init(struct drm_device * dev)145*4882a593Smuzhiyun static int cdv_backlight_init(struct drm_device *dev)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
148*4882a593Smuzhiyun struct backlight_properties props;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun memset(&props, 0, sizeof(struct backlight_properties));
151*4882a593Smuzhiyun props.max_brightness = 100;
152*4882a593Smuzhiyun props.type = BACKLIGHT_PLATFORM;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun cdv_backlight_device = backlight_device_register("psb-bl",
155*4882a593Smuzhiyun NULL, (void *)dev, &cdv_ops, &props);
156*4882a593Smuzhiyun if (IS_ERR(cdv_backlight_device))
157*4882a593Smuzhiyun return PTR_ERR(cdv_backlight_device);
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun cdv_backlight_device->props.brightness =
160*4882a593Smuzhiyun cdv_get_brightness(cdv_backlight_device);
161*4882a593Smuzhiyun backlight_update_status(cdv_backlight_device);
162*4882a593Smuzhiyun dev_priv->backlight_device = cdv_backlight_device;
163*4882a593Smuzhiyun dev_priv->backlight_enabled = true;
164*4882a593Smuzhiyun return 0;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun #endif
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun /*
170*4882a593Smuzhiyun * Provide the Cedarview specific chip logic and low level methods
171*4882a593Smuzhiyun * for power management
172*4882a593Smuzhiyun *
173*4882a593Smuzhiyun * FIXME: we need to implement the apm/ospm base management bits
174*4882a593Smuzhiyun * for this and the MID devices.
175*4882a593Smuzhiyun */
176*4882a593Smuzhiyun
CDV_MSG_READ32(int domain,uint port,uint offset)177*4882a593Smuzhiyun static inline u32 CDV_MSG_READ32(int domain, uint port, uint offset)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun int mcr = (0x10<<24) | (port << 16) | (offset << 8);
180*4882a593Smuzhiyun uint32_t ret_val = 0;
181*4882a593Smuzhiyun struct pci_dev *pci_root = pci_get_domain_bus_and_slot(domain, 0, 0);
182*4882a593Smuzhiyun pci_write_config_dword(pci_root, 0xD0, mcr);
183*4882a593Smuzhiyun pci_read_config_dword(pci_root, 0xD4, &ret_val);
184*4882a593Smuzhiyun pci_dev_put(pci_root);
185*4882a593Smuzhiyun return ret_val;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
CDV_MSG_WRITE32(int domain,uint port,uint offset,u32 value)188*4882a593Smuzhiyun static inline void CDV_MSG_WRITE32(int domain, uint port, uint offset,
189*4882a593Smuzhiyun u32 value)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun int mcr = (0x11<<24) | (port << 16) | (offset << 8) | 0xF0;
192*4882a593Smuzhiyun struct pci_dev *pci_root = pci_get_domain_bus_and_slot(domain, 0, 0);
193*4882a593Smuzhiyun pci_write_config_dword(pci_root, 0xD4, value);
194*4882a593Smuzhiyun pci_write_config_dword(pci_root, 0xD0, mcr);
195*4882a593Smuzhiyun pci_dev_put(pci_root);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun #define PSB_PM_SSC 0x20
199*4882a593Smuzhiyun #define PSB_PM_SSS 0x30
200*4882a593Smuzhiyun #define PSB_PWRGT_GFX_ON 0x02
201*4882a593Smuzhiyun #define PSB_PWRGT_GFX_OFF 0x01
202*4882a593Smuzhiyun #define PSB_PWRGT_GFX_D0 0x00
203*4882a593Smuzhiyun #define PSB_PWRGT_GFX_D3 0x03
204*4882a593Smuzhiyun
cdv_init_pm(struct drm_device * dev)205*4882a593Smuzhiyun static void cdv_init_pm(struct drm_device *dev)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
208*4882a593Smuzhiyun u32 pwr_cnt;
209*4882a593Smuzhiyun int domain = pci_domain_nr(dev->pdev->bus);
210*4882a593Smuzhiyun int i;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun dev_priv->apm_base = CDV_MSG_READ32(domain, PSB_PUNIT_PORT,
213*4882a593Smuzhiyun PSB_APMBA) & 0xFFFF;
214*4882a593Smuzhiyun dev_priv->ospm_base = CDV_MSG_READ32(domain, PSB_PUNIT_PORT,
215*4882a593Smuzhiyun PSB_OSPMBA) & 0xFFFF;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun /* Power status */
218*4882a593Smuzhiyun pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun /* Enable the GPU */
221*4882a593Smuzhiyun pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
222*4882a593Smuzhiyun pwr_cnt |= PSB_PWRGT_GFX_ON;
223*4882a593Smuzhiyun outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* Wait for the GPU power */
226*4882a593Smuzhiyun for (i = 0; i < 5; i++) {
227*4882a593Smuzhiyun u32 pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
228*4882a593Smuzhiyun if ((pwr_sts & PSB_PWRGT_GFX_MASK) == 0)
229*4882a593Smuzhiyun return;
230*4882a593Smuzhiyun udelay(10);
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun dev_err(dev->dev, "GPU: power management timed out.\n");
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
cdv_errata(struct drm_device * dev)235*4882a593Smuzhiyun static void cdv_errata(struct drm_device *dev)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun /* Disable bonus launch.
238*4882a593Smuzhiyun * CPU and GPU competes for memory and display misses updates and
239*4882a593Smuzhiyun * flickers. Worst with dual core, dual displays.
240*4882a593Smuzhiyun *
241*4882a593Smuzhiyun * Fixes were done to Win 7 gfx driver to disable a feature called
242*4882a593Smuzhiyun * Bonus Launch to work around the issue, by degrading
243*4882a593Smuzhiyun * performance.
244*4882a593Smuzhiyun */
245*4882a593Smuzhiyun CDV_MSG_WRITE32(pci_domain_nr(dev->pdev->bus), 3, 0x30, 0x08027108);
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun /**
249*4882a593Smuzhiyun * cdv_save_display_registers - save registers lost on suspend
250*4882a593Smuzhiyun * @dev: our DRM device
251*4882a593Smuzhiyun *
252*4882a593Smuzhiyun * Save the state we need in order to be able to restore the interface
253*4882a593Smuzhiyun * upon resume from suspend
254*4882a593Smuzhiyun */
cdv_save_display_registers(struct drm_device * dev)255*4882a593Smuzhiyun static int cdv_save_display_registers(struct drm_device *dev)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
258*4882a593Smuzhiyun struct psb_save_area *regs = &dev_priv->regs;
259*4882a593Smuzhiyun struct drm_connector *connector;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun dev_dbg(dev->dev, "Saving GPU registers.\n");
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun pci_read_config_byte(dev->pdev, 0xF4, ®s->cdv.saveLBB);
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun regs->cdv.saveDSPCLK_GATE_D = REG_READ(DSPCLK_GATE_D);
266*4882a593Smuzhiyun regs->cdv.saveRAMCLK_GATE_D = REG_READ(RAMCLK_GATE_D);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun regs->cdv.saveDSPARB = REG_READ(DSPARB);
269*4882a593Smuzhiyun regs->cdv.saveDSPFW[0] = REG_READ(DSPFW1);
270*4882a593Smuzhiyun regs->cdv.saveDSPFW[1] = REG_READ(DSPFW2);
271*4882a593Smuzhiyun regs->cdv.saveDSPFW[2] = REG_READ(DSPFW3);
272*4882a593Smuzhiyun regs->cdv.saveDSPFW[3] = REG_READ(DSPFW4);
273*4882a593Smuzhiyun regs->cdv.saveDSPFW[4] = REG_READ(DSPFW5);
274*4882a593Smuzhiyun regs->cdv.saveDSPFW[5] = REG_READ(DSPFW6);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun regs->cdv.saveADPA = REG_READ(ADPA);
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun regs->cdv.savePP_CONTROL = REG_READ(PP_CONTROL);
279*4882a593Smuzhiyun regs->cdv.savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS);
280*4882a593Smuzhiyun regs->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
281*4882a593Smuzhiyun regs->saveBLC_PWM_CTL2 = REG_READ(BLC_PWM_CTL2);
282*4882a593Smuzhiyun regs->cdv.saveLVDS = REG_READ(LVDS);
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun regs->cdv.savePFIT_CONTROL = REG_READ(PFIT_CONTROL);
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun regs->cdv.savePP_ON_DELAYS = REG_READ(PP_ON_DELAYS);
287*4882a593Smuzhiyun regs->cdv.savePP_OFF_DELAYS = REG_READ(PP_OFF_DELAYS);
288*4882a593Smuzhiyun regs->cdv.savePP_CYCLE = REG_READ(PP_CYCLE);
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun regs->cdv.saveVGACNTRL = REG_READ(VGACNTRL);
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun regs->cdv.saveIER = REG_READ(PSB_INT_ENABLE_R);
293*4882a593Smuzhiyun regs->cdv.saveIMR = REG_READ(PSB_INT_MASK_R);
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun list_for_each_entry(connector, &dev->mode_config.connector_list, head)
296*4882a593Smuzhiyun connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun return 0;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun /**
302*4882a593Smuzhiyun * cdv_restore_display_registers - restore lost register state
303*4882a593Smuzhiyun * @dev: our DRM device
304*4882a593Smuzhiyun *
305*4882a593Smuzhiyun * Restore register state that was lost during suspend and resume.
306*4882a593Smuzhiyun *
307*4882a593Smuzhiyun * FIXME: review
308*4882a593Smuzhiyun */
cdv_restore_display_registers(struct drm_device * dev)309*4882a593Smuzhiyun static int cdv_restore_display_registers(struct drm_device *dev)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
312*4882a593Smuzhiyun struct psb_save_area *regs = &dev_priv->regs;
313*4882a593Smuzhiyun struct drm_connector *connector;
314*4882a593Smuzhiyun u32 temp;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun pci_write_config_byte(dev->pdev, 0xF4, regs->cdv.saveLBB);
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun REG_WRITE(DSPCLK_GATE_D, regs->cdv.saveDSPCLK_GATE_D);
319*4882a593Smuzhiyun REG_WRITE(RAMCLK_GATE_D, regs->cdv.saveRAMCLK_GATE_D);
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun /* BIOS does below anyway */
322*4882a593Smuzhiyun REG_WRITE(DPIO_CFG, 0);
323*4882a593Smuzhiyun REG_WRITE(DPIO_CFG, DPIO_MODE_SELECT_0 | DPIO_CMN_RESET_N);
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun temp = REG_READ(DPLL_A);
326*4882a593Smuzhiyun if ((temp & DPLL_SYNCLOCK_ENABLE) == 0) {
327*4882a593Smuzhiyun REG_WRITE(DPLL_A, temp | DPLL_SYNCLOCK_ENABLE);
328*4882a593Smuzhiyun REG_READ(DPLL_A);
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun temp = REG_READ(DPLL_B);
332*4882a593Smuzhiyun if ((temp & DPLL_SYNCLOCK_ENABLE) == 0) {
333*4882a593Smuzhiyun REG_WRITE(DPLL_B, temp | DPLL_SYNCLOCK_ENABLE);
334*4882a593Smuzhiyun REG_READ(DPLL_B);
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun udelay(500);
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun REG_WRITE(DSPFW1, regs->cdv.saveDSPFW[0]);
340*4882a593Smuzhiyun REG_WRITE(DSPFW2, regs->cdv.saveDSPFW[1]);
341*4882a593Smuzhiyun REG_WRITE(DSPFW3, regs->cdv.saveDSPFW[2]);
342*4882a593Smuzhiyun REG_WRITE(DSPFW4, regs->cdv.saveDSPFW[3]);
343*4882a593Smuzhiyun REG_WRITE(DSPFW5, regs->cdv.saveDSPFW[4]);
344*4882a593Smuzhiyun REG_WRITE(DSPFW6, regs->cdv.saveDSPFW[5]);
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun REG_WRITE(DSPARB, regs->cdv.saveDSPARB);
347*4882a593Smuzhiyun REG_WRITE(ADPA, regs->cdv.saveADPA);
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun REG_WRITE(BLC_PWM_CTL2, regs->saveBLC_PWM_CTL2);
350*4882a593Smuzhiyun REG_WRITE(LVDS, regs->cdv.saveLVDS);
351*4882a593Smuzhiyun REG_WRITE(PFIT_CONTROL, regs->cdv.savePFIT_CONTROL);
352*4882a593Smuzhiyun REG_WRITE(PFIT_PGM_RATIOS, regs->cdv.savePFIT_PGM_RATIOS);
353*4882a593Smuzhiyun REG_WRITE(BLC_PWM_CTL, regs->saveBLC_PWM_CTL);
354*4882a593Smuzhiyun REG_WRITE(PP_ON_DELAYS, regs->cdv.savePP_ON_DELAYS);
355*4882a593Smuzhiyun REG_WRITE(PP_OFF_DELAYS, regs->cdv.savePP_OFF_DELAYS);
356*4882a593Smuzhiyun REG_WRITE(PP_CYCLE, regs->cdv.savePP_CYCLE);
357*4882a593Smuzhiyun REG_WRITE(PP_CONTROL, regs->cdv.savePP_CONTROL);
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun REG_WRITE(VGACNTRL, regs->cdv.saveVGACNTRL);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun REG_WRITE(PSB_INT_ENABLE_R, regs->cdv.saveIER);
362*4882a593Smuzhiyun REG_WRITE(PSB_INT_MASK_R, regs->cdv.saveIMR);
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /* Fix arbitration bug */
365*4882a593Smuzhiyun cdv_errata(dev);
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun drm_mode_config_reset(dev);
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun list_for_each_entry(connector, &dev->mode_config.connector_list, head)
370*4882a593Smuzhiyun connector->funcs->dpms(connector, DRM_MODE_DPMS_ON);
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun /* Resume the modeset for every activated CRTC */
373*4882a593Smuzhiyun drm_helper_resume_force_mode(dev);
374*4882a593Smuzhiyun return 0;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
cdv_power_down(struct drm_device * dev)377*4882a593Smuzhiyun static int cdv_power_down(struct drm_device *dev)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
380*4882a593Smuzhiyun u32 pwr_cnt, pwr_mask, pwr_sts;
381*4882a593Smuzhiyun int tries = 5;
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
384*4882a593Smuzhiyun pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
385*4882a593Smuzhiyun pwr_cnt |= PSB_PWRGT_GFX_OFF;
386*4882a593Smuzhiyun pwr_mask = PSB_PWRGT_GFX_MASK;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun while (tries--) {
391*4882a593Smuzhiyun pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
392*4882a593Smuzhiyun if ((pwr_sts & pwr_mask) == PSB_PWRGT_GFX_D3)
393*4882a593Smuzhiyun return 0;
394*4882a593Smuzhiyun udelay(10);
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun return 0;
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun
cdv_power_up(struct drm_device * dev)399*4882a593Smuzhiyun static int cdv_power_up(struct drm_device *dev)
400*4882a593Smuzhiyun {
401*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
402*4882a593Smuzhiyun u32 pwr_cnt, pwr_mask, pwr_sts;
403*4882a593Smuzhiyun int tries = 5;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
406*4882a593Smuzhiyun pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
407*4882a593Smuzhiyun pwr_cnt |= PSB_PWRGT_GFX_ON;
408*4882a593Smuzhiyun pwr_mask = PSB_PWRGT_GFX_MASK;
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun while (tries--) {
413*4882a593Smuzhiyun pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
414*4882a593Smuzhiyun if ((pwr_sts & pwr_mask) == PSB_PWRGT_GFX_D0)
415*4882a593Smuzhiyun return 0;
416*4882a593Smuzhiyun udelay(10);
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun return 0;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
cdv_hotplug_work_func(struct work_struct * work)421*4882a593Smuzhiyun static void cdv_hotplug_work_func(struct work_struct *work)
422*4882a593Smuzhiyun {
423*4882a593Smuzhiyun struct drm_psb_private *dev_priv = container_of(work, struct drm_psb_private,
424*4882a593Smuzhiyun hotplug_work);
425*4882a593Smuzhiyun struct drm_device *dev = dev_priv->dev;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun /* Just fire off a uevent and let userspace tell us what to do */
428*4882a593Smuzhiyun drm_helper_hpd_irq_event(dev);
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun
431*4882a593Smuzhiyun /* The core driver has received a hotplug IRQ. We are in IRQ context
432*4882a593Smuzhiyun so extract the needed information and kick off queued processing */
433*4882a593Smuzhiyun
cdv_hotplug_event(struct drm_device * dev)434*4882a593Smuzhiyun static int cdv_hotplug_event(struct drm_device *dev)
435*4882a593Smuzhiyun {
436*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
437*4882a593Smuzhiyun schedule_work(&dev_priv->hotplug_work);
438*4882a593Smuzhiyun REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT));
439*4882a593Smuzhiyun return 1;
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun
cdv_hotplug_enable(struct drm_device * dev,bool on)442*4882a593Smuzhiyun static void cdv_hotplug_enable(struct drm_device *dev, bool on)
443*4882a593Smuzhiyun {
444*4882a593Smuzhiyun if (on) {
445*4882a593Smuzhiyun u32 hotplug = REG_READ(PORT_HOTPLUG_EN);
446*4882a593Smuzhiyun hotplug |= HDMIB_HOTPLUG_INT_EN | HDMIC_HOTPLUG_INT_EN |
447*4882a593Smuzhiyun HDMID_HOTPLUG_INT_EN | CRT_HOTPLUG_INT_EN;
448*4882a593Smuzhiyun REG_WRITE(PORT_HOTPLUG_EN, hotplug);
449*4882a593Smuzhiyun } else {
450*4882a593Smuzhiyun REG_WRITE(PORT_HOTPLUG_EN, 0);
451*4882a593Smuzhiyun REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT));
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun static const char *force_audio_names[] = {
456*4882a593Smuzhiyun "off",
457*4882a593Smuzhiyun "auto",
458*4882a593Smuzhiyun "on",
459*4882a593Smuzhiyun };
460*4882a593Smuzhiyun
cdv_intel_attach_force_audio_property(struct drm_connector * connector)461*4882a593Smuzhiyun void cdv_intel_attach_force_audio_property(struct drm_connector *connector)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun struct drm_device *dev = connector->dev;
464*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
465*4882a593Smuzhiyun struct drm_property *prop;
466*4882a593Smuzhiyun int i;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun prop = dev_priv->force_audio_property;
469*4882a593Smuzhiyun if (prop == NULL) {
470*4882a593Smuzhiyun prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
471*4882a593Smuzhiyun "audio",
472*4882a593Smuzhiyun ARRAY_SIZE(force_audio_names));
473*4882a593Smuzhiyun if (prop == NULL)
474*4882a593Smuzhiyun return;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(force_audio_names); i++)
477*4882a593Smuzhiyun drm_property_add_enum(prop, i-1, force_audio_names[i]);
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun dev_priv->force_audio_property = prop;
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun drm_object_attach_property(&connector->base, prop, 0);
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun static const char *broadcast_rgb_names[] = {
486*4882a593Smuzhiyun "Full",
487*4882a593Smuzhiyun "Limited 16:235",
488*4882a593Smuzhiyun };
489*4882a593Smuzhiyun
cdv_intel_attach_broadcast_rgb_property(struct drm_connector * connector)490*4882a593Smuzhiyun void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector)
491*4882a593Smuzhiyun {
492*4882a593Smuzhiyun struct drm_device *dev = connector->dev;
493*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
494*4882a593Smuzhiyun struct drm_property *prop;
495*4882a593Smuzhiyun int i;
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun prop = dev_priv->broadcast_rgb_property;
498*4882a593Smuzhiyun if (prop == NULL) {
499*4882a593Smuzhiyun prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
500*4882a593Smuzhiyun "Broadcast RGB",
501*4882a593Smuzhiyun ARRAY_SIZE(broadcast_rgb_names));
502*4882a593Smuzhiyun if (prop == NULL)
503*4882a593Smuzhiyun return;
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(broadcast_rgb_names); i++)
506*4882a593Smuzhiyun drm_property_add_enum(prop, i, broadcast_rgb_names[i]);
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun dev_priv->broadcast_rgb_property = prop;
509*4882a593Smuzhiyun }
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun drm_object_attach_property(&connector->base, prop, 0);
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun /* Cedarview */
515*4882a593Smuzhiyun static const struct psb_offset cdv_regmap[2] = {
516*4882a593Smuzhiyun {
517*4882a593Smuzhiyun .fp0 = FPA0,
518*4882a593Smuzhiyun .fp1 = FPA1,
519*4882a593Smuzhiyun .cntr = DSPACNTR,
520*4882a593Smuzhiyun .conf = PIPEACONF,
521*4882a593Smuzhiyun .src = PIPEASRC,
522*4882a593Smuzhiyun .dpll = DPLL_A,
523*4882a593Smuzhiyun .dpll_md = DPLL_A_MD,
524*4882a593Smuzhiyun .htotal = HTOTAL_A,
525*4882a593Smuzhiyun .hblank = HBLANK_A,
526*4882a593Smuzhiyun .hsync = HSYNC_A,
527*4882a593Smuzhiyun .vtotal = VTOTAL_A,
528*4882a593Smuzhiyun .vblank = VBLANK_A,
529*4882a593Smuzhiyun .vsync = VSYNC_A,
530*4882a593Smuzhiyun .stride = DSPASTRIDE,
531*4882a593Smuzhiyun .size = DSPASIZE,
532*4882a593Smuzhiyun .pos = DSPAPOS,
533*4882a593Smuzhiyun .base = DSPABASE,
534*4882a593Smuzhiyun .surf = DSPASURF,
535*4882a593Smuzhiyun .addr = DSPABASE,
536*4882a593Smuzhiyun .status = PIPEASTAT,
537*4882a593Smuzhiyun .linoff = DSPALINOFF,
538*4882a593Smuzhiyun .tileoff = DSPATILEOFF,
539*4882a593Smuzhiyun .palette = PALETTE_A,
540*4882a593Smuzhiyun },
541*4882a593Smuzhiyun {
542*4882a593Smuzhiyun .fp0 = FPB0,
543*4882a593Smuzhiyun .fp1 = FPB1,
544*4882a593Smuzhiyun .cntr = DSPBCNTR,
545*4882a593Smuzhiyun .conf = PIPEBCONF,
546*4882a593Smuzhiyun .src = PIPEBSRC,
547*4882a593Smuzhiyun .dpll = DPLL_B,
548*4882a593Smuzhiyun .dpll_md = DPLL_B_MD,
549*4882a593Smuzhiyun .htotal = HTOTAL_B,
550*4882a593Smuzhiyun .hblank = HBLANK_B,
551*4882a593Smuzhiyun .hsync = HSYNC_B,
552*4882a593Smuzhiyun .vtotal = VTOTAL_B,
553*4882a593Smuzhiyun .vblank = VBLANK_B,
554*4882a593Smuzhiyun .vsync = VSYNC_B,
555*4882a593Smuzhiyun .stride = DSPBSTRIDE,
556*4882a593Smuzhiyun .size = DSPBSIZE,
557*4882a593Smuzhiyun .pos = DSPBPOS,
558*4882a593Smuzhiyun .base = DSPBBASE,
559*4882a593Smuzhiyun .surf = DSPBSURF,
560*4882a593Smuzhiyun .addr = DSPBBASE,
561*4882a593Smuzhiyun .status = PIPEBSTAT,
562*4882a593Smuzhiyun .linoff = DSPBLINOFF,
563*4882a593Smuzhiyun .tileoff = DSPBTILEOFF,
564*4882a593Smuzhiyun .palette = PALETTE_B,
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun };
567*4882a593Smuzhiyun
cdv_chip_setup(struct drm_device * dev)568*4882a593Smuzhiyun static int cdv_chip_setup(struct drm_device *dev)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
571*4882a593Smuzhiyun INIT_WORK(&dev_priv->hotplug_work, cdv_hotplug_work_func);
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun if (pci_enable_msi(dev->pdev))
574*4882a593Smuzhiyun dev_warn(dev->dev, "Enabling MSI failed!\n");
575*4882a593Smuzhiyun dev_priv->regmap = cdv_regmap;
576*4882a593Smuzhiyun gma_get_core_freq(dev);
577*4882a593Smuzhiyun psb_intel_opregion_init(dev);
578*4882a593Smuzhiyun psb_intel_init_bios(dev);
579*4882a593Smuzhiyun cdv_hotplug_enable(dev, false);
580*4882a593Smuzhiyun return 0;
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun /* CDV is much like Poulsbo but has MID like SGX offsets and PM */
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun const struct psb_ops cdv_chip_ops = {
586*4882a593Smuzhiyun .name = "GMA3600/3650",
587*4882a593Smuzhiyun .accel_2d = 0,
588*4882a593Smuzhiyun .pipes = 2,
589*4882a593Smuzhiyun .crtcs = 2,
590*4882a593Smuzhiyun .hdmi_mask = (1 << 0) | (1 << 1),
591*4882a593Smuzhiyun .lvds_mask = (1 << 1),
592*4882a593Smuzhiyun .sdvo_mask = (1 << 0),
593*4882a593Smuzhiyun .cursor_needs_phys = 0,
594*4882a593Smuzhiyun .sgx_offset = MRST_SGX_OFFSET,
595*4882a593Smuzhiyun .chip_setup = cdv_chip_setup,
596*4882a593Smuzhiyun .errata = cdv_errata,
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun .crtc_helper = &cdv_intel_helper_funcs,
599*4882a593Smuzhiyun .crtc_funcs = &cdv_intel_crtc_funcs,
600*4882a593Smuzhiyun .clock_funcs = &cdv_clock_funcs,
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun .output_init = cdv_output_init,
603*4882a593Smuzhiyun .hotplug = cdv_hotplug_event,
604*4882a593Smuzhiyun .hotplug_enable = cdv_hotplug_enable,
605*4882a593Smuzhiyun
606*4882a593Smuzhiyun #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
607*4882a593Smuzhiyun .backlight_init = cdv_backlight_init,
608*4882a593Smuzhiyun #endif
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun .init_pm = cdv_init_pm,
611*4882a593Smuzhiyun .save_regs = cdv_save_display_registers,
612*4882a593Smuzhiyun .restore_regs = cdv_restore_display_registers,
613*4882a593Smuzhiyun .save_crtc = gma_crtc_save,
614*4882a593Smuzhiyun .restore_crtc = gma_crtc_restore,
615*4882a593Smuzhiyun .power_down = cdv_power_down,
616*4882a593Smuzhiyun .power_up = cdv_power_up,
617*4882a593Smuzhiyun .update_wm = cdv_update_wm,
618*4882a593Smuzhiyun .disable_sr = cdv_disable_sr,
619*4882a593Smuzhiyun };
620