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