2 *-----------------------------------------------------------------------------
5 *-----------------------------------------------------------------------------
6 * Copyright (c) 2002-2010, Intel Corporation.
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 *-----------------------------------------------------------------------------
28 * This file is contains all necessary functions for Internal
30 * This is written according to the port interface defined in pd.h.
31 *-----------------------------------------------------------------------------
33 #include <linux/kernel.h>
35 #include <linux/version.h>
36 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
37 #include <linux/bug.h>
46 /* One space between the #define and the backslash,else compilers complain */
47 #define PTR_OFFSET_UCHAR(ptr,offset) (*((unsigned char *)ptr + offset))
48 #define PTR_OFFSET_USHORT(ptr, offset) (*(unsigned short *)((unsigned char *)ptr + offset))
50 #define PTR_OFFSET_ULONG(ptr, offset) (*(unsigned long *)((unsigned char *)ptr + offset))
51 /* END OF OPTIMIZATION MACROS */
53 /* This constant = 10,000,000. The value is used to
54 * get effective results from the integer math, and
55 * to not divide by 0. */
56 #define PWM_FREQ_CALC_CONSTANT_1 0x00989680
57 /* This constant is 1,000,000 - to multiply to get
58 * the Display Clock Frequency to the order of Mhz */
59 #define PWM_FREQ_CALC_CONSTANT_2 0x000F4240
63 #define ARRAY_SIZE(p) (sizeof(p)/sizeof((p)[0]))
66 static pd_version_t lvds_version = {11, 0, 0, 0}; /* driver version */
67 static unsigned long lvds_dab_list[] = {
71 static unsigned long supported_chipset[] =
74 PCI_DEVICE_ID_VGA_855,
77 PCI_DEVICE_ID_VGA_915AL,
80 PCI_DEVICE_ID_VGA_945GM,
81 PCI_DEVICE_ID_VGA_945GME,
84 PCI_DEVICE_ID_VGA_GM965,
85 PCI_DEVICE_ID_VGA_GME965,
88 PCI_DEVICE_ID_VGA_CTG,
91 PCI_DEVICE_ID_VGA_PLB,
94 PCI_DEVICE_ID_VGA_TNC,
95 PCI_DEVICE_ID_VGA_TNC_A0,
96 PCI_DEVICE_ID_VGA_LNC,
100 static pd_driver_t lvds_driver = {
102 "Internal LVDS Port Driver",
117 lvds_get_timing_list,
125 /* This is a common attribute table for all chipsets. At the end of the table
126 * there are multiple end entries to add chipset specific attributes.
127 * Chipset specific attributes:
128 * 965GM/GM45- Maintain aspect ratio
130 * 1. Make sure to update the chipset_attr_index whenever adding a
131 * chipset specific new attr.
133 static pd_attr_t lvds_attrs[] =
135 /* Range attributes */
137 /*<-------ID-----------> <----TYPE--------> <---NAME-----> <----flag----> <---DEF_VAL----> <--CURR_VALUE--> min max st */
138 PD_MAKE_ATTR (PD_ATTR_ID_FP_PWR_T1, PD_ATTR_TYPE_RANGE, "FP Power T1", PD_ATTR_FLAG_USER_INVISIBLE, 0, 0, 0, 819, 1),
139 PD_MAKE_ATTR (PD_ATTR_ID_FP_PWR_T2, PD_ATTR_TYPE_RANGE, "FP Power T2", PD_ATTR_FLAG_USER_INVISIBLE, 0, 0, 0, 819, 1),
140 PD_MAKE_ATTR (PD_ATTR_ID_FP_PWR_T3, PD_ATTR_TYPE_RANGE, "FP Power T3", PD_ATTR_FLAG_USER_INVISIBLE, 0, 0, 0, 819, 1),
141 PD_MAKE_ATTR (PD_ATTR_ID_FP_PWR_T4, PD_ATTR_TYPE_RANGE, "FP Power T4", PD_ATTR_FLAG_USER_INVISIBLE, 0, 0, 0, 819, 1),
142 PD_MAKE_ATTR (PD_ATTR_ID_FP_PWR_T5, PD_ATTR_TYPE_RANGE, "FP Power T5", PD_ATTR_FLAG_USER_INVISIBLE, 400, 400, 0, 3000, 1),
143 PD_MAKE_ATTR (PD_ATTR_ID_PANEL_DEPTH, PD_ATTR_TYPE_RANGE, "Panel Depth", PD_ATTR_FLAG_USER_INVISIBLE, LVDS_DEF_PANEL_DEPTH, LVDS_DEF_PANEL_DEPTH, 18, 24, 6),
145 PD_MAKE_ATTR (PD_ATTR_ID_PWM_INTENSITY, PD_ATTR_TYPE_RANGE, "PWM cycle", PD_ATTR_FLAG_USER_INVISIBLE, 100, 0, 0, 100, 0),
146 PD_MAKE_ATTR (PD_ATTR_ID_INVERTER_FREQ, PD_ATTR_TYPE_RANGE, "Inverter Frequency", PD_ATTR_FLAG_USER_INVISIBLE, 100, 0, 0, 0, 0),
147 PD_MAKE_ATTR (PD_ATTR_ID_BLM_LEGACY_MODE, PD_ATTR_TYPE_BOOL, "Backlight Legacy mode", PD_ATTR_FLAG_USER_INVISIBLE, 0, 0, 0, 0, 0),
149 /*<-------ID------------> <----TYPE--------> <---NAME-----> <------flag---------------> <---DEF_VAL----> <--CURR_VALUE--> <---pad--> */
150 PD_MAKE_ATTR (PD_ATTR_ID_2_CHANNEL_PANEL, PD_ATTR_TYPE_BOOL, "Dual-channel panel", PD_ATTR_FLAG_USER_INVISIBLE, 0, 0, 0, 0, 0),
151 PD_MAKE_ATTR (PD_ATTR_ID_LVDS_PANEL_TYPE, PD_ATTR_TYPE_BOOL, "Panel Type", PD_ATTR_FLAG_USER_INVISIBLE, 0, 0, 0, 0, 0),
152 PD_MAKE_ATTR (PD_ATTR_ID_PANEL_FIT, PD_ATTR_TYPE_BOOL, "Panel Upscale", PD_ATTR_FLAG_USER_INVISIBLE, LVDS_DEF_PANEL_FIT,LVDS_DEF_PANEL_FIT,0, 0, 0),
153 PD_MAKE_ATTR (PD_ATTR_ID_DITHER, PD_ATTR_TYPE_BOOL, "Dither", PD_ATTR_FLAG_USER_INVISIBLE, LVDS_DEF_DITHER, LVDS_DEF_DITHER, 0, 0, 0),
154 PD_MAKE_ATTR (LVDS_ATTR_ID_TC_LVDS_CLK, PD_ATTR_TYPE_BOOL, "TC LVDS CLK 110MHz", PD_ATTR_FLAG_USER_INVISIBLE, 0, 0, 0, 0, 0),
156 /* Start of chipset specific attributes */
157 /* Maintain aspect ratio */
158 PD_MAKE_ATTR (PD_ATTR_LIST_END, 0, "", 0, 0, 0, 0, 0, 0),
160 PD_MAKE_ATTR (PD_ATTR_LIST_END, 0, "", 0, 0, 0, 0, 0, 0),
162 /* Attribute list end */
163 PD_MAKE_ATTR (PD_ATTR_LIST_END, 0, "", 0, 0, 0, 0, 0, 0)
166 /* Rightnow it is -3 to reach starting of chipset specific attributes */
167 static unsigned short chipset_attr_index = (unsigned short)
168 sizeof(lvds_attrs)/sizeof(pd_attr_t) - 3;
170 static pd_attr_t lvds_965gm_attrs[] = {
171 PD_MAKE_ATTR (PD_ATTR_ID_MAINTAIN_ASPECT_RATIO,PD_ATTR_TYPE_BOOL, "Maintain Aspect Ratio",0, 0, 0, 0, 0, 0),
172 PD_MAKE_ATTR (PD_ATTR_ID_TEXT_TUNING, PD_ATTR_TYPE_RANGE, "Text Enhancement", 0, 0, 0, 0, 2, 1),
175 static void lvds_write_reg(lvds_context_t *pd_context, unsigned long reg,
176 unsigned long value, unsigned long change_bits, unsigned long reg_type);
178 static unsigned long lvds_read_reg(lvds_context_t *pd_context,
179 unsigned long reg, unsigned long reg_type);
180 static void lvds_panel_fit(lvds_context_t *pd_context);
182 static void lvds_get_dclk(lvds_context_t *pd_context, pd_dvo_info_t *lvds_info);
184 /*----------------------------------------------------------------------------
186 * Function: PD_MODULE_INIT(lvds_init)
189 * This is the entry function into LVDS port driver when
190 * it first loads. This will call pd_register() to register
191 * with Display driver. Only the driver object is initialized in this
192 * function, similar to DrverEntry() in a WDM driver.
195 * [OUT] *handle: Not used. Place holder for supporting dynamic pd
198 * PD_SUCCESS(0) success
199 * PD_ERR_XXXXXX otherwise
201 *----------------------------------------------------------------------------
204 int PD_MODULE_INIT(lvds_init, (void *handle))
206 /* register the LVDS driver */
207 return pd_register(handle, &lvds_driver);
210 /*----------------------------------------------------------------------------
212 * Function: PD_MODULE_EXIT(lvds_exit, (void))
215 * This function cleans up resources used by the LVDS driver
221 * PD_SUCCESS(0): always returns this
223 *----------------------------------------------------------------------------
226 int PD_MODULE_EXIT(lvds_exit, (void))
231 /*----------------------------------------------------------------------------
233 * Function: lvds_validate
236 * Place holder for a future function
244 *----------------------------------------------------------------------------
247 unsigned long lvds_validate (unsigned long cookie)
248 { /* lvds_validate */
249 /* Validate magic cookie */
250 /* TODO: Implement the magic cookie algorithm */
252 } /* lvds_validate */
254 /*----------------------------------------------------------------------------
256 * Function: lvds_open
259 * This function creates an LVDS context and intialize it with LVDS
262 * Internal LVDS port is available only on MGM platform, this port driver
263 * reads the GMCH device ID with pd_read_regs(PD_REG_PCI) to verify that
264 * it is supported on the current platform.
267 * [IN] callback: callback context
268 * [INOUT] context: Device context. This function will set the attributes
269 * for this context, provided it is already allocated,
273 * PD_ERR_NULL_PTR: if either of the parameters is NULL
274 * PD_ERR_NODEV: if an LVDS device is already up and running
275 * PD_ERR_NOMEM: if a memory allocation failed
276 * PD_SUCCESS: if successful
278 *----------------------------------------------------------------------------
281 /* lvds context structure being declared and initialized */
283 static lvds_context_t lvds_context = { /* lvds context structure */
286 0, /* dual_channel: Default single channel */
287 0, /* panel_type : 0-SPWG 1-OpenLDI*/
288 LVDS_DEF_PANEL_FIT, /* panel_fit */
289 LVDS_DEF_PANEL_DEPTH,/* panel_depth */
291 0, /* Main aspect ratio, default no */
292 0, /* panel filter: Default fuzzy filtering */
293 100, /* PWM Intensity */
294 0xFFFF, /* Inverter Frequency*/
295 0, /* BLM Legacy Mode */
297 PD_POWER_MODE_D0, /* power_state */
302 0, /* Graphics Frequency */
303 0, /* is gn4 based LVDS controller? */
304 0, /* is pwm_done? */
305 0, /* is tc_110MHz_clk? i.e., TC max LVDS to 110MHz*/
307 NULL, /* ptr to callback */
308 NULL, /* ptr to timing table */
309 lvds_attrs, /* ptr to attribute list */
310 NULL, /* ptr to native timing */
311 NULL, /* current mode */
314 int lvds_open(pd_callback_t *callback, void **context)
316 lvds_context_t *pd_context = (lvds_context_t*) &(lvds_context); /* static Global */
317 pd_reg_t reg_list[2];
318 int ret, i, valid_chipset = 0;
319 unsigned short chipset;
325 /* make sure parameters are valid */
326 if (!callback || !context) {
327 PD_ERROR("invalid parameter");
328 return (PD_ERR_NULL_PTR);
330 /* GMCH cannot support more than one device */
331 if (lvds_driver.num_devices > 0) {
332 return (PD_ERR_NODEV);
334 /* Verify that this is an GMCH with Internal LVDS available */
336 reg_list[1].reg = PD_REG_LIST_END;
337 ret = callback->read_regs(callback->callback_context, reg_list, PD_REG_PCI);
338 if(ret != PD_SUCCESS) {
341 chipset = (unsigned short)(reg_list[0].value & 0xffff);
342 for (i = 0; i < ARRAY_SIZE(supported_chipset); i++) {
343 if (chipset == supported_chipset[i]){
352 /* Special handling for gn4 and beyond chipsets */
353 if (chipset == PCI_DEVICE_ID_VGA_GM965 ||
354 chipset == PCI_DEVICE_ID_VGA_GME965 ||
355 chipset == PCI_DEVICE_ID_VGA_CTG ||
356 chipset == PCI_DEVICE_ID_VGA_TNC ||
357 chipset == PCI_DEVICE_ID_VGA_TNC_A0 ||
358 chipset == PCI_DEVICE_ID_VGA_LNC) {
359 lvds_context.gn4_plus = 1;
362 /* Initialize number of attributes */
363 /* +1 is to include the end attribute */
364 lvds_context.num_attrs = (unsigned char)chipset_attr_index + 1;
366 /* Add chipset specific attrbutes.
367 * This can be expanded into a switch statement in future if required. */
368 if (lvds_context.gn4_plus) {
369 lvds_context.num_attrs += sizeof(lvds_965gm_attrs)/sizeof(pd_attr_t);
370 for (i=0; i < sizeof(lvds_965gm_attrs)/sizeof(pd_attr_t); i++) {
371 lvds_attrs[chipset_attr_index+i] = lvds_965gm_attrs[i];
374 /* Make this a compile time so that size of vBIOS doesn't become > 64KB */
375 #if defined(CONFIG_PLB) || defined(CONFIG_TNC)
376 /* pwm backlight control needs graphics frequency. we currently implement
377 * pwm backlight control for pouslbo only to limit the vbios lvds size. Each
378 * chipset have a different method of getting this value and chipset has a
379 * different multiplier. */
380 if (chipset == PCI_DEVICE_ID_VGA_PLB ||
381 chipset == PCI_DEVICE_ID_VGA_TNC ||
382 chipset == PCI_DEVICE_ID_VGA_TNC_A0 ||
383 chipset == PCI_DEVICE_ID_VGA_LNC) {
385 /* For plb/tnc, graphics frequency is obtained by sending an opcode to
386 * port 5 in the SCH Message Network. We call the read_regs with
387 * PD_REG_BRIDGE_OPCODE specifically for this purpose only.
389 * The input for this read_reg is the opcode that register data that
390 * we send into the Message control register*/
391 reg_list[1].reg = PD_REG_LIST_END;
392 ret = callback->read_regs(callback->callback_context,
393 reg_list, PD_REG_BRIDGE_OPCODE);
394 if(ret != PD_SUCCESS) {
398 /*set the graphics frequency*/
399 pd_context->gfx_freq = (unsigned short) reg_list[0].value;
403 pd_context->callback = callback; /* Save callback context */
404 pd_context->chipset = chipset; /* save the chipset ID */
405 *context = (void *) pd_context;
411 /*----------------------------------------------------------------------------
413 * Function: lvds_init_device
416 * Initializes the LVDS device, using the device context from the parameter
419 * [INOUT] context: device context
422 * PD_ERR_NULL_PTR: if parameter is invalid
423 * PD_SUCCESS: if successful
425 *----------------------------------------------------------------------------
428 int lvds_init_device (void *context)
429 { /* lvds_init_device */
431 return (PD_ERR_NULL_PTR);
434 /* Don't need to do much to initialize this device */
435 ((lvds_context_t *)context)->init_done = 1;
436 lvds_driver.num_devices++;
439 } /* lvds_init_device */
442 /*----------------------------------------------------------------------------
444 * Function: lvds_close
447 * Releases resources allocated during lvds_open(). This function
448 * essentially frees a device object.
451 * [INOUT] device_context: device to be freed
454 * PD_SUCCESS: always returns this
456 *----------------------------------------------------------------------------
459 int lvds_close(void *device_context)
463 lvds_context_t *pd_context = (lvds_context_t *)device_context;
469 /* Deallocate memory occupied by this device */
470 if (device_context) {
471 if ( NULL != pd_context->timing_table) {
472 pd_free(pd_context->timing_table);
473 pd_context->timing_table = NULL;
476 /* Free attribute list, if necessary */
477 /* FIXME -- The following test will never call pd_free(), because the
478 * expression "!lvds_driver.num_devices" will yield 0 or 1, and that
479 * will never be greater than 1. This is a potentially small memory
480 * leak, unless some other code frees it.
482 if (!lvds_driver.num_devices > 1) {
483 pd_free(pd_context->attr_list);
485 /* This allocated statically no need to free it */
486 /* pd_free(device_context); */
487 lvds_driver.num_devices--;
488 pd_context->init_done = 0;
496 /*----------------------------------------------------------------------------
498 * Function: lvds_set_mode
501 * Sets LVDS to a new mode, specified by "mode".
504 * [INOUT] context: device context
505 * [IN] mode: information about the mode to switch to
506 * [IN] flags: can contain the following value
507 * PD_SET_MODE_FLAG_TEST: only testing to see if mode is supported
510 * PD_ERR_NULL_PTR: if invalid parameter detected
511 * PD_ERR_MODE_NOTSUPP: if mode specified is not supported
512 * PD_SUCCESS: if successful
514 *----------------------------------------------------------------------------
516 int lvds_set_mode(void *context, pd_timing_t *mode, unsigned long flags)
518 lvds_context_t *pd_context = NULL;
520 PD_DEBUG("lvds_set_mode)\n");
523 pd_context = (lvds_context_t *)context;
525 /* Make sure these parameters are valid */
526 if (!pd_context || !mode) {
527 return (PD_ERR_NULL_PTR);
529 PD_DEBUG("lvds_set_mode: %ux%u", mode->width, mode->height);
531 /* Make sure specified mode is supported */
532 if ((pd_context->fp_width && (mode->width > pd_context->fp_width)) ||
533 (pd_context->fp_height && (mode->height > pd_context->fp_height))) {
534 return PD_ERR_MODE_NOTSUPP;
537 /* Do nothing if we are only want to know if a mode is supported */
538 if (flags & PD_SET_MODE_FLAG_TEST) {
542 pd_context->current_mode = mode;
543 pd_context->pipe = flags;
544 /* Enable panel fitting and return */
545 lvds_panel_fit(pd_context);
552 int lvds_post_set_mode(void *context, pd_timing_t *mode, unsigned long flags)
553 { /* lvds_set_mode */
554 lvds_context_t *pd_context = NULL;
555 unsigned long port_control = 0;
556 unsigned long preserve = 0;
560 pd_context = (lvds_context_t *)context;
562 PD_DEBUG("lvds_post_set_mode)\n");
563 /* Make sure these parameters are valide */
564 if (!pd_context || !mode) {
565 return (PD_ERR_NULL_PTR);
568 /* Before enabling the LVDS port, make sure that display PLL for this pipe
569 * is enabled and the port is power sequenced on using the panel power
570 * sequencing logic. */
572 preserve = 0x3E007803;
573 port_control = preserve & lvds_read_reg(pd_context, 0x61180, PD_REG_MIO);
574 port_control |= BIT(31); /* enable LVDS port */
575 if (flags & PD_SET_MODE_PIPE_B) {
576 port_control |= BIT(30);
578 port_control |= (BIT(9)|BIT(8)); /* power up */
579 port_control &= ~(BIT(21)|BIT(20)); /* Default sync polarites active high */
581 /* For gn4+, dither moved to port_control from panel_fit reg */
582 if (pd_context->gn4_plus) {
583 if (pd_context->panel_depth == 18) {
584 port_control |= BIT(25);
587 if (pd_context->dither != 0xFFFF) {
588 if (pd_context->dither) {
589 port_control |= BIT(25);
591 port_control &= ~BIT(25);
596 if((!pd_context->panel_fit) &&
597 ( mode->width < pd_context->fp_width ||
598 mode->height < pd_context->fp_height ) ){
599 port_control |= BIT(15); /* enable border in active for centering */
603 * Bit 24 for OpenLDI should be set to a 0 (1x18.0, 2x18.0, 1x24.0, 2x24.0).
604 * From (B-Spec, Ref# 22316, section 1.15.3.8.3)
606 * Bit 24 for SPWG should be set to a 1 (1x24.1 or 2x24.1).
608 * This was verified by comparing the timing diagram for 1x24.0 in
609 * OpenLDI spec (5.4.2.2 24-bit Single Pixel Mode, Unbalanced) with the
610 * same diagram in the Display BSpec for Napa or Gen4.
611 * A0
\96 A3 signals match the 1x24.0.
613 * From the OpenLDI spec (bit mappings are different):
615 * Table 5-2, Bit Number Mappings
616 * 18 bpp bit# 24 bpp bit# OpenLDI bit#
627 /* Attribute panel_type description:
628 * Attr ID Attr Value IntLVDS dataformat
629 * ======= =============== ==================
630 * 49 0 (SPWG) 1 (value of Bit 24)
631 * 49 1 (OpenLDI) 0 (value of Bit 24)
633 if (pd_context->panel_type == 0) {
634 port_control |= BIT(24); /* Dataformat 0 = SPWG, 1 = OpenLDI */
637 /* If the dual-channel is required, then power up second channel
638 * ClkB and B0, B1, B2, (B3) */
639 if (pd_context->dual_channel) {
640 port_control |= (BIT(5)|BIT(4)); /* ClkB */
641 port_control |= (BIT(3)|BIT(2)); /* B0, B1, B2, (B3) */
644 /* Check for 18 or 24 bit panel */
645 if (pd_context->panel_depth == 24) {
646 /* If the panel is 24-bit (8-bpp), enable A3, (B3) pair. */
647 port_control |= (BIT(7)|BIT(6));
650 /* Set the sync polarities correctly if there is a native dtd */
651 if (pd_context->native_dtd) {
652 /* Set bit 20 for hsync active low */
653 if ((pd_context->native_dtd->mode_info_flags & PD_HSYNC_HIGH) == 0) {
654 port_control |= BIT(20);
656 /* Set bit 21 for vsync active low */
657 if ((pd_context->native_dtd->mode_info_flags & PD_VSYNC_HIGH) == 0) {
658 port_control |= BIT(21);
662 lvds_write_reg(pd_context, 0x61204, 0xABCD0000, 0xFFFFFFFF, PD_REG_MIO);
664 lvds_write_reg(pd_context, 0x61180, port_control, 0xFFFFFFFF, PD_REG_MIO);
665 ret = lvds_set_power(pd_context, PD_POWER_MODE_D0);
667 PD_ERROR("PD set_power (D0) returned: 0x%x", ret);
671 lvds_write_reg(pd_context, 0x61204, BIT(1), BIT(1), PD_REG_MIO);
675 /* Set the mode as per given timings */
677 } /* lvds_set_mode */
679 /*----------------------------------------------------------------------------
681 * Function: lvds_set_attrs
684 * Incorporate attributes in "list" into the device context. This function
685 * will override the initial attributes set by init_attrs.
688 * [INOUT] context: device context
689 * [IN] num: not used, but must not be 0.
690 * [IN] list: list of attributes to incorporate into device context
693 * PD_ERR_NULL_PTR: if one of the parameters is invalid
694 * PD_ERR_ATTR_CANT_CHANGE: attributes cannot be modified
695 * PD_SUCCESS: if successful
697 *----------------------------------------------------------------------------
699 /* Tables required by Optimization Code. lvds_set_attrs() */
700 typedef struct _opt_table_data {
702 unsigned short block;
703 unsigned short offset;
704 } lvds_opt_table_data_t;
705 static lvds_opt_table_data_t table_opt_data1[] = {
707 /*<--- id ----------> <---block---> <------ offset -------------------> */
708 {PD_ATTR_ID_PANEL_DEPTH, 1, PD_OFFSETOF(lvds_context_t, panel_depth) },
710 {PD_ATTR_ID_2_CHANNEL_PANEL, 1, PD_OFFSETOF(lvds_context_t, dual_channel)},
711 {PD_ATTR_ID_LVDS_PANEL_TYPE, 1, PD_OFFSETOF(lvds_context_t, panel_type) },
712 {PD_ATTR_ID_PANEL_FIT, 1, PD_OFFSETOF(lvds_context_t, panel_fit) },
713 {PD_ATTR_ID_DITHER, 1, PD_OFFSETOF(lvds_context_t, dither) },
714 {PD_ATTR_ID_MAINTAIN_ASPECT_RATIO,1,PD_OFFSETOF(lvds_context_t,aspect_ratio)},
715 {PD_ATTR_ID_TEXT_TUNING, 1, PD_OFFSETOF(lvds_context_t, text_tune)},
716 {PD_ATTR_ID_PWM_INTENSITY, 1, PD_OFFSETOF(lvds_context_t, pwm_intensity)},
717 {PD_ATTR_ID_INVERTER_FREQ, 1, PD_OFFSETOF(lvds_context_t, inverter_freq)},
718 {PD_ATTR_ID_BLM_LEGACY_MODE, 1, PD_OFFSETOF(lvds_context_t, blm_legacy_mode)},
719 {LVDS_ATTR_ID_TC_LVDS_CLK, 1, PD_OFFSETOF(lvds_context_t, tc_110MHz_clk)},
721 /*<--- id ----------> <---block---> <------ offset -------------------> */
722 {PD_ATTR_ID_FP_PWR_T1, 2, 0 }, /* 6 */
723 {PD_ATTR_ID_FP_PWR_T2, 2, 0 }, /* 7 */
724 {PD_ATTR_ID_FP_PWR_T3, 2, 0 }, /* 8 */
725 {PD_ATTR_ID_FP_PWR_T4, 2, 0 }, /* 9 */
726 {PD_ATTR_ID_FP_PWR_T5, 2, 0 } /* 10 */
728 /* End of Tables required by Optimization Code. lvds_set_attrs() */
730 int lvds_set_attrs (void *context, unsigned long num, pd_attr_t *list)
732 lvds_context_t *pd_context = (lvds_context_t *) context;
733 pd_attr_t *attr = NULL;
734 unsigned short i = 0;
735 unsigned short j = 0;
736 int ret = PD_SUCCESS;
737 /* no of case IDs in the global table */
738 int num_case_ids = sizeof(table_opt_data1)/sizeof(lvds_opt_table_data_t);
740 /* basic parameter check */
741 if (!context || !num || !list) {
742 return PD_ERR_NULL_PTR;
745 PD_DEBUG("lvds_set_attrs()\n");
746 for (i = 0; i < num; i++, list++) {
747 /* do nothing if the attribute has not been changed */
748 if (!(list->flags & PD_ATTR_FLAG_VALUE_CHANGED)) {
752 /* attributes can't be changed after init has been completed */
753 if (list->flags & PD_ATTR_FLAG_USER_INVISIBLE &&
754 pd_context->init_done) {
755 return PD_ERR_ATTR_CANT_CHANGE;
758 /* Set the internal attributes' list. Note that although get_attr() can
759 * return NULL theortically, it will not do so here because all the
760 * attribute IDs in this switch statement comes from lvds_attrs[],
761 * a list that is automatically initialized into pd_context. This is
762 * why we are not checking for NULL after calling get_attr().
764 #if 0 /* ORIGINAL SWITCH STATEMENT */
766 case PD_ATTR_ID_FP_PWR_T1:
767 case PD_ATTR_ID_FP_PWR_T2:
768 case PD_ATTR_ID_FP_PWR_T3:
769 case PD_ATTR_ID_FP_PWR_T4:
770 case PD_ATTR_ID_FP_PWR_T5:
771 /* current_value should not exceed the predefined max value */
772 attr = pd_get_attr(pd_context->attr_list, pd_context->num_attrs,
774 attr->current_value = LVDS_MIN(
775 ((pd_range_attr_t *)list)->current_value,
779 case PD_ATTR_ID_PANEL_DEPTH:
780 attr = pd_get_attr(pd_context->attr_list, pd_context->num_attrs,
782 attr->current_value = list->current_value;
783 pd_context->panel_depth = (unsigned char) attr->current_value;
786 case PD_ATTR_ID_2_CHANNEL_PANEL:
787 attr = pd_get_attr(pd_context->attr_list, pd_context->num_attrs,
789 attr->current_value = ((pd_bool_attr_t *)list)->current_value;
790 pd_context->dual_channel = (attr->current_value?1:0);
793 case PD_ATTR_ID_LVDS_PANEL_TYPE:
794 attr = pd_get_attr(pd_context->attr_list, pd_context->num_attrs,
796 attr->current_value = ((pd_bool_attr_t *)list)->current_value;
797 pd_context->panel_type = (attr->current_value?1:0);
800 case PD_ATTR_ID_PANEL_FIT:
801 attr = pd_get_attr(pd_context->attr_list, pd_context->num_attrs,
803 attr->current_value = ((pd_bool_attr_t *)list)->current_value;
804 pd_context->panel_fit = (attr->current_value?1:0);
808 /* do nothing if we have an unknown ID */
812 /* OPTIMIZATION CODE BEGINS FOR THE ABOVE SWITCH
813 *----------------------------------------------
814 * Step 1: First identify the code common to all case blocks.
815 * This we call it "common code block" Since this is to be
816 * executed by all the case blocks.
818 * Step 2: Group the cases into blocks based on how we can combine them
820 * Eg: case 0: ptr->x = 1; break; // similar code
821 * case 1: ptr->y = 1; break; // similar code
822 * ------Combined block ----
824 * case 1: PTR_OFFSET_TYPE(ptr, table[i].offset) = 1; break;
825 * This is an important step because we save code space by
826 * mapping many cases to smaller number of blocks. In the above
827 * we use index to get the right offset of the ptr.
829 * Step 3: Assign block IDs to each block. Put the case ID, block ID
830 * and other information such as offsets of ptr in a global
833 * Step 4: During run-time , search through the global table, find the
834 * matching case ID, and execute the common code. Next get the
835 * block ID and execute the block specific code. For the index,
836 * retrieve it from the table for the corresponding case ID.
838 * Step 5: If no matching case ID is found, error it out.
840 * Let's look at the optimization code for the above switch.
841 * The code below searches for the list->id in a global table where
842 * we store all the case values along with block numbers and other
843 * information. After we find a valid id in the table , we execute the
844 * common code for all cases first and then we retrieve the block id.
845 * The block id is necesary to determine which block does the id belong
846 * to. This is used to execute block specific code. Similar to switch
847 * cases ONLY here we try to minimize the no of blocks.
849 * In this example , we store the offsets of field names of a ptr in the
850 * global table.This is necessary to combine cases with "similar" but
853 * Eg: case 0: ptr->x = 1; break; // similar code
854 * case 1: ptr->y = 1; break; // similar code
855 * -- Combined block ----
857 * case 1: PTR_OFFSET_TYPE(ptr, table[i].offset) = 1; break;
859 * By reducing the number of blocks we save on Code space. After we
860 * finish our work , we exit the for loop and check for invalid ID
861 * passed by the upper layer. This is akin to default in the
864 * CAUTION: If there is any change in the switch above, this code
865 * along with the tables have to be re-written and changed according
866 * to the new behaviour of the switch. Examples include adding a new
867 * case in the switch. The reason for all this mumbo-jumbo is to
868 * reduce code size in VBIOS, where we are running out of code space.
870 for(j = 0; j < num_case_ids; j++) {
871 /* Search for the attribute ID in the global table */
872 if(list->id == table_opt_data1[j].id) {
873 /* Run the common code for all the blocks */
874 attr = pd_get_attr(pd_context->attr_list, pd_context->num_attrs,
876 /* Once we get a valid ID, need to find out which block it
877 * belongs so we can execute block specific code.
879 if(table_opt_data1[j].block == 1) { /* block 1 */
880 /* Got the block. Need the offset to the struct for that ID
881 * so we can store value at corresponding offset to get the
882 * desired behaviour for that ID.This is the code that makes
883 * the whole optimization work because we are combining the
884 * case IDs into a single block which saves code.
886 attr->current_value = list->current_value;
887 PTR_OFFSET_USHORT(pd_context, table_opt_data1[j].offset) =
888 (unsigned short) attr->current_value;
890 } else { /* block 2. We only have two blocks. */
891 attr->current_value = LVDS_MIN(
892 list->current_value, ((pd_range_attr_t *)attr)->max);
894 break; /* We found a valid ID, so break inner for loop */
898 /* panel_type 0 (SPWG) isn't available for 18-bit depth */
899 PD_DEBUG("in LVDS_set_attributes()\n");
900 if (pd_context->panel_depth == 18) {
901 pd_context->panel_type = 1;
903 PD_DEBUG("IntLVDS: dual_channel=%u", pd_context->dual_channel);
904 PD_DEBUG("IntLVDS: panel_type=%u panel_fit=%u panel_dep=%u dither=%u",
905 pd_context->panel_type, pd_context->panel_fit,
906 pd_context->panel_depth, pd_context->dither);
907 PD_DEBUG("IntLVDS: keep_aspect_ratio=%u text_tune=%lu",
908 pd_context->aspect_ratio, pd_context->text_tune);
909 PD_DEBUG("IntLVDS: PWM Intensity=%u Inverter Freq=%u, BLM legacy mode =%u",
910 pd_context->pwm_intensity, pd_context->inverter_freq,
911 pd_context->blm_legacy_mode);
912 PD_DEBUG("IntLVDS: tc_110MHz_clk = %u", pd_context->tc_110MHz_clk);
914 if (pd_context->init_done) {
915 /* When emgd_driver_pre_init() pokes new attrs into this port driver,
916 * pd_context->current_mode must be set before calling
917 * lvds_panel_fit(), so set it to the first entry in the timing table:
919 if (pd_context->current_mode == NULL) {
920 pd_context->current_mode = pd_context->timing_table;
922 lvds_panel_fit(pd_context);
926 } /* lvds_set_attrs */
928 /*----------------------------------------------------------------------------
930 * Function: lvds_get_attrs
933 * Extracts the attribute list and the number of elements in the list
934 * from the device context.
937 * [IN] context: device context to extract information from
938 * [OUT] num: number of elements in list
939 * [OUT] list: list of attributes from the device context
942 * PD_ERR_NULL_PTR: if one of the parameters is invalid
943 * PD_SUCCESS: if successful
945 *----------------------------------------------------------------------------
947 int lvds_get_attrs (void *context, unsigned long *num, pd_attr_t **list)
948 { /* lvds_get_attrs */
949 /* basic parameter check */
950 if (!context || !num || !list) {
951 return PD_ERR_NULL_PTR;
954 PD_DEBUG("lvds_get_attrs()\n");
955 /* Nothing fancy, just extracting the elements from the list */
956 *list = ((lvds_context_t *)context)->attr_list;
957 *num = ((lvds_context_t *)context)->num_attrs;
960 } /* lvds_get_attrs */
962 /*----------------------------------------------------------------------------
964 * Function: lvds_get_timing_list
970 * [IN] context: device context to extract information from
975 * PD_ERR_NULL_PTR: if one of the parameters is invalid
976 * PD_ERR_NOMEM: if internal memory allocate failed
977 * PD_SUCCESS: if successful
979 *----------------------------------------------------------------------------
981 int lvds_get_timing_list (void *context, pd_timing_t *in_list,
983 { /* lvds_get_timing_list */
984 lvds_context_t *pd_context = (lvds_context_t *)context;
985 pd_dvo_info_t lvds_info = {0, 0, 1, 0, 0, 0, 0, 0};
986 pd_display_info_t lvds_display_info = {0, 0, 0, 0, NULL};
989 PD_DEBUG("lvds_get_timing_list()\n");
990 lvds_get_dclk( pd_context, &lvds_info );
992 PD_DEBUG("chipset = 0x%x", pd_context->chipset);
993 lvds_display_info.panel_fit = (unsigned char) pd_context->panel_fit;
994 ret = pd_filter_timings(pd_context->callback->callback_context,
995 in_list, &pd_context->timing_table, &lvds_info, &lvds_display_info);
997 /* Helper function will return the below values */
998 pd_context->native_dtd = lvds_display_info.native_dtd;
999 pd_context->fp_width = lvds_display_info.width;
1000 pd_context->fp_height = lvds_display_info.height;
1002 *list = pd_context->timing_table;
1004 } /* lvds_get_timing_list */
1006 /*----------------------------------------------------------------------------
1008 * Function: lvds_set_power
1011 * Sets LVDS to the specified power state
1013 * Conversion between IEGD timer values to LVDS port timer values
1015 * SEPG(?) IEGD LVDS Port Program bits min max
1016 * ---- ------- ------------ ---------------- ------ ---------
1017 * T1+T2 T1 ms T1+T2 100us 0x61208 [28:16] 0 ms 819.2 ms
1018 * T5 T2 ms T5 100us 0x61208 [12:00] 0 ms 819.2 ms
1019 * T6 T3 ms Tx 100us 0x6120C [12:00] 0 ms 819.2 ms
1020 * T3 T4 ms T3 100us 0x6120C [28:16] 0 ms 819.2 ms
1021 * T4 T5 ms T4 100ms 0x61210 [04:00] 0 ms 3200 ms
1030 * [IN] context: device context to extract information from
1035 * PD_ERR_NULL_PTR: if context is NULL
1036 * PD_ERR_INVALID_POWER: if "state" is invalid
1037 * PD_SUCCESS: if successful
1039 *----------------------------------------------------------------------------
1041 /* Tables required by the optimization codes in lvds_set_power() */
1049 static opt_set_power_t table_set_power[] = {
1050 /* id1 id2 bit reg */
1051 { PD_ATTR_ID_FP_PWR_T1, PD_ATTR_ID_FP_PWR_T2, 1, 0x61208 }, /* D0 */
1052 { PD_ATTR_ID_FP_PWR_T4, PD_ATTR_ID_FP_PWR_T3, 0, 0x6120C }, /* Dx */
1055 int lvds_set_power(void *context, unsigned long state)
1056 { /* lvds_set_power */
1057 unsigned long i = 0;
1058 lvds_context_t *pd_context = (lvds_context_t *)context;
1059 pd_attr_t *tattr = NULL; /* holds time delay b/ pwr transition */
1060 unsigned long delay = 0, delay1;
1062 PD_DEBUG("state = %lu", state);
1064 PD_DEBUG("lvds_set_power() to state = %lu\n",state);
1065 /* Basic parameter check */
1067 PD_DEBUG("No context");
1068 return PD_ERR_NULL_PTR;
1070 PD_DEBUG("pd_context=0x%lx", (unsigned long)pd_context);
1072 /* Check for invalid state */
1073 if (state > PD_POWER_MODE_D3) {
1074 PD_DEBUG("Invalid power state");
1075 return PD_ERR_INVALID_POWER;
1078 /* Get the index into above table */
1079 if (state == PD_POWER_MODE_D0) {
1085 lvds_write_reg(pd_context, 0x61204, 0xABCD0000, 0xFFFFFFFF, PD_REG_MIO);
1087 /* Program panel power up/down delays: Either T1/T2 or T3/T4*/
1088 tattr = pd_get_attr(pd_context->attr_list,
1089 pd_context->num_attrs, table_set_power[i].id1, 0);
1090 /* Convert ms to 100us */
1091 delay1 = tattr->current_value;
1092 delay = (tattr->current_value * 10) << 16;
1093 tattr = pd_get_attr(pd_context->attr_list,
1094 pd_context->num_attrs, table_set_power[i].id2, 0);
1095 delay1 += tattr->current_value;
1096 delay |= tattr->current_value * 10;
1098 lvds_write_reg(pd_context, table_set_power[i].reg, delay, 0x1FFF1FFF, PD_REG_MIO);
1100 /* Program power cycle delay: convert ms to 100ms */
1101 delay = pd_get_attr(pd_context->attr_list,
1102 pd_context->num_attrs, PD_ATTR_ID_FP_PWR_T5, 0)->current_value;
1104 /* TODO: Write reference divider [31:8] */
1105 delay = ((delay/100+1) & 0xFF) | ((pd_context->gfx_freq*100/2-1)<<8);
1106 lvds_write_reg(pd_context, 0x61210, delay, 0xFFFFFFFF, PD_REG_MIO);
1108 /* Power state target */
1109 lvds_write_reg(pd_context, 0x61204, table_set_power[i].bit, BIT(0), PD_REG_MIO);
1111 /* Power down on reset available on crestline onwards */
1112 lvds_write_reg(pd_context, 0x61204, BIT(1), BIT(1), PD_REG_MIO);
1114 /* Make this a compile time so that size of vBIOS doesn't become > 64KB */
1115 #if defined(CONFIG_PLB) || defined(CONFIG_TNC)
1116 /* PWM is a method of controlling the backlight intensity.
1117 * It is not method to turn on baclkight.
1118 * We still need the PD method to turn on the backlight.
1120 * This feature is for Pouslbo Only. We check that the user has set the
1121 * inverter frequency. Default intensity, if not set, is 100%
1123 * Due to the high amount of calculation, we want to only set this register
1124 * if it has not been ser previously. The register could be
1125 * "brought forward" from VBIOS.
1127 if(pd_context->inverter_freq != 0xFFFF && /* Overwritten by set_attr */
1128 (pd_context->chipset == PCI_DEVICE_ID_VGA_PLB ||
1129 pd_context->chipset == PCI_DEVICE_ID_VGA_TNC ||
1130 pd_context->chipset == PCI_DEVICE_ID_VGA_TNC_A0 ||
1131 pd_context->chipset == PCI_DEVICE_ID_VGA_LNC) &&
1132 !pd_context->pwm_done) {
1133 unsigned long reg_value = 0;
1134 unsigned long percentage = 0;
1135 unsigned long calculation = 0;
1136 unsigned long blc_pwm_ctl2 = 0;
1138 /* We first need to get the graphics frequency, which will be used to
1139 * calculate Backlight Modulation Frequency[BMF]. BMF will be used to
1140 * fill up the 15 MSB in the 0x61254 register
1142 * The calculation for the Modulation Frequency field in the
1143 * BLC_PWM_CTL Register is:
1145 * Reference Clock Freq 1
1146 * ----------------------- x ------------------
1147 * Divider PWM Freq in Hz
1151 /* GMA accurate calculation that requires "calculation" to be an
1152 * unsigned long long typedef */
1153 calculation = pd_context->gfx_freq * PWM_FREQ_CALC_CONSTANT_2;
1154 calculation = calculation / 0x20; /*pouslbo specific divider*/
1155 calculation = calculation * PWM_FREQ_CALC_CONSTANT_1;
1156 calculation = calculation / pd_context->inverter_freq;
1157 calculation = calculation / PWM_FREQ_CALC_CONSTANT_1;
1159 /* Some system bios cannot take 64 bit data type. Using a more
1160 * simplified calculation that is not too accurate if the inputs
1161 * are not round numbers */
1162 calculation = pd_context->inverter_freq * 0x20; /* plb/tnc divider */
1163 calculation = (pd_context->gfx_freq * PWM_FREQ_CALC_CONSTANT_2) /
1166 /* Writing the register: 15 MSB is the max lvds clock / 32.
1167 * Bit 16 can either be legacy or non legacy depending upon Attr #72. */
1168 if (pd_context->gn4_plus) {
1169 blc_pwm_ctl2 = (1L << 31) |
1170 (pd_context->blm_legacy_mode << 30) |
1171 ((pd_context->pipe == PD_SET_MODE_PIPE_B)?1L:0L << 29);
1172 lvds_write_reg(pd_context, 0x61250, blc_pwm_ctl2, 0xFFFFFFFF, PD_REG_MIO);
1173 reg_value = (calculation & 0xFFFF)<<16;
1175 reg_value = ((calculation & 0xFFFE) |
1176 pd_context->blm_legacy_mode)<<16;
1179 /* The 16 LSB is a value that the user sets in configuration.
1180 * user sets the value in percentage.
1181 * We convert it into the clock speed */
1182 percentage = ( pd_context->pwm_intensity * (unsigned long)calculation);
1183 reg_value |= (unsigned long)( percentage / (int)100 ) & 0xFFFE;
1184 lvds_write_reg(pd_context, 0x61254, reg_value, 0xFFFFFFFF, PD_REG_MIO);
1186 /* set the flag so that we only do this function once */
1187 pd_context->pwm_done = 1;
1191 if (state != PD_POWER_MODE_D0) {
1192 /* Wait until the current power up/down sequence is complete */
1194 while(lvds_read_reg(pd_context, 0x61200, PD_REG_MIO) & 0x80000000L) {
1200 lvds_write_reg(pd_context, 0x61180, 0, BIT(31), PD_REG_MIO);
1206 /*-----------------------------------------------------------------------------
1207 * LPC Register Offsets. Used for LVDS BKLT control. Registers are part
1208 * Atom E6xx [D31:F0]
1209 ----------------------------------------------------------------------------*/
1213 #define TNC_LVDS_VDDEN BIT(0)
1214 #define TNC_LVDS_BKLTEN BIT(1)
1215 #define TNC_LVDS_BKLTCTL BIT(2)
1217 if (pd_context->chipset == PCI_DEVICE_ID_VGA_TNC ||
1218 pd_context->chipset == PCI_DEVICE_ID_VGA_TNC_A0 ||
1219 pd_context->chipset == PCI_DEVICE_ID_VGA_LNC) {
1220 unsigned long value;
1222 /* Enable backlight for LVDS: based on observed si behavior:
1223 * Subject to change based on si DE feedback */
1224 if (state == PD_POWER_MODE_D0) {
1225 value = TNC_LVDS_BKLTCTL|TNC_LVDS_BKLTEN|TNC_LVDS_BKLTCTL;
1229 lvds_write_reg(pd_context, RGEN, value,
1230 TNC_LVDS_BKLTCTL|TNC_LVDS_BKLTEN|TNC_LVDS_BKLTCTL, PD_REG_LPC);
1231 lvds_write_reg(pd_context, RGIO, value,
1232 TNC_LVDS_BKLTCTL|TNC_LVDS_BKLTEN|TNC_LVDS_BKLTCTL, PD_REG_LPC);
1237 /* update power state */
1238 pd_context->power_state = state;
1240 } /* lvds_set_power */
1242 /*----------------------------------------------------------------------------
1244 * Function: lvds_get_power
1247 * Returns the current LVDS power state back to the caller.
1250 * [IN] context: device context to extract information from
1251 * [OUT] state: current power state
1254 * PD_ERR_NULL_PTR: if one of the parameters is invalid
1255 * PD_SUCCESS: if successful
1257 *----------------------------------------------------------------------------
1260 int lvds_get_power (void *context, unsigned long *state)
1261 { /* lvds_get_power */
1262 if ((NULL == context) || (NULL == state)) {
1263 return PD_ERR_NULL_PTR;
1265 PD_DEBUG("lvds_get_power()\n");
1266 /* The caller should be able to do this himself, but whatever */
1267 *state = ((lvds_context_t *) context)->power_state;
1269 } /* lvds_get_power */
1271 int lvds_save(void *context, void **state, unsigned long flags)
1277 int lvds_restore(void *context, void *state, unsigned long flags)
1279 int ret = PD_SUCCESS;
1283 /*----------------------------------------------------------------------
1284 * Function: lvds_get_port_status()
1286 * Description: It is called to get the information about the display
1288 * Parameters: context - Port driver's context
1289 * port_status - Returns the display type and connection state
1291 * Return: PD_SUCCESS(0) success
1292 * PD_ERR_XXXXXX otherwise
1293 *----------------------------------------------------------------------*/
1294 int lvds_get_port_status(void *context, pd_port_status_t *port_status)
1296 /* Display connection cannot be determined */
1297 port_status->display_type = PD_DISPLAY_LVDS_INT;
1298 port_status->connected = PD_DISP_STATUS_UNKNOWN;
1302 static unsigned long lvds_read_reg(lvds_context_t *pd_context,unsigned long reg,
1303 unsigned long reg_type)
1310 list[1].reg = PD_REG_LIST_END;
1311 ret = pd_context->callback->read_regs(
1312 pd_context->callback->callback_context, list, reg_type);
1314 PD_ERROR("LVDS read regs: Failed.");
1316 return list[0].value;
1319 /*----------------------------------------------------------------------------
1321 * Function: lvds_write_reg
1324 * Writes bits in "value" into a register. Bits written are dictated by
1325 * the "change_bits" mask.
1328 * [IN] pd_context: device context, dispatcher to the actual write_reg
1330 * [IN] reg: register to write value to
1331 * [IN] value: value to change the register to
1332 * [IN] change_bits: bit mask, the bits set to "1" will be modified by
1333 * the corresponding bits in "value"
1338 *----------------------------------------------------------------------------
1340 static void lvds_write_reg(lvds_context_t *pd_context, unsigned long reg,
1341 unsigned long value,
1342 unsigned long change_bits,
1343 unsigned long reg_type)
1344 { /* lvds_write_reg */
1351 list[0].value = (lvds_read_reg(pd_context, reg, PD_REG_MIO) & ~change_bits) | value;
1352 list[1].reg = PD_REG_LIST_END;
1353 ret = pd_context->callback->write_regs(
1354 pd_context->callback->callback_context, list, reg_type);
1356 PD_ERROR("LVDS write regs: Failed.");
1360 } /* lvds_write_reg */
1362 /*----------------------------------------------------------------------------
1364 * Function: lvds_panel_fit
1367 * Enables panel fitting
1370 * [IN] pd_context: device context
1375 *----------------------------------------------------------------------------
1377 static void lvds_panel_fit(lvds_context_t *pd_context)
1379 /* enable auto vertical ratio */
1380 /* enable auto horizantal ratio */
1382 unsigned long panel_fit_reg = 0x00000220;
1383 PD_DEBUG("lvds_panel_fit() \n");
1387 if (pd_context->current_mode->width != pd_context->fp_width ||
1388 pd_context->current_mode->height != pd_context->fp_height) {
1389 /* Enable panel fitting */
1390 /* vertical interpolation = bilinear */
1391 /* horizontal interpolation = bilinear */
1392 panel_fit_reg |= 0x80000440;
1394 /* Enable dither based on default/user-set value:
1396 * dither = 1 for 18-bit panels
1397 * = 0 for 24-bit panels.
1398 * But this behavior can be changed by setting the DITHER attribute.
1399 * When user sets the attribute, dither will be updated
1400 * as part of attribute processing in set attributes. */
1401 /* For gn4 based chipsets dither is controlled in port_control register */
1402 if (!pd_context->gn4_plus) {
1403 /* Default behavior */
1404 if (pd_context->panel_depth == 18) {
1405 panel_fit_reg |= BIT(3);
1408 /* Overwritten by set attribute */
1409 if (pd_context->dither != 0xFFFF) {
1410 if (pd_context->dither) {
1411 panel_fit_reg |= BIT(3);
1413 panel_fit_reg &= ~BIT(3);
1418 if (pd_context->gn4_plus) {
1419 unsigned long src_ratio, dest_ratio;
1421 if (pd_context->native_dtd &&
1422 (pd_context->current_mode->width != pd_context->native_dtd->width ||
1423 pd_context->current_mode->height !=
1424 pd_context->native_dtd->height)) {
1425 /* Enable panel fitter */
1426 panel_fit_reg = 0x80000000;
1428 /* Select the pipe */
1429 if (pd_context->pipe & PD_SET_MODE_PIPE_B) {
1430 panel_fit_reg |= BIT(29); /* bits[30:29] = 01 for pipe B */
1434 * Default - Auto scaling src_ratio == dest_ratio
1435 * Piller box scaling - src_ratio < dest_ratio
1436 * Letter box scaling - src_ratio > dest_ratio */
1438 /* To make this work correctly, port driver shall know the
1439 * size of the framebuffer, not the src mode. Most of the
1440 * times the src mode is fb, but not all the cases.
1441 * User has an attribute to change
1442 * 1. Between Pillerbox and auto, and vice versa
1444 * 2. Between Letterbox and auto, and vice versa.
1446 if (pd_context->aspect_ratio) {
1447 src_ratio = (pd_context->current_mode->width << 10)/
1448 (pd_context->current_mode->height);
1449 dest_ratio = (pd_context->native_dtd->width << 10)/
1450 (pd_context->native_dtd->height);
1452 if (dest_ratio > src_ratio) {
1453 /* Pillarbox scaling */
1454 panel_fit_reg |= BIT(27);
1455 } else if (dest_ratio < src_ratio) {
1456 /* Letterbox scaling */
1457 panel_fit_reg |= BIT(27) | BIT(26);
1461 /* Filter coefficient select: pd_context->text_tune = 0,1,2 */
1462 panel_fit_reg |= (pd_context->text_tune << 24);
1466 lvds_write_reg(pd_context, 0x61230, panel_fit_reg, 0xFFFFFFFF, PD_REG_MIO);
1467 PD_DEBUG("panel_fit_reg 0x61230 = 0x%lx", panel_fit_reg);
1471 /*----------------------------------------------------------------------------
1473 * Function: lvds_get_dclk
1476 * Gets the Dclk for LVDS
1479 * [IN] pd_context: device context
1480 * [OUT]lvds_info: Structure that contains the min and max dclk
1485 *----------------------------------------------------------------------------
1487 static void lvds_get_dclk(lvds_context_t *pd_context, pd_dvo_info_t *lvds_info )
1489 PD_DEBUG("lvds_get_dclk()\n");
1490 /* Get the min and max dclks for lvds */
1491 if (pd_context->dual_channel) {
1492 lvds_info->min_dclk = LVDS_MIN_DCLK * 2 ;
1493 lvds_info->max_dclk = LVDS_MAX_DCLK * 2;
1495 lvds_info->min_dclk = LVDS_MIN_DCLK;
1496 lvds_info->max_dclk = LVDS_MAX_DCLK;
1498 /* This #define is the result of code size reduction effort. */
1500 /* Set dclk for GM965 */
1501 if(pd_context->chipset==PCI_DEVICE_ID_VGA_CTG){
1502 /* Set dclk for GM965/CTG */
1503 if (pd_context->dual_channel) {
1504 lvds_info->min_dclk = LVDS_GM965_DUAL_MIN_DCLK ;
1505 lvds_info->max_dclk = LVDS_GM965_DUAL_MAX_DCLK;
1507 lvds_info->min_dclk = LVDS_GM965_SINGLE_MIN_DCLK;
1508 lvds_info->max_dclk = LVDS_GM965_SINGLE_MAX_DCLK;
1513 /* Set dclk for 915GM */
1514 if(pd_context->chipset==PCI_DEVICE_ID_VGA_915AL){
1515 if (pd_context->dual_channel) {
1516 lvds_info->min_dclk = LVDS_915GM_DUAL_MIN_DCLK ;
1517 lvds_info->max_dclk = LVDS_915GM_DUAL_MAX_DCLK;
1519 lvds_info->min_dclk = LVDS_915GM_SINGLE_MIN_DCLK;
1520 lvds_info->max_dclk = LVDS_915GM_SINGLE_MAX_DCLK;
1522 } else if(pd_context->chipset==PCI_DEVICE_ID_VGA_945GM ||
1523 pd_context->chipset==PCI_DEVICE_ID_VGA_945GME){
1524 /* Set dclk for 945GM */
1525 if (pd_context->dual_channel) {
1526 lvds_info->min_dclk = LVDS_945GM_DUAL_MIN_DCLK ;
1527 lvds_info->max_dclk = LVDS_945GM_DUAL_MAX_DCLK;
1529 lvds_info->min_dclk = LVDS_945GM_SINGLE_MIN_DCLK;
1530 lvds_info->max_dclk = LVDS_945GM_SINGLE_MAX_DCLK;
1532 } else if(pd_context->chipset==PCI_DEVICE_ID_VGA_GM965 ||
1533 pd_context->chipset==PCI_DEVICE_ID_VGA_GME965){
1534 /* Set dclk for GM965 */
1535 if (pd_context->dual_channel) {
1536 lvds_info->min_dclk = LVDS_GM965_DUAL_MIN_DCLK ;
1537 lvds_info->max_dclk = LVDS_GM965_DUAL_MAX_DCLK;
1539 lvds_info->min_dclk = LVDS_GM965_SINGLE_MIN_DCLK;
1540 lvds_info->max_dclk = LVDS_GM965_SINGLE_MAX_DCLK;
1546 /* Get the min and max dclks for Atom E6xx lvds */
1547 if ((pd_context->chipset == PCI_DEVICE_ID_VGA_TNC) ||
1548 (pd_context->chipset == PCI_DEVICE_ID_VGA_TNC_A0) ||
1549 (pd_context->chipset == PCI_DEVICE_ID_VGA_LNC)) {
1550 lvds_info->min_dclk = LVDS_TNC_SINGLE_MIN_DCLK;
1551 /* Experimental feature to raise TC LVDS clk to 110MHz. */
1552 if (pd_context->tc_110MHz_clk) {
1553 lvds_info->max_dclk = 110000L;
1555 lvds_info->max_dclk = LVDS_TNC_SINGLE_MAX_DCLK;