Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mdfld_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 "psb_drv.h"
21 #include "mid_bios.h"
22 #include "mdfld_output.h"
23 #include "mdfld_dsi_output.h"
24 #include "tc35876x-dsi-lvds.h"
25 
26 #include <asm/intel_scu_ipc.h>
27 
28 #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
29 
30 #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF
31 #define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */
32 #define BLC_PWM_FREQ_CALC_CONSTANT 32
33 #define MHz 1000000
34 #define BRIGHTNESS_MIN_LEVEL 1
35 #define BRIGHTNESS_MAX_LEVEL 100
36 #define BRIGHTNESS_MASK 0xFF
37 #define BLC_POLARITY_NORMAL 0
38 #define BLC_POLARITY_INVERSE 1
39 #define BLC_ADJUSTMENT_MAX 100
40 
41 #define MDFLD_BLC_PWM_PRECISION_FACTOR 10
42 #define MDFLD_BLC_MAX_PWM_REG_FREQ 0xFFFE
43 #define MDFLD_BLC_MIN_PWM_REG_FREQ 0x2
44 
45 #define MDFLD_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
46 #define MDFLD_BACKLIGHT_PWM_CTL_SHIFT (16)
47 
48 static struct backlight_device *mdfld_backlight_device;
49 
51 {
52  struct drm_device *dev =
53  (struct drm_device *)bl_get_data(mdfld_backlight_device);
54  struct drm_psb_private *dev_priv = dev->dev_private;
55  int level = bd->props.brightness;
56 
57  DRM_DEBUG_DRIVER("backlight level set to %d\n", level);
58 
59  /* Perform value bounds checking */
60  if (level < BRIGHTNESS_MIN_LEVEL)
61  level = BRIGHTNESS_MIN_LEVEL;
62 
63  if (gma_power_begin(dev, false)) {
64  u32 adjusted_level = 0;
65 
66  /*
67  * Adjust the backlight level with the percent in
68  * dev_priv->blc_adj2
69  */
70  adjusted_level = level * dev_priv->blc_adj2;
71  adjusted_level = adjusted_level / BLC_ADJUSTMENT_MAX;
72  dev_priv->brightness_adjusted = adjusted_level;
73 
74  if (mdfld_get_panel_type(dev, 0) == TC35876X) {
75  if (dev_priv->dpi_panel_on[0] ||
76  dev_priv->dpi_panel_on[2])
78  dev_priv->brightness_adjusted);
79  } else {
80  if (dev_priv->dpi_panel_on[0])
82  dev_priv->brightness_adjusted);
83  }
84 
85  if (dev_priv->dpi_panel_on[2])
87  dev_priv->brightness_adjusted);
88  gma_power_end(dev);
89  }
90 
91  /* cache the brightness for later use */
92  dev_priv->brightness = level;
93  return 0;
94 }
95 
96 static int mdfld_get_brightness(struct backlight_device *bd)
97 {
98  struct drm_device *dev =
99  (struct drm_device *)bl_get_data(mdfld_backlight_device);
100  struct drm_psb_private *dev_priv = dev->dev_private;
101 
102  DRM_DEBUG_DRIVER("brightness = 0x%x \n", dev_priv->brightness);
103 
104  /* return locally cached var instead of HW read (due to DPST etc.) */
105  return dev_priv->brightness;
106 }
107 
108 static const struct backlight_ops mdfld_ops = {
109  .get_brightness = mdfld_get_brightness,
110  .update_status = mdfld_set_brightness,
111 };
112 
113 static int device_backlight_init(struct drm_device *dev)
114 {
115  struct drm_psb_private *dev_priv = (struct drm_psb_private *)
116  dev->dev_private;
117 
118  dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX;
119  dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX;
120 
121  return 0;
122 }
123 
124 static int mdfld_backlight_init(struct drm_device *dev)
125 {
126  struct backlight_properties props;
127  int ret = 0;
128 
129  memset(&props, 0, sizeof(struct backlight_properties));
130  props.max_brightness = BRIGHTNESS_MAX_LEVEL;
131  props.type = BACKLIGHT_PLATFORM;
132  mdfld_backlight_device = backlight_device_register("mdfld-bl",
133  NULL, (void *)dev, &mdfld_ops, &props);
134 
135  if (IS_ERR(mdfld_backlight_device))
136  return PTR_ERR(mdfld_backlight_device);
137 
138  ret = device_backlight_init(dev);
139  if (ret)
140  return ret;
141 
142  mdfld_backlight_device->props.brightness = BRIGHTNESS_MAX_LEVEL;
143  mdfld_backlight_device->props.max_brightness = BRIGHTNESS_MAX_LEVEL;
144  backlight_update_status(mdfld_backlight_device);
145  return 0;
146 }
147 #endif
148 
150 {
151 #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
152  return mdfld_backlight_device;
153 #else
154  return NULL;
155 #endif
156 }
157 
158 /*
159  * mdfld_save_display_registers
160  *
161  * Description: We are going to suspend so save current display
162  * register state.
163  *
164  * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio
165  */
166 static int mdfld_save_display_registers(struct drm_device *dev, int pipenum)
167 {
168  struct drm_psb_private *dev_priv = dev->dev_private;
169  struct medfield_state *regs = &dev_priv->regs.mdfld;
170  struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum];
171  const struct psb_offset *map = &dev_priv->regmap[pipenum];
172  int i;
173  u32 *mipi_val;
174 
175  /* register */
176  u32 mipi_reg = MIPI;
177 
178  switch (pipenum) {
179  case 0:
180  mipi_val = &regs->saveMIPI;
181  break;
182  case 1:
183  mipi_val = &regs->saveMIPI;
184  break;
185  case 2:
186  /* register */
187  mipi_reg = MIPI_C;
188  /* pointer to values */
189  mipi_val = &regs->saveMIPI_C;
190  break;
191  default:
192  DRM_ERROR("%s, invalid pipe number.\n", __func__);
193  return -EINVAL;
194  }
195 
196  /* Pipe & plane A info */
197  pipe->dpll = PSB_RVDC32(map->dpll);
198  pipe->fp0 = PSB_RVDC32(map->fp0);
199  pipe->conf = PSB_RVDC32(map->conf);
200  pipe->htotal = PSB_RVDC32(map->htotal);
201  pipe->hblank = PSB_RVDC32(map->hblank);
202  pipe->hsync = PSB_RVDC32(map->hsync);
203  pipe->vtotal = PSB_RVDC32(map->vtotal);
204  pipe->vblank = PSB_RVDC32(map->vblank);
205  pipe->vsync = PSB_RVDC32(map->vsync);
206  pipe->src = PSB_RVDC32(map->src);
207  pipe->stride = PSB_RVDC32(map->stride);
208  pipe->linoff = PSB_RVDC32(map->linoff);
209  pipe->tileoff = PSB_RVDC32(map->tileoff);
210  pipe->size = PSB_RVDC32(map->size);
211  pipe->pos = PSB_RVDC32(map->pos);
212  pipe->surf = PSB_RVDC32(map->surf);
213  pipe->cntr = PSB_RVDC32(map->cntr);
214  pipe->status = PSB_RVDC32(map->status);
215 
216  /*save palette (gamma) */
217  for (i = 0; i < 256; i++)
218  pipe->palette[i] = PSB_RVDC32(map->palette + (i << 2));
219 
220  if (pipenum == 1) {
223 
226  return 0;
227  }
228 
229  *mipi_val = PSB_RVDC32(mipi_reg);
230  return 0;
231 }
232 
233 /*
234  * mdfld_restore_display_registers
235  *
236  * Description: We are going to resume so restore display register state.
237  *
238  * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio
239  */
240 static int mdfld_restore_display_registers(struct drm_device *dev, int pipenum)
241 {
242  /* To get panel out of ULPS mode. */
243  u32 temp = 0;
244  u32 device_ready_reg = DEVICE_READY_REG;
245  struct drm_psb_private *dev_priv = dev->dev_private;
246  struct mdfld_dsi_config *dsi_config = NULL;
247  struct medfield_state *regs = &dev_priv->regs.mdfld;
248  struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum];
249  const struct psb_offset *map = &dev_priv->regmap[pipenum];
250  u32 i;
251  u32 dpll;
252  u32 timeout = 0;
253 
254  /* register */
255  u32 mipi_reg = MIPI;
256 
257  /* values */
258  u32 dpll_val = pipe->dpll;
259  u32 mipi_val = regs->saveMIPI;
260 
261  switch (pipenum) {
262  case 0:
263  dpll_val &= ~DPLL_VCO_ENABLE;
264  dsi_config = dev_priv->dsi_configs[0];
265  break;
266  case 1:
267  dpll_val &= ~DPLL_VCO_ENABLE;
268  break;
269  case 2:
270  mipi_reg = MIPI_C;
271  mipi_val = regs->saveMIPI_C;
272  dsi_config = dev_priv->dsi_configs[1];
273  break;
274  default:
275  DRM_ERROR("%s, invalid pipe number.\n", __func__);
276  return -EINVAL;
277  }
278 
279  /*make sure VGA plane is off. it initializes to on after reset!*/
280  PSB_WVDC32(0x80000000, VGACNTRL);
281 
282  if (pipenum == 1) {
283  PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, map->dpll);
284  PSB_RVDC32(map->dpll);
285 
286  PSB_WVDC32(pipe->fp0, map->fp0);
287  } else {
288 
289  dpll = PSB_RVDC32(map->dpll);
290 
291  if (!(dpll & DPLL_VCO_ENABLE)) {
292 
293  /* When ungating power of DPLL, needs to wait 0.5us
294  before enable the VCO */
295  if (dpll & MDFLD_PWR_GATE_EN) {
296  dpll &= ~MDFLD_PWR_GATE_EN;
297  PSB_WVDC32(dpll, map->dpll);
298  /* FIXME_MDFLD PO - change 500 to 1 after PO */
299  udelay(500);
300  }
301 
302  PSB_WVDC32(pipe->fp0, map->fp0);
303  PSB_WVDC32(dpll_val, map->dpll);
304  /* FIXME_MDFLD PO - change 500 to 1 after PO */
305  udelay(500);
306 
307  dpll_val |= DPLL_VCO_ENABLE;
308  PSB_WVDC32(dpll_val, map->dpll);
309  PSB_RVDC32(map->dpll);
310 
311  /* wait for DSI PLL to lock */
312  while (timeout < 20000 &&
313  !(PSB_RVDC32(map->conf) & PIPECONF_DSIPLL_LOCK)) {
314  udelay(150);
315  timeout++;
316  }
317 
318  if (timeout == 20000) {
319  DRM_ERROR("%s, can't lock DSIPLL.\n",
320  __func__);
321  return -EINVAL;
322  }
323  }
324  }
325  /* Restore mode */
326  PSB_WVDC32(pipe->htotal, map->htotal);
327  PSB_WVDC32(pipe->hblank, map->hblank);
328  PSB_WVDC32(pipe->hsync, map->hsync);
329  PSB_WVDC32(pipe->vtotal, map->vtotal);
330  PSB_WVDC32(pipe->vblank, map->vblank);
331  PSB_WVDC32(pipe->vsync, map->vsync);
332  PSB_WVDC32(pipe->src, map->src);
333  PSB_WVDC32(pipe->status, map->status);
334 
335  /*set up the plane*/
336  PSB_WVDC32(pipe->stride, map->stride);
337  PSB_WVDC32(pipe->linoff, map->linoff);
338  PSB_WVDC32(pipe->tileoff, map->tileoff);
339  PSB_WVDC32(pipe->size, map->size);
340  PSB_WVDC32(pipe->pos, map->pos);
341  PSB_WVDC32(pipe->surf, map->surf);
342 
343  if (pipenum == 1) {
344  /* restore palette (gamma) */
345  /*DRM_UDELAY(50000); */
346  for (i = 0; i < 256; i++)
347  PSB_WVDC32(pipe->palette[i], map->palette + (i << 2));
348 
351 
352  /*TODO: resume HDMI port */
353 
354  /*TODO: resume pipe*/
355 
356  /*enable the plane*/
357  PSB_WVDC32(pipe->cntr & ~DISPLAY_PLANE_ENABLE, map->cntr);
358 
359  return 0;
360  }
361 
362  /*set up pipe related registers*/
363  PSB_WVDC32(mipi_val, mipi_reg);
364 
365  /*setup MIPI adapter + MIPI IP registers*/
366  if (dsi_config)
367  mdfld_dsi_controller_init(dsi_config, pipenum);
368 
369  if (in_atomic() || in_interrupt())
370  mdelay(20);
371  else
372  msleep(20);
373 
374  /*enable the plane*/
375  PSB_WVDC32(pipe->cntr, map->cntr);
376 
377  if (in_atomic() || in_interrupt())
378  mdelay(20);
379  else
380  msleep(20);
381 
382  /* LP Hold Release */
383  temp = REG_READ(mipi_reg);
384  temp |= LP_OUTPUT_HOLD_RELEASE;
385  REG_WRITE(mipi_reg, temp);
386  mdelay(1);
387 
388 
389  /* Set DSI host to exit from Utra Low Power State */
390  temp = REG_READ(device_ready_reg);
391  temp &= ~ULPS_MASK;
392  temp |= 0x3;
393  temp |= EXIT_ULPS_DEV_READY;
394  REG_WRITE(device_ready_reg, temp);
395  mdelay(1);
396 
397  temp = REG_READ(device_ready_reg);
398  temp &= ~ULPS_MASK;
399  temp |= EXITING_ULPS;
400  REG_WRITE(device_ready_reg, temp);
401  mdelay(1);
402 
403  /*enable the pipe*/
404  PSB_WVDC32(pipe->conf, map->conf);
405 
406  /* restore palette (gamma) */
407  /*DRM_UDELAY(50000); */
408  for (i = 0; i < 256; i++)
409  PSB_WVDC32(pipe->palette[i], map->palette + (i << 2));
410 
411  return 0;
412 }
413 
414 static int mdfld_save_registers(struct drm_device *dev)
415 {
416  /* mdfld_save_cursor_overlay_registers(dev); */
417  mdfld_save_display_registers(dev, 0);
418  mdfld_save_display_registers(dev, 2);
419  mdfld_disable_crtc(dev, 0);
420  mdfld_disable_crtc(dev, 2);
421 
422  return 0;
423 }
424 
425 static int mdfld_restore_registers(struct drm_device *dev)
426 {
427  mdfld_restore_display_registers(dev, 2);
428  mdfld_restore_display_registers(dev, 0);
429  /* mdfld_restore_cursor_overlay_registers(dev); */
430 
431  return 0;
432 }
433 
434 static int mdfld_power_down(struct drm_device *dev)
435 {
436  /* FIXME */
437  return 0;
438 }
439 
440 static int mdfld_power_up(struct drm_device *dev)
441 {
442  /* FIXME */
443  return 0;
444 }
445 
446 /* Medfield */
447 static const struct psb_offset mdfld_regmap[3] = {
448  {
449  .fp0 = MRST_FPA0,
450  .fp1 = MRST_FPA1,
451  .cntr = DSPACNTR,
452  .conf = PIPEACONF,
453  .src = PIPEASRC,
454  .dpll = MRST_DPLL_A,
455  .htotal = HTOTAL_A,
456  .hblank = HBLANK_A,
457  .hsync = HSYNC_A,
458  .vtotal = VTOTAL_A,
459  .vblank = VBLANK_A,
460  .vsync = VSYNC_A,
461  .stride = DSPASTRIDE,
462  .size = DSPASIZE,
463  .pos = DSPAPOS,
464  .surf = DSPASURF,
465  .addr = MRST_DSPABASE,
466  .status = PIPEASTAT,
467  .linoff = DSPALINOFF,
468  .tileoff = DSPATILEOFF,
469  .palette = PALETTE_A,
470  },
471  {
472  .fp0 = MDFLD_DPLL_DIV0,
473  .cntr = DSPBCNTR,
474  .conf = PIPEBCONF,
475  .src = PIPEBSRC,
476  .dpll = MDFLD_DPLL_B,
477  .htotal = HTOTAL_B,
478  .hblank = HBLANK_B,
479  .hsync = HSYNC_B,
480  .vtotal = VTOTAL_B,
481  .vblank = VBLANK_B,
482  .vsync = VSYNC_B,
483  .stride = DSPBSTRIDE,
484  .size = DSPBSIZE,
485  .pos = DSPBPOS,
486  .surf = DSPBSURF,
487  .addr = MRST_DSPBBASE,
488  .status = PIPEBSTAT,
489  .linoff = DSPBLINOFF,
490  .tileoff = DSPBTILEOFF,
491  .palette = PALETTE_B,
492  },
493  {
494  .fp0 = MRST_FPA0, /* This is what the old code did ?? */
495  .cntr = DSPCCNTR,
496  .conf = PIPECCONF,
497  .src = PIPECSRC,
498  /* No DPLL_C */
499  .dpll = MRST_DPLL_A,
500  .htotal = HTOTAL_C,
501  .hblank = HBLANK_C,
502  .hsync = HSYNC_C,
503  .vtotal = VTOTAL_C,
504  .vblank = VBLANK_C,
505  .vsync = VSYNC_C,
506  .stride = DSPCSTRIDE,
507  .size = DSPBSIZE,
508  .pos = DSPCPOS,
509  .surf = DSPCSURF,
510  .addr = MDFLD_DSPCBASE,
511  .status = PIPECSTAT,
512  .linoff = DSPCLINOFF,
513  .tileoff = DSPCTILEOFF,
514  .palette = PALETTE_C,
515  },
516 };
517 
518 static int mdfld_chip_setup(struct drm_device *dev)
519 {
520  struct drm_psb_private *dev_priv = dev->dev_private;
521  if (pci_enable_msi(dev->pdev))
522  dev_warn(dev->dev, "Enabling MSI failed!\n");
523  dev_priv->regmap = mdfld_regmap;
524  return mid_chip_setup(dev);
525 }
526 
527 const struct psb_ops mdfld_chip_ops = {
528  .name = "mdfld",
529  .accel_2d = 0,
530  .pipes = 3,
531  .crtcs = 3,
532  .lvds_mask = (1 << 1),
533  .hdmi_mask = (1 << 1),
534  .cursor_needs_phys = 0,
535  .sgx_offset = MRST_SGX_OFFSET,
536 
537  .chip_setup = mdfld_chip_setup,
538  .crtc_helper = &mdfld_helper_funcs,
539  .crtc_funcs = &psb_intel_crtc_funcs,
540 
541  .output_init = mdfld_output_init,
542 
543 #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
544  .backlight_init = mdfld_backlight_init,
545 #endif
546 
547  .save_regs = mdfld_save_registers,
548  .restore_regs = mdfld_restore_registers,
549  .power_down = mdfld_power_down,
550  .power_up = mdfld_power_up,
551 };