diff options
author | Patrik Jakobsson <patrik.r.jakobsson@gmail.com> | 2011-12-19 21:41:10 +0000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-12-20 10:23:14 +0000 |
commit | 5736995b473b8853d5ee048c7dfb9c1d20ebf0ea (patch) | |
tree | 95b82546611ca39c8ae383201e9154175ccaf4df /drivers/gpu/drm/gma500/psb_intel_sdvo.c | |
parent | 5c0c1d50d7ba7a678b7d6e2c4f2ff31edafb1067 (diff) |
gma500: Replace SDVO code with slightly modified version from i915
Our current SDVO implementation is not working properly, so replace it with
a modified version of the i915. Further testing and debugging is needed to make
sure we can handle the different SDVO setups and wiring.
Signed-off-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/gma500/psb_intel_sdvo.c')
-rw-r--r-- | drivers/gpu/drm/gma500/psb_intel_sdvo.c | 3054 |
1 files changed, 2185 insertions, 869 deletions
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index a4bad1af4b7c..20d536618701 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1,76 +1,245 @@ /* - * Copyright (c) 2006-2007 Intel Corporation + * Copyright 2006 Dave Airlie <airlied@linux.ie> + * Copyright © 2006-2007 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. * * Authors: * Eric Anholt <eric@anholt.net> */ - +#include <linux/module.h> #include <linux/i2c.h> +#include <linux/slab.h> #include <linux/delay.h> -/* #include <drm/drm_crtc.h> */ -#include <drm/drmP.h> -#include "psb_drv.h" +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_edid.h" #include "psb_intel_drv.h" -#include "psb_intel_reg.h" +#include "gma_drm.h" +#include "psb_drv.h" #include "psb_intel_sdvo_regs.h" +#include "psb_intel_reg.h" + +#define SDVO_TMDS_MASK (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1) +#define SDVO_RGB_MASK (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1) +#define SDVO_LVDS_MASK (SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1) +#define SDVO_TV_MASK (SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_SVID0) + +#define SDVO_OUTPUT_MASK (SDVO_TMDS_MASK | SDVO_RGB_MASK | SDVO_LVDS_MASK |\ + SDVO_TV_MASK) + +#define IS_TV(c) (c->output_flag & SDVO_TV_MASK) +#define IS_TMDS(c) (c->output_flag & SDVO_TMDS_MASK) +#define IS_LVDS(c) (c->output_flag & SDVO_LVDS_MASK) +#define IS_TV_OR_LVDS(c) (c->output_flag & (SDVO_TV_MASK | SDVO_LVDS_MASK)) + + +static const char *tv_format_names[] = { + "NTSC_M" , "NTSC_J" , "NTSC_443", + "PAL_B" , "PAL_D" , "PAL_G" , + "PAL_H" , "PAL_I" , "PAL_M" , + "PAL_N" , "PAL_NC" , "PAL_60" , + "SECAM_B" , "SECAM_D" , "SECAM_G" , + "SECAM_K" , "SECAM_K1", "SECAM_L" , + "SECAM_60" +}; + +#define TV_FORMAT_NUM (sizeof(tv_format_names) / sizeof(*tv_format_names)) + +struct psb_intel_sdvo { + struct psb_intel_encoder base; + + struct i2c_adapter *i2c; + u8 slave_addr; + + struct i2c_adapter ddc; -struct psb_intel_sdvo_priv { - struct psb_intel_i2c_chan *i2c_bus; - int slaveaddr; - int output_device; + /* Register for the SDVO device: SDVOB or SDVOC */ + int sdvo_reg; - u16 active_outputs; + /* Active outputs controlled by this SDVO output */ + uint16_t controlled_output; + /* + * Capabilities of the SDVO device returned by + * i830_sdvo_get_capabilities() + */ struct psb_intel_sdvo_caps caps; + + /* Pixel clock limitations reported by the SDVO device, in kHz */ int pixel_clock_min, pixel_clock_max; - int save_sdvo_mult; - u16 save_active_outputs; - struct psb_intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; - struct psb_intel_sdvo_dtd save_output_dtd[16]; - u32 save_SDVOX; - u8 in_out_map[4]; + /* + * For multiple function SDVO device, + * this is for current attached outputs. + */ + uint16_t attached_output; + + /** + * This is used to select the color range of RBG outputs in HDMI mode. + * It is only valid when using TMDS encoding and 8 bit per color mode. + */ + uint32_t color_range; + + /** + * This is set if we're going to treat the device as TV-out. + * + * While we have these nice friendly flags for output types that ought + * to decide this for us, the S-Video output on our HDMI+S-Video card + * shows up as RGB1 (VGA). + */ + bool is_tv; + + /* This is for current tv format name */ + int tv_format_index; + + /** + * This is set if we treat the device as HDMI, instead of DVI. + */ + bool is_hdmi; + bool has_hdmi_monitor; + bool has_hdmi_audio; + + /** + * This is set if we detect output of sdvo device as LVDS and + * have a valid fixed mode to use with the panel. + */ + bool is_lvds; + + /** + * This is sdvo fixed pannel mode pointer + */ + struct drm_display_mode *sdvo_lvds_fixed_mode; - u8 by_input_wiring; - u32 active_device; + /* DDC bus used by this SDVO encoder */ + uint8_t ddc_bus; + + /* Input timings for adjusted_mode */ + struct psb_intel_sdvo_dtd input_dtd; }; +struct psb_intel_sdvo_connector { + struct psb_intel_connector base; + + /* Mark the type of connector */ + uint16_t output_flag; + + int force_audio; + + /* This contains all current supported TV format */ + u8 tv_format_supported[TV_FORMAT_NUM]; + int format_supported_num; + struct drm_property *tv_format; + + /* add the property for the SDVO-TV */ + struct drm_property *left; + struct drm_property *right; + struct drm_property *top; + struct drm_property *bottom; + struct drm_property *hpos; + struct drm_property *vpos; + struct drm_property *contrast; + struct drm_property *saturation; + struct drm_property *hue; + struct drm_property *sharpness; + struct drm_property *flicker_filter; + struct drm_property *flicker_filter_adaptive; + struct drm_property *flicker_filter_2d; + struct drm_property *tv_chroma_filter; + struct drm_property *tv_luma_filter; + struct drm_property *dot_crawl; + + /* add the property for the SDVO-TV/LVDS */ + struct drm_property *brightness; + + /* Add variable to record current setting for the above property */ + u32 left_margin, right_margin, top_margin, bottom_margin; + + /* this is to get the range of margin.*/ + u32 max_hscan, max_vscan; + u32 max_hpos, cur_hpos; + u32 max_vpos, cur_vpos; + u32 cur_brightness, max_brightness; + u32 cur_contrast, max_contrast; + u32 cur_saturation, max_saturation; + u32 cur_hue, max_hue; + u32 cur_sharpness, max_sharpness; + u32 cur_flicker_filter, max_flicker_filter; + u32 cur_flicker_filter_adaptive, max_flicker_filter_adaptive; + u32 cur_flicker_filter_2d, max_flicker_filter_2d; + u32 cur_tv_chroma_filter, max_tv_chroma_filter; + u32 cur_tv_luma_filter, max_tv_luma_filter; + u32 cur_dot_crawl, max_dot_crawl; +}; + +static struct psb_intel_sdvo *to_psb_intel_sdvo(struct drm_encoder *encoder) +{ + return container_of(encoder, struct psb_intel_sdvo, base.base); +} + +static struct psb_intel_sdvo *intel_attached_sdvo(struct drm_connector *connector) +{ + return container_of(psb_intel_attached_encoder(connector), + struct psb_intel_sdvo, base); +} + +static struct psb_intel_sdvo_connector *to_psb_intel_sdvo_connector(struct drm_connector *connector) +{ + return container_of(to_psb_intel_connector(connector), struct psb_intel_sdvo_connector, base); +} + +static bool +psb_intel_sdvo_output_setup(struct psb_intel_sdvo *psb_intel_sdvo, uint16_t flags); +static bool +psb_intel_sdvo_tv_create_property(struct psb_intel_sdvo *psb_intel_sdvo, + struct psb_intel_sdvo_connector *psb_intel_sdvo_connector, + int type); +static bool +psb_intel_sdvo_create_enhance_property(struct psb_intel_sdvo *psb_intel_sdvo, + struct psb_intel_sdvo_connector *psb_intel_sdvo_connector); + /** * Writes the SDVOB or SDVOC with the given value, but always writes both * SDVOB and SDVOC to work around apparent hardware issues (according to * comments in the BIOS). */ -void psb_intel_sdvo_write_sdvox(struct psb_intel_output *psb_intel_output, - u32 val) +static void psb_intel_sdvo_write_sdvox(struct psb_intel_sdvo *psb_intel_sdvo, u32 val) { - struct drm_device *dev = psb_intel_output->base.dev; - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + struct drm_device *dev = psb_intel_sdvo->base.base.dev; u32 bval = val, cval = val; int i; - if (sdvo_priv->output_device == SDVOB) + if (psb_intel_sdvo->sdvo_reg == SDVOB) { cval = REG_READ(SDVOC); - else + } else { bval = REG_READ(SDVOB); + } /* * Write the registers twice for luck. Sometimes, * writing them only once doesn't appear to 'stick'. * The BIOS does this too. Yay, magic */ - for (i = 0; i < 2; i++) { + for (i = 0; i < 2; i++) + { REG_WRITE(SDVOB, bval); REG_READ(SDVOB); REG_WRITE(SDVOC, cval); @@ -78,61 +247,28 @@ void psb_intel_sdvo_write_sdvox(struct psb_intel_output *psb_intel_output, } } -static bool psb_intel_sdvo_read_byte( - struct psb_intel_output *psb_intel_output, - u8 addr, u8 *ch) +static bool psb_intel_sdvo_read_byte(struct psb_intel_sdvo *psb_intel_sdvo, u8 addr, u8 *ch) { - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - u8 out_buf[2]; - u8 buf[2]; - int ret; - struct i2c_msg msgs[] = { { - .addr = sdvo_priv->i2c_bus->slave_addr, - .flags = 0, - .len = 1, - .buf = out_buf, - }, + .addr = psb_intel_sdvo->slave_addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, { - .addr = sdvo_priv->i2c_bus->slave_addr, - .flags = I2C_M_RD, - .len = 1, - .buf = buf, - } + .addr = psb_intel_sdvo->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = ch, + } }; + int ret; - out_buf[0] = addr; - out_buf[1] = 0; - - ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2); - if (ret == 2) { - *ch = buf[0]; + if ((ret = i2c_transfer(psb_intel_sdvo->i2c, msgs, 2)) == 2) return true; - } - return false; -} - -static bool psb_intel_sdvo_write_byte( - struct psb_intel_output *psb_intel_output, - int addr, u8 ch) -{ - u8 out_buf[2]; - struct i2c_msg msgs[] = { - { - .addr = psb_intel_output->i2c_bus->slave_addr, - .flags = 0, - .len = 2, - .buf = out_buf, - } - }; - - out_buf[0] = addr; - out_buf[1] = ch; - - if (i2c_transfer(&psb_intel_output->i2c_bus->adapter, msgs, 1) == 1) - return true; + DRM_DEBUG_KMS("i2c transfer returned %d\n", ret); return false; } @@ -140,98 +276,147 @@ static bool psb_intel_sdvo_write_byte( /** Mapping of command numbers to names, for debug output */ static const struct _sdvo_cmd_name { u8 cmd; - char *name; + const char *name; } sdvo_cmd_names[] = { -SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_SET_TV_RESOLUTION_SUPPORT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),}; - -#define SDVO_NAME(dev_priv) \ - ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC") -#define SDVO_PRIV(output) ((struct psb_intel_sdvo_priv *) (output)->dev_priv) - -static void psb_intel_sdvo_write_cmd(struct psb_intel_output *psb_intel_output, - u8 cmd, - void *args, - int args_len) -{ - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS), + + /* Add the op code for SDVO enhancements */ + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_VPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_VPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_VPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_2D), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_2D), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_2D), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SHARPNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SHARPNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SHARPNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DOT_CRAWL), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DOT_CRAWL), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_CHROMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_CHROMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_CHROMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_LUMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_LUMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_LUMA_FILTER), + + /* HDMI op code */ + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA), +}; + +#define IS_SDVOB(reg) (reg == SDVOB) +#define SDVO_NAME(svdo) (IS_SDVOB((svdo)->sdvo_reg) ? "SDVOB" : "SDVOC") + +static void psb_intel_sdvo_debug_write(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, + const void *args, int args_len) +{ int i; - if (0) { - printk(KERN_DEBUG "%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd); - for (i = 0; i < args_len; i++) - printk(KERN_CONT "%02X ", ((u8 *) args)[i]); - for (; i < 8; i++) - printk(KERN_CONT " "); - for (i = 0; - i < - sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); - i++) { - if (cmd == sdvo_cmd_names[i].cmd) { - printk(KERN_CONT - "(%s)", sdvo_cmd_names[i].name); - break; - } + DRM_DEBUG_KMS("%s: W: %02X ", + SDVO_NAME(psb_intel_sdvo), cmd); + for (i = 0; i < args_len; i++) + DRM_LOG_KMS("%02X ", ((u8 *)args)[i]); + for (; i < 8; i++) + DRM_LOG_KMS(" "); + for (i = 0; i < ARRAY_SIZE(sdvo_cmd_names); i++) { + if (cmd == sdvo_cmd_names[i].cmd) { + DRM_LOG_KMS("(%s)", sdvo_cmd_names[i].name); + break; } - if (i == - sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0])) - printk(KERN_CONT "(%02X)", cmd); - printk(KERN_CONT "\n"); } - - for (i = 0; i < args_len; i++) { - psb_intel_sdvo_write_byte(psb_intel_output, - SDVO_I2C_ARG_0 - i, - ((u8 *) args)[i]); - } - - psb_intel_sdvo_write_byte(psb_intel_output, SDVO_I2C_OPCODE, cmd); + if (i == ARRAY_SIZE(sdvo_cmd_names)) + DRM_LOG_KMS("(%02X)", cmd); + DRM_LOG_KMS("\n"); } -static const char *const cmd_status_names[] = { +static const char *cmd_status_names[] = { "Power on", "Success", "Not supported", @@ -241,52 +426,111 @@ static const char *const cmd_status_names[] = { "Scaling not supported" }; -static u8 psb_intel_sdvo_read_response( - struct psb_intel_output *psb_intel_output, - void *response, int response_len) +static bool psb_intel_sdvo_write_cmd(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, + const void *args, int args_len) { - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - int i; + u8 buf[args_len*2 + 2], status; + struct i2c_msg msgs[args_len + 3]; + int i, ret; + + psb_intel_sdvo_debug_write(psb_intel_sdvo, cmd, args, args_len); + + for (i = 0; i < args_len; i++) { + msgs[i].addr = psb_intel_sdvo->slave_addr; + msgs[i].flags = 0; + msgs[i].len = 2; + msgs[i].buf = buf + 2 *i; + buf[2*i + 0] = SDVO_I2C_ARG_0 - i; + buf[2*i + 1] = ((u8*)args)[i]; + } + msgs[i].addr = psb_intel_sdvo->slave_addr; + msgs[i].flags = 0; + msgs[i].len = 2; + msgs[i].buf = buf + 2*i; + buf[2*i + 0] = SDVO_I2C_OPCODE; + buf[2*i + 1] = cmd; + + /* the following two are to read the response */ + status = SDVO_I2C_CMD_STATUS; + msgs[i+1].addr = psb_intel_sdvo->slave_addr; + msgs[i+1].flags = 0; + msgs[i+1].len = 1; + msgs[i+1].buf = &status; + + msgs[i+2].addr = psb_intel_sdvo->slave_addr; + msgs[i+2].flags = I2C_M_RD; + msgs[i+2].len = 1; + msgs[i+2].buf = &status; + + ret = i2c_transfer(psb_intel_sdvo->i2c, msgs, i+3); + if (ret < 0) { + DRM_DEBUG_KMS("I2c transfer returned %d\n", ret); + return false; + } + if (ret != i+3) { + /* failure in I2C transfer */ + DRM_DEBUG_KMS("I2c transfer returned %d/%d\n", ret, i+3); + return false; + } + + return true; +} + +static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo, + void *response, int response_len) +{ + u8 retry = 5; u8 status; - u8 retry = 50; - - while (retry--) { - /* Read the command response */ - for (i = 0; i < response_len; i++) { - psb_intel_sdvo_read_byte(psb_intel_output, - SDVO_I2C_RETURN_0 + i, - &((u8 *) response)[i]); - } + int i; - /* read the return status */ - psb_intel_sdvo_read_byte(psb_intel_output, - SDVO_I2C_CMD_STATUS, - &status); - - if (0) { - pr_debug("%s: R: ", SDVO_NAME(sdvo_priv)); - for (i = 0; i < response_len; i++) - printk(KERN_CONT "%02X ", ((u8 *) response)[i]); - for (; i < 8; i++) - printk(" "); - if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) - printk(KERN_CONT "(%s)", - cmd_status_names[status]); - else - printk(KERN_CONT "(??? %d)", status); - printk(KERN_CONT "\n"); - } + DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(psb_intel_sdvo)); + + /* + * The documentation states that all commands will be + * processed within 15µs, and that we need only poll + * the status byte a maximum of 3 times in order for the + * command to be complete. + * + * Check 5 times in case the hardware failed to read the docs. + */ + if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, + SDVO_I2C_CMD_STATUS, + &status)) + goto log_fail; + + while (status == SDVO_CMD_STATUS_PENDING && retry--) { + udelay(15); + if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, + SDVO_I2C_CMD_STATUS, + &status)) + goto log_fail; + } - if (status != SDVO_CMD_STATUS_PENDING) - return status; + if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) + DRM_LOG_KMS("(%s)", cmd_status_names[status]); + else + DRM_LOG_KMS("(??? %d)", status); - mdelay(50); + if (status != SDVO_CMD_STATUS_SUCCESS) + goto log_fail; + + /* Read the command response */ + for (i = 0; i < response_len; i++) { + if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, + SDVO_I2C_RETURN_0 + i, + &((u8 *)response)[i])) + goto log_fail; + DRM_LOG_KMS(" %02X", ((u8 *)response)[i]); } + DRM_LOG_KMS("\n"); + return true; - return status; +log_fail: + DRM_LOG_KMS("... failed\n"); + return false; } -int psb_intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) +static int psb_intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) { if (mode->clock >= 100000) return 1; @@ -296,40 +540,38 @@ int psb_intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) return 4; } -/** - * Don't check status code from this as it switches the bus back to the - * SDVO chips which defeats the purpose of doing a bus switch in the first - * place. - */ -void psb_intel_sdvo_set_control_bus_switch( - struct psb_intel_output *psb_intel_output, - u8 target) +static bool psb_intel_sdvo_set_control_bus_switch(struct psb_intel_sdvo *psb_intel_sdvo, + u8 ddc_bus) { - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_SET_CONTROL_BUS_SWITCH, - &target, - 1); + /* This must be the immediately preceding write before the i2c xfer */ + return psb_intel_sdvo_write_cmd(psb_intel_sdvo, + SDVO_CMD_SET_CONTROL_BUS_SWITCH, + &ddc_bus, 1); } -static bool psb_intel_sdvo_set_target_input( - struct psb_intel_output *psb_intel_output, - bool target_0, bool target_1) +static bool psb_intel_sdvo_set_value(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, const void *data, int len) { - struct psb_intel_sdvo_set_target_input_args targets = { 0 }; - u8 status; - - if (target_0 && target_1) - return SDVO_CMD_STATUS_NOTSUPP; + if (!psb_intel_sdvo_write_cmd(psb_intel_sdvo, cmd, data, len)) + return false; - if (target_1) - targets.target_1 = 1; + return psb_intel_sdvo_read_response(psb_intel_sdvo, NULL, 0); +} - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_TARGET_INPUT, - &targets, sizeof(targets)); +static bool +psb_intel_sdvo_get_value(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, void *value, int len) +{ + if (!psb_intel_sdvo_write_cmd(psb_intel_sdvo, cmd, NULL, 0)) + return false; - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); + return psb_intel_sdvo_read_response(psb_intel_sdvo, value, len); +} - return status == SDVO_CMD_STATUS_SUCCESS; +static bool psb_intel_sdvo_set_target_input(struct psb_intel_sdvo *psb_intel_sdvo) +{ + struct psb_intel_sdvo_set_target_input_args targets = {0}; + return psb_intel_sdvo_set_value(psb_intel_sdvo, + SDVO_CMD_SET_TARGET_INPUT, + &targets, sizeof(targets)); } /** @@ -338,19 +580,13 @@ static bool psb_intel_sdvo_set_target_input( * This function is making an assumption about the layout of the response, * which should be checked against the docs. */ -static bool psb_intel_sdvo_get_trained_inputs(struct psb_intel_output - *psb_intel_output, bool *input_1, - bool *input_2) +static bool psb_intel_sdvo_get_trained_inputs(struct psb_intel_sdvo *psb_intel_sdvo, bool *input_1, bool *input_2) { struct psb_intel_sdvo_get_trained_inputs_response response; - u8 status; - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_GET_TRAINED_INPUTS, - NULL, 0); - status = - psb_intel_sdvo_read_response(psb_intel_output, &response, - sizeof(response)); - if (status != SDVO_CMD_STATUS_SUCCESS) + BUILD_BUG_ON(sizeof(response) != 1); + if (!psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_TRAINED_INPUTS, + &response, sizeof(response))) return false; *input_1 = response.input0_trained; @@ -358,35 +594,18 @@ static bool psb_intel_sdvo_get_trained_inputs(struct psb_intel_output return true; } -static bool psb_intel_sdvo_get_active_outputs(struct psb_intel_output - *psb_intel_output, u16 *outputs) +static bool psb_intel_sdvo_set_active_outputs(struct psb_intel_sdvo *psb_intel_sdvo, + u16 outputs) { - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS, - NULL, 0); - status = - psb_intel_sdvo_read_response(psb_intel_output, outputs, - sizeof(*outputs)); - - return status == SDVO_CMD_STATUS_SUCCESS; + return psb_intel_sdvo_set_value(psb_intel_sdvo, + SDVO_CMD_SET_ACTIVE_OUTPUTS, + &outputs, sizeof(outputs)); } -static bool psb_intel_sdvo_set_active_outputs(struct psb_intel_output - *psb_intel_output, u16 outputs) +static bool psb_intel_sdvo_set_encoder_power_state(struct psb_intel_sdvo *psb_intel_sdvo, + int mode) { - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS, - &outputs, sizeof(outputs)); - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - return status == SDVO_CMD_STATUS_SUCCESS; -} - -static bool psb_intel_sdvo_set_encoder_power_state(struct psb_intel_output - *psb_intel_output, int mode) -{ - u8 status, state = SDVO_ENCODER_STATE_ON; + u8 state = SDVO_ENCODER_STATE_ON; switch (mode) { case DRM_MODE_DPMS_ON: @@ -403,260 +622,360 @@ static bool psb_intel_sdvo_set_encoder_power_state(struct psb_intel_output break; } - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_SET_ENCODER_POWER_STATE, &state, - sizeof(state)); - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - - return status == SDVO_CMD_STATUS_SUCCESS; + return psb_intel_sdvo_set_value(psb_intel_sdvo, + SDVO_CMD_SET_ENCODER_POWER_STATE, &state, sizeof(state)); } -static bool psb_intel_sdvo_get_input_pixel_clock_range(struct psb_intel_output - *psb_intel_output, +static bool psb_intel_sdvo_get_input_pixel_clock_range(struct psb_intel_sdvo *psb_intel_sdvo, int *clock_min, int *clock_max) { struct psb_intel_sdvo_pixel_clock_range clocks; - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, NULL, - 0); - - status = - psb_intel_sdvo_read_response(psb_intel_output, &clocks, - sizeof(clocks)); - if (status != SDVO_CMD_STATUS_SUCCESS) + BUILD_BUG_ON(sizeof(clocks) != 4); + if (!psb_intel_sdvo_get_value(psb_intel_sdvo, + SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, + &clocks, sizeof(clocks))) return false; /* Convert the values from units of 10 kHz to kHz. */ *clock_min = clocks.min * 10; *clock_max = clocks.max * 10; - return true; } -static bool psb_intel_sdvo_set_target_output( - struct psb_intel_output *psb_intel_output, - u16 outputs) +static bool psb_intel_sdvo_set_target_output(struct psb_intel_sdvo *psb_intel_sdvo, + u16 outputs) { - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_TARGET_OUTPUT, - &outputs, sizeof(outputs)); + return psb_intel_sdvo_set_value(psb_intel_sdvo, + SDVO_CMD_SET_TARGET_OUTPUT, + &outputs, sizeof(outputs)); +} - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - return status == SDVO_CMD_STATUS_SUCCESS; +static bool psb_intel_sdvo_set_timing(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, + struct psb_intel_sdvo_dtd *dtd) +{ + return psb_intel_sdvo_set_value(psb_intel_sdvo, cmd, &dtd->part1, sizeof(dtd->part1)) && + psb_intel_sdvo_set_value(psb_intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2)); } -static bool psb_intel_sdvo_get_timing(struct psb_intel_output *psb_intel_output, - u8 cmd, struct psb_intel_sdvo_dtd *dtd) +static bool psb_intel_sdvo_set_input_timing(struct psb_intel_sdvo *psb_intel_sdvo, + struct psb_intel_sdvo_dtd *dtd) { - u8 status; + return psb_intel_sdvo_set_timing(psb_intel_sdvo, + SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd); +} - psb_intel_sdvo_write_cmd(psb_intel_output, cmd, NULL, 0); - status = psb_intel_sdvo_read_response(psb_intel_output, &dtd->part1, - sizeof(dtd->part1)); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; +static bool psb_intel_sdvo_set_output_timing(struct psb_intel_sdvo *psb_intel_sdvo, + struct psb_intel_sdvo_dtd *dtd) +{ + return psb_intel_sdvo_set_timing(psb_intel_sdvo, + SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd); +} - psb_intel_sdvo_write_cmd(psb_intel_output, cmd + 1, NULL, 0); - status = psb_intel_sdvo_read_response(psb_intel_output, &dtd->part2, - sizeof(dtd->part2)); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; +static bool +psb_intel_sdvo_create_preferred_input_timing(struct psb_intel_sdvo *psb_intel_sdvo, + uint16_t clock, + uint16_t width, + uint16_t height) +{ + struct psb_intel_sdvo_preferred_input_timing_args args; + + memset(&args, 0, sizeof(args)); + args.clock = clock; + args.width = width; + args.height = height; + args.interlace = 0; + + if (psb_intel_sdvo->is_lvds && + (psb_intel_sdvo->sdvo_lvds_fixed_mode->hdisplay != width || + psb_intel_sdvo->sdvo_lvds_fixed_mode->vdisplay != height)) + args.scaled = 1; + + return psb_intel_sdvo_set_value(psb_intel_sdvo, + SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING, + &args, sizeof(args)); +} - return true; +static bool psb_intel_sdvo_get_preferred_input_timing(struct psb_intel_sdvo *psb_intel_sdvo, + struct psb_intel_sdvo_dtd *dtd) +{ + BUILD_BUG_ON(sizeof(dtd->part1) != 8); + BUILD_BUG_ON(sizeof(dtd->part2) != 8); + return psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1, + &dtd->part1, sizeof(dtd->part1)) && + psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2, + &dtd->part2, sizeof(dtd->part2)); } -static bool psb_intel_sdvo_get_input_timing( - struct psb_intel_output *psb_intel_output, - struct psb_intel_sdvo_dtd *dtd) +static bool psb_intel_sdvo_set_clock_rate_mult(struct psb_intel_sdvo *psb_intel_sdvo, u8 val) { - return psb_intel_sdvo_get_timing(psb_intel_output, - SDVO_CMD_GET_INPUT_TIMINGS_PART1, - dtd); + ret |