ch7017.c revision fa225cbc
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#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include <stdint.h> 33#include <unistd.h> 34 35#include "xf86.h" 36#include "xf86_OSproc.h" 37#include "compiler.h" 38#include "miscstruct.h" 39#include "xf86i2c.h" 40#include "xf86Crtc.h" 41#ifdef HAVE_XEXTPROTO_71 42#include <X11/extensions/dpmsconst.h> 43#else 44#define DPMS_SERVER 45#include <X11/extensions/dpms.h> 46#endif 47 48 49#include "../i2c_vid.h" 50#include "ch7017_reg.h" 51 52struct ch7017_priv { 53 I2CDevRec d; 54 55 uint8_t save_hapi; 56 uint8_t save_vali; 57 uint8_t save_valo; 58 uint8_t save_ailo; 59 uint8_t save_lvds_pll_vco; 60 uint8_t save_feedback_div; 61 uint8_t save_lvds_control_2; 62 uint8_t save_outputs_enable; 63 uint8_t save_lvds_power_down; 64 uint8_t save_power_management; 65}; 66 67static void 68ch7017_dump_regs(I2CDevPtr d); 69static void 70ch7017_dpms(I2CDevPtr d, int mode); 71 72static Bool 73ch7017_read(struct ch7017_priv *priv, int addr, uint8_t *val) 74{ 75 if (!xf86I2CReadByte(&priv->d, addr, val)) { 76 xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR, 77 "Unable to read from %s Slave %d.\n", 78 priv->d.pI2CBus->BusName, priv->d.SlaveAddr); 79 return FALSE; 80 } 81 return TRUE; 82} 83 84static Bool 85ch7017_write(struct ch7017_priv *priv, int addr, uint8_t val) 86{ 87 if (!xf86I2CWriteByte(&priv->d, addr, val)) { 88 xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR, 89 "Unable to write to %s Slave %d.\n", 90 priv->d.pI2CBus->BusName, priv->d.SlaveAddr); 91 return FALSE; 92 } 93 return TRUE; 94} 95 96/** Probes for a CH7017 on the given bus and slave address. */ 97static void * 98ch7017_init(I2CBusPtr b, I2CSlaveAddr addr) 99{ 100 struct ch7017_priv *priv; 101 uint8_t val; 102 103 priv = xcalloc(1, sizeof(struct ch7017_priv)); 104 if (priv == NULL) 105 return NULL; 106 107 priv->d.DevName = "CH7017/7018/7019 LVDS Controller"; 108 priv->d.SlaveAddr = addr; 109 priv->d.pI2CBus = b; 110 priv->d.StartTimeout = b->StartTimeout; 111 priv->d.BitTimeout = b->BitTimeout; 112 priv->d.AcknTimeout = b->AcknTimeout; 113 priv->d.ByteTimeout = b->ByteTimeout; 114 priv->d.DriverPrivate.ptr = priv; 115 116 if (!xf86I2CReadByte(&priv->d, CH7017_DEVICE_ID, &val)) 117 goto fail; 118 119 if (val != CH7017_DEVICE_ID_VALUE && 120 val != CH7018_DEVICE_ID_VALUE && 121 val != CH7019_DEVICE_ID_VALUE) { 122 xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR, 123 "ch701x not detected, got %d: from %s Slave %d.\n", 124 val, priv->d.pI2CBus->BusName, priv->d.SlaveAddr); 125 goto fail; 126 } 127 128 if (!xf86I2CDevInit(&(priv->d))) 129 goto fail; 130 131 return priv; 132 133fail: 134 xfree(priv); 135 return NULL; 136} 137 138static xf86OutputStatus 139ch7017_detect(I2CDevPtr d) 140{ 141 return XF86OutputStatusUnknown; 142} 143 144static ModeStatus 145ch7017_mode_valid(I2CDevPtr d, DisplayModePtr mode) 146{ 147 if (mode->Clock > 160000) 148 return MODE_CLOCK_HIGH; 149 150 return MODE_OK; 151} 152 153static void 154ch7017_mode_set(I2CDevPtr d, DisplayModePtr mode, DisplayModePtr adjusted_mode) 155{ 156 struct ch7017_priv *priv = d->DriverPrivate.ptr; 157 uint8_t lvds_pll_feedback_div, lvds_pll_vco_control; 158 uint8_t outputs_enable, lvds_control_2, lvds_power_down; 159 uint8_t horizontal_active_pixel_input; 160 uint8_t horizontal_active_pixel_output, vertical_active_line_output; 161 uint8_t active_input_line_output; 162 163 xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, 164 "Registers before mode setting\n"); 165 ch7017_dump_regs(d); 166 167 /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/ 168 if (mode->Clock < 100000) { 169 outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW; 170 lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | 171 (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | 172 (13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); 173 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | 174 (2 << CH7017_LVDS_PLL_VCO_SHIFT) | 175 (3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); 176 lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) | 177 (0 << CH7017_PHASE_DETECTOR_SHIFT); 178 } else { 179 outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH; 180 lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | 181 (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | 182 (3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); 183 lvds_pll_feedback_div = 35; 184 lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) | 185 (0 << CH7017_PHASE_DETECTOR_SHIFT); 186 if (1) { /* XXX: dual channel panel detection. Assume yes for now. */ 187 outputs_enable |= CH7017_LVDS_CHANNEL_B; 188 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | 189 (2 << CH7017_LVDS_PLL_VCO_SHIFT) | 190 (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); 191 } else { 192 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | 193 (1 << CH7017_LVDS_PLL_VCO_SHIFT) | 194 (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); 195 } 196 } 197 198 horizontal_active_pixel_input = mode->HDisplay & 0x00ff; 199 200 vertical_active_line_output = mode->VDisplay & 0x00ff; 201 horizontal_active_pixel_output = mode->HDisplay & 0x00ff; 202 203 active_input_line_output = ((mode->HDisplay & 0x0700) >> 8) | 204 (((mode->VDisplay & 0x0700) >> 8) << 3); 205 206 lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED | 207 (mode->HDisplay & 0x0700) >> 8; 208 209 ch7017_dpms(d, DPMSModeOff); 210 ch7017_write(priv, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, 211 horizontal_active_pixel_input); 212 ch7017_write(priv, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT, 213 horizontal_active_pixel_output); 214 ch7017_write(priv, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, 215 vertical_active_line_output); 216 ch7017_write(priv, CH7017_ACTIVE_INPUT_LINE_OUTPUT, 217 active_input_line_output); 218 ch7017_write(priv, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control); 219 ch7017_write(priv, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div); 220 ch7017_write(priv, CH7017_LVDS_CONTROL_2, lvds_control_2); 221 ch7017_write(priv, CH7017_OUTPUTS_ENABLE, outputs_enable); 222 223 /* Turn the LVDS back on with new settings. */ 224 ch7017_write(priv, CH7017_LVDS_POWER_DOWN, lvds_power_down); 225 226 xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, 227 "Registers after mode setting\n"); 228 ch7017_dump_regs(d); 229} 230 231/* set the CH7017 power state */ 232static void 233ch7017_dpms(I2CDevPtr d, int mode) 234{ 235 struct ch7017_priv *priv = d->DriverPrivate.ptr; 236 uint8_t val; 237 238 ch7017_read(priv, CH7017_LVDS_POWER_DOWN, &val); 239 240 /* Turn off TV/VGA, and never turn it on since we don't support it. */ 241 ch7017_write(priv, CH7017_POWER_MANAGEMENT, 242 CH7017_DAC0_POWER_DOWN | 243 CH7017_DAC1_POWER_DOWN | 244 CH7017_DAC2_POWER_DOWN | 245 CH7017_DAC3_POWER_DOWN | 246 CH7017_TV_POWER_DOWN_EN); 247 248 if (mode == DPMSModeOn) { 249 /* Turn on the LVDS */ 250 ch7017_write(priv, CH7017_LVDS_POWER_DOWN, 251 val & ~CH7017_LVDS_POWER_DOWN_EN); 252 } else { 253 /* Turn off the LVDS */ 254 ch7017_write(priv, CH7017_LVDS_POWER_DOWN, 255 val | CH7017_LVDS_POWER_DOWN_EN); 256 } 257 258 /* XXX: Should actually wait for update power status somehow */ 259 usleep(50000); 260} 261 262static void 263ch7017_dump_regs(I2CDevPtr d) 264{ 265 struct ch7017_priv *priv = d->DriverPrivate.ptr; 266 uint8_t val; 267 268#define DUMP(reg) \ 269do { \ 270 ch7017_read(priv, reg, &val); \ 271 xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, \ 272 #reg ": %02x\n", val); \ 273} while (0) 274 275 DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT); 276 DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT); 277 DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT); 278 DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT); 279 DUMP(CH7017_LVDS_PLL_VCO_CONTROL); 280 DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV); 281 DUMP(CH7017_LVDS_CONTROL_2); 282 DUMP(CH7017_OUTPUTS_ENABLE); 283 DUMP(CH7017_LVDS_POWER_DOWN); 284} 285 286static void 287ch7017_save(I2CDevPtr d) 288{ 289 struct ch7017_priv *priv = d->DriverPrivate.ptr; 290 291 ch7017_read(priv, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi); 292 ch7017_read(priv, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo); 293 ch7017_read(priv, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo); 294 ch7017_read(priv, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco); 295 ch7017_read(priv, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div); 296 ch7017_read(priv, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2); 297 ch7017_read(priv, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable); 298 ch7017_read(priv, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down); 299 ch7017_read(priv, CH7017_POWER_MANAGEMENT, &priv->save_power_management); 300} 301 302static void 303ch7017_restore(I2CDevPtr d) 304{ 305 struct ch7017_priv *priv = d->DriverPrivate.ptr; 306 307 /* Power down before changing mode */ 308 ch7017_dpms(d, DPMSModeOff); 309 310 ch7017_write(priv, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi); 311 ch7017_write(priv, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo); 312 ch7017_write(priv, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo); 313 ch7017_write(priv, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco); 314 ch7017_write(priv, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div); 315 ch7017_write(priv, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2); 316 ch7017_write(priv, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable); 317 ch7017_write(priv, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down); 318 ch7017_write(priv, CH7017_POWER_MANAGEMENT, priv->save_power_management); 319} 320 321_X_EXPORT I830I2CVidOutputRec ch7017_methods = { 322 .init = ch7017_init, 323 .detect = ch7017_detect, 324 .mode_valid = ch7017_mode_valid, 325 .mode_set = ch7017_mode_set, 326 .dpms = ch7017_dpms, 327 .dump_regs = ch7017_dump_regs, 328 .save = ch7017_save, 329 .restore = ch7017_restore, 330}; 331