diff options
author | Alan Cox <alan@linux.intel.com> | 2011-07-15 12:34:41 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-07-15 13:05:08 -0400 |
commit | 1e585b52fd8ea59792634bc31d227eb3456af22e (patch) | |
tree | b291fd77abd0622ec9a671158b8ef979ca233f2d | |
parent | 80e2f055e05b62d1132eadc934f83c6d81c7cfa4 (diff) |
gma500: Add the Oaktrail HDMI support
This differs enough from the Cedarview HDMI sufficiently to want to keep
them separated.
We need to sort out the power management for Oaktrail/Moorestown in order
to plumb this lot into the register handling logic.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/staging/gma500/Makefile | 4 | ||||
-rw-r--r-- | drivers/staging/gma500/mrst.h | 35 | ||||
-rw-r--r-- | drivers/staging/gma500/mrst_device.c | 246 | ||||
-rw-r--r-- | drivers/staging/gma500/mrst_hdmi.c | 852 | ||||
-rw-r--r-- | drivers/staging/gma500/mrst_hdmi_i2c.c | 327 | ||||
-rw-r--r-- | drivers/staging/gma500/psb_drv.c | 2 | ||||
-rw-r--r-- | drivers/staging/gma500/psb_drv.h | 4 |
7 files changed, 1401 insertions, 69 deletions
diff --git a/drivers/staging/gma500/Makefile b/drivers/staging/gma500/Makefile index e515edfb1d27..fe34f1831415 100644 --- a/drivers/staging/gma500/Makefile +++ b/drivers/staging/gma500/Makefile | |||
@@ -32,7 +32,9 @@ psb_gfx-$(CONFIG_DRM_PSB_CDV) += cdv_device.o \ | |||
32 | 32 | ||
33 | psb_gfx-$(CONFIG_DRM_PSB_MRST) += mrst_device.o \ | 33 | psb_gfx-$(CONFIG_DRM_PSB_MRST) += mrst_device.o \ |
34 | mrst_crtc.o \ | 34 | mrst_crtc.o \ |
35 | mrst_lvds.o | 35 | mrst_lvds.o \ |
36 | mrst_hdmi.o \ | ||
37 | mrst_hdmi_i2c.o | ||
36 | 38 | ||
37 | psb_gfx-$(CONFIG_DRM_PSB_MFLD) += mdfld_device.o \ | 39 | psb_gfx-$(CONFIG_DRM_PSB_MFLD) += mdfld_device.o \ |
38 | mdfld_output.o \ | 40 | mdfld_output.o \ |
diff --git a/drivers/staging/gma500/mrst.h b/drivers/staging/gma500/mrst.h index 7bacb9a0083a..b563dbc73104 100644 --- a/drivers/staging/gma500/mrst.h +++ b/drivers/staging/gma500/mrst.h | |||
@@ -215,3 +215,38 @@ struct mrst_gct_data { | |||
215 | 215 | ||
216 | #define GCT_R10_HEADER_SIZE 16 | 216 | #define GCT_R10_HEADER_SIZE 16 |
217 | #define GCT_R10_DISPLAY_DESC_SIZE 28 | 217 | #define GCT_R10_DISPLAY_DESC_SIZE 28 |
218 | |||
219 | /* | ||
220 | * Moorestown HDMI interfaces | ||
221 | */ | ||
222 | |||
223 | struct mrst_hdmi_dev { | ||
224 | struct pci_dev *dev; | ||
225 | void __iomem *regs; | ||
226 | unsigned int mmio, mmio_len; | ||
227 | int dpms_mode; | ||
228 | struct hdmi_i2c_dev *i2c_dev; | ||
229 | |||
230 | /* register state */ | ||
231 | u32 saveDPLL_CTRL; | ||
232 | u32 saveDPLL_DIV_CTRL; | ||
233 | u32 saveDPLL_ADJUST; | ||
234 | u32 saveDPLL_UPDATE; | ||
235 | u32 saveDPLL_CLK_ENABLE; | ||
236 | u32 savePCH_HTOTAL_B; | ||
237 | u32 savePCH_HBLANK_B; | ||
238 | u32 savePCH_HSYNC_B; | ||
239 | u32 savePCH_VTOTAL_B; | ||
240 | u32 savePCH_VBLANK_B; | ||
241 | u32 savePCH_VSYNC_B; | ||
242 | u32 savePCH_PIPEBCONF; | ||
243 | u32 savePCH_PIPEBSRC; | ||
244 | }; | ||
245 | |||
246 | extern void mrst_hdmi_setup(struct drm_device *dev); | ||
247 | extern void mrst_hdmi_teardown(struct drm_device *dev); | ||
248 | extern int mrst_hdmi_i2c_init(struct pci_dev *dev); | ||
249 | extern void mrst_hdmi_i2c_exit(struct pci_dev *dev); | ||
250 | extern void mrst_hdmi_save(struct drm_device *dev); | ||
251 | extern void mrst_hdmi_restore(struct drm_device *dev); | ||
252 | extern void mrst_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); | ||
diff --git a/drivers/staging/gma500/mrst_device.c b/drivers/staging/gma500/mrst_device.c index 3d525a032135..20677f9a2bde 100644 --- a/drivers/staging/gma500/mrst_device.c +++ b/drivers/staging/gma500/mrst_device.c | |||
@@ -38,12 +38,13 @@ static const struct psb_ops oaktrail_chip_ops; | |||
38 | static int mrst_output_init(struct drm_device *dev) | 38 | static int mrst_output_init(struct drm_device *dev) |
39 | { | 39 | { |
40 | struct drm_psb_private *dev_priv = dev->dev_private; | 40 | struct drm_psb_private *dev_priv = dev->dev_private; |
41 | if (dev_priv->iLVDS_enable) { | 41 | if (dev_priv->iLVDS_enable) |
42 | mrst_lvds_init(dev, &dev_priv->mode_dev); | 42 | mrst_lvds_init(dev, &dev_priv->mode_dev); |
43 | return 0; | 43 | else |
44 | } | 44 | dev_err(dev->dev, "DSI is not supported\n"); |
45 | dev_err(dev->dev, "DSI is not supported\n"); | 45 | if (dev_priv->hdmi_priv) |
46 | return -ENODEV; | 46 | mrst_hdmi_init(dev, &dev_priv->mode_dev); |
47 | return 0; | ||
47 | } | 48 | } |
48 | 49 | ||
49 | /* | 50 | /* |
@@ -195,8 +196,8 @@ static void mrst_init_pm(struct drm_device *dev) | |||
195 | static int mrst_save_display_registers(struct drm_device *dev) | 196 | static int mrst_save_display_registers(struct drm_device *dev) |
196 | { | 197 | { |
197 | struct drm_psb_private *dev_priv = dev->dev_private; | 198 | struct drm_psb_private *dev_priv = dev->dev_private; |
198 | struct drm_crtc *crtc; | 199 | int i; |
199 | struct drm_connector *connector; | 200 | u32 pp_stat; |
200 | 201 | ||
201 | /* Display arbitration control + watermarks */ | 202 | /* Display arbitration control + watermarks */ |
202 | dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); | 203 | dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); |
@@ -208,17 +209,91 @@ static int mrst_save_display_registers(struct drm_device *dev) | |||
208 | dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); | 209 | dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); |
209 | dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); | 210 | dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); |
210 | 211 | ||
211 | /* Save crtc and output state */ | 212 | /* Pipe & plane A info */ |
212 | mutex_lock(&dev->mode_config.mutex); | 213 | dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF); |
213 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 214 | dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC); |
214 | if (drm_helper_crtc_in_use(crtc)) | 215 | dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0); |
215 | crtc->funcs->save(crtc); | 216 | dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1); |
216 | } | 217 | dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A); |
218 | dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A); | ||
219 | dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A); | ||
220 | dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A); | ||
221 | dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A); | ||
222 | dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A); | ||
223 | dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A); | ||
224 | dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); | ||
225 | dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR); | ||
226 | dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE); | ||
227 | dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE); | ||
228 | dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF); | ||
229 | dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF); | ||
230 | dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF); | ||
231 | |||
232 | /* Save cursor regs */ | ||
233 | dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); | ||
234 | dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); | ||
235 | dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); | ||
236 | |||
237 | /* Save palette (gamma) */ | ||
238 | for (i = 0; i < 256; i++) | ||
239 | dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2)); | ||
240 | |||
241 | if (dev_priv->hdmi_priv) | ||
242 | mrst_hdmi_save(dev); | ||
243 | |||
244 | /* Save performance state */ | ||
245 | dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); | ||
246 | |||
247 | /* LVDS state */ | ||
248 | dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL); | ||
249 | dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); | ||
250 | dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); | ||
251 | dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); | ||
252 | dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); | ||
253 | dev_priv->saveLVDS = PSB_RVDC32(LVDS); | ||
254 | dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); | ||
255 | dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); | ||
256 | dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); | ||
257 | dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); | ||
258 | |||
259 | /* HW overlay */ | ||
260 | dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD); | ||
261 | dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); | ||
262 | dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); | ||
263 | dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); | ||
264 | dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); | ||
265 | dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); | ||
266 | dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); | ||
267 | |||
268 | /* DPST registers */ | ||
269 | dev_priv->saveHISTOGRAM_INT_CONTROL_REG = PSB_RVDC32(HISTOGRAM_INT_CONTROL); | ||
270 | dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG = PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); | ||
271 | dev_priv->savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); | ||
272 | |||
273 | if (dev_priv->iLVDS_enable) { | ||
274 | /* Shut down the panel */ | ||
275 | PSB_WVDC32(0, PP_CONTROL); | ||
217 | 276 | ||
218 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) | 277 | do { |
219 | connector->funcs->save(connector); | 278 | pp_stat = PSB_RVDC32(PP_STATUS); |
279 | } while (pp_stat & 0x80000000); | ||
220 | 280 | ||
221 | mutex_unlock(&dev->mode_config.mutex); | 281 | /* Turn off the plane */ |
282 | PSB_WVDC32(0x58000000, DSPACNTR); | ||
283 | /* Trigger the plane disable */ | ||
284 | PSB_WVDC32(0, DSPASURF); | ||
285 | |||
286 | /* Wait ~4 ticks */ | ||
287 | msleep(4); | ||
288 | |||
289 | /* Turn off pipe */ | ||
290 | PSB_WVDC32(0x0, PIPEACONF); | ||
291 | /* Wait ~8 ticks */ | ||
292 | msleep(8); | ||
293 | |||
294 | /* Turn off PLLs */ | ||
295 | PSB_WVDC32(0, MRST_DPLL_A); | ||
296 | } | ||
222 | return 0; | 297 | return 0; |
223 | } | 298 | } |
224 | 299 | ||
@@ -231,18 +306,8 @@ static int mrst_save_display_registers(struct drm_device *dev) | |||
231 | static int mrst_restore_display_registers(struct drm_device *dev) | 306 | static int mrst_restore_display_registers(struct drm_device *dev) |
232 | { | 307 | { |
233 | struct drm_psb_private *dev_priv = dev->dev_private; | 308 | struct drm_psb_private *dev_priv = dev->dev_private; |
234 | struct drm_crtc *crtc; | 309 | u32 pp_stat; |
235 | struct drm_connector *connector; | 310 | int i; |
236 | int pp_stat; | ||
237 | |||
238 | if (!dev_priv->iLVDS_enable) { | ||
239 | #ifdef CONFIG_X86_MRST | ||
240 | intel_scu_ipc_simple_command(IPC_MSG_PANEL_ON_OFF, | ||
241 | IPC_CMD_PANEL_ON); | ||
242 | /* FIXME: can we avoid this delay ? */ | ||
243 | msleep(2000); /* wait 2 seconds */ | ||
244 | #endif | ||
245 | } | ||
246 | 311 | ||
247 | /* Display arbitration + watermarks */ | 312 | /* Display arbitration + watermarks */ |
248 | PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); | 313 | PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); |
@@ -254,54 +319,92 @@ static int mrst_restore_display_registers(struct drm_device *dev) | |||
254 | PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); | 319 | PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); |
255 | PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); | 320 | PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); |
256 | 321 | ||
257 | /*make sure VGA plane is off. it initializes to on after reset!*/ | 322 | /* Make sure VGA plane is off. it initializes to on after reset!*/ |
258 | PSB_WVDC32(0x80000000, VGACNTRL); | 323 | PSB_WVDC32(0x80000000, VGACNTRL); |
259 | 324 | ||
260 | mutex_lock(&dev->mode_config.mutex); | 325 | /* set the plls */ |
261 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | 326 | PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0); |
262 | if (drm_helper_crtc_in_use(crtc)) | 327 | PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1); |
263 | crtc->funcs->restore(crtc); | ||
264 | 328 | ||
265 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) | 329 | /* Actually enable it */ |
266 | connector->funcs->restore(connector); | 330 | PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A); |
331 | DRM_UDELAY(150); | ||
267 | 332 | ||
268 | mutex_unlock(&dev->mode_config.mutex); | 333 | /* Restore mode */ |
334 | PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A); | ||
335 | PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A); | ||
336 | PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A); | ||
337 | PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A); | ||
338 | PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A); | ||
339 | PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A); | ||
340 | PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC); | ||
341 | PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A); | ||
269 | 342 | ||
270 | if (dev_priv->iLVDS_enable) { | 343 | /* Restore performance mode*/ |
271 | /*shutdown the panel*/ | 344 | PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE); |
272 | PSB_WVDC32(0, PP_CONTROL); | ||
273 | do { | ||
274 | pp_stat = PSB_RVDC32(PP_STATUS); | ||
275 | } while (pp_stat & 0x80000000); | ||
276 | 345 | ||
277 | /* Turn off the plane */ | 346 | /* Enable the pipe*/ |
278 | PSB_WVDC32(0x58000000, DSPACNTR); | 347 | if (dev_priv->iLVDS_enable) |
279 | PSB_WVDC32(0, DSPASURF);/*trigger the plane disable*/ | 348 | PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF); |
280 | /* Wait ~4 ticks */ | ||
281 | msleep(4); | ||
282 | /* Turn off pipe */ | ||
283 | PSB_WVDC32(0x0, PIPEACONF); | ||
284 | /* Wait ~8 ticks */ | ||
285 | msleep(8); | ||
286 | 349 | ||
287 | /* Turn off PLLs */ | 350 | /* Set up the plane*/ |
288 | PSB_WVDC32(0, MRST_DPLL_A); | 351 | PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF); |
289 | } else { | 352 | PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE); |
290 | PSB_WVDC32(DPI_SHUT_DOWN, DPI_CONTROL_REG); | 353 | PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF); |
291 | PSB_WVDC32(0x0, PIPEACONF); | 354 | |
292 | PSB_WVDC32(0x2faf0000, BLC_PWM_CTL); | 355 | /* Enable the plane */ |
293 | while (REG_READ(0x70008) & 0x40000000) | 356 | PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR); |
294 | cpu_relax(); | 357 | PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF); |
295 | while ((PSB_RVDC32(GEN_FIFO_STAT_REG) & DPI_FIFO_EMPTY) | 358 | |
296 | != DPI_FIFO_EMPTY) | 359 | /* Enable Cursor A */ |
297 | cpu_relax(); | 360 | PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR); |
298 | PSB_WVDC32(0, DEVICE_READY_REG); | 361 | PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS); |
299 | /* Turn off panel power */ | 362 | PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE); |
300 | #ifdef CONFIG_X86_MRST /* FIXME: kill define once modular */ | 363 | |
301 | intel_scu_ipc_simple_command(IPC_MSG_PANEL_ON_OFF, | 364 | /* Restore palette (gamma) */ |
302 | IPC_CMD_PANEL_OFF); | 365 | for (i = 0; i < 256; i++) |
303 | #endif | 366 | PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2)); |
367 | |||
368 | if (dev_priv->hdmi_priv) | ||
369 | mrst_hdmi_restore(dev); | ||
370 | |||
371 | if (dev_priv->iLVDS_enable) { | ||
372 | PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2); | ||
373 | PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/ | ||
374 | PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL); | ||
375 | PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); | ||
376 | PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); | ||
377 | PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL); | ||
378 | PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON); | ||
379 | PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF); | ||
380 | PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE); | ||
381 | PSB_WVDC32(dev_priv->savePP_CONTROL, PP_CONTROL); | ||
304 | } | 382 | } |
383 | |||
384 | /* Wait for cycle delay */ | ||
385 | do { | ||
386 | pp_stat = PSB_RVDC32(PP_STATUS); | ||
387 | } while (pp_stat & 0x08000000); | ||
388 | |||
389 | /* Wait for panel power up */ | ||
390 | do { | ||
391 | pp_stat = PSB_RVDC32(PP_STATUS); | ||
392 | } while (pp_stat & 0x10000000); | ||
393 | |||
394 | /* Restore HW overlay */ | ||
395 | PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD); | ||
396 | PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0); | ||
397 | PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1); | ||
398 | PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2); | ||
399 | PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3); | ||
400 | PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4); | ||
401 | PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5); | ||
402 | |||
403 | /* DPST registers */ | ||
404 | PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG, HISTOGRAM_INT_CONTROL); | ||
405 | PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG, HISTOGRAM_LOGIC_CONTROL); | ||
406 | PSB_WVDC32(dev_priv->savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); | ||
407 | |||
305 | return 0; | 408 | return 0; |
306 | } | 409 | } |
307 | 410 | ||
@@ -364,9 +467,15 @@ static int mrst_chip_setup(struct drm_device *dev) | |||
364 | return mid_chip_setup(dev); | 467 | return mid_chip_setup(dev); |
365 | #endif | 468 | #endif |
366 | dev_priv->ops = &oaktrail_chip_ops; | 469 | dev_priv->ops = &oaktrail_chip_ops; |
470 | mrst_hdmi_setup(dev); | ||
367 | /* Check - may be better to go via BIOS paths ? */ | 471 | /* Check - may be better to go via BIOS paths ? */ |
368 | return mid_chip_setup(dev); | 472 | return mid_chip_setup(dev); |
369 | } | 473 | } |
474 | |||
475 | static void oaktrail_teardown(struct drm_device *dev) | ||
476 | { | ||
477 | mrst_hdmi_teardown(dev); | ||
478 | } | ||
370 | 479 | ||
371 | const struct psb_ops mrst_chip_ops = { | 480 | const struct psb_ops mrst_chip_ops = { |
372 | .name = "Moorestown", | 481 | .name = "Moorestown", |
@@ -400,6 +509,7 @@ static const struct psb_ops oaktrail_chip_ops = { | |||
400 | .sgx_offset = MRST_SGX_OFFSET, | 509 | .sgx_offset = MRST_SGX_OFFSET, |
401 | 510 | ||
402 | .chip_setup = mid_chip_setup, | 511 | .chip_setup = mid_chip_setup, |
512 | .chip_teardown = oaktrail_teardown, | ||
403 | .crtc_helper = &mrst_helper_funcs, | 513 | .crtc_helper = &mrst_helper_funcs, |
404 | .crtc_funcs = &psb_intel_crtc_funcs, | 514 | .crtc_funcs = &psb_intel_crtc_funcs, |
405 | 515 | ||
diff --git a/drivers/staging/gma500/mrst_hdmi.c b/drivers/staging/gma500/mrst_hdmi.c new file mode 100644 index 000000000000..d6a517971ba8 --- /dev/null +++ b/drivers/staging/gma500/mrst_hdmi.c | |||
@@ -0,0 +1,852 @@ | |||
1 | /* | ||
2 | * Copyright © 2010 Intel Corporation | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice (including the next | ||
12 | * paragraph) shall be included in all copies or substantial portions of the | ||
13 | * Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
21 | * DEALINGS IN THE SOFTWARE. | ||
22 | * | ||
23 | * Authors: | ||
24 | * Li Peng <peng.li@intel.com> | ||
25 | */ | ||
26 | |||
27 | #include <drm/drmP.h> | ||
28 | #include <drm/drm.h> | ||
29 | #include "psb_intel_drv.h" | ||
30 | #include "psb_intel_reg.h" | ||
31 | #include "psb_drv.h" | ||
32 | |||
33 | #define HDMI_READ(reg) readl(hdmi_dev->regs + (reg)) | ||
34 | #define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg)) | ||
35 | |||
36 | #define HDMI_HCR 0x1000 | ||
37 | #define HCR_ENABLE_HDCP (1 << 5) | ||
38 | #define HCR_ENABLE_AUDIO (1 << 2) | ||
39 | #define HCR_ENABLE_PIXEL (1 << 1) | ||
40 | #define HCR_ENABLE_TMDS (1 << 0) | ||
41 | |||
42 | #define HDMI_HICR 0x1004 | ||
43 | #define HDMI_HSR 0x1008 | ||
44 | #define HDMI_HISR 0x100C | ||
45 | #define HDMI_DETECT_HDP (1 << 0) | ||
46 | |||
47 | #define HDMI_VIDEO_REG 0x3000 | ||
48 | #define HDMI_UNIT_EN (1 << 7) | ||
49 | #define HDMI_MODE_OUTPUT (1 << 0) | ||
50 | #define HDMI_HBLANK_A 0x3100 | ||
51 | |||
52 | #define HDMI_AUDIO_CTRL 0x4000 | ||
53 | #define HDMI_ENABLE_AUDIO (1 << 0) | ||
54 | |||
55 | #define PCH_HTOTAL_B 0x3100 | ||
56 | #define PCH_HBLANK_B 0x3104 | ||
57 | #define PCH_HSYNC_B 0x3108 | ||
58 | #define PCH_VTOTAL_B 0x310C | ||
59 | #define PCH_VBLANK_B 0x3110 | ||
60 | #define PCH_VSYNC_B 0x3114 | ||
61 | #define PCH_PIPEBSRC 0x311C | ||
62 | |||
63 | #define PCH_PIPEB_DSL 0x3800 | ||
64 | #define PCH_PIPEB_SLC 0x3804 | ||
65 | #define PCH_PIPEBCONF 0x3808 | ||
66 | #define PCH_PIPEBSTAT 0x3824 | ||
67 | |||
68 | #define CDVO_DFT 0x5000 | ||
69 | #define CDVO_SLEWRATE 0x5004 | ||
70 | #define CDVO_STRENGTH 0x5008 | ||
71 | #define CDVO_RCOMP 0x500C | ||
72 | |||
73 | #define DPLL_CTRL 0x6000 | ||
74 | #define DPLL_PDIV_SHIFT 16 | ||
75 | #define DPLL_PDIV_MASK (0xf << 16) | ||
76 | #define DPLL_PWRDN (1 << 4) | ||
77 | #define DPLL_RESET (1 << 3) | ||
78 | #define DPLL_FASTEN (1 << 2) | ||
79 | #define DPLL_ENSTAT (1 << 1) | ||
80 | #define DPLL_DITHEN (1 << 0) | ||
81 | |||
82 | #define DPLL_DIV_CTRL 0x6004 | ||
83 | #define DPLL_CLKF_MASK 0xffffffc0 | ||
84 | #define DPLL_CLKR_MASK (0x3f) | ||
85 | |||
86 | #define DPLL_CLK_ENABLE 0x6008 | ||
87 | #define DPLL_EN_DISP (1 << 31) | ||
88 | #define DPLL_SEL_HDMI (1 << 8) | ||
89 | #define DPLL_EN_HDMI (1 << 1) | ||
90 | #define DPLL_EN_VGA (1 << 0) | ||
91 | |||
92 | #define DPLL_ADJUST 0x600C | ||
93 | #define DPLL_STATUS 0x6010 | ||
94 | #define DPLL_UPDATE 0x6014 | ||
95 | #define DPLL_DFT 0x6020 | ||
96 | |||
97 | struct intel_range { | ||
98 | int min, max; | ||
99 | }; | ||
100 | |||
101 | struct mrst_hdmi_limit { | ||
102 | struct intel_range vco, np, nr, nf; | ||
103 | }; | ||
104 | |||
105 | struct mrst_hdmi_clock { | ||
106 | int np; | ||
107 | int nr; | ||
108 | int nf; | ||
109 | int dot; | ||
110 | }; | ||
111 | |||
112 | #define VCO_MIN 320000 | ||
113 | #define VCO_MAX 1650000 | ||
114 | #define NP_MIN 1 | ||
115 | #define NP_MAX 15 | ||
116 | #define NR_MIN 1 | ||
117 | #define NR_MAX 64 | ||
118 | #define NF_MIN 2 | ||
119 | #define NF_MAX 4095 | ||
120 | |||
121 | static const struct mrst_hdmi_limit mrst_hdmi_limit = { | ||
122 | .vco = { .min = VCO_MIN, .max = VCO_MAX }, | ||
123 | .np = { .min = NP_MIN, .max = NP_MAX }, | ||
124 | .nr = { .min = NR_MIN, .max = NR_MAX }, | ||
125 | .nf = { .min = NF_MIN, .max = NF_MAX }, | ||
126 | }; | ||
127 | |||
128 | static void wait_for_vblank(struct drm_device *dev) | ||
129 | { | ||
130 | /* FIXME: Can we do this as a sleep ? */ | ||
131 | /* Wait for 20ms, i.e. one cycle at 50hz. */ | ||
132 | udelay(20000); | ||
133 | } | ||
134 | |||
135 | static void scu_busy_loop(void *scu_base) | ||
136 | { | ||
137 | u32 status = 0; | ||
138 | u32 loop_count = 0; | ||
139 | |||
140 | status = readl(scu_base + 0x04); | ||
141 | while (status & 1) { | ||
142 | udelay(1); /* scu processing time is in few u secods */ | ||
143 | status = readl(scu_base + 0x04); | ||
144 | loop_count++; | ||
145 | /* break if scu doesn't reset busy bit after huge retry */ | ||
146 | if (loop_count > 1000) { | ||
147 | DRM_DEBUG_KMS("SCU IPC timed out"); | ||
148 | return; | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | static void mrst_hdmi_reset(struct drm_device *dev) | ||
154 | { | ||
155 | void *base; | ||
156 | /* FIXME: at least make these defines */ | ||
157 | unsigned int scu_ipc_mmio = 0xff11c000; | ||
158 | int scu_len = 1024; | ||
159 | |||
160 | base = ioremap((resource_size_t)scu_ipc_mmio, scu_len); | ||
161 | if (base == NULL) { | ||
162 | DRM_ERROR("failed to map SCU mmio\n"); | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | /* scu ipc: assert hdmi controller reset */ | ||
167 | writel(0xff11d118, base + 0x0c); | ||
168 | writel(0x7fffffdf, base + 0x80); | ||
169 | writel(0x42005, base + 0x0); | ||
170 | scu_busy_loop(base); | ||
171 | |||
172 | /* scu ipc: de-assert hdmi controller reset */ | ||
173 | writel(0xff11d118, base + 0x0c); | ||
174 | writel(0x7fffffff, base + 0x80); | ||
175 | writel(0x42005, base + 0x0); | ||
176 | scu_busy_loop(base); | ||
177 | |||
178 | iounmap(base); | ||
179 | } | ||
180 | |||
181 | static void mrst_hdmi_audio_enable(struct drm_device *dev) | ||
182 | { | ||
183 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
184 | struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
185 | |||
186 | HDMI_WRITE(HDMI_HCR, 0x67); | ||
187 | HDMI_READ(HDMI_HCR); | ||
188 | |||
189 | HDMI_WRITE(0x51a8, 0x10); | ||
190 | HDMI_READ(0x51a8); | ||
191 | |||
192 | HDMI_WRITE(HDMI_AUDIO_CTRL, 0x1); | ||
193 | HDMI_READ(HDMI_AUDIO_CTRL); | ||
194 | } | ||
195 | |||
196 | static void mrst_hdmi_audio_disable(struct drm_device *dev) | ||
197 | { | ||
198 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
199 | struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
200 | |||
201 | HDMI_WRITE(0x51a8, 0x0); | ||
202 | HDMI_READ(0x51a8); | ||
203 | |||
204 | HDMI_WRITE(HDMI_AUDIO_CTRL, 0x0); | ||
205 | HDMI_READ(HDMI_AUDIO_CTRL); | ||
206 | |||
207 | HDMI_WRITE(HDMI_HCR, 0x47); | ||
208 | HDMI_READ(HDMI_HCR); | ||
209 | } | ||
210 | |||
211 | void mrst_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode) | ||
212 | { | ||
213 | struct drm_device *dev = crtc->dev; | ||
214 | u32 temp; | ||
215 | |||
216 | switch (mode) { | ||
217 | case DRM_MODE_DPMS_OFF: | ||
218 | /* Disable VGACNTRL */ | ||
219 | REG_WRITE(VGACNTRL, 0x80000000); | ||
220 | |||
221 | /* Disable plane */ | ||
222 | temp = REG_READ(DSPBCNTR); | ||
223 | if ((temp & DISPLAY_PLANE_ENABLE) != 0) { | ||
224 | REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE); | ||
225 | REG_READ(DSPBCNTR); | ||
226 | /* Flush the plane changes */ | ||
227 | REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); | ||
228 | REG_READ(DSPBSURF); | ||
229 | } | ||
230 | |||
231 | /* Disable pipe B */ | ||
232 | temp = REG_READ(PIPEBCONF); | ||
233 | if ((temp & PIPEACONF_ENABLE) != 0) { | ||
234 | REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE); | ||
235 | REG_READ(PIPEBCONF); | ||
236 | } | ||
237 | |||
238 | /* Disable LNW Pipes, etc */ | ||
239 | temp = REG_READ(PCH_PIPEBCONF); | ||
240 | if ((temp & PIPEACONF_ENABLE) != 0) { | ||
241 | REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE); | ||
242 | REG_READ(PCH_PIPEBCONF); | ||
243 | } | ||
244 | /* wait for pipe off */ | ||
245 | udelay(150); | ||
246 | /* Disable dpll */ | ||
247 | temp = REG_READ(DPLL_CTRL); | ||
248 | if ((temp & DPLL_PWRDN) == 0) { | ||
249 | REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET)); | ||
250 | REG_WRITE(DPLL_STATUS, 0x1); | ||
251 | } | ||
252 | /* wait for dpll off */ | ||
253 | udelay(150); | ||
254 | break; | ||
255 | case DRM_MODE_DPMS_ON: | ||
256 | case DRM_MODE_DPMS_STANDBY: | ||
257 | case DRM_MODE_DPMS_SUSPEND: | ||
258 | /* Enable dpll */ | ||
259 | temp = REG_READ(DPLL_CTRL); | ||
260 | if ((temp & DPLL_PWRDN) != 0) { | ||
261 | REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET)); | ||
262 | temp = REG_READ(DPLL_CLK_ENABLE); | ||
263 | REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI); | ||
264 | REG_READ(DPLL_CLK_ENABLE); | ||
265 | } | ||
266 | /* wait for dpll warm up */ | ||
267 | udelay(150); | ||
268 | |||
269 | /* Enable pipe B */ | ||
270 | temp = REG_READ(PIPEBCONF); | ||
271 | if ((temp & PIPEACONF_ENABLE) == 0) { | ||
272 | REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE); | ||
273 | REG_READ(PIPEBCONF); | ||
274 | } | ||
275 | |||
276 | /* Enable LNW Pipe B */ | ||
277 | temp = REG_READ(PCH_PIPEBCONF); | ||
278 | if ((temp & PIPEACONF_ENABLE) == 0) { | ||
279 | REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE); | ||
280 | REG_READ(PCH_PIPEBCONF); | ||
281 | } | ||
282 | wait_for_vblank(dev); | ||
283 | |||
284 | /* Enable plane */ | ||
285 | temp = REG_READ(DSPBCNTR); | ||
286 | if ((temp & DISPLAY_PLANE_ENABLE) == 0) { | ||
287 | REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE); | ||
288 | /* Flush the plane changes */ | ||
289 | REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); | ||
290 | REG_READ(DSPBSURF); | ||
291 | } | ||
292 | psb_intel_crtc_load_lut(crtc); | ||
293 | } | ||
294 | /* DSPARB */ | ||
295 | REG_WRITE(DSPARB, 0x00003fbf); | ||
296 | /* FW1 */ | ||
297 | REG_WRITE(0x70034, 0x3f880a0a); | ||
298 | /* FW2 */ | ||
299 | REG_WRITE(0x70038, 0x0b060808); | ||
300 | /* FW4 */ | ||
301 | REG_WRITE(0x70050, 0x08030404); | ||
302 | /* FW5 */ | ||
303 | REG_WRITE(0x70054, 0x04040404); | ||
304 | /* LNC Chicken Bits */ | ||
305 | REG_WRITE(0x70400, 0x4000); | ||
306 | } | ||
307 | |||
308 | |||
309 | static void mrst_hdmi_dpms(struct drm_encoder *encoder, int mode) | ||
310 | { | ||
311 | static int dpms_mode = -1; | ||
312 | |||
313 | struct drm_device *dev = encoder->dev; | ||
314 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
315 | struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
316 | u32 temp; | ||
317 | |||
318 | if (dpms_mode == mode) | ||
319 | return; | ||
320 | |||
321 | if (mode != DRM_MODE_DPMS_ON) | ||
322 | temp = 0x0; | ||
323 | else | ||
324 | temp = 0x99; | ||
325 | |||
326 | dpms_mode = mode; | ||
327 | HDMI_WRITE(HDMI_VIDEO_REG, temp); | ||
328 | } | ||
329 | |||
330 | static unsigned int htotal_calculate(struct drm_display_mode *mode) | ||
331 | { | ||
332 | u32 htotal, new_crtc_htotal; | ||
333 | |||
334 | htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16); | ||
335 | |||
336 | /* | ||
337 | * 1024 x 768 new_crtc_htotal = 0x1024; | ||
338 | * 1280 x 1024 new_crtc_htotal = 0x0c34; | ||
339 | */ | ||
340 | new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock; | ||
341 | |||
342 | return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16); | ||
343 | } | ||
344 | |||
345 | static void mrst_hdmi_find_dpll(struct drm_crtc *crtc, int target, | ||
346 | int refclk, struct mrst_hdmi_clock *best_clock) | ||
347 | { | ||
348 | int np_min, np_max, nr_min, nr_max; | ||
349 | int np, nr, nf; | ||
350 | |||
351 | np_min = DIV_ROUND_UP(mrst_hdmi_limit.vco.min, target * 10); | ||
352 | np_max = mrst_hdmi_limit.vco.max / (target * 10); | ||
353 | if (np_min < mrst_hdmi_limit.np.min) | ||
354 | np_min = mrst_hdmi_limit.np.min; | ||
355 | if (np_max > mrst_hdmi_limit.np.max) | ||
356 | np_max = mrst_hdmi_limit.np.max; | ||
357 | |||
358 | nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max)); | ||
359 | nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min)); | ||
360 | if (nr_min < mrst_hdmi_limit.nr.min) | ||
361 | nr_min = mrst_hdmi_limit.nr.min; | ||
362 | if (nr_max > mrst_hdmi_limit.nr.max) | ||
363 | nr_max = mrst_hdmi_limit.nr.max; | ||
364 | |||
365 | np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max)); | ||
366 | nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np)); | ||
367 | nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk); | ||
368 | DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf); | ||
369 | |||
370 | /* | ||
371 | * 1024 x 768 np = 1; nr = 0x26; nf = 0x0fd8000; | ||
372 | * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000; | ||
373 | */ | ||
374 | best_clock->np = np; | ||
375 | best_clock->nr = nr - 1; | ||
376 | best_clock->nf = (nf << 14); | ||
377 | } | ||
378 | |||
379 | int mrst_crtc_hdmi_mode_set(struct drm_crtc *crtc, | ||
380 | struct drm_display_mode *mode, | ||
381 | struct drm_display_mode *adjusted_mode, | ||
382 | int x, int y, | ||
383 | struct drm_framebuffer *old_fb) | ||
384 | { | ||
385 | struct drm_device *dev = crtc->dev; | ||
386 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
387 | struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
388 | int pipe = 1; | ||
389 | int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; | ||
390 | int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; | ||
391 | int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; | ||
392 | int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; | ||
393 | int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; | ||
394 | int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; | ||
395 | int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; | ||
396 | int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; | ||
397 | int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; | ||
398 | int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; | ||
399 | int refclk; | ||
400 | struct mrst_hdmi_clock clock; | ||
401 | u32 dspcntr, pipeconf, dpll, temp; | ||
402 | int dspcntr_reg = DSPBCNTR; | ||
403 | |||
404 | /* Disable the VGA plane that we never use */ | ||
405 | REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); | ||
406 | |||
407 | /* XXX: Disable the panel fitter if it was on our pipe */ | ||
408 | |||
409 | /* Disable dpll if necessary */ | ||
410 | dpll = REG_READ(DPLL_CTRL); | ||
411 | if ((dpll & DPLL_PWRDN) == 0) { | ||
412 | REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET)); | ||
413 | REG_WRITE(DPLL_DIV_CTRL, 0x00000000); | ||
414 | REG_WRITE(DPLL_STATUS, 0x1); | ||
415 | } | ||
416 | udelay(150); | ||
417 | |||
418 | /* reset controller: FIXME - can we sort out the ioremap mess ? */ | ||
419 | iounmap(hdmi_dev->regs); | ||
420 | mrst_hdmi_reset(dev); | ||
421 | |||
422 | /* program and enable dpll */ | ||
423 | refclk = 25000; | ||
424 | mrst_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock); | ||
425 | |||
426 | /* Setting DPLL */ | ||
427 | dpll = REG_READ(DPLL_CTRL); | ||
428 | dpll &= ~DPLL_PDIV_MASK; | ||
429 | dpll &= ~(DPLL_PWRDN | DPLL_RESET); | ||
430 | REG_WRITE(DPLL_CTRL, 0x00000008); | ||
431 | REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr)); | ||
432 | REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1)); | ||
433 | REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN)); | ||
434 | REG_WRITE(DPLL_UPDATE, 0x80000000); | ||
435 | REG_WRITE(DPLL_CLK_ENABLE, 0x80050102); | ||
436 | udelay(150); | ||
437 | |||
438 | hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); | ||
439 | if (hdmi_dev->regs == NULL) { | ||
440 | DRM_ERROR("failed to do hdmi mmio mapping\n"); | ||
441 | return -ENOMEM; | ||
442 | } | ||
443 | |||
444 | /* configure HDMI */ | ||
445 | HDMI_WRITE(0x1004, 0x1fd); | ||
446 | HDMI_WRITE(0x2000, 0x1); | ||
447 | HDMI_WRITE(0x2008, 0x0); | ||
448 | HDMI_WRITE(0x3130, 0x8); | ||
449 | HDMI_WRITE(0x101c, 0x1800810); | ||
450 | |||
451 | temp = htotal_calculate(adjusted_mode); | ||
452 | REG_WRITE(htot_reg, temp); | ||
453 | REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); | ||
454 | REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); | ||
455 | REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); | ||
456 | REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); | ||
457 | REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); | ||
458 | REG_WRITE(pipesrc_reg, | ||
459 | ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); | ||
460 | |||
461 | REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); | ||
462 | REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); | ||
463 | REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); | ||
464 | REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); | ||
465 | REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); | ||
466 | REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); | ||
467 | REG_WRITE(PCH_PIPEBSRC, | ||
468 | ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); | ||
469 | |||
470 | temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start; | ||
471 | HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) | temp); | ||
472 | |||
473 | REG_WRITE(dspsize_reg, | ||
474 | ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); | ||
475 | REG_WRITE(dsppos_reg, 0); | ||
476 | |||
477 | /* Flush the plane changes */ | ||
478 | { | ||
479 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
480 | crtc_funcs->mode_set_base(crtc, x, y, old_fb); | ||
481 | } | ||
482 | |||
483 | /* Set up the display plane register */ | ||
484 | dspcntr = REG_READ(dspcntr_reg); | ||
485 | dspcntr |= DISPPLANE_GAMMA_ENABLE; | ||
486 | dspcntr |= DISPPLANE_SEL_PIPE_B; | ||
487 | dspcntr |= DISPLAY_PLANE_ENABLE; | ||
488 | |||
489 | /* setup pipeconf */ | ||
490 | pipeconf = REG_READ(pipeconf_reg); | ||
491 | pipeconf |= PIPEACONF_ENABLE; | ||
492 | |||
493 | REG_WRITE(pipeconf_reg, pipeconf); | ||
494 | REG_READ(pipeconf_reg); | ||
495 | |||
496 | REG_WRITE(PCH_PIPEBCONF, pipeconf); | ||
497 | REG_READ(PCH_PIPEBCONF); | ||
498 | wait_for_vblank(dev); | ||
499 | |||
500 | REG_WRITE(dspcntr_reg, dspcntr); | ||
501 | wait_for_vblank(dev); | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int mrst_hdmi_mode_valid(struct drm_connector *connector, | ||
507 | struct drm_display_mode *mode) | ||
508 | { | ||
509 | if (mode->clock > 165000) | ||
510 | return MODE_CLOCK_HIGH; | ||
511 | if (mode->clock < 20000) | ||
512 | return MODE_CLOCK_LOW; | ||
513 | |||
514 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
515 | return MODE_NO_DBLESCAN; | ||
516 | |||
517 | return MODE_OK; | ||
518 | } | ||
519 | |||
520 | static bool mrst_hdmi_mode_fixup(struct drm_encoder *encoder, | ||
521 | struct drm_display_mode *mode, | ||
522 | struct drm_display_mode *adjusted_mode) | ||
523 | { | ||
524 | return true; | ||
525 | } | ||
526 | |||
527 | static enum drm_connector_status | ||
528 | mrst_hdmi_detect(struct drm_connector *connector, bool force) | ||
529 | { | ||
530 | enum drm_connector_status status; | ||
531 | struct drm_device *dev = connector->dev; | ||
532 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
533 | struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
534 | u32 temp; | ||
535 | |||
536 | temp = HDMI_READ(HDMI_HSR); | ||
537 | DRM_DEBUG_KMS("HDMI_HSR %x\n", temp); | ||
538 | |||
539 | if ((temp & HDMI_DETECT_HDP) != 0) | ||
540 | status = connector_status_connected; | ||
541 | else | ||
542 | status = connector_status_disconnected; | ||
543 | |||
544 | return status; | ||
545 | } | ||
546 | |||
547 | static const unsigned char raw_edid[] = { | ||
548 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x2f, 0xa0, | ||
549 | 0x53, 0x55, 0x33, 0x30, 0x16, 0x13, 0x01, 0x03, 0x0e, 0x3a, 0x24, 0x78, | ||
550 | 0xea, 0xe9, 0xf5, 0xac, 0x51, 0x30, 0xb4, 0x25, 0x11, 0x50, 0x54, 0xa5, | ||
551 | 0x4b, 0x00, 0x81, 0x80, 0xa9, 0x40, 0x71, 0x4f, 0xb3, 0x00, 0x01, 0x01, | ||
552 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, | ||
553 | 0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x46, 0x6c, 0x21, 0x00, 0x00, 0x1a, | ||
554 | 0x00, 0x00, 0x00, 0xff, 0x00, 0x47, 0x4e, 0x37, 0x32, 0x31, 0x39, 0x35, | ||
555 | 0x52, 0x30, 0x33, 0x55, 0x53, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44, | ||
556 | 0x45, 0x4c, 0x4c, 0x20, 0x32, 0x37, 0x30, 0x39, 0x57, 0x0a, 0x20, 0x20, | ||
557 | 0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4c, 0x1e, 0x53, 0x11, 0x00, 0x0a, | ||
558 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x8d | ||
559 | }; | ||
560 | |||
561 | static int mrst_hdmi_get_modes(struct drm_connector *connector) | ||
562 | { | ||
563 | struct drm_device *dev = connector->dev; | ||
564 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
565 | struct i2c_adapter *i2c_adap; | ||
566 | struct edid *edid; | ||
567 | struct drm_display_mode *mode, *t; | ||
568 | int i = 0, ret = 0; | ||
569 | |||
570 | i2c_adap = i2c_get_adapter(3); | ||
571 | if (i2c_adap == NULL) { | ||
572 | DRM_ERROR("No ddc adapter available!\n"); | ||
573 | edid = (struct edid *)raw_edid; | ||
574 | } else { | ||
575 | edid = (struct edid *)raw_edid; | ||
576 | /* FIXME ? edid = drm_get_edid(connector, i2c_adap); */ | ||
577 | } | ||
578 | |||
579 | if (edid) { | ||
580 | drm_mode_connector_update_edid_property(connector, edid); | ||
581 | ret = drm_add_edid_modes(connector, edid); | ||
582 | connector->display_info.raw_edid = NULL; | ||
583 | } | ||
584 | |||
585 | /* | ||
586 | * prune modes that require frame buffer bigger than stolen mem | ||
587 | */ | ||
588 | list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { | ||
589 | if ((mode->hdisplay * mode->vdisplay * 4) >= dev_priv->vram_stolen_size) { | ||
590 | i++; | ||
591 | drm_mode_remove(connector, mode); | ||
592 | } | ||
593 | } | ||
594 | return ret - i; | ||
595 | } | ||
596 | |||
597 | static void mrst_hdmi_mode_set(struct drm_encoder *encoder, | ||
598 | struct drm_display_mode *mode, | ||
599 | struct drm_display_mode *adjusted_mode) | ||
600 | { | ||
601 | struct drm_device *dev = encoder->dev; | ||
602 | |||
603 | mrst_hdmi_audio_enable(dev); | ||
604 | return; | ||
605 | } | ||
606 | |||
607 | static void mrst_hdmi_destroy(struct drm_connector *connector) | ||
608 | { | ||
609 | return; | ||
610 | } | ||
611 | |||
612 | static const struct drm_encoder_helper_funcs mrst_hdmi_helper_funcs = { | ||
613 | .dpms = mrst_hdmi_dpms, | ||
614 | .mode_fixup = mrst_hdmi_mode_fixup, | ||
615 | .prepare = psb_intel_encoder_prepare, | ||
616 | .mode_set = mrst_hdmi_mode_set, | ||
617 | .commit = psb_intel_encoder_commit, | ||
618 | }; | ||
619 | |||
620 | static const struct drm_connector_helper_funcs | ||
621 | mrst_hdmi_connector_helper_funcs = { | ||
622 | .get_modes = mrst_hdmi_get_modes, | ||
623 | .mode_valid = mrst_hdmi_mode_valid, | ||
624 | .best_encoder = psb_intel_best_encoder, | ||
625 | }; | ||
626 | |||
627 | static const struct drm_connector_funcs mrst_hdmi_connector_funcs = { | ||
628 | .dpms = drm_helper_connector_dpms, | ||
629 | .detect = mrst_hdmi_detect, | ||
630 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
631 | .destroy = mrst_hdmi_destroy, | ||
632 | }; | ||
633 | |||
634 | static void mrst_hdmi_enc_destroy(struct drm_encoder *encoder) | ||
635 | { | ||
636 | drm_encoder_cleanup(encoder); | ||
637 | } | ||
638 | |||
639 | static const struct drm_encoder_funcs mrst_hdmi_enc_funcs = { | ||
640 | .destroy = mrst_hdmi_enc_destroy, | ||
641 | }; | ||
642 | |||
643 | void mrst_hdmi_init(struct drm_device *dev, | ||
644 | struct psb_intel_mode_device *mode_dev) | ||
645 | { | ||
646 | struct psb_intel_output *psb_intel_output; | ||
647 | struct drm_connector *connector; | ||
648 | struct drm_encoder *encoder; | ||
649 | |||
650 | psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); | ||
651 | if (!psb_intel_output) | ||
652 | return; | ||
653 | |||
654 | psb_intel_output->mode_dev = mode_dev; | ||
655 | connector = &psb_intel_output->base; | ||
656 | encoder = &psb_intel_output->enc; | ||
657 | drm_connector_init(dev, &psb_intel_output->base, | ||
658 | &mrst_hdmi_connector_funcs, | ||
659 | DRM_MODE_CONNECTOR_DVID); | ||
660 | |||
661 | drm_encoder_init(dev, &psb_intel_output->enc, | ||
662 | &mrst_hdmi_enc_funcs, | ||
663 | DRM_MODE_ENCODER_TMDS); | ||
664 | |||
665 | drm_mode_connector_attach_encoder(&psb_intel_output->base, | ||
666 | &psb_intel_output->enc); | ||
667 | |||
668 | psb_intel_output->type = INTEL_OUTPUT_HDMI; | ||
669 | drm_encoder_helper_add(encoder, &mrst_hdmi_helper_funcs); | ||
670 | drm_connector_helper_add(connector, &mrst_hdmi_connector_helper_funcs); | ||
671 | |||
672 | connector->display_info.subpixel_order = SubPixelHorizontalRGB; | ||
673 | connector->interlace_allowed = false; | ||
674 | connector->doublescan_allowed = false; | ||
675 | drm_sysfs_connector_add(connector); | ||
676 | |||
677 | return; | ||
678 | } | ||
679 | |||
680 | static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = { | ||
681 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) }, | ||
682 | {} | ||
683 | }; | ||
684 | |||
685 | void mrst_hdmi_setup(struct drm_device *dev) | ||
686 | { | ||
687 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
688 | struct pci_dev *pdev; | ||
689 | struct mrst_hdmi_dev *hdmi_dev; | ||
690 | int ret; | ||
691 | |||
692 | pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x080d, NULL); | ||
693 | if (!pdev) | ||
694 | return; | ||
695 | |||
696 | hdmi_dev = kzalloc(sizeof(struct mrst_hdmi_dev), GFP_KERNEL); | ||
697 | if (!hdmi_dev) { | ||
698 | dev_err(dev->dev, "failed to allocate memory\n"); | ||
699 | goto out; | ||
700 | } | ||
701 | |||
702 | |||
703 | ret = pci_enable_device(pdev); | ||
704 | if (ret) { | ||
705 | dev_err(dev->dev, "failed to enable hdmi controller\n"); | ||
706 | goto free; | ||
707 | } | ||
708 | |||
709 | hdmi_dev->mmio = pci_resource_start(pdev, 0); | ||
710 | hdmi_dev->mmio_len = pci_resource_len(pdev, 0); | ||
711 | hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); | ||
712 | if (!hdmi_dev->regs) { | ||
713 | dev_err(dev->dev, "failed to map hdmi mmio\n"); | ||
714 | goto free; | ||
715 | } | ||
716 | |||
717 | hdmi_dev->dev = pdev; | ||
718 | pci_set_drvdata(pdev, hdmi_dev); | ||
719 | |||
720 | /* Initialize i2c controller */ | ||
721 | ret = mrst_hdmi_i2c_init(hdmi_dev->dev); | ||
722 | if (ret) | ||
723 | dev_err(dev->dev, "HDMI I2C initialization failed\n"); | ||
724 | |||
725 | dev_priv->hdmi_priv = hdmi_dev; | ||
726 | mrst_hdmi_audio_disable(dev); | ||
727 | return; | ||
728 | |||
729 | free: | ||
730 | kfree(hdmi_dev); | ||
731 | out: | ||
732 | return; | ||
733 | } | ||
734 | |||
735 | void mrst_hdmi_teardown(struct drm_device *dev) | ||
736 | { | ||
737 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
738 | struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
739 | struct pci_dev *pdev; | ||
740 | |||
741 | if (hdmi_dev) { | ||
742 | pdev = hdmi_dev->dev; | ||
743 | pci_set_drvdata(pdev, NULL); | ||
744 | mrst_hdmi_i2c_exit(pdev); | ||
745 | iounmap(hdmi_dev->regs); | ||
746 | kfree(hdmi_dev); | ||
747 | pci_dev_put(pdev); | ||
748 | } | ||
749 | } | ||
750 | |||
751 | /* save HDMI register state */ | ||
752 | void mrst_hdmi_save(struct drm_device *dev) | ||
753 | { | ||
754 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
755 | struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
756 | int i; | ||
757 | |||
758 | /* dpll */ | ||
759 | hdmi_dev->saveDPLL_CTRL = PSB_RVDC32(DPLL_CTRL); | ||
760 | hdmi_dev->saveDPLL_DIV_CTRL = PSB_RVDC32(DPLL_DIV_CTRL); | ||
761 | hdmi_dev->saveDPLL_ADJUST = PSB_RVDC32(DPLL_ADJUST); | ||
762 | hdmi_dev->saveDPLL_UPDATE = PSB_RVDC32(DPLL_UPDATE); | ||
763 | hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE); | ||
764 | |||
765 | /* pipe B */ | ||
766 | dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF); | ||
767 | dev_priv->savePIPEBSRC = PSB_RVDC32(PIPEBSRC); | ||
768 | dev_priv->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B); | ||
769 | dev_priv->saveHBLANK_B = PSB_RVDC32(HBLANK_B); | ||
770 | dev_priv->saveHSYNC_B = PSB_RVDC32(HSYNC_B); | ||
771 | dev_priv->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B); | ||
772 | dev_priv->saveVBLANK_B = PSB_RVDC32(VBLANK_B); | ||
773 | dev_priv->saveVSYNC_B = PSB_RVDC32(VSYNC_B); | ||
774 | |||
775 | hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF); | ||
776 | hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC); | ||
777 | hdmi_dev->savePCH_HTOTAL_B = PSB_RVDC32(PCH_HTOTAL_B); | ||
778 | hdmi_dev->savePCH_HBLANK_B = PSB_RVDC32(PCH_HBLANK_B); | ||
779 | hdmi_dev->savePCH_HSYNC_B = PSB_RVDC32(PCH_HSYNC_B); | ||
780 | hdmi_dev->savePCH_VTOTAL_B = PSB_RVDC32(PCH_VTOTAL_B); | ||
781 | hdmi_dev->savePCH_VBLANK_B = PSB_RVDC32(PCH_VBLANK_B); | ||
782 | hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B); | ||
783 | |||
784 | /* plane */ | ||
785 | dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR); | ||
786 | dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE); | ||
787 | dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE); | ||
788 | dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF); | ||
789 | dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF); | ||
790 | dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF); | ||
791 | |||
792 | /* cursor B */ | ||
793 | dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); | ||
794 | dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); | ||
795 | dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); | ||
796 | |||
797 | /* save palette */ | ||
798 | for (i = 0; i < 256; i++) | ||
799 | dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2)); | ||
800 | } | ||
801 | |||
802 | /* restore HDMI register state */ | ||
803 | void mrst_hdmi_restore(struct drm_device *dev) | ||
804 | { | ||
805 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
806 | struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; | ||
807 | int i; | ||
808 | |||
809 | /* dpll */ | ||
810 | PSB_WVDC32(hdmi_dev->saveDPLL_CTRL, DPLL_CTRL); | ||
811 | PSB_WVDC32(hdmi_dev->saveDPLL_DIV_CTRL, DPLL_DIV_CTRL); | ||
812 | PSB_WVDC32(hdmi_dev->saveDPLL_ADJUST, DPLL_ADJUST); | ||
813 | PSB_WVDC32(hdmi_dev->saveDPLL_UPDATE, DPLL_UPDATE); | ||
814 | PSB_WVDC32(hdmi_dev->saveDPLL_CLK_ENABLE, DPLL_CLK_ENABLE); | ||
815 | DRM_UDELAY(150); | ||
816 | |||
817 | /* pipe */ | ||
818 | PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC); | ||
819 | PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B); | ||
820 | PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B); | ||
821 | PSB_WVDC32(dev_priv->saveHSYNC_B, HSYNC_B); | ||
822 | PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B); | ||
823 | PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B); | ||
824 | PSB_WVDC32(dev_priv->saveVSYNC_B, VSYNC_B); | ||
825 | |||
826 | PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC); | ||
827 | PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B); | ||
828 | PSB_WVDC32(hdmi_dev->savePCH_HBLANK_B, PCH_HBLANK_B); | ||
829 | PSB_WVDC32(hdmi_dev->savePCH_HSYNC_B, PCH_HSYNC_B); | ||
830 | PSB_WVDC32(hdmi_dev->savePCH_VTOTAL_B, PCH_VTOTAL_B); | ||
831 | PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B); | ||
832 | PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B); | ||
833 | |||
834 | PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF); | ||
835 | PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF); | ||
836 | |||
837 | /* plane */ | ||
838 | PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF); | ||
839 | PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE); | ||
840 | PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF); | ||
841 | PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR); | ||
842 | PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF); | ||
843 | |||
844 | /* cursor B */ | ||
845 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR); | ||
846 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS); | ||
847 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE); | ||
848 | |||
849 | /* restore palette */ | ||
850 | for (i = 0; i < 256; i++) | ||
851 | PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2)); | ||
852 | } | ||
diff --git a/drivers/staging/gma500/mrst_hdmi_i2c.c b/drivers/staging/gma500/mrst_hdmi_i2c.c new file mode 100644 index 000000000000..351b9d897b9f --- /dev/null +++ b/drivers/staging/gma500/mrst_hdmi_i2c.c | |||
@@ -0,0 +1,327 @@ | |||
1 | /* | ||
2 | * Copyright © 2010 Intel Corporation | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice (including the next | ||
12 | * paragraph) shall be included in all copies or substantial portions of the | ||
13 | * Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
21 | * DEALINGS IN THE SOFTWARE. | ||
22 | * | ||
23 | * Authors: | ||
24 | * Li Peng <peng.li@intel.com> | ||
25 | */ | ||
26 | |||
27 | #include <linux/mutex.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/i2c.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include "psb_drv.h" | ||
33 | |||
34 | #define HDMI_READ(reg) readl(hdmi_dev->regs + (reg)) | ||
35 | #define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg)) | ||
36 | |||
37 | #define HDMI_HCR 0x1000 | ||
38 | #define HCR_DETECT_HDP (1 << 6) | ||
39 | #define HCR_ENABLE_HDCP (1 << 5) | ||
40 | #define HCR_ENABLE_AUDIO (1 << 2) | ||
41 | #define HCR_ENABLE_PIXEL (1 << 1) | ||
42 | #define HCR_ENABLE_TMDS (1 << 0) | ||
43 | #define HDMI_HICR 0x1004 | ||
44 | #define HDMI_INTR_I2C_ERROR (1 << 4) | ||
45 | #define HDMI_INTR_I2C_FULL (1 << 3) | ||
46 | #define HDMI_INTR_I2C_DONE (1 << 2) | ||
47 | #define HDMI_INTR_HPD (1 << 0) | ||
48 | #define HDMI_HSR 0x1008 | ||
49 | #define HDMI_HISR 0x100C | ||
50 | #define HDMI_HI2CRDB0 0x1200 | ||
51 | #define HDMI_HI2CHCR 0x1240 | ||
52 | #define HI2C_HDCP_WRITE (0 << 2) | ||
53 | #define HI2C_HDCP_RI_READ (1 << 2) | ||
54 | #define HI2C_HDCP_READ (2 << 2) | ||
55 | #define HI2C_EDID_READ (3 << 2) | ||
56 | #define HI2C_READ_CONTINUE (1 << 1) | ||
57 | #define HI2C_ENABLE_TRANSACTION (1 << 0) | ||
58 | |||
59 | #define HDMI_ICRH 0x1100 | ||
60 | #define HDMI_HI2CTDR0 0x1244 | ||
61 | #define HDMI_HI2CTDR1 0x1248 | ||
62 | |||
63 | #define I2C_STAT_INIT 0 | ||
64 | #define I2C_READ_DONE 1 | ||
65 | #define I2C_TRANSACTION_DONE 2 | ||
66 | |||
67 | struct hdmi_i2c_dev { | ||
68 | struct i2c_adapter *adap; | ||
69 | struct mutex i2c_lock; | ||
70 | struct completion complete; | ||
71 | int status; | ||
72 | struct i2c_msg *msg; | ||
73 | int buf_offset; | ||
74 | }; | ||
75 | |||
76 | static void hdmi_i2c_irq_enable(struct mrst_hdmi_dev *hdmi_dev) | ||
77 | { | ||
78 | u32 temp; | ||
79 | |||
80 | temp = HDMI_READ(HDMI_HICR); | ||
81 | temp |= (HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE); | ||
82 | HDMI_WRITE(HDMI_HICR, temp); | ||
83 | HDMI_READ(HDMI_HICR); | ||
84 | } | ||
85 | |||
86 | static void hdmi_i2c_irq_disable(struct mrst_hdmi_dev *hdmi_dev) | ||
87 | { | ||
88 | HDMI_WRITE(HDMI_HICR, 0x0); | ||
89 | HDMI_READ(HDMI_HICR); | ||
90 | } | ||
91 | |||
92 | static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) | ||
93 | { | ||
94 | struct mrst_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); | ||
95 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
96 | u32 temp; | ||
97 | |||
98 | i2c_dev->status = I2C_STAT_INIT; | ||
99 | i2c_dev->msg = pmsg; | ||
100 | i2c_dev->buf_offset = 0; | ||
101 | INIT_COMPLETION(i2c_dev->complete); | ||
102 | |||
103 | /* Enable I2C transaction */ | ||
104 | temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION; | ||
105 | HDMI_WRITE(HDMI_HI2CHCR, temp); | ||
106 | HDMI_READ(HDMI_HI2CHCR); | ||
107 | |||
108 | while (i2c_dev->status != I2C_TRANSACTION_DONE) | ||
109 | wait_for_completion_interruptible_timeout(&i2c_dev->complete, | ||
110 | 10 * HZ); | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int xfer_write(struct i2c_adapter *adap, struct i2c_msg *pmsg) | ||
116 | { | ||
117 | /* | ||
118 | * XXX: i2c write seems isn't useful for EDID probe, don't do anything | ||
119 | */ | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int mrst_hdmi_i2c_access(struct i2c_adapter *adap, | ||
124 | struct i2c_msg *pmsg, | ||
125 | int num) | ||
126 | { | ||
127 | struct mrst_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); | ||
128 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
129 | int i, err = 0; | ||
130 | |||
131 | mutex_lock(&i2c_dev->i2c_lock); | ||
132 | |||
133 | /* Enable i2c unit */ | ||
134 | HDMI_WRITE(HDMI_ICRH, 0x00008760); | ||
135 | |||
136 | /* Enable irq */ | ||
137 | hdmi_i2c_irq_enable(hdmi_dev); | ||
138 | for (i = 0; i < num; i++) { | ||
139 | if (pmsg->len && pmsg->buf) { | ||
140 | if (pmsg->flags & I2C_M_RD) | ||
141 | err = xfer_read(adap, pmsg); | ||
142 | else | ||
143 | err = xfer_write(adap, pmsg); | ||
144 | } | ||
145 | pmsg++; /* next message */ | ||
146 | } | ||
147 | |||
148 | /* Disable irq */ | ||
149 | hdmi_i2c_irq_disable(hdmi_dev); | ||
150 | |||
151 | mutex_unlock(&i2c_dev->i2c_lock); | ||
152 | |||
153 | return i; | ||
154 | } | ||
155 | |||
156 | static u32 mrst_hdmi_i2c_func(struct i2c_adapter *adapter) | ||
157 | { | ||
158 | return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; | ||
159 | } | ||
160 | |||
161 | static const struct i2c_algorithm mrst_hdmi_i2c_algorithm = { | ||
162 | .master_xfer = mrst_hdmi_i2c_access, | ||
163 | .functionality = mrst_hdmi_i2c_func, | ||
164 | }; | ||
165 | |||
166 | static struct i2c_adapter mrst_hdmi_i2c_adapter = { | ||
167 | .name = "mrst_hdmi_i2c", | ||
168 | .nr = 3, | ||
169 | .owner = THIS_MODULE, | ||
170 | .class = I2C_CLASS_DDC, | ||
171 | .algo = &mrst_hdmi_i2c_algorithm, | ||
172 | }; | ||
173 | |||
174 | static void hdmi_i2c_read(struct mrst_hdmi_dev *hdmi_dev) | ||
175 | { | ||
176 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
177 | struct i2c_msg *msg = i2c_dev->msg; | ||
178 | u8 *buf = msg->buf; | ||
179 | u32 temp; | ||
180 | int i, offset; | ||
181 | |||
182 | offset = i2c_dev->buf_offset; | ||
183 | for (i = 0; i < 0x10; i++) { | ||
184 | temp = HDMI_READ(HDMI_HI2CRDB0 + (i * 4)); | ||
185 | memcpy(buf + (offset + i * 4), &temp, 4); | ||
186 | } | ||
187 | i2c_dev->buf_offset += (0x10 * 4); | ||
188 | |||
189 | /* clearing read buffer full intr */ | ||
190 | temp = HDMI_READ(HDMI_HISR); | ||
191 | HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_FULL); | ||
192 | HDMI_READ(HDMI_HISR); | ||
193 | |||
194 | /* continue read transaction */ | ||
195 | temp = HDMI_READ(HDMI_HI2CHCR); | ||
196 | HDMI_WRITE(HDMI_HI2CHCR, temp | HI2C_READ_CONTINUE); | ||
197 | HDMI_READ(HDMI_HI2CHCR); | ||
198 | |||
199 | i2c_dev->status = I2C_READ_DONE; | ||
200 | return; | ||
201 | } | ||
202 | |||
203 | static void hdmi_i2c_transaction_done(struct mrst_hdmi_dev *hdmi_dev) | ||
204 | { | ||
205 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
206 | u32 temp; | ||
207 | |||
208 | /* clear transaction done intr */ | ||
209 | temp = HDMI_READ(HDMI_HISR); | ||
210 | HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_DONE); | ||
211 | HDMI_READ(HDMI_HISR); | ||
212 | |||
213 | |||
214 | temp = HDMI_READ(HDMI_HI2CHCR); | ||
215 | HDMI_WRITE(HDMI_HI2CHCR, temp & ~HI2C_ENABLE_TRANSACTION); | ||
216 | HDMI_READ(HDMI_HI2CHCR); | ||
217 | |||
218 | i2c_dev->status = I2C_TRANSACTION_DONE; | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | static irqreturn_t mrst_hdmi_i2c_handler(int this_irq, void *dev) | ||
223 | { | ||
224 | struct mrst_hdmi_dev *hdmi_dev = dev; | ||
225 | struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; | ||
226 | u32 stat; | ||
227 | |||
228 | stat = HDMI_READ(HDMI_HISR); | ||
229 | |||
230 | if (stat & HDMI_INTR_HPD) { | ||
231 | HDMI_WRITE(HDMI_HISR, stat | HDMI_INTR_HPD); | ||
232 | HDMI_READ(HDMI_HISR); | ||
233 | } | ||
234 | |||
235 | if (stat & HDMI_INTR_I2C_FULL) | ||
236 | hdmi_i2c_read(hdmi_dev); | ||
237 | |||
238 | if (stat & HDMI_INTR_I2C_DONE) | ||
239 | hdmi_i2c_transaction_done(hdmi_dev); | ||
240 | |||
241 | complete(&i2c_dev->complete); | ||
242 | |||
243 | return IRQ_HANDLED; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * choose alternate function 2 of GPIO pin 52, 53, | ||
248 | * which is used by HDMI I2C logic | ||
249 | */ | ||
250 | static void mrst_hdmi_i2c_gpio_fix(void) | ||
251 | { | ||
252 | void *base; | ||
253 | unsigned int gpio_base = 0xff12c000; | ||
254 | int gpio_len = 0x1000; | ||
255 | u32 temp; | ||
256 | |||
257 | base = ioremap((resource_size_t)gpio_base, gpio_len); | ||
258 | if (base == NULL) { | ||
259 | DRM_ERROR("gpio ioremap fail\n"); | ||
260 | return; | ||
261 | } | ||
262 | |||
263 | temp = readl(base + 0x44); | ||
264 | DRM_DEBUG_DRIVER("old gpio val %x\n", temp); | ||
265 | writel((temp | 0x00000a00), (base + 0x44)); | ||
266 | temp = readl(base + 0x44); | ||
267 | DRM_DEBUG_DRIVER("new gpio val %x\n", temp); | ||
268 | |||
269 | iounmap(base); | ||
270 | } | ||
271 | |||
272 | int mrst_hdmi_i2c_init(struct pci_dev *dev) | ||
273 | { | ||
274 | struct mrst_hdmi_dev *hdmi_dev; | ||
275 | struct hdmi_i2c_dev *i2c_dev; | ||
276 | int ret; | ||
277 | |||
278 | hdmi_dev = pci_get_drvdata(dev); | ||
279 | |||
280 | i2c_dev = kzalloc(sizeof(struct hdmi_i2c_dev), GFP_KERNEL); | ||
281 | if (i2c_dev == NULL) { | ||
282 | DRM_ERROR("Can't allocate interface\n"); | ||
283 | ret = -ENOMEM; | ||
284 | goto exit; | ||
285 | } | ||
286 | |||
287 | i2c_dev->adap = &mrst_hdmi_i2c_adapter; | ||
288 | i2c_dev->status = I2C_STAT_INIT; | ||
289 | init_completion(&i2c_dev->complete); | ||
290 | mutex_init(&i2c_dev->i2c_lock); | ||
291 | i2c_set_adapdata(&mrst_hdmi_i2c_adapter, hdmi_dev); | ||
292 | hdmi_dev->i2c_dev = i2c_dev; | ||
293 | |||
294 | /* Enable HDMI I2C function on gpio */ | ||
295 | mrst_hdmi_i2c_gpio_fix(); | ||
296 | |||
297 | /* request irq */ | ||
298 | ret = request_irq(dev->irq, mrst_hdmi_i2c_handler, IRQF_SHARED, | ||
299 | mrst_hdmi_i2c_adapter.name, hdmi_dev); | ||
300 | if (ret) { | ||
301 | DRM_ERROR("Failed to request IRQ for I2C controller\n"); | ||
302 | goto err; | ||
303 | } | ||
304 | |||
305 | /* Adapter registration */ | ||
306 | ret = i2c_add_numbered_adapter(&mrst_hdmi_i2c_adapter); | ||
307 | return ret; | ||
308 | |||
309 | err: | ||
310 | kfree(i2c_dev); | ||
311 | exit: | ||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | void mrst_hdmi_i2c_exit(struct pci_dev *dev) | ||
316 | { | ||
317 | struct mrst_hdmi_dev *hdmi_dev; | ||
318 | struct hdmi_i2c_dev *i2c_dev; | ||
319 | |||
320 | hdmi_dev = pci_get_drvdata(dev); | ||
321 | if (i2c_del_adapter(&mrst_hdmi_i2c_adapter)) | ||
322 | DRM_DEBUG_DRIVER("Failed to delete hdmi-i2c adapter\n"); | ||
323 | |||
324 | i2c_dev = hdmi_dev->i2c_dev; | ||
325 | kfree(i2c_dev); | ||
326 | free_irq(dev->irq, hdmi_dev); | ||
327 | } | ||
diff --git a/drivers/staging/gma500/psb_drv.c b/drivers/staging/gma500/psb_drv.c index 6b03951ebd04..4b740aac2a42 100644 --- a/drivers/staging/gma500/psb_drv.c +++ b/drivers/staging/gma500/psb_drv.c | |||
@@ -261,6 +261,8 @@ static int psb_driver_unload(struct drm_device *dev) | |||
261 | psb_lid_timer_takedown(dev_priv); | 261 | psb_lid_timer_takedown(dev_priv); |
262 | gma_intel_opregion_exit(dev); | 262 | gma_intel_opregion_exit(dev); |
263 | 263 | ||
264 | if (dev_priv->ops->chip_teardown) | ||
265 | dev_priv->ops->chip_teardown(dev); | ||
264 | psb_do_takedown(dev); | 266 | psb_do_takedown(dev); |
265 | 267 | ||
266 | 268 | ||
diff --git a/drivers/staging/gma500/psb_drv.h b/drivers/staging/gma500/psb_drv.h index 8184c2349d14..25275f7d7895 100644 --- a/drivers/staging/gma500/psb_drv.h +++ b/drivers/staging/gma500/psb_drv.h | |||
@@ -375,6 +375,9 @@ struct drm_psb_private { | |||
375 | bool dbi_panel_on2; /* The DBI panel power is on */ | 375 | bool dbi_panel_on2; /* The DBI panel power is on */ |
376 | u32 dsr_fb_update; /* DSR FB update counter */ | 376 | u32 dsr_fb_update; /* DSR FB update counter */ |
377 | 377 | ||
378 | /* Moorestown HDMI state */ | ||
379 | struct mrst_hdmi_dev *hdmi_priv; | ||
380 | |||
378 | /* Moorestown pipe config register value cache */ | 381 | /* Moorestown pipe config register value cache */ |
379 | uint32_t pipeconf; | 382 | uint32_t pipeconf; |
380 | uint32_t pipeconf1; | 383 | uint32_t pipeconf1; |
@@ -631,6 +634,7 @@ struct psb_ops { | |||
631 | 634 | ||
632 | /* Setup hooks */ | 635 | /* Setup hooks */ |
633 | int (*chip_setup)(struct drm_device *dev); | 636 | int (*chip_setup)(struct drm_device *dev); |
637 | void (*chip_teardown)(struct drm_device *dev); | ||
634 | 638 | ||
635 | /* Display management hooks */ | 639 | /* Display management hooks */ |
636 | int (*output_init)(struct drm_device *dev); | 640 | int (*output_init)(struct drm_device *dev); |