cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cdv_device.c (16708B)


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