aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/dvo_ch7017.c
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2008-11-07 17:24:08 -0500
committerDave Airlie <airlied@linux.ie>2008-12-29 02:47:23 -0500
commit79e539453b34e35f39299a899d263b0a1f1670bd (patch)
tree6d1285f2b78fab399aab75a3557b7d6bc0dbd112 /drivers/gpu/drm/i915/dvo_ch7017.c
parentf453ba0460742ad027ae0c4c7d61e62817b3e7ef (diff)
DRM: i915: add mode setting support
This commit adds i915 driver support for the DRM mode setting APIs. Currently, VGA, LVDS, SDVO DVI & VGA, TV and DVO LVDS outputs are supported. HDMI, DisplayPort and additional SDVO output support will follow. Support for the mode setting code is controlled by the new 'modeset' module option. A new config option, CONFIG_DRM_I915_KMS controls the default behavior, and whether a PCI ID list is built into the module for use by user level module utilities. Note that if mode setting is enabled, user level drivers that access display registers directly or that don't use the kernel graphics memory manager will likely corrupt kernel graphics memory, disrupt output configuration (possibly leading to hangs and/or blank displays), and prevent panic/oops messages from appearing. So use caution when enabling this code; be sure your user level code supports the new interfaces. A new SysRq key, 'g', provides emergency support for switching back to the kernel's framebuffer console; which is useful for testing. Co-authors: Dave Airlie <airlied@linux.ie>, Hong Liu <hong.liu@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/i915/dvo_ch7017.c')
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7017.c454
1 files changed, 454 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c
new file mode 100644
index 000000000000..03d4b4973b02
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_ch7017.c
@@ -0,0 +1,454 @@
1/*
2 * Copyright © 2006 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 * Eric Anholt <eric@anholt.net>
25 *
26 */
27
28#include "dvo.h"
29
30#define CH7017_TV_DISPLAY_MODE 0x00
31#define CH7017_FLICKER_FILTER 0x01
32#define CH7017_VIDEO_BANDWIDTH 0x02
33#define CH7017_TEXT_ENHANCEMENT 0x03
34#define CH7017_START_ACTIVE_VIDEO 0x04
35#define CH7017_HORIZONTAL_POSITION 0x05
36#define CH7017_VERTICAL_POSITION 0x06
37#define CH7017_BLACK_LEVEL 0x07
38#define CH7017_CONTRAST_ENHANCEMENT 0x08
39#define CH7017_TV_PLL 0x09
40#define CH7017_TV_PLL_M 0x0a
41#define CH7017_TV_PLL_N 0x0b
42#define CH7017_SUB_CARRIER_0 0x0c
43#define CH7017_CIV_CONTROL 0x10
44#define CH7017_CIV_0 0x11
45#define CH7017_CHROMA_BOOST 0x14
46#define CH7017_CLOCK_MODE 0x1c
47#define CH7017_INPUT_CLOCK 0x1d
48#define CH7017_GPIO_CONTROL 0x1e
49#define CH7017_INPUT_DATA_FORMAT 0x1f
50#define CH7017_CONNECTION_DETECT 0x20
51#define CH7017_DAC_CONTROL 0x21
52#define CH7017_BUFFERED_CLOCK_OUTPUT 0x22
53#define CH7017_DEFEAT_VSYNC 0x47
54#define CH7017_TEST_PATTERN 0x48
55
56#define CH7017_POWER_MANAGEMENT 0x49
57/** Enables the TV output path. */
58#define CH7017_TV_EN (1 << 0)
59#define CH7017_DAC0_POWER_DOWN (1 << 1)
60#define CH7017_DAC1_POWER_DOWN (1 << 2)
61#define CH7017_DAC2_POWER_DOWN (1 << 3)
62#define CH7017_DAC3_POWER_DOWN (1 << 4)
63/** Powers down the TV out block, and DAC0-3 */
64#define CH7017_TV_POWER_DOWN_EN (1 << 5)
65
66#define CH7017_VERSION_ID 0x4a
67
68#define CH7017_DEVICE_ID 0x4b
69#define CH7017_DEVICE_ID_VALUE 0x1b
70#define CH7018_DEVICE_ID_VALUE 0x1a
71#define CH7019_DEVICE_ID_VALUE 0x19
72
73#define CH7017_XCLK_D2_ADJUST 0x53
74#define CH7017_UP_SCALER_COEFF_0 0x55
75#define CH7017_UP_SCALER_COEFF_1 0x56
76#define CH7017_UP_SCALER_COEFF_2 0x57
77#define CH7017_UP_SCALER_COEFF_3 0x58
78#define CH7017_UP_SCALER_COEFF_4 0x59
79#define CH7017_UP_SCALER_VERTICAL_INC_0 0x5a
80#define CH7017_UP_SCALER_VERTICAL_INC_1 0x5b
81#define CH7017_GPIO_INVERT 0x5c
82#define CH7017_UP_SCALER_HORIZONTAL_INC_0 0x5d
83#define CH7017_UP_SCALER_HORIZONTAL_INC_1 0x5e
84
85#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT 0x5f
86/**< Low bits of horizontal active pixel input */
87
88#define CH7017_ACTIVE_INPUT_LINE_OUTPUT 0x60
89/** High bits of horizontal active pixel input */
90#define CH7017_LVDS_HAP_INPUT_MASK (0x7 << 0)
91/** High bits of vertical active line output */
92#define CH7017_LVDS_VAL_HIGH_MASK (0x7 << 3)
93
94#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT 0x61
95/**< Low bits of vertical active line output */
96
97#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT 0x62
98/**< Low bits of horizontal active pixel output */
99
100#define CH7017_LVDS_POWER_DOWN 0x63
101/** High bits of horizontal active pixel output */
102#define CH7017_LVDS_HAP_HIGH_MASK (0x7 << 0)
103/** Enables the LVDS power down state transition */
104#define CH7017_LVDS_POWER_DOWN_EN (1 << 6)
105/** Enables the LVDS upscaler */
106#define CH7017_LVDS_UPSCALER_EN (1 << 7)
107#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08
108
109#define CH7017_LVDS_ENCODING 0x64
110#define CH7017_LVDS_DITHER_2D (1 << 2)
111#define CH7017_LVDS_DITHER_DIS (1 << 3)
112#define CH7017_LVDS_DUAL_CHANNEL_EN (1 << 4)
113#define CH7017_LVDS_24_BIT (1 << 5)
114
115#define CH7017_LVDS_ENCODING_2 0x65
116
117#define CH7017_LVDS_PLL_CONTROL 0x66
118/** Enables the LVDS panel output path */
119#define CH7017_LVDS_PANEN (1 << 0)
120/** Enables the LVDS panel backlight */
121#define CH7017_LVDS_BKLEN (1 << 3)
122
123#define CH7017_POWER_SEQUENCING_T1 0x67
124#define CH7017_POWER_SEQUENCING_T2 0x68
125#define CH7017_POWER_SEQUENCING_T3 0x69
126#define CH7017_POWER_SEQUENCING_T4 0x6a
127#define CH7017_POWER_SEQUENCING_T5 0x6b
128#define CH7017_GPIO_DRIVER_TYPE 0x6c
129#define CH7017_GPIO_DATA 0x6d
130#define CH7017_GPIO_DIRECTION_CONTROL 0x6e
131
132#define CH7017_LVDS_PLL_FEEDBACK_DIV 0x71
133# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4
134# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0
135# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80
136
137#define CH7017_LVDS_PLL_VCO_CONTROL 0x72
138# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80
139# define CH7017_LVDS_PLL_VCO_SHIFT 4
140# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0
141
142#define CH7017_OUTPUTS_ENABLE 0x73
143# define CH7017_CHARGE_PUMP_LOW 0x0
144# define CH7017_CHARGE_PUMP_HIGH 0x3
145# define CH7017_LVDS_CHANNEL_A (1 << 3)
146# define CH7017_LVDS_CHANNEL_B (1 << 4)
147# define CH7017_TV_DAC_A (1 << 5)
148# define CH7017_TV_DAC_B (1 << 6)
149# define CH7017_DDC_SELECT_DC2 (1 << 7)
150
151#define CH7017_LVDS_OUTPUT_AMPLITUDE 0x74
152#define CH7017_LVDS_PLL_EMI_REDUCTION 0x75
153#define CH7017_LVDS_POWER_DOWN_FLICKER 0x76
154
155#define CH7017_LVDS_CONTROL_2 0x78
156# define CH7017_LOOP_FILTER_SHIFT 5
157# define CH7017_PHASE_DETECTOR_SHIFT 0
158
159#define CH7017_BANG_LIMIT_CONTROL 0x7f
160
161struct ch7017_priv {
162 uint8_t save_hapi;
163 uint8_t save_vali;
164 uint8_t save_valo;
165 uint8_t save_ailo;
166 uint8_t save_lvds_pll_vco;
167 uint8_t save_feedback_div;
168 uint8_t save_lvds_control_2;
169 uint8_t save_outputs_enable;
170 uint8_t save_lvds_power_down;
171 uint8_t save_power_management;
172};
173
174static void ch7017_dump_regs(struct intel_dvo_device *dvo);
175static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
176
177static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
178{
179 struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
180 u8 out_buf[2];
181 u8 in_buf[2];
182
183 struct i2c_msg msgs[] = {
184 {
185 .addr = i2cbus->slave_addr,
186 .flags = 0,
187 .len = 1,
188 .buf = out_buf,
189 },
190 {
191 .addr = i2cbus->slave_addr,
192 .flags = I2C_M_RD,
193 .len = 1,
194 .buf = in_buf,
195 }
196 };
197
198 out_buf[0] = addr;
199 out_buf[1] = 0;
200
201 if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
202 *val= in_buf[0];
203 return true;
204 };
205
206 return false;
207}
208
209static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
210{
211 struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
212 uint8_t out_buf[2];
213 struct i2c_msg msg = {
214 .addr = i2cbus->slave_addr,
215 .flags = 0,
216 .len = 2,
217 .buf = out_buf,
218 };
219
220 out_buf[0] = addr;
221 out_buf[1] = val;
222
223 if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
224 return true;
225
226 return false;
227}
228
229/** Probes for a CH7017 on the given bus and slave address. */
230static bool ch7017_init(struct intel_dvo_device *dvo,
231 struct intel_i2c_chan *i2cbus)
232{
233 struct ch7017_priv *priv;
234 uint8_t val;
235
236 priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL);
237 if (priv == NULL)
238 return false;
239
240 dvo->i2c_bus = i2cbus;
241 dvo->i2c_bus->slave_addr = dvo->slave_addr;
242 dvo->dev_priv = priv;
243
244 if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))
245 goto fail;
246
247 if (val != CH7017_DEVICE_ID_VALUE &&
248 val != CH7018_DEVICE_ID_VALUE &&
249 val != CH7019_DEVICE_ID_VALUE) {
250 DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n",
251 val, i2cbus->adapter.name,i2cbus->slave_addr);
252 goto fail;
253 }
254
255 return true;
256fail:
257 kfree(priv);
258 return false;
259}
260
261static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo)
262{
263 return connector_status_unknown;
264}
265
266static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo,
267 struct drm_display_mode *mode)
268{
269 if (mode->clock > 160000)
270 return MODE_CLOCK_HIGH;
271
272 return MODE_OK;
273}
274
275static void ch7017_mode_set(struct intel_dvo_device *dvo,
276 struct drm_display_mode *mode,
277 struct drm_display_mode *adjusted_mode)
278{
279 uint8_t lvds_pll_feedback_div, lvds_pll_vco_control;
280 uint8_t outputs_enable, lvds_control_2, lvds_power_down;
281 uint8_t horizontal_active_pixel_input;
282 uint8_t horizontal_active_pixel_output, vertical_active_line_output;
283 uint8_t active_input_line_output;
284
285 DRM_DEBUG("Registers before mode setting\n");
286 ch7017_dump_regs(dvo);
287
288 /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
289 if (mode->clock < 100000) {
290 outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW;
291 lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
292 (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
293 (13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
294 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
295 (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
296 (3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
297 lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) |
298 (0 << CH7017_PHASE_DETECTOR_SHIFT);
299 } else {
300 outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH;
301 lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
302 (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
303 (3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
304 lvds_pll_feedback_div = 35;
305 lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) |
306 (0 << CH7017_PHASE_DETECTOR_SHIFT);
307 if (1) { /* XXX: dual channel panel detection. Assume yes for now. */
308 outputs_enable |= CH7017_LVDS_CHANNEL_B;
309 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
310 (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
311 (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
312 } else {
313 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
314 (1 << CH7017_LVDS_PLL_VCO_SHIFT) |
315 (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
316 }
317 }
318
319 horizontal_active_pixel_input = mode->hdisplay & 0x00ff;
320
321 vertical_active_line_output = mode->vdisplay & 0x00ff;
322 horizontal_active_pixel_output = mode->hdisplay & 0x00ff;
323
324 active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) |
325 (((mode->vdisplay & 0x0700) >> 8) << 3);
326
327 lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
328 (mode->hdisplay & 0x0700) >> 8;
329
330 ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
331 ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
332 horizontal_active_pixel_input);
333 ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
334 horizontal_active_pixel_output);
335 ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT,
336 vertical_active_line_output);
337 ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT,
338 active_input_line_output);
339 ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control);
340 ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div);
341 ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2);
342 ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable);
343
344 /* Turn the LVDS back on with new settings. */
345 ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
346
347 DRM_DEBUG("Registers after mode setting\n");
348 ch7017_dump_regs(dvo);
349}
350
351/* set the CH7017 power state */
352static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
353{
354 uint8_t val;
355
356 ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
357
358 /* Turn off TV/VGA, and never turn it on since we don't support it. */
359 ch7017_write(dvo, CH7017_POWER_MANAGEMENT,
360 CH7017_DAC0_POWER_DOWN |
361 CH7017_DAC1_POWER_DOWN |
362 CH7017_DAC2_POWER_DOWN |
363 CH7017_DAC3_POWER_DOWN |
364 CH7017_TV_POWER_DOWN_EN);
365
366 if (mode == DRM_MODE_DPMS_ON) {
367 /* Turn on the LVDS */
368 ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
369 val & ~CH7017_LVDS_POWER_DOWN_EN);
370 } else {
371 /* Turn off the LVDS */
372 ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
373 val | CH7017_LVDS_POWER_DOWN_EN);
374 }
375
376 /* XXX: Should actually wait for update power status somehow */
377 udelay(20000);
378}
379
380static void ch7017_dump_regs(struct intel_dvo_device *dvo)
381{
382 uint8_t val;
383
384#define DUMP(reg) \
385do { \
386 ch7017_read(dvo, reg, &val); \
387 DRM_DEBUG(#reg ": %02x\n", val); \
388} while (0)
389
390 DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
391 DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT);
392 DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT);
393 DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT);
394 DUMP(CH7017_LVDS_PLL_VCO_CONTROL);
395 DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV);
396 DUMP(CH7017_LVDS_CONTROL_2);
397 DUMP(CH7017_OUTPUTS_ENABLE);
398 DUMP(CH7017_LVDS_POWER_DOWN);
399}
400
401static void ch7017_save(struct intel_dvo_device *dvo)
402{
403 struct ch7017_priv *priv = dvo->dev_priv;
404
405 ch7017_read(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi);
406 ch7017_read(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo);
407 ch7017_read(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo);
408 ch7017_read(dvo, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco);
409 ch7017_read(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div);
410 ch7017_read(dvo, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2);
411 ch7017_read(dvo, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable);
412 ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down);
413 ch7017_read(dvo, CH7017_POWER_MANAGEMENT, &priv->save_power_management);
414}
415
416static void ch7017_restore(struct intel_dvo_device *dvo)
417{
418 struct ch7017_priv *priv = dvo->dev_priv;
419
420 /* Power down before changing mode */
421 ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
422
423 ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi);
424 ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo);
425 ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo);
426 ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco);
427 ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div);
428 ch7017_write(dvo, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2);
429 ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable);
430 ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down);
431 ch7017_write(dvo, CH7017_POWER_MANAGEMENT, priv->save_power_management);
432}
433
434static void ch7017_destroy(struct intel_dvo_device *dvo)
435{
436 struct ch7017_priv *priv = dvo->dev_priv;
437
438 if (priv) {
439 kfree(priv);
440 dvo->dev_priv = NULL;
441 }
442}
443
444struct intel_dvo_dev_ops ch7017_ops = {
445 .init = ch7017_init,
446 .detect = ch7017_detect,
447 .mode_valid = ch7017_mode_valid,
448 .mode_set = ch7017_mode_set,
449 .dpms = ch7017_dpms,
450 .dump_regs = ch7017_dump_regs,
451 .save = ch7017_save,
452 .restore = ch7017_restore,
453 .destroy = ch7017_destroy,
454};