Path: blob/master/drivers/gpu/drm/i915/dvo_ch7017.c
15113 views
/*1* Copyright © 2006 Intel Corporation2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER20* DEALINGS IN THE SOFTWARE.21*22* Authors:23* Eric Anholt <[email protected]>24*25*/2627#include "dvo.h"2829#define CH7017_TV_DISPLAY_MODE 0x0030#define CH7017_FLICKER_FILTER 0x0131#define CH7017_VIDEO_BANDWIDTH 0x0232#define CH7017_TEXT_ENHANCEMENT 0x0333#define CH7017_START_ACTIVE_VIDEO 0x0434#define CH7017_HORIZONTAL_POSITION 0x0535#define CH7017_VERTICAL_POSITION 0x0636#define CH7017_BLACK_LEVEL 0x0737#define CH7017_CONTRAST_ENHANCEMENT 0x0838#define CH7017_TV_PLL 0x0939#define CH7017_TV_PLL_M 0x0a40#define CH7017_TV_PLL_N 0x0b41#define CH7017_SUB_CARRIER_0 0x0c42#define CH7017_CIV_CONTROL 0x1043#define CH7017_CIV_0 0x1144#define CH7017_CHROMA_BOOST 0x1445#define CH7017_CLOCK_MODE 0x1c46#define CH7017_INPUT_CLOCK 0x1d47#define CH7017_GPIO_CONTROL 0x1e48#define CH7017_INPUT_DATA_FORMAT 0x1f49#define CH7017_CONNECTION_DETECT 0x2050#define CH7017_DAC_CONTROL 0x2151#define CH7017_BUFFERED_CLOCK_OUTPUT 0x2252#define CH7017_DEFEAT_VSYNC 0x4753#define CH7017_TEST_PATTERN 0x485455#define CH7017_POWER_MANAGEMENT 0x4956/** Enables the TV output path. */57#define CH7017_TV_EN (1 << 0)58#define CH7017_DAC0_POWER_DOWN (1 << 1)59#define CH7017_DAC1_POWER_DOWN (1 << 2)60#define CH7017_DAC2_POWER_DOWN (1 << 3)61#define CH7017_DAC3_POWER_DOWN (1 << 4)62/** Powers down the TV out block, and DAC0-3 */63#define CH7017_TV_POWER_DOWN_EN (1 << 5)6465#define CH7017_VERSION_ID 0x4a6667#define CH7017_DEVICE_ID 0x4b68#define CH7017_DEVICE_ID_VALUE 0x1b69#define CH7018_DEVICE_ID_VALUE 0x1a70#define CH7019_DEVICE_ID_VALUE 0x197172#define CH7017_XCLK_D2_ADJUST 0x5373#define CH7017_UP_SCALER_COEFF_0 0x5574#define CH7017_UP_SCALER_COEFF_1 0x5675#define CH7017_UP_SCALER_COEFF_2 0x5776#define CH7017_UP_SCALER_COEFF_3 0x5877#define CH7017_UP_SCALER_COEFF_4 0x5978#define CH7017_UP_SCALER_VERTICAL_INC_0 0x5a79#define CH7017_UP_SCALER_VERTICAL_INC_1 0x5b80#define CH7017_GPIO_INVERT 0x5c81#define CH7017_UP_SCALER_HORIZONTAL_INC_0 0x5d82#define CH7017_UP_SCALER_HORIZONTAL_INC_1 0x5e8384#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT 0x5f85/**< Low bits of horizontal active pixel input */8687#define CH7017_ACTIVE_INPUT_LINE_OUTPUT 0x6088/** High bits of horizontal active pixel input */89#define CH7017_LVDS_HAP_INPUT_MASK (0x7 << 0)90/** High bits of vertical active line output */91#define CH7017_LVDS_VAL_HIGH_MASK (0x7 << 3)9293#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT 0x6194/**< Low bits of vertical active line output */9596#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT 0x6297/**< Low bits of horizontal active pixel output */9899#define CH7017_LVDS_POWER_DOWN 0x63100/** High bits of horizontal active pixel output */101#define CH7017_LVDS_HAP_HIGH_MASK (0x7 << 0)102/** Enables the LVDS power down state transition */103#define CH7017_LVDS_POWER_DOWN_EN (1 << 6)104/** Enables the LVDS upscaler */105#define CH7017_LVDS_UPSCALER_EN (1 << 7)106#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08107108#define CH7017_LVDS_ENCODING 0x64109#define CH7017_LVDS_DITHER_2D (1 << 2)110#define CH7017_LVDS_DITHER_DIS (1 << 3)111#define CH7017_LVDS_DUAL_CHANNEL_EN (1 << 4)112#define CH7017_LVDS_24_BIT (1 << 5)113114#define CH7017_LVDS_ENCODING_2 0x65115116#define CH7017_LVDS_PLL_CONTROL 0x66117/** Enables the LVDS panel output path */118#define CH7017_LVDS_PANEN (1 << 0)119/** Enables the LVDS panel backlight */120#define CH7017_LVDS_BKLEN (1 << 3)121122#define CH7017_POWER_SEQUENCING_T1 0x67123#define CH7017_POWER_SEQUENCING_T2 0x68124#define CH7017_POWER_SEQUENCING_T3 0x69125#define CH7017_POWER_SEQUENCING_T4 0x6a126#define CH7017_POWER_SEQUENCING_T5 0x6b127#define CH7017_GPIO_DRIVER_TYPE 0x6c128#define CH7017_GPIO_DATA 0x6d129#define CH7017_GPIO_DIRECTION_CONTROL 0x6e130131#define CH7017_LVDS_PLL_FEEDBACK_DIV 0x71132# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4133# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0134# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80135136#define CH7017_LVDS_PLL_VCO_CONTROL 0x72137# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80138# define CH7017_LVDS_PLL_VCO_SHIFT 4139# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0140141#define CH7017_OUTPUTS_ENABLE 0x73142# define CH7017_CHARGE_PUMP_LOW 0x0143# define CH7017_CHARGE_PUMP_HIGH 0x3144# define CH7017_LVDS_CHANNEL_A (1 << 3)145# define CH7017_LVDS_CHANNEL_B (1 << 4)146# define CH7017_TV_DAC_A (1 << 5)147# define CH7017_TV_DAC_B (1 << 6)148# define CH7017_DDC_SELECT_DC2 (1 << 7)149150#define CH7017_LVDS_OUTPUT_AMPLITUDE 0x74151#define CH7017_LVDS_PLL_EMI_REDUCTION 0x75152#define CH7017_LVDS_POWER_DOWN_FLICKER 0x76153154#define CH7017_LVDS_CONTROL_2 0x78155# define CH7017_LOOP_FILTER_SHIFT 5156# define CH7017_PHASE_DETECTOR_SHIFT 0157158#define CH7017_BANG_LIMIT_CONTROL 0x7f159160struct ch7017_priv {161uint8_t dummy;162};163164static void ch7017_dump_regs(struct intel_dvo_device *dvo);165static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);166167static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val)168{169struct i2c_msg msgs[] = {170{171.addr = dvo->slave_addr,172.flags = 0,173.len = 1,174.buf = &addr,175},176{177.addr = dvo->slave_addr,178.flags = I2C_M_RD,179.len = 1,180.buf = val,181}182};183return i2c_transfer(dvo->i2c_bus, msgs, 2) == 2;184}185186static bool ch7017_write(struct intel_dvo_device *dvo, u8 addr, u8 val)187{188uint8_t buf[2] = { addr, val };189struct i2c_msg msg = {190.addr = dvo->slave_addr,191.flags = 0,192.len = 2,193.buf = buf,194};195return i2c_transfer(dvo->i2c_bus, &msg, 1) == 1;196}197198/** Probes for a CH7017 on the given bus and slave address. */199static bool ch7017_init(struct intel_dvo_device *dvo,200struct i2c_adapter *adapter)201{202struct ch7017_priv *priv;203const char *str;204u8 val;205206priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL);207if (priv == NULL)208return false;209210dvo->i2c_bus = adapter;211dvo->dev_priv = priv;212213if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))214goto fail;215216switch (val) {217case CH7017_DEVICE_ID_VALUE:218str = "ch7017";219break;220case CH7018_DEVICE_ID_VALUE:221str = "ch7018";222break;223case CH7019_DEVICE_ID_VALUE:224str = "ch7019";225break;226default:227DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "228"slave %d.\n",229val, adapter->name,dvo->slave_addr);230goto fail;231}232233DRM_DEBUG_KMS("%s detected on %s, addr %d\n",234str, adapter->name, dvo->slave_addr);235return true;236237fail:238kfree(priv);239return false;240}241242static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo)243{244return connector_status_connected;245}246247static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo,248struct drm_display_mode *mode)249{250if (mode->clock > 160000)251return MODE_CLOCK_HIGH;252253return MODE_OK;254}255256static void ch7017_mode_set(struct intel_dvo_device *dvo,257struct drm_display_mode *mode,258struct drm_display_mode *adjusted_mode)259{260uint8_t lvds_pll_feedback_div, lvds_pll_vco_control;261uint8_t outputs_enable, lvds_control_2, lvds_power_down;262uint8_t horizontal_active_pixel_input;263uint8_t horizontal_active_pixel_output, vertical_active_line_output;264uint8_t active_input_line_output;265266DRM_DEBUG_KMS("Registers before mode setting\n");267ch7017_dump_regs(dvo);268269/* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/270if (mode->clock < 100000) {271outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW;272lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |273(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |274(13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);275lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |276(2 << CH7017_LVDS_PLL_VCO_SHIFT) |277(3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);278lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) |279(0 << CH7017_PHASE_DETECTOR_SHIFT);280} else {281outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH;282lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |283(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |284(3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);285lvds_pll_feedback_div = 35;286lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) |287(0 << CH7017_PHASE_DETECTOR_SHIFT);288if (1) { /* XXX: dual channel panel detection. Assume yes for now. */289outputs_enable |= CH7017_LVDS_CHANNEL_B;290lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |291(2 << CH7017_LVDS_PLL_VCO_SHIFT) |292(13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);293} else {294lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |295(1 << CH7017_LVDS_PLL_VCO_SHIFT) |296(13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);297}298}299300horizontal_active_pixel_input = mode->hdisplay & 0x00ff;301302vertical_active_line_output = mode->vdisplay & 0x00ff;303horizontal_active_pixel_output = mode->hdisplay & 0x00ff;304305active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) |306(((mode->vdisplay & 0x0700) >> 8) << 3);307308lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |309(mode->hdisplay & 0x0700) >> 8;310311ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);312ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,313horizontal_active_pixel_input);314ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,315horizontal_active_pixel_output);316ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT,317vertical_active_line_output);318ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT,319active_input_line_output);320ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control);321ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div);322ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2);323ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable);324325/* Turn the LVDS back on with new settings. */326ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);327328DRM_DEBUG_KMS("Registers after mode setting\n");329ch7017_dump_regs(dvo);330}331332/* set the CH7017 power state */333static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)334{335uint8_t val;336337ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);338339/* Turn off TV/VGA, and never turn it on since we don't support it. */340ch7017_write(dvo, CH7017_POWER_MANAGEMENT,341CH7017_DAC0_POWER_DOWN |342CH7017_DAC1_POWER_DOWN |343CH7017_DAC2_POWER_DOWN |344CH7017_DAC3_POWER_DOWN |345CH7017_TV_POWER_DOWN_EN);346347if (mode == DRM_MODE_DPMS_ON) {348/* Turn on the LVDS */349ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,350val & ~CH7017_LVDS_POWER_DOWN_EN);351} else {352/* Turn off the LVDS */353ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,354val | CH7017_LVDS_POWER_DOWN_EN);355}356357/* XXX: Should actually wait for update power status somehow */358msleep(20);359}360361static void ch7017_dump_regs(struct intel_dvo_device *dvo)362{363uint8_t val;364365#define DUMP(reg) \366do { \367ch7017_read(dvo, reg, &val); \368DRM_DEBUG_KMS(#reg ": %02x\n", val); \369} while (0)370371DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);372DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT);373DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT);374DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT);375DUMP(CH7017_LVDS_PLL_VCO_CONTROL);376DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV);377DUMP(CH7017_LVDS_CONTROL_2);378DUMP(CH7017_OUTPUTS_ENABLE);379DUMP(CH7017_LVDS_POWER_DOWN);380}381382static void ch7017_destroy(struct intel_dvo_device *dvo)383{384struct ch7017_priv *priv = dvo->dev_priv;385386if (priv) {387kfree(priv);388dvo->dev_priv = NULL;389}390}391392struct intel_dvo_dev_ops ch7017_ops = {393.init = ch7017_init,394.detect = ch7017_detect,395.mode_valid = ch7017_mode_valid,396.mode_set = ch7017_mode_set,397.dpms = ch7017_dpms,398.dump_regs = ch7017_dump_regs,399.destroy = ch7017_destroy,400};401402403