18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2006 Dave Airlie <airlied@linux.ie> 38c2ecf20Sopenharmony_ci * Copyright © 2006-2007 Intel Corporation 48c2ecf20Sopenharmony_ci * Jesse Barnes <jesse.barnes@intel.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 88c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 98c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 108c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 118c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 148c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 158c2ecf20Sopenharmony_ci * Software. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 188c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 198c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 208c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 218c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 228c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 238c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Authors: 268c2ecf20Sopenharmony_ci * Eric Anholt <eric@anholt.net> 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/delay.h> 308c2ecf20Sopenharmony_ci#include <linux/i2c.h> 318c2ecf20Sopenharmony_ci#include <linux/kernel.h> 328c2ecf20Sopenharmony_ci#include <linux/module.h> 338c2ecf20Sopenharmony_ci#include <linux/slab.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "psb_drv.h" 398c2ecf20Sopenharmony_ci#include "psb_intel_drv.h" 408c2ecf20Sopenharmony_ci#include "psb_intel_reg.h" 418c2ecf20Sopenharmony_ci#include "psb_intel_sdvo_regs.h" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define SDVO_TMDS_MASK (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1) 448c2ecf20Sopenharmony_ci#define SDVO_RGB_MASK (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1) 458c2ecf20Sopenharmony_ci#define SDVO_LVDS_MASK (SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1) 468c2ecf20Sopenharmony_ci#define SDVO_TV_MASK (SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_SVID0) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define SDVO_OUTPUT_MASK (SDVO_TMDS_MASK | SDVO_RGB_MASK | SDVO_LVDS_MASK |\ 498c2ecf20Sopenharmony_ci SDVO_TV_MASK) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define IS_TV(c) (c->output_flag & SDVO_TV_MASK) 528c2ecf20Sopenharmony_ci#define IS_TMDS(c) (c->output_flag & SDVO_TMDS_MASK) 538c2ecf20Sopenharmony_ci#define IS_LVDS(c) (c->output_flag & SDVO_LVDS_MASK) 548c2ecf20Sopenharmony_ci#define IS_TV_OR_LVDS(c) (c->output_flag & (SDVO_TV_MASK | SDVO_LVDS_MASK)) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const char *tv_format_names[] = { 588c2ecf20Sopenharmony_ci "NTSC_M" , "NTSC_J" , "NTSC_443", 598c2ecf20Sopenharmony_ci "PAL_B" , "PAL_D" , "PAL_G" , 608c2ecf20Sopenharmony_ci "PAL_H" , "PAL_I" , "PAL_M" , 618c2ecf20Sopenharmony_ci "PAL_N" , "PAL_NC" , "PAL_60" , 628c2ecf20Sopenharmony_ci "SECAM_B" , "SECAM_D" , "SECAM_G" , 638c2ecf20Sopenharmony_ci "SECAM_K" , "SECAM_K1", "SECAM_L" , 648c2ecf20Sopenharmony_ci "SECAM_60" 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct psb_intel_sdvo { 688c2ecf20Sopenharmony_ci struct gma_encoder base; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 718c2ecf20Sopenharmony_ci u8 slave_addr; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci struct i2c_adapter ddc; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Register for the SDVO device: SDVOB or SDVOC */ 768c2ecf20Sopenharmony_ci int sdvo_reg; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* Active outputs controlled by this SDVO output */ 798c2ecf20Sopenharmony_ci uint16_t controlled_output; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* 828c2ecf20Sopenharmony_ci * Capabilities of the SDVO device returned by 838c2ecf20Sopenharmony_ci * i830_sdvo_get_capabilities() 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci struct psb_intel_sdvo_caps caps; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Pixel clock limitations reported by the SDVO device, in kHz */ 888c2ecf20Sopenharmony_ci int pixel_clock_min, pixel_clock_max; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * For multiple function SDVO device, 928c2ecf20Sopenharmony_ci * this is for current attached outputs. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci uint16_t attached_output; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /** 978c2ecf20Sopenharmony_ci * This is used to select the color range of RBG outputs in HDMI mode. 988c2ecf20Sopenharmony_ci * It is only valid when using TMDS encoding and 8 bit per color mode. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci uint32_t color_range; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /** 1038c2ecf20Sopenharmony_ci * This is set if we're going to treat the device as TV-out. 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * While we have these nice friendly flags for output types that ought 1068c2ecf20Sopenharmony_ci * to decide this for us, the S-Video output on our HDMI+S-Video card 1078c2ecf20Sopenharmony_ci * shows up as RGB1 (VGA). 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci bool is_tv; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* This is for current tv format name */ 1128c2ecf20Sopenharmony_ci int tv_format_index; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /** 1158c2ecf20Sopenharmony_ci * This is set if we treat the device as HDMI, instead of DVI. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci bool is_hdmi; 1188c2ecf20Sopenharmony_ci bool has_hdmi_monitor; 1198c2ecf20Sopenharmony_ci bool has_hdmi_audio; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /** 1228c2ecf20Sopenharmony_ci * This is set if we detect output of sdvo device as LVDS and 1238c2ecf20Sopenharmony_ci * have a valid fixed mode to use with the panel. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci bool is_lvds; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /** 1288c2ecf20Sopenharmony_ci * This is sdvo fixed panel mode pointer 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci struct drm_display_mode *sdvo_lvds_fixed_mode; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* DDC bus used by this SDVO encoder */ 1338c2ecf20Sopenharmony_ci uint8_t ddc_bus; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci u8 pixel_multiplier; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Input timings for adjusted_mode */ 1388c2ecf20Sopenharmony_ci struct psb_intel_sdvo_dtd input_dtd; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Saved SDVO output states */ 1418c2ecf20Sopenharmony_ci uint32_t saveSDVO; /* Can be SDVOB or SDVOC depending on sdvo_reg */ 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct psb_intel_sdvo_connector { 1458c2ecf20Sopenharmony_ci struct gma_connector base; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Mark the type of connector */ 1488c2ecf20Sopenharmony_ci uint16_t output_flag; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci int force_audio; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* This contains all current supported TV format */ 1538c2ecf20Sopenharmony_ci u8 tv_format_supported[ARRAY_SIZE(tv_format_names)]; 1548c2ecf20Sopenharmony_ci int format_supported_num; 1558c2ecf20Sopenharmony_ci struct drm_property *tv_format; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* add the property for the SDVO-TV */ 1588c2ecf20Sopenharmony_ci struct drm_property *left; 1598c2ecf20Sopenharmony_ci struct drm_property *right; 1608c2ecf20Sopenharmony_ci struct drm_property *top; 1618c2ecf20Sopenharmony_ci struct drm_property *bottom; 1628c2ecf20Sopenharmony_ci struct drm_property *hpos; 1638c2ecf20Sopenharmony_ci struct drm_property *vpos; 1648c2ecf20Sopenharmony_ci struct drm_property *contrast; 1658c2ecf20Sopenharmony_ci struct drm_property *saturation; 1668c2ecf20Sopenharmony_ci struct drm_property *hue; 1678c2ecf20Sopenharmony_ci struct drm_property *sharpness; 1688c2ecf20Sopenharmony_ci struct drm_property *flicker_filter; 1698c2ecf20Sopenharmony_ci struct drm_property *flicker_filter_adaptive; 1708c2ecf20Sopenharmony_ci struct drm_property *flicker_filter_2d; 1718c2ecf20Sopenharmony_ci struct drm_property *tv_chroma_filter; 1728c2ecf20Sopenharmony_ci struct drm_property *tv_luma_filter; 1738c2ecf20Sopenharmony_ci struct drm_property *dot_crawl; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* add the property for the SDVO-TV/LVDS */ 1768c2ecf20Sopenharmony_ci struct drm_property *brightness; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Add variable to record current setting for the above property */ 1798c2ecf20Sopenharmony_ci u32 left_margin, right_margin, top_margin, bottom_margin; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* this is to get the range of margin.*/ 1828c2ecf20Sopenharmony_ci u32 max_hscan, max_vscan; 1838c2ecf20Sopenharmony_ci u32 max_hpos, cur_hpos; 1848c2ecf20Sopenharmony_ci u32 max_vpos, cur_vpos; 1858c2ecf20Sopenharmony_ci u32 cur_brightness, max_brightness; 1868c2ecf20Sopenharmony_ci u32 cur_contrast, max_contrast; 1878c2ecf20Sopenharmony_ci u32 cur_saturation, max_saturation; 1888c2ecf20Sopenharmony_ci u32 cur_hue, max_hue; 1898c2ecf20Sopenharmony_ci u32 cur_sharpness, max_sharpness; 1908c2ecf20Sopenharmony_ci u32 cur_flicker_filter, max_flicker_filter; 1918c2ecf20Sopenharmony_ci u32 cur_flicker_filter_adaptive, max_flicker_filter_adaptive; 1928c2ecf20Sopenharmony_ci u32 cur_flicker_filter_2d, max_flicker_filter_2d; 1938c2ecf20Sopenharmony_ci u32 cur_tv_chroma_filter, max_tv_chroma_filter; 1948c2ecf20Sopenharmony_ci u32 cur_tv_luma_filter, max_tv_luma_filter; 1958c2ecf20Sopenharmony_ci u32 cur_dot_crawl, max_dot_crawl; 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct psb_intel_sdvo *to_psb_intel_sdvo(struct drm_encoder *encoder) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return container_of(encoder, struct psb_intel_sdvo, base.base); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic struct psb_intel_sdvo *intel_attached_sdvo(struct drm_connector *connector) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci return container_of(gma_attached_encoder(connector), 2068c2ecf20Sopenharmony_ci struct psb_intel_sdvo, base); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct psb_intel_sdvo_connector *to_psb_intel_sdvo_connector(struct drm_connector *connector) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci return container_of(to_gma_connector(connector), struct psb_intel_sdvo_connector, base); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic bool 2158c2ecf20Sopenharmony_cipsb_intel_sdvo_output_setup(struct psb_intel_sdvo *psb_intel_sdvo, uint16_t flags); 2168c2ecf20Sopenharmony_cistatic bool 2178c2ecf20Sopenharmony_cipsb_intel_sdvo_tv_create_property(struct psb_intel_sdvo *psb_intel_sdvo, 2188c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector, 2198c2ecf20Sopenharmony_ci int type); 2208c2ecf20Sopenharmony_cistatic bool 2218c2ecf20Sopenharmony_cipsb_intel_sdvo_create_enhance_property(struct psb_intel_sdvo *psb_intel_sdvo, 2228c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * Writes the SDVOB or SDVOC with the given value, but always writes both 2268c2ecf20Sopenharmony_ci * SDVOB and SDVOC to work around apparent hardware issues (according to 2278c2ecf20Sopenharmony_ci * comments in the BIOS). 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_write_sdvox(struct psb_intel_sdvo *psb_intel_sdvo, u32 val) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct drm_device *dev = psb_intel_sdvo->base.base.dev; 2328c2ecf20Sopenharmony_ci u32 bval = val, cval = val; 2338c2ecf20Sopenharmony_ci int i, j; 2348c2ecf20Sopenharmony_ci int need_aux = IS_MRST(dev) ? 1 : 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci for (j = 0; j <= need_aux; j++) { 2378c2ecf20Sopenharmony_ci if (psb_intel_sdvo->sdvo_reg == SDVOB) 2388c2ecf20Sopenharmony_ci cval = REG_READ_WITH_AUX(SDVOC, j); 2398c2ecf20Sopenharmony_ci else 2408c2ecf20Sopenharmony_ci bval = REG_READ_WITH_AUX(SDVOB, j); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* 2438c2ecf20Sopenharmony_ci * Write the registers twice for luck. Sometimes, 2448c2ecf20Sopenharmony_ci * writing them only once doesn't appear to 'stick'. 2458c2ecf20Sopenharmony_ci * The BIOS does this too. Yay, magic 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 2488c2ecf20Sopenharmony_ci REG_WRITE_WITH_AUX(SDVOB, bval, j); 2498c2ecf20Sopenharmony_ci REG_READ_WITH_AUX(SDVOB, j); 2508c2ecf20Sopenharmony_ci REG_WRITE_WITH_AUX(SDVOC, cval, j); 2518c2ecf20Sopenharmony_ci REG_READ_WITH_AUX(SDVOC, j); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_read_byte(struct psb_intel_sdvo *psb_intel_sdvo, u8 addr, u8 *ch) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct i2c_msg msgs[] = { 2598c2ecf20Sopenharmony_ci { 2608c2ecf20Sopenharmony_ci .addr = psb_intel_sdvo->slave_addr, 2618c2ecf20Sopenharmony_ci .flags = 0, 2628c2ecf20Sopenharmony_ci .len = 1, 2638c2ecf20Sopenharmony_ci .buf = &addr, 2648c2ecf20Sopenharmony_ci }, 2658c2ecf20Sopenharmony_ci { 2668c2ecf20Sopenharmony_ci .addr = psb_intel_sdvo->slave_addr, 2678c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 2688c2ecf20Sopenharmony_ci .len = 1, 2698c2ecf20Sopenharmony_ci .buf = ch, 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci }; 2728c2ecf20Sopenharmony_ci int ret; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if ((ret = i2c_transfer(psb_intel_sdvo->i2c, msgs, 2)) == 2) 2758c2ecf20Sopenharmony_ci return true; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("i2c transfer returned %d\n", ret); 2788c2ecf20Sopenharmony_ci return false; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} 2828c2ecf20Sopenharmony_ci/** Mapping of command numbers to names, for debug output */ 2838c2ecf20Sopenharmony_cistatic const struct _sdvo_cmd_name { 2848c2ecf20Sopenharmony_ci u8 cmd; 2858c2ecf20Sopenharmony_ci const char *name; 2868c2ecf20Sopenharmony_ci} sdvo_cmd_names[] = { 2878c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), 2888c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), 2898c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), 2908c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), 2918c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), 2928c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), 2938c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), 2948c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), 2958c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), 2968c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), 2978c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), 2988c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), 2998c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), 3008c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), 3018c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), 3028c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), 3038c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), 3048c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), 3058c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), 3068c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), 3078c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), 3088c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), 3098c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), 3108c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), 3118c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), 3128c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), 3138c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), 3148c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), 3158c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), 3168c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), 3178c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), 3188c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), 3198c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), 3208c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), 3218c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), 3228c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES), 3238c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE), 3248c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE), 3258c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE), 3268c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH), 3278c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT), 3288c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT), 3298c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS), 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* Add the op code for SDVO enhancements */ 3328c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HPOS), 3338c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HPOS), 3348c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HPOS), 3358c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_VPOS), 3368c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_VPOS), 3378c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_VPOS), 3388c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION), 3398c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION), 3408c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION), 3418c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE), 3428c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE), 3438c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE), 3448c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST), 3458c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST), 3468c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST), 3478c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS), 3488c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS), 3498c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS), 3508c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H), 3518c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H), 3528c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H), 3538c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V), 3548c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V), 3558c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V), 3568c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER), 3578c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER), 3588c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER), 3598c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE), 3608c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE), 3618c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE), 3628c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_2D), 3638c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_2D), 3648c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_2D), 3658c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SHARPNESS), 3668c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SHARPNESS), 3678c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SHARPNESS), 3688c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DOT_CRAWL), 3698c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DOT_CRAWL), 3708c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_CHROMA_FILTER), 3718c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_CHROMA_FILTER), 3728c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_CHROMA_FILTER), 3738c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_LUMA_FILTER), 3748c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_LUMA_FILTER), 3758c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_LUMA_FILTER), 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* HDMI op code */ 3788c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE), 3798c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE), 3808c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE), 3818c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI), 3828c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI), 3838c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP), 3848c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY), 3858c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY), 3868c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER), 3878c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT), 3888c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT), 3898c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX), 3908c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX), 3918c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO), 3928c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT), 3938c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT), 3948c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE), 3958c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE), 3968c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA), 3978c2ecf20Sopenharmony_ci SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA), 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci#define IS_SDVOB(reg) (reg == SDVOB) 4018c2ecf20Sopenharmony_ci#define SDVO_NAME(svdo) (IS_SDVOB((svdo)->sdvo_reg) ? "SDVOB" : "SDVOC") 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_debug_write(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, 4048c2ecf20Sopenharmony_ci const void *args, int args_len) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci int i; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%s: W: %02X ", 4098c2ecf20Sopenharmony_ci SDVO_NAME(psb_intel_sdvo), cmd); 4108c2ecf20Sopenharmony_ci for (i = 0; i < args_len; i++) 4118c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%02X ", ((u8 *)args)[i]); 4128c2ecf20Sopenharmony_ci for (; i < 8; i++) 4138c2ecf20Sopenharmony_ci DRM_DEBUG_KMS(" "); 4148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sdvo_cmd_names); i++) { 4158c2ecf20Sopenharmony_ci if (cmd == sdvo_cmd_names[i].cmd) { 4168c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("(%s)", sdvo_cmd_names[i].name); 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(sdvo_cmd_names)) 4218c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("(%02X)", cmd); 4228c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic const char *cmd_status_names[] = { 4268c2ecf20Sopenharmony_ci "Power on", 4278c2ecf20Sopenharmony_ci "Success", 4288c2ecf20Sopenharmony_ci "Not supported", 4298c2ecf20Sopenharmony_ci "Invalid arg", 4308c2ecf20Sopenharmony_ci "Pending", 4318c2ecf20Sopenharmony_ci "Target not specified", 4328c2ecf20Sopenharmony_ci "Scaling not supported" 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci#define MAX_ARG_LEN 32 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_write_cmd(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, 4388c2ecf20Sopenharmony_ci const void *args, int args_len) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci u8 buf[MAX_ARG_LEN*2 + 2], status; 4418c2ecf20Sopenharmony_ci struct i2c_msg msgs[MAX_ARG_LEN + 3]; 4428c2ecf20Sopenharmony_ci int i, ret; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (args_len > MAX_ARG_LEN) { 4458c2ecf20Sopenharmony_ci DRM_ERROR("Need to increase arg length\n"); 4468c2ecf20Sopenharmony_ci return false; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci psb_intel_sdvo_debug_write(psb_intel_sdvo, cmd, args, args_len); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci for (i = 0; i < args_len; i++) { 4528c2ecf20Sopenharmony_ci msgs[i].addr = psb_intel_sdvo->slave_addr; 4538c2ecf20Sopenharmony_ci msgs[i].flags = 0; 4548c2ecf20Sopenharmony_ci msgs[i].len = 2; 4558c2ecf20Sopenharmony_ci msgs[i].buf = buf + 2 *i; 4568c2ecf20Sopenharmony_ci buf[2*i + 0] = SDVO_I2C_ARG_0 - i; 4578c2ecf20Sopenharmony_ci buf[2*i + 1] = ((u8*)args)[i]; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci msgs[i].addr = psb_intel_sdvo->slave_addr; 4608c2ecf20Sopenharmony_ci msgs[i].flags = 0; 4618c2ecf20Sopenharmony_ci msgs[i].len = 2; 4628c2ecf20Sopenharmony_ci msgs[i].buf = buf + 2*i; 4638c2ecf20Sopenharmony_ci buf[2*i + 0] = SDVO_I2C_OPCODE; 4648c2ecf20Sopenharmony_ci buf[2*i + 1] = cmd; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* the following two are to read the response */ 4678c2ecf20Sopenharmony_ci status = SDVO_I2C_CMD_STATUS; 4688c2ecf20Sopenharmony_ci msgs[i+1].addr = psb_intel_sdvo->slave_addr; 4698c2ecf20Sopenharmony_ci msgs[i+1].flags = 0; 4708c2ecf20Sopenharmony_ci msgs[i+1].len = 1; 4718c2ecf20Sopenharmony_ci msgs[i+1].buf = &status; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci msgs[i+2].addr = psb_intel_sdvo->slave_addr; 4748c2ecf20Sopenharmony_ci msgs[i+2].flags = I2C_M_RD; 4758c2ecf20Sopenharmony_ci msgs[i+2].len = 1; 4768c2ecf20Sopenharmony_ci msgs[i+2].buf = &status; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci ret = i2c_transfer(psb_intel_sdvo->i2c, msgs, i+3); 4798c2ecf20Sopenharmony_ci if (ret < 0) { 4808c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("I2c transfer returned %d\n", ret); 4818c2ecf20Sopenharmony_ci return false; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci if (ret != i+3) { 4848c2ecf20Sopenharmony_ci /* failure in I2C transfer */ 4858c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("I2c transfer returned %d/%d\n", ret, i+3); 4868c2ecf20Sopenharmony_ci return false; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return true; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo, 4938c2ecf20Sopenharmony_ci void *response, int response_len) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci u8 retry = 5; 4968c2ecf20Sopenharmony_ci u8 status; 4978c2ecf20Sopenharmony_ci int i; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(psb_intel_sdvo)); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* 5028c2ecf20Sopenharmony_ci * The documentation states that all commands will be 5038c2ecf20Sopenharmony_ci * processed within 15µs, and that we need only poll 5048c2ecf20Sopenharmony_ci * the status byte a maximum of 3 times in order for the 5058c2ecf20Sopenharmony_ci * command to be complete. 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * Check 5 times in case the hardware failed to read the docs. 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, 5108c2ecf20Sopenharmony_ci SDVO_I2C_CMD_STATUS, 5118c2ecf20Sopenharmony_ci &status)) 5128c2ecf20Sopenharmony_ci goto log_fail; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci while ((status == SDVO_CMD_STATUS_PENDING || 5158c2ecf20Sopenharmony_ci status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && retry--) { 5168c2ecf20Sopenharmony_ci udelay(15); 5178c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, 5188c2ecf20Sopenharmony_ci SDVO_I2C_CMD_STATUS, 5198c2ecf20Sopenharmony_ci &status)) 5208c2ecf20Sopenharmony_ci goto log_fail; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) 5248c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("(%s)", cmd_status_names[status]); 5258c2ecf20Sopenharmony_ci else 5268c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("(??? %d)", status); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (status != SDVO_CMD_STATUS_SUCCESS) 5298c2ecf20Sopenharmony_ci goto log_fail; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Read the command response */ 5328c2ecf20Sopenharmony_ci for (i = 0; i < response_len; i++) { 5338c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, 5348c2ecf20Sopenharmony_ci SDVO_I2C_RETURN_0 + i, 5358c2ecf20Sopenharmony_ci &((u8 *)response)[i])) 5368c2ecf20Sopenharmony_ci goto log_fail; 5378c2ecf20Sopenharmony_ci DRM_DEBUG_KMS(" %02X", ((u8 *)response)[i]); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 5408c2ecf20Sopenharmony_ci return true; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cilog_fail: 5438c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("... failed\n"); 5448c2ecf20Sopenharmony_ci return false; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int psb_intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci if (mode->clock >= 100000) 5508c2ecf20Sopenharmony_ci return 1; 5518c2ecf20Sopenharmony_ci else if (mode->clock >= 50000) 5528c2ecf20Sopenharmony_ci return 2; 5538c2ecf20Sopenharmony_ci else 5548c2ecf20Sopenharmony_ci return 4; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_control_bus_switch(struct psb_intel_sdvo *psb_intel_sdvo, 5588c2ecf20Sopenharmony_ci u8 ddc_bus) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci /* This must be the immediately preceding write before the i2c xfer */ 5618c2ecf20Sopenharmony_ci return psb_intel_sdvo_write_cmd(psb_intel_sdvo, 5628c2ecf20Sopenharmony_ci SDVO_CMD_SET_CONTROL_BUS_SWITCH, 5638c2ecf20Sopenharmony_ci &ddc_bus, 1); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_value(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, const void *data, int len) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_write_cmd(psb_intel_sdvo, cmd, data, len)) 5698c2ecf20Sopenharmony_ci return false; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return psb_intel_sdvo_read_response(psb_intel_sdvo, NULL, 0); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic bool 5758c2ecf20Sopenharmony_cipsb_intel_sdvo_get_value(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, void *value, int len) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_write_cmd(psb_intel_sdvo, cmd, NULL, 0)) 5788c2ecf20Sopenharmony_ci return false; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return psb_intel_sdvo_read_response(psb_intel_sdvo, value, len); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_target_input(struct psb_intel_sdvo *psb_intel_sdvo) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct psb_intel_sdvo_set_target_input_args targets = {0}; 5868c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, 5878c2ecf20Sopenharmony_ci SDVO_CMD_SET_TARGET_INPUT, 5888c2ecf20Sopenharmony_ci &targets, sizeof(targets)); 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci/** 5928c2ecf20Sopenharmony_ci * Return whether each input is trained. 5938c2ecf20Sopenharmony_ci * 5948c2ecf20Sopenharmony_ci * This function is making an assumption about the layout of the response, 5958c2ecf20Sopenharmony_ci * which should be checked against the docs. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_get_trained_inputs(struct psb_intel_sdvo *psb_intel_sdvo, bool *input_1, bool *input_2) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct psb_intel_sdvo_get_trained_inputs_response response; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(response) != 1); 6028c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_TRAINED_INPUTS, 6038c2ecf20Sopenharmony_ci &response, sizeof(response))) 6048c2ecf20Sopenharmony_ci return false; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci *input_1 = response.input0_trained; 6078c2ecf20Sopenharmony_ci *input_2 = response.input1_trained; 6088c2ecf20Sopenharmony_ci return true; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_active_outputs(struct psb_intel_sdvo *psb_intel_sdvo, 6128c2ecf20Sopenharmony_ci u16 outputs) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, 6158c2ecf20Sopenharmony_ci SDVO_CMD_SET_ACTIVE_OUTPUTS, 6168c2ecf20Sopenharmony_ci &outputs, sizeof(outputs)); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_encoder_power_state(struct psb_intel_sdvo *psb_intel_sdvo, 6208c2ecf20Sopenharmony_ci int mode) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci u8 state = SDVO_ENCODER_STATE_ON; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci switch (mode) { 6258c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_ON: 6268c2ecf20Sopenharmony_ci state = SDVO_ENCODER_STATE_ON; 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_STANDBY: 6298c2ecf20Sopenharmony_ci state = SDVO_ENCODER_STATE_STANDBY; 6308c2ecf20Sopenharmony_ci break; 6318c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_SUSPEND: 6328c2ecf20Sopenharmony_ci state = SDVO_ENCODER_STATE_SUSPEND; 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_OFF: 6358c2ecf20Sopenharmony_ci state = SDVO_ENCODER_STATE_OFF; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, 6408c2ecf20Sopenharmony_ci SDVO_CMD_SET_ENCODER_POWER_STATE, &state, sizeof(state)); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_get_input_pixel_clock_range(struct psb_intel_sdvo *psb_intel_sdvo, 6448c2ecf20Sopenharmony_ci int *clock_min, 6458c2ecf20Sopenharmony_ci int *clock_max) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct psb_intel_sdvo_pixel_clock_range clocks; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(clocks) != 4); 6508c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, 6518c2ecf20Sopenharmony_ci SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, 6528c2ecf20Sopenharmony_ci &clocks, sizeof(clocks))) 6538c2ecf20Sopenharmony_ci return false; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* Convert the values from units of 10 kHz to kHz. */ 6568c2ecf20Sopenharmony_ci *clock_min = clocks.min * 10; 6578c2ecf20Sopenharmony_ci *clock_max = clocks.max * 10; 6588c2ecf20Sopenharmony_ci return true; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_target_output(struct psb_intel_sdvo *psb_intel_sdvo, 6628c2ecf20Sopenharmony_ci u16 outputs) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, 6658c2ecf20Sopenharmony_ci SDVO_CMD_SET_TARGET_OUTPUT, 6668c2ecf20Sopenharmony_ci &outputs, sizeof(outputs)); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_timing(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd, 6708c2ecf20Sopenharmony_ci struct psb_intel_sdvo_dtd *dtd) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, cmd, &dtd->part1, sizeof(dtd->part1)) && 6738c2ecf20Sopenharmony_ci psb_intel_sdvo_set_value(psb_intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2)); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_input_timing(struct psb_intel_sdvo *psb_intel_sdvo, 6778c2ecf20Sopenharmony_ci struct psb_intel_sdvo_dtd *dtd) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_timing(psb_intel_sdvo, 6808c2ecf20Sopenharmony_ci SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_output_timing(struct psb_intel_sdvo *psb_intel_sdvo, 6848c2ecf20Sopenharmony_ci struct psb_intel_sdvo_dtd *dtd) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_timing(psb_intel_sdvo, 6878c2ecf20Sopenharmony_ci SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd); 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic bool 6918c2ecf20Sopenharmony_cipsb_intel_sdvo_create_preferred_input_timing(struct psb_intel_sdvo *psb_intel_sdvo, 6928c2ecf20Sopenharmony_ci uint16_t clock, 6938c2ecf20Sopenharmony_ci uint16_t width, 6948c2ecf20Sopenharmony_ci uint16_t height) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct psb_intel_sdvo_preferred_input_timing_args args; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci memset(&args, 0, sizeof(args)); 6998c2ecf20Sopenharmony_ci args.clock = clock; 7008c2ecf20Sopenharmony_ci args.width = width; 7018c2ecf20Sopenharmony_ci args.height = height; 7028c2ecf20Sopenharmony_ci args.interlace = 0; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (psb_intel_sdvo->is_lvds && 7058c2ecf20Sopenharmony_ci (psb_intel_sdvo->sdvo_lvds_fixed_mode->hdisplay != width || 7068c2ecf20Sopenharmony_ci psb_intel_sdvo->sdvo_lvds_fixed_mode->vdisplay != height)) 7078c2ecf20Sopenharmony_ci args.scaled = 1; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, 7108c2ecf20Sopenharmony_ci SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING, 7118c2ecf20Sopenharmony_ci &args, sizeof(args)); 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_get_preferred_input_timing(struct psb_intel_sdvo *psb_intel_sdvo, 7158c2ecf20Sopenharmony_ci struct psb_intel_sdvo_dtd *dtd) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(dtd->part1) != 8); 7188c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(dtd->part2) != 8); 7198c2ecf20Sopenharmony_ci return psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1, 7208c2ecf20Sopenharmony_ci &dtd->part1, sizeof(dtd->part1)) && 7218c2ecf20Sopenharmony_ci psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2, 7228c2ecf20Sopenharmony_ci &dtd->part2, sizeof(dtd->part2)); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_clock_rate_mult(struct psb_intel_sdvo *psb_intel_sdvo, u8 val) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1); 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_get_dtd_from_mode(struct psb_intel_sdvo_dtd *dtd, 7318c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci uint16_t width, height; 7348c2ecf20Sopenharmony_ci uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len; 7358c2ecf20Sopenharmony_ci uint16_t h_sync_offset, v_sync_offset; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci width = mode->crtc_hdisplay; 7388c2ecf20Sopenharmony_ci height = mode->crtc_vdisplay; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* do some mode translations */ 7418c2ecf20Sopenharmony_ci h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; 7428c2ecf20Sopenharmony_ci h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; 7458c2ecf20Sopenharmony_ci v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; 7488c2ecf20Sopenharmony_ci v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci dtd->part1.clock = mode->clock / 10; 7518c2ecf20Sopenharmony_ci dtd->part1.h_active = width & 0xff; 7528c2ecf20Sopenharmony_ci dtd->part1.h_blank = h_blank_len & 0xff; 7538c2ecf20Sopenharmony_ci dtd->part1.h_high = (((width >> 8) & 0xf) << 4) | 7548c2ecf20Sopenharmony_ci ((h_blank_len >> 8) & 0xf); 7558c2ecf20Sopenharmony_ci dtd->part1.v_active = height & 0xff; 7568c2ecf20Sopenharmony_ci dtd->part1.v_blank = v_blank_len & 0xff; 7578c2ecf20Sopenharmony_ci dtd->part1.v_high = (((height >> 8) & 0xf) << 4) | 7588c2ecf20Sopenharmony_ci ((v_blank_len >> 8) & 0xf); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci dtd->part2.h_sync_off = h_sync_offset & 0xff; 7618c2ecf20Sopenharmony_ci dtd->part2.h_sync_width = h_sync_len & 0xff; 7628c2ecf20Sopenharmony_ci dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | 7638c2ecf20Sopenharmony_ci (v_sync_len & 0xf); 7648c2ecf20Sopenharmony_ci dtd->part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) | 7658c2ecf20Sopenharmony_ci ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) | 7668c2ecf20Sopenharmony_ci ((v_sync_len & 0x30) >> 4); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci dtd->part2.dtd_flags = 0x18; 7698c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_PHSYNC) 7708c2ecf20Sopenharmony_ci dtd->part2.dtd_flags |= 0x2; 7718c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_PVSYNC) 7728c2ecf20Sopenharmony_ci dtd->part2.dtd_flags |= 0x4; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci dtd->part2.sdvo_flags = 0; 7758c2ecf20Sopenharmony_ci dtd->part2.v_sync_off_high = v_sync_offset & 0xc0; 7768c2ecf20Sopenharmony_ci dtd->part2.reserved = 0; 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode, 7808c2ecf20Sopenharmony_ci const struct psb_intel_sdvo_dtd *dtd) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci mode->hdisplay = dtd->part1.h_active; 7838c2ecf20Sopenharmony_ci mode->hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8; 7848c2ecf20Sopenharmony_ci mode->hsync_start = mode->hdisplay + dtd->part2.h_sync_off; 7858c2ecf20Sopenharmony_ci mode->hsync_start += (dtd->part2.sync_off_width_high & 0xc0) << 2; 7868c2ecf20Sopenharmony_ci mode->hsync_end = mode->hsync_start + dtd->part2.h_sync_width; 7878c2ecf20Sopenharmony_ci mode->hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4; 7888c2ecf20Sopenharmony_ci mode->htotal = mode->hdisplay + dtd->part1.h_blank; 7898c2ecf20Sopenharmony_ci mode->htotal += (dtd->part1.h_high & 0xf) << 8; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci mode->vdisplay = dtd->part1.v_active; 7928c2ecf20Sopenharmony_ci mode->vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8; 7938c2ecf20Sopenharmony_ci mode->vsync_start = mode->vdisplay; 7948c2ecf20Sopenharmony_ci mode->vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf; 7958c2ecf20Sopenharmony_ci mode->vsync_start += (dtd->part2.sync_off_width_high & 0x0c) << 2; 7968c2ecf20Sopenharmony_ci mode->vsync_start += dtd->part2.v_sync_off_high & 0xc0; 7978c2ecf20Sopenharmony_ci mode->vsync_end = mode->vsync_start + 7988c2ecf20Sopenharmony_ci (dtd->part2.v_sync_off_width & 0xf); 7998c2ecf20Sopenharmony_ci mode->vsync_end += (dtd->part2.sync_off_width_high & 0x3) << 4; 8008c2ecf20Sopenharmony_ci mode->vtotal = mode->vdisplay + dtd->part1.v_blank; 8018c2ecf20Sopenharmony_ci mode->vtotal += (dtd->part1.v_high & 0xf) << 8; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci mode->clock = dtd->part1.clock * 10; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci mode->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); 8068c2ecf20Sopenharmony_ci if (dtd->part2.dtd_flags & 0x2) 8078c2ecf20Sopenharmony_ci mode->flags |= DRM_MODE_FLAG_PHSYNC; 8088c2ecf20Sopenharmony_ci if (dtd->part2.dtd_flags & 0x4) 8098c2ecf20Sopenharmony_ci mode->flags |= DRM_MODE_FLAG_PVSYNC; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_check_supp_encode(struct psb_intel_sdvo *psb_intel_sdvo) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci struct psb_intel_sdvo_encode encode; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(encode) != 2); 8178c2ecf20Sopenharmony_ci return psb_intel_sdvo_get_value(psb_intel_sdvo, 8188c2ecf20Sopenharmony_ci SDVO_CMD_GET_SUPP_ENCODE, 8198c2ecf20Sopenharmony_ci &encode, sizeof(encode)); 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_encode(struct psb_intel_sdvo *psb_intel_sdvo, 8238c2ecf20Sopenharmony_ci uint8_t mode) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, SDVO_CMD_SET_ENCODE, &mode, 1); 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_colorimetry(struct psb_intel_sdvo *psb_intel_sdvo, 8298c2ecf20Sopenharmony_ci uint8_t mode) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, SDVO_CMD_SET_COLORIMETRY, &mode, 1); 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci#if 0 8358c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_dump_hdmi_buf(struct psb_intel_sdvo *psb_intel_sdvo) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci int i, j; 8388c2ecf20Sopenharmony_ci uint8_t set_buf_index[2]; 8398c2ecf20Sopenharmony_ci uint8_t av_split; 8408c2ecf20Sopenharmony_ci uint8_t buf_size; 8418c2ecf20Sopenharmony_ci uint8_t buf[48]; 8428c2ecf20Sopenharmony_ci uint8_t *pos; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci psb_intel_sdvo_get_value(encoder, SDVO_CMD_GET_HBUF_AV_SPLIT, &av_split, 1); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci for (i = 0; i <= av_split; i++) { 8478c2ecf20Sopenharmony_ci set_buf_index[0] = i; set_buf_index[1] = 0; 8488c2ecf20Sopenharmony_ci psb_intel_sdvo_write_cmd(encoder, SDVO_CMD_SET_HBUF_INDEX, 8498c2ecf20Sopenharmony_ci set_buf_index, 2); 8508c2ecf20Sopenharmony_ci psb_intel_sdvo_write_cmd(encoder, SDVO_CMD_GET_HBUF_INFO, NULL, 0); 8518c2ecf20Sopenharmony_ci psb_intel_sdvo_read_response(encoder, &buf_size, 1); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci pos = buf; 8548c2ecf20Sopenharmony_ci for (j = 0; j <= buf_size; j += 8) { 8558c2ecf20Sopenharmony_ci psb_intel_sdvo_write_cmd(encoder, SDVO_CMD_GET_HBUF_DATA, 8568c2ecf20Sopenharmony_ci NULL, 0); 8578c2ecf20Sopenharmony_ci psb_intel_sdvo_read_response(encoder, pos, 8); 8588c2ecf20Sopenharmony_ci pos += 8; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci#endif 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_avi_infoframe(struct psb_intel_sdvo *psb_intel_sdvo) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci DRM_INFO("HDMI is not supported yet"); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci return false; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_set_tv_format(struct psb_intel_sdvo *psb_intel_sdvo) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct psb_intel_sdvo_tv_format format; 8748c2ecf20Sopenharmony_ci uint32_t format_map; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci format_map = 1 << psb_intel_sdvo->tv_format_index; 8778c2ecf20Sopenharmony_ci memset(&format, 0, sizeof(format)); 8788c2ecf20Sopenharmony_ci memcpy(&format, &format_map, min(sizeof(format), sizeof(format_map))); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(format) != 6); 8818c2ecf20Sopenharmony_ci return psb_intel_sdvo_set_value(psb_intel_sdvo, 8828c2ecf20Sopenharmony_ci SDVO_CMD_SET_TV_FORMAT, 8838c2ecf20Sopenharmony_ci &format, sizeof(format)); 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic bool 8878c2ecf20Sopenharmony_cipsb_intel_sdvo_set_output_timings_from_mode(struct psb_intel_sdvo *psb_intel_sdvo, 8888c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct psb_intel_sdvo_dtd output_dtd; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_target_output(psb_intel_sdvo, 8938c2ecf20Sopenharmony_ci psb_intel_sdvo->attached_output)) 8948c2ecf20Sopenharmony_ci return false; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci psb_intel_sdvo_get_dtd_from_mode(&output_dtd, mode); 8978c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_output_timing(psb_intel_sdvo, &output_dtd)) 8988c2ecf20Sopenharmony_ci return false; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci return true; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic bool 9048c2ecf20Sopenharmony_cipsb_intel_sdvo_set_input_timings_for_mode(struct psb_intel_sdvo *psb_intel_sdvo, 9058c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 9068c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci /* Reset the input timing to the screen. Assume always input 0. */ 9098c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_target_input(psb_intel_sdvo)) 9108c2ecf20Sopenharmony_ci return false; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_create_preferred_input_timing(psb_intel_sdvo, 9138c2ecf20Sopenharmony_ci mode->clock / 10, 9148c2ecf20Sopenharmony_ci mode->hdisplay, 9158c2ecf20Sopenharmony_ci mode->vdisplay)) 9168c2ecf20Sopenharmony_ci return false; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_preferred_input_timing(psb_intel_sdvo, 9198c2ecf20Sopenharmony_ci &psb_intel_sdvo->input_dtd)) 9208c2ecf20Sopenharmony_ci return false; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci psb_intel_sdvo_get_mode_from_dtd(adjusted_mode, &psb_intel_sdvo->input_dtd); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci drm_mode_set_crtcinfo(adjusted_mode, 0); 9258c2ecf20Sopenharmony_ci return true; 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_mode_fixup(struct drm_encoder *encoder, 9298c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 9308c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* We need to construct preferred input timings based on our 9358c2ecf20Sopenharmony_ci * output timings. To do that, we have to set the output 9368c2ecf20Sopenharmony_ci * timings, even though this isn't really the right place in 9378c2ecf20Sopenharmony_ci * the sequence to do it. Oh well. 9388c2ecf20Sopenharmony_ci */ 9398c2ecf20Sopenharmony_ci if (psb_intel_sdvo->is_tv) { 9408c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_output_timings_from_mode(psb_intel_sdvo, mode)) 9418c2ecf20Sopenharmony_ci return false; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci (void) psb_intel_sdvo_set_input_timings_for_mode(psb_intel_sdvo, 9448c2ecf20Sopenharmony_ci mode, 9458c2ecf20Sopenharmony_ci adjusted_mode); 9468c2ecf20Sopenharmony_ci } else if (psb_intel_sdvo->is_lvds) { 9478c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_output_timings_from_mode(psb_intel_sdvo, 9488c2ecf20Sopenharmony_ci psb_intel_sdvo->sdvo_lvds_fixed_mode)) 9498c2ecf20Sopenharmony_ci return false; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci (void) psb_intel_sdvo_set_input_timings_for_mode(psb_intel_sdvo, 9528c2ecf20Sopenharmony_ci mode, 9538c2ecf20Sopenharmony_ci adjusted_mode); 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* Make the CRTC code factor in the SDVO pixel multiplier. The 9578c2ecf20Sopenharmony_ci * SDVO device will factor out the multiplier during mode_set. 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_ci psb_intel_sdvo->pixel_multiplier = 9608c2ecf20Sopenharmony_ci psb_intel_sdvo_get_pixel_multiplier(adjusted_mode); 9618c2ecf20Sopenharmony_ci adjusted_mode->clock *= psb_intel_sdvo->pixel_multiplier; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return true; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_mode_set(struct drm_encoder *encoder, 9678c2ecf20Sopenharmony_ci struct drm_display_mode *mode, 9688c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 9718c2ecf20Sopenharmony_ci struct drm_crtc *crtc = encoder->crtc; 9728c2ecf20Sopenharmony_ci struct gma_crtc *gma_crtc = to_gma_crtc(crtc); 9738c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder); 9748c2ecf20Sopenharmony_ci u32 sdvox; 9758c2ecf20Sopenharmony_ci struct psb_intel_sdvo_in_out_map in_out; 9768c2ecf20Sopenharmony_ci struct psb_intel_sdvo_dtd input_dtd; 9778c2ecf20Sopenharmony_ci int rate; 9788c2ecf20Sopenharmony_ci int need_aux = IS_MRST(dev) ? 1 : 0; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (!mode) 9818c2ecf20Sopenharmony_ci return; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* First, set the input mapping for the first input to our controlled 9848c2ecf20Sopenharmony_ci * output. This is only correct if we're a single-input device, in 9858c2ecf20Sopenharmony_ci * which case the first input is the output from the appropriate SDVO 9868c2ecf20Sopenharmony_ci * channel on the motherboard. In a two-input device, the first input 9878c2ecf20Sopenharmony_ci * will be SDVOB and the second SDVOC. 9888c2ecf20Sopenharmony_ci */ 9898c2ecf20Sopenharmony_ci in_out.in0 = psb_intel_sdvo->attached_output; 9908c2ecf20Sopenharmony_ci in_out.in1 = 0; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci psb_intel_sdvo_set_value(psb_intel_sdvo, 9938c2ecf20Sopenharmony_ci SDVO_CMD_SET_IN_OUT_MAP, 9948c2ecf20Sopenharmony_ci &in_out, sizeof(in_out)); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* Set the output timings to the screen */ 9978c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_target_output(psb_intel_sdvo, 9988c2ecf20Sopenharmony_ci psb_intel_sdvo->attached_output)) 9998c2ecf20Sopenharmony_ci return; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* We have tried to get input timing in mode_fixup, and filled into 10028c2ecf20Sopenharmony_ci * adjusted_mode. 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_ci if (psb_intel_sdvo->is_tv || psb_intel_sdvo->is_lvds) { 10058c2ecf20Sopenharmony_ci input_dtd = psb_intel_sdvo->input_dtd; 10068c2ecf20Sopenharmony_ci } else { 10078c2ecf20Sopenharmony_ci /* Set the output timing to the screen */ 10088c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_target_output(psb_intel_sdvo, 10098c2ecf20Sopenharmony_ci psb_intel_sdvo->attached_output)) 10108c2ecf20Sopenharmony_ci return; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci psb_intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); 10138c2ecf20Sopenharmony_ci (void) psb_intel_sdvo_set_output_timing(psb_intel_sdvo, &input_dtd); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* Set the input timing to the screen. Assume always input 0. */ 10178c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_target_input(psb_intel_sdvo)) 10188c2ecf20Sopenharmony_ci return; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (psb_intel_sdvo->has_hdmi_monitor) { 10218c2ecf20Sopenharmony_ci psb_intel_sdvo_set_encode(psb_intel_sdvo, SDVO_ENCODE_HDMI); 10228c2ecf20Sopenharmony_ci psb_intel_sdvo_set_colorimetry(psb_intel_sdvo, 10238c2ecf20Sopenharmony_ci SDVO_COLORIMETRY_RGB256); 10248c2ecf20Sopenharmony_ci psb_intel_sdvo_set_avi_infoframe(psb_intel_sdvo); 10258c2ecf20Sopenharmony_ci } else 10268c2ecf20Sopenharmony_ci psb_intel_sdvo_set_encode(psb_intel_sdvo, SDVO_ENCODE_DVI); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (psb_intel_sdvo->is_tv && 10298c2ecf20Sopenharmony_ci !psb_intel_sdvo_set_tv_format(psb_intel_sdvo)) 10308c2ecf20Sopenharmony_ci return; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci (void) psb_intel_sdvo_set_input_timing(psb_intel_sdvo, &input_dtd); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci switch (psb_intel_sdvo->pixel_multiplier) { 10358c2ecf20Sopenharmony_ci default: 10368c2ecf20Sopenharmony_ci case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break; 10378c2ecf20Sopenharmony_ci case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break; 10388c2ecf20Sopenharmony_ci case 4: rate = SDVO_CLOCK_RATE_MULT_4X; break; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_clock_rate_mult(psb_intel_sdvo, rate)) 10418c2ecf20Sopenharmony_ci return; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* Set the SDVO control regs. */ 10448c2ecf20Sopenharmony_ci if (need_aux) 10458c2ecf20Sopenharmony_ci sdvox = REG_READ_AUX(psb_intel_sdvo->sdvo_reg); 10468c2ecf20Sopenharmony_ci else 10478c2ecf20Sopenharmony_ci sdvox = REG_READ(psb_intel_sdvo->sdvo_reg); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci switch (psb_intel_sdvo->sdvo_reg) { 10508c2ecf20Sopenharmony_ci case SDVOB: 10518c2ecf20Sopenharmony_ci sdvox &= SDVOB_PRESERVE_MASK; 10528c2ecf20Sopenharmony_ci break; 10538c2ecf20Sopenharmony_ci case SDVOC: 10548c2ecf20Sopenharmony_ci sdvox &= SDVOC_PRESERVE_MASK; 10558c2ecf20Sopenharmony_ci break; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (gma_crtc->pipe == 1) 10608c2ecf20Sopenharmony_ci sdvox |= SDVO_PIPE_B_SELECT; 10618c2ecf20Sopenharmony_ci if (psb_intel_sdvo->has_hdmi_audio) 10628c2ecf20Sopenharmony_ci sdvox |= SDVO_AUDIO_ENABLE; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* FIXME: Check if this is needed for PSB 10658c2ecf20Sopenharmony_ci sdvox |= (pixel_multiplier - 1) << SDVO_PORT_MULTIPLY_SHIFT; 10668c2ecf20Sopenharmony_ci */ 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (input_dtd.part2.sdvo_flags & SDVO_NEED_TO_STALL) 10698c2ecf20Sopenharmony_ci sdvox |= SDVO_STALL_SELECT; 10708c2ecf20Sopenharmony_ci psb_intel_sdvo_write_sdvox(psb_intel_sdvo, sdvox); 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 10768c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder); 10778c2ecf20Sopenharmony_ci u32 temp; 10788c2ecf20Sopenharmony_ci int i; 10798c2ecf20Sopenharmony_ci int need_aux = IS_MRST(dev) ? 1 : 0; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci switch (mode) { 10828c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_ON: 10838c2ecf20Sopenharmony_ci DRM_DEBUG("DPMS_ON"); 10848c2ecf20Sopenharmony_ci break; 10858c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_OFF: 10868c2ecf20Sopenharmony_ci DRM_DEBUG("DPMS_OFF"); 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci default: 10898c2ecf20Sopenharmony_ci DRM_DEBUG("DPMS: %d", mode); 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (mode != DRM_MODE_DPMS_ON) { 10938c2ecf20Sopenharmony_ci psb_intel_sdvo_set_active_outputs(psb_intel_sdvo, 0); 10948c2ecf20Sopenharmony_ci if (0) 10958c2ecf20Sopenharmony_ci psb_intel_sdvo_set_encoder_power_state(psb_intel_sdvo, mode); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (mode == DRM_MODE_DPMS_OFF) { 10988c2ecf20Sopenharmony_ci if (need_aux) 10998c2ecf20Sopenharmony_ci temp = REG_READ_AUX(psb_intel_sdvo->sdvo_reg); 11008c2ecf20Sopenharmony_ci else 11018c2ecf20Sopenharmony_ci temp = REG_READ(psb_intel_sdvo->sdvo_reg); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci if ((temp & SDVO_ENABLE) != 0) { 11048c2ecf20Sopenharmony_ci psb_intel_sdvo_write_sdvox(psb_intel_sdvo, temp & ~SDVO_ENABLE); 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci } else { 11088c2ecf20Sopenharmony_ci bool input1, input2; 11098c2ecf20Sopenharmony_ci u8 status; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (need_aux) 11128c2ecf20Sopenharmony_ci temp = REG_READ_AUX(psb_intel_sdvo->sdvo_reg); 11138c2ecf20Sopenharmony_ci else 11148c2ecf20Sopenharmony_ci temp = REG_READ(psb_intel_sdvo->sdvo_reg); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if ((temp & SDVO_ENABLE) == 0) 11178c2ecf20Sopenharmony_ci psb_intel_sdvo_write_sdvox(psb_intel_sdvo, temp | SDVO_ENABLE); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 11208c2ecf20Sopenharmony_ci gma_wait_for_vblank(dev); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci status = psb_intel_sdvo_get_trained_inputs(psb_intel_sdvo, &input1, &input2); 11238c2ecf20Sopenharmony_ci /* Warn if the device reported failure to sync. 11248c2ecf20Sopenharmony_ci * A lot of SDVO devices fail to notify of sync, but it's 11258c2ecf20Sopenharmony_ci * a given it the status is a success, we succeeded. 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_ci if (status == SDVO_CMD_STATUS_SUCCESS && !input1) { 11288c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("First %s output reported failure to " 11298c2ecf20Sopenharmony_ci "sync\n", SDVO_NAME(psb_intel_sdvo)); 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (0) 11338c2ecf20Sopenharmony_ci psb_intel_sdvo_set_encoder_power_state(psb_intel_sdvo, mode); 11348c2ecf20Sopenharmony_ci psb_intel_sdvo_set_active_outputs(psb_intel_sdvo, psb_intel_sdvo->attached_output); 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci return; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic enum drm_mode_status psb_intel_sdvo_mode_valid(struct drm_connector *connector, 11408c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 11458c2ecf20Sopenharmony_ci return MODE_NO_DBLESCAN; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (psb_intel_sdvo->pixel_clock_min > mode->clock) 11488c2ecf20Sopenharmony_ci return MODE_CLOCK_LOW; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (psb_intel_sdvo->pixel_clock_max < mode->clock) 11518c2ecf20Sopenharmony_ci return MODE_CLOCK_HIGH; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (psb_intel_sdvo->is_lvds) { 11548c2ecf20Sopenharmony_ci if (mode->hdisplay > psb_intel_sdvo->sdvo_lvds_fixed_mode->hdisplay) 11558c2ecf20Sopenharmony_ci return MODE_PANEL; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (mode->vdisplay > psb_intel_sdvo->sdvo_lvds_fixed_mode->vdisplay) 11588c2ecf20Sopenharmony_ci return MODE_PANEL; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return MODE_OK; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_get_capabilities(struct psb_intel_sdvo *psb_intel_sdvo, struct psb_intel_sdvo_caps *caps) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*caps) != 8); 11678c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, 11688c2ecf20Sopenharmony_ci SDVO_CMD_GET_DEVICE_CAPS, 11698c2ecf20Sopenharmony_ci caps, sizeof(*caps))) 11708c2ecf20Sopenharmony_ci return false; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SDVO capabilities:\n" 11738c2ecf20Sopenharmony_ci " vendor_id: %d\n" 11748c2ecf20Sopenharmony_ci " device_id: %d\n" 11758c2ecf20Sopenharmony_ci " device_rev_id: %d\n" 11768c2ecf20Sopenharmony_ci " sdvo_version_major: %d\n" 11778c2ecf20Sopenharmony_ci " sdvo_version_minor: %d\n" 11788c2ecf20Sopenharmony_ci " sdvo_inputs_mask: %d\n" 11798c2ecf20Sopenharmony_ci " smooth_scaling: %d\n" 11808c2ecf20Sopenharmony_ci " sharp_scaling: %d\n" 11818c2ecf20Sopenharmony_ci " up_scaling: %d\n" 11828c2ecf20Sopenharmony_ci " down_scaling: %d\n" 11838c2ecf20Sopenharmony_ci " stall_support: %d\n" 11848c2ecf20Sopenharmony_ci " output_flags: %d\n", 11858c2ecf20Sopenharmony_ci caps->vendor_id, 11868c2ecf20Sopenharmony_ci caps->device_id, 11878c2ecf20Sopenharmony_ci caps->device_rev_id, 11888c2ecf20Sopenharmony_ci caps->sdvo_version_major, 11898c2ecf20Sopenharmony_ci caps->sdvo_version_minor, 11908c2ecf20Sopenharmony_ci caps->sdvo_inputs_mask, 11918c2ecf20Sopenharmony_ci caps->smooth_scaling, 11928c2ecf20Sopenharmony_ci caps->sharp_scaling, 11938c2ecf20Sopenharmony_ci caps->up_scaling, 11948c2ecf20Sopenharmony_ci caps->down_scaling, 11958c2ecf20Sopenharmony_ci caps->stall_support, 11968c2ecf20Sopenharmony_ci caps->output_flags); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return true; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic bool 12028c2ecf20Sopenharmony_cipsb_intel_sdvo_multifunc_encoder(struct psb_intel_sdvo *psb_intel_sdvo) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci /* Is there more than one type of output? */ 12058c2ecf20Sopenharmony_ci int caps = psb_intel_sdvo->caps.output_flags & 0xf; 12068c2ecf20Sopenharmony_ci return caps & -caps; 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_cistatic struct edid * 12108c2ecf20Sopenharmony_cipsb_intel_sdvo_get_edid(struct drm_connector *connector) 12118c2ecf20Sopenharmony_ci{ 12128c2ecf20Sopenharmony_ci struct psb_intel_sdvo *sdvo = intel_attached_sdvo(connector); 12138c2ecf20Sopenharmony_ci return drm_get_edid(connector, &sdvo->ddc); 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci/* Mac mini hack -- use the same DDC as the analog connector */ 12178c2ecf20Sopenharmony_cistatic struct edid * 12188c2ecf20Sopenharmony_cipsb_intel_sdvo_get_analog_edid(struct drm_connector *connector) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = connector->dev->dev_private; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci return drm_get_edid(connector, 12238c2ecf20Sopenharmony_ci &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic enum drm_connector_status 12278c2ecf20Sopenharmony_cipsb_intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); 12308c2ecf20Sopenharmony_ci enum drm_connector_status status; 12318c2ecf20Sopenharmony_ci struct edid *edid; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci edid = psb_intel_sdvo_get_edid(connector); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (edid == NULL && psb_intel_sdvo_multifunc_encoder(psb_intel_sdvo)) { 12368c2ecf20Sopenharmony_ci u8 ddc, saved_ddc = psb_intel_sdvo->ddc_bus; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* 12398c2ecf20Sopenharmony_ci * Don't use the 1 as the argument of DDC bus switch to get 12408c2ecf20Sopenharmony_ci * the EDID. It is used for SDVO SPD ROM. 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci for (ddc = psb_intel_sdvo->ddc_bus >> 1; ddc > 1; ddc >>= 1) { 12438c2ecf20Sopenharmony_ci psb_intel_sdvo->ddc_bus = ddc; 12448c2ecf20Sopenharmony_ci edid = psb_intel_sdvo_get_edid(connector); 12458c2ecf20Sopenharmony_ci if (edid) 12468c2ecf20Sopenharmony_ci break; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci /* 12498c2ecf20Sopenharmony_ci * If we found the EDID on the other bus, 12508c2ecf20Sopenharmony_ci * assume that is the correct DDC bus. 12518c2ecf20Sopenharmony_ci */ 12528c2ecf20Sopenharmony_ci if (edid == NULL) 12538c2ecf20Sopenharmony_ci psb_intel_sdvo->ddc_bus = saved_ddc; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci /* 12578c2ecf20Sopenharmony_ci * When there is no edid and no monitor is connected with VGA 12588c2ecf20Sopenharmony_ci * port, try to use the CRT ddc to read the EDID for DVI-connector. 12598c2ecf20Sopenharmony_ci */ 12608c2ecf20Sopenharmony_ci if (edid == NULL) 12618c2ecf20Sopenharmony_ci edid = psb_intel_sdvo_get_analog_edid(connector); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci status = connector_status_unknown; 12648c2ecf20Sopenharmony_ci if (edid != NULL) { 12658c2ecf20Sopenharmony_ci /* DDC bus is shared, match EDID to connector type */ 12668c2ecf20Sopenharmony_ci if (edid->input & DRM_EDID_INPUT_DIGITAL) { 12678c2ecf20Sopenharmony_ci status = connector_status_connected; 12688c2ecf20Sopenharmony_ci if (psb_intel_sdvo->is_hdmi) { 12698c2ecf20Sopenharmony_ci psb_intel_sdvo->has_hdmi_monitor = drm_detect_hdmi_monitor(edid); 12708c2ecf20Sopenharmony_ci psb_intel_sdvo->has_hdmi_audio = drm_detect_monitor_audio(edid); 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci } else 12738c2ecf20Sopenharmony_ci status = connector_status_disconnected; 12748c2ecf20Sopenharmony_ci kfree(edid); 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (status == connector_status_connected) { 12788c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector); 12798c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->force_audio) 12808c2ecf20Sopenharmony_ci psb_intel_sdvo->has_hdmi_audio = psb_intel_sdvo_connector->force_audio > 0; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci return status; 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic enum drm_connector_status 12878c2ecf20Sopenharmony_cipsb_intel_sdvo_detect(struct drm_connector *connector, bool force) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci uint16_t response; 12908c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); 12918c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector); 12928c2ecf20Sopenharmony_ci enum drm_connector_status ret; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_write_cmd(psb_intel_sdvo, 12958c2ecf20Sopenharmony_ci SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0)) 12968c2ecf20Sopenharmony_ci return connector_status_unknown; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* add 30ms delay when the output type might be TV */ 12998c2ecf20Sopenharmony_ci if (psb_intel_sdvo->caps.output_flags & 13008c2ecf20Sopenharmony_ci (SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_CVBS0)) 13018c2ecf20Sopenharmony_ci mdelay(30); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_read_response(psb_intel_sdvo, &response, 2)) 13048c2ecf20Sopenharmony_ci return connector_status_unknown; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SDVO response %d %d [%x]\n", 13078c2ecf20Sopenharmony_ci response & 0xff, response >> 8, 13088c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->output_flag); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (response == 0) 13118c2ecf20Sopenharmony_ci return connector_status_disconnected; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci psb_intel_sdvo->attached_output = response; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci psb_intel_sdvo->has_hdmi_monitor = false; 13168c2ecf20Sopenharmony_ci psb_intel_sdvo->has_hdmi_audio = false; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci if ((psb_intel_sdvo_connector->output_flag & response) == 0) 13198c2ecf20Sopenharmony_ci ret = connector_status_disconnected; 13208c2ecf20Sopenharmony_ci else if (IS_TMDS(psb_intel_sdvo_connector)) 13218c2ecf20Sopenharmony_ci ret = psb_intel_sdvo_hdmi_sink_detect(connector); 13228c2ecf20Sopenharmony_ci else { 13238c2ecf20Sopenharmony_ci struct edid *edid; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* if we have an edid check it matches the connection */ 13268c2ecf20Sopenharmony_ci edid = psb_intel_sdvo_get_edid(connector); 13278c2ecf20Sopenharmony_ci if (edid == NULL) 13288c2ecf20Sopenharmony_ci edid = psb_intel_sdvo_get_analog_edid(connector); 13298c2ecf20Sopenharmony_ci if (edid != NULL) { 13308c2ecf20Sopenharmony_ci if (edid->input & DRM_EDID_INPUT_DIGITAL) 13318c2ecf20Sopenharmony_ci ret = connector_status_disconnected; 13328c2ecf20Sopenharmony_ci else 13338c2ecf20Sopenharmony_ci ret = connector_status_connected; 13348c2ecf20Sopenharmony_ci kfree(edid); 13358c2ecf20Sopenharmony_ci } else 13368c2ecf20Sopenharmony_ci ret = connector_status_connected; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci /* May update encoder flag for like clock for SDVO TV, etc.*/ 13408c2ecf20Sopenharmony_ci if (ret == connector_status_connected) { 13418c2ecf20Sopenharmony_ci psb_intel_sdvo->is_tv = false; 13428c2ecf20Sopenharmony_ci psb_intel_sdvo->is_lvds = false; 13438c2ecf20Sopenharmony_ci psb_intel_sdvo->base.needs_tv_clock = false; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (response & SDVO_TV_MASK) { 13468c2ecf20Sopenharmony_ci psb_intel_sdvo->is_tv = true; 13478c2ecf20Sopenharmony_ci psb_intel_sdvo->base.needs_tv_clock = true; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci if (response & SDVO_LVDS_MASK) 13508c2ecf20Sopenharmony_ci psb_intel_sdvo->is_lvds = psb_intel_sdvo->sdvo_lvds_fixed_mode != NULL; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci return ret; 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_get_ddc_modes(struct drm_connector *connector) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct edid *edid; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* set the bus switch and get the modes */ 13618c2ecf20Sopenharmony_ci edid = psb_intel_sdvo_get_edid(connector); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci /* 13648c2ecf20Sopenharmony_ci * Mac mini hack. On this device, the DVI-I connector shares one DDC 13658c2ecf20Sopenharmony_ci * link between analog and digital outputs. So, if the regular SDVO 13668c2ecf20Sopenharmony_ci * DDC fails, check to see if the analog output is disconnected, in 13678c2ecf20Sopenharmony_ci * which case we'll look there for the digital DDC data. 13688c2ecf20Sopenharmony_ci */ 13698c2ecf20Sopenharmony_ci if (edid == NULL) 13708c2ecf20Sopenharmony_ci edid = psb_intel_sdvo_get_analog_edid(connector); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci if (edid != NULL) { 13738c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector); 13748c2ecf20Sopenharmony_ci bool monitor_is_digital = !!(edid->input & DRM_EDID_INPUT_DIGITAL); 13758c2ecf20Sopenharmony_ci bool connector_is_digital = !!IS_TMDS(psb_intel_sdvo_connector); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (connector_is_digital == monitor_is_digital) { 13788c2ecf20Sopenharmony_ci drm_connector_update_edid_property(connector, edid); 13798c2ecf20Sopenharmony_ci drm_add_edid_modes(connector, edid); 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci kfree(edid); 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci} 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci/* 13878c2ecf20Sopenharmony_ci * Set of SDVO TV modes. 13888c2ecf20Sopenharmony_ci * Note! This is in reply order (see loop in get_tv_modes). 13898c2ecf20Sopenharmony_ci * XXX: all 60Hz refresh? 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_cistatic const struct drm_display_mode sdvo_tv_modes[] = { 13928c2ecf20Sopenharmony_ci { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815, 320, 321, 384, 13938c2ecf20Sopenharmony_ci 416, 0, 200, 201, 232, 233, 0, 13948c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 13958c2ecf20Sopenharmony_ci { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 6814, 320, 321, 384, 13968c2ecf20Sopenharmony_ci 416, 0, 240, 241, 272, 273, 0, 13978c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 13988c2ecf20Sopenharmony_ci { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 9910, 400, 401, 464, 13998c2ecf20Sopenharmony_ci 496, 0, 300, 301, 332, 333, 0, 14008c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14018c2ecf20Sopenharmony_ci { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 16913, 640, 641, 704, 14028c2ecf20Sopenharmony_ci 736, 0, 350, 351, 382, 383, 0, 14038c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14048c2ecf20Sopenharmony_ci { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121, 640, 641, 704, 14058c2ecf20Sopenharmony_ci 736, 0, 400, 401, 432, 433, 0, 14068c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14078c2ecf20Sopenharmony_ci { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 22654, 640, 641, 704, 14088c2ecf20Sopenharmony_ci 736, 0, 480, 481, 512, 513, 0, 14098c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14108c2ecf20Sopenharmony_ci { DRM_MODE("704x480", DRM_MODE_TYPE_DRIVER, 24624, 704, 705, 768, 14118c2ecf20Sopenharmony_ci 800, 0, 480, 481, 512, 513, 0, 14128c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14138c2ecf20Sopenharmony_ci { DRM_MODE("704x576", DRM_MODE_TYPE_DRIVER, 29232, 704, 705, 768, 14148c2ecf20Sopenharmony_ci 800, 0, 576, 577, 608, 609, 0, 14158c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14168c2ecf20Sopenharmony_ci { DRM_MODE("720x350", DRM_MODE_TYPE_DRIVER, 18751, 720, 721, 784, 14178c2ecf20Sopenharmony_ci 816, 0, 350, 351, 382, 383, 0, 14188c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14198c2ecf20Sopenharmony_ci { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 21199, 720, 721, 784, 14208c2ecf20Sopenharmony_ci 816, 0, 400, 401, 432, 433, 0, 14218c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14228c2ecf20Sopenharmony_ci { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25116, 720, 721, 784, 14238c2ecf20Sopenharmony_ci 816, 0, 480, 481, 512, 513, 0, 14248c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14258c2ecf20Sopenharmony_ci { DRM_MODE("720x540", DRM_MODE_TYPE_DRIVER, 28054, 720, 721, 784, 14268c2ecf20Sopenharmony_ci 816, 0, 540, 541, 572, 573, 0, 14278c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14288c2ecf20Sopenharmony_ci { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 29816, 720, 721, 784, 14298c2ecf20Sopenharmony_ci 816, 0, 576, 577, 608, 609, 0, 14308c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14318c2ecf20Sopenharmony_ci { DRM_MODE("768x576", DRM_MODE_TYPE_DRIVER, 31570, 768, 769, 832, 14328c2ecf20Sopenharmony_ci 864, 0, 576, 577, 608, 609, 0, 14338c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14348c2ecf20Sopenharmony_ci { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 34030, 800, 801, 864, 14358c2ecf20Sopenharmony_ci 896, 0, 600, 601, 632, 633, 0, 14368c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14378c2ecf20Sopenharmony_ci { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 36581, 832, 833, 896, 14388c2ecf20Sopenharmony_ci 928, 0, 624, 625, 656, 657, 0, 14398c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14408c2ecf20Sopenharmony_ci { DRM_MODE("920x766", DRM_MODE_TYPE_DRIVER, 48707, 920, 921, 984, 14418c2ecf20Sopenharmony_ci 1016, 0, 766, 767, 798, 799, 0, 14428c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14438c2ecf20Sopenharmony_ci { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 53827, 1024, 1025, 1088, 14448c2ecf20Sopenharmony_ci 1120, 0, 768, 769, 800, 801, 0, 14458c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14468c2ecf20Sopenharmony_ci { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 87265, 1280, 1281, 1344, 14478c2ecf20Sopenharmony_ci 1376, 0, 1024, 1025, 1056, 1057, 0, 14488c2ecf20Sopenharmony_ci DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 14498c2ecf20Sopenharmony_ci}; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_get_tv_modes(struct drm_connector *connector) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); 14548c2ecf20Sopenharmony_ci struct psb_intel_sdvo_sdtv_resolution_request tv_res; 14558c2ecf20Sopenharmony_ci uint32_t reply = 0, format_map = 0; 14568c2ecf20Sopenharmony_ci int i; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci /* Read the list of supported input resolutions for the selected TV 14598c2ecf20Sopenharmony_ci * format. 14608c2ecf20Sopenharmony_ci */ 14618c2ecf20Sopenharmony_ci format_map = 1 << psb_intel_sdvo->tv_format_index; 14628c2ecf20Sopenharmony_ci memcpy(&tv_res, &format_map, 14638c2ecf20Sopenharmony_ci min(sizeof(format_map), sizeof(struct psb_intel_sdvo_sdtv_resolution_request))); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_target_output(psb_intel_sdvo, psb_intel_sdvo->attached_output)) 14668c2ecf20Sopenharmony_ci return; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(tv_res) != 3); 14698c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_write_cmd(psb_intel_sdvo, 14708c2ecf20Sopenharmony_ci SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, 14718c2ecf20Sopenharmony_ci &tv_res, sizeof(tv_res))) 14728c2ecf20Sopenharmony_ci return; 14738c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_read_response(psb_intel_sdvo, &reply, 3)) 14748c2ecf20Sopenharmony_ci return; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sdvo_tv_modes); i++) 14778c2ecf20Sopenharmony_ci if (reply & (1 << i)) { 14788c2ecf20Sopenharmony_ci struct drm_display_mode *nmode; 14798c2ecf20Sopenharmony_ci nmode = drm_mode_duplicate(connector->dev, 14808c2ecf20Sopenharmony_ci &sdvo_tv_modes[i]); 14818c2ecf20Sopenharmony_ci if (nmode) 14828c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, nmode); 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci} 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_get_lvds_modes(struct drm_connector *connector) 14878c2ecf20Sopenharmony_ci{ 14888c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); 14898c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = connector->dev->dev_private; 14908c2ecf20Sopenharmony_ci struct drm_display_mode *newmode; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci /* 14938c2ecf20Sopenharmony_ci * Attempt to get the mode list from DDC. 14948c2ecf20Sopenharmony_ci * Assume that the preferred modes are 14958c2ecf20Sopenharmony_ci * arranged in priority order. 14968c2ecf20Sopenharmony_ci */ 14978c2ecf20Sopenharmony_ci psb_intel_ddc_get_modes(connector, psb_intel_sdvo->i2c); 14988c2ecf20Sopenharmony_ci if (list_empty(&connector->probed_modes) == false) 14998c2ecf20Sopenharmony_ci goto end; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci /* Fetch modes from VBT */ 15028c2ecf20Sopenharmony_ci if (dev_priv->sdvo_lvds_vbt_mode != NULL) { 15038c2ecf20Sopenharmony_ci newmode = drm_mode_duplicate(connector->dev, 15048c2ecf20Sopenharmony_ci dev_priv->sdvo_lvds_vbt_mode); 15058c2ecf20Sopenharmony_ci if (newmode != NULL) { 15068c2ecf20Sopenharmony_ci /* Guarantee the mode is preferred */ 15078c2ecf20Sopenharmony_ci newmode->type = (DRM_MODE_TYPE_PREFERRED | 15088c2ecf20Sopenharmony_ci DRM_MODE_TYPE_DRIVER); 15098c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, newmode); 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ciend: 15148c2ecf20Sopenharmony_ci list_for_each_entry(newmode, &connector->probed_modes, head) { 15158c2ecf20Sopenharmony_ci if (newmode->type & DRM_MODE_TYPE_PREFERRED) { 15168c2ecf20Sopenharmony_ci psb_intel_sdvo->sdvo_lvds_fixed_mode = 15178c2ecf20Sopenharmony_ci drm_mode_duplicate(connector->dev, newmode); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci drm_mode_set_crtcinfo(psb_intel_sdvo->sdvo_lvds_fixed_mode, 15208c2ecf20Sopenharmony_ci 0); 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci psb_intel_sdvo->is_lvds = true; 15238c2ecf20Sopenharmony_ci break; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci } 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci} 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_cistatic int psb_intel_sdvo_get_modes(struct drm_connector *connector) 15308c2ecf20Sopenharmony_ci{ 15318c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci if (IS_TV(psb_intel_sdvo_connector)) 15348c2ecf20Sopenharmony_ci psb_intel_sdvo_get_tv_modes(connector); 15358c2ecf20Sopenharmony_ci else if (IS_LVDS(psb_intel_sdvo_connector)) 15368c2ecf20Sopenharmony_ci psb_intel_sdvo_get_lvds_modes(connector); 15378c2ecf20Sopenharmony_ci else 15388c2ecf20Sopenharmony_ci psb_intel_sdvo_get_ddc_modes(connector); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci return !list_empty(&connector->probed_modes); 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_destroy(struct drm_connector *connector) 15448c2ecf20Sopenharmony_ci{ 15458c2ecf20Sopenharmony_ci drm_connector_unregister(connector); 15468c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 15478c2ecf20Sopenharmony_ci kfree(connector); 15488c2ecf20Sopenharmony_ci} 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_detect_hdmi_audio(struct drm_connector *connector) 15518c2ecf20Sopenharmony_ci{ 15528c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); 15538c2ecf20Sopenharmony_ci struct edid *edid; 15548c2ecf20Sopenharmony_ci bool has_audio = false; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci if (!psb_intel_sdvo->is_hdmi) 15578c2ecf20Sopenharmony_ci return false; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci edid = psb_intel_sdvo_get_edid(connector); 15608c2ecf20Sopenharmony_ci if (edid != NULL && edid->input & DRM_EDID_INPUT_DIGITAL) 15618c2ecf20Sopenharmony_ci has_audio = drm_detect_monitor_audio(edid); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci return has_audio; 15648c2ecf20Sopenharmony_ci} 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_cistatic int 15678c2ecf20Sopenharmony_cipsb_intel_sdvo_set_property(struct drm_connector *connector, 15688c2ecf20Sopenharmony_ci struct drm_property *property, 15698c2ecf20Sopenharmony_ci uint64_t val) 15708c2ecf20Sopenharmony_ci{ 15718c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); 15728c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector); 15738c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = connector->dev->dev_private; 15748c2ecf20Sopenharmony_ci uint16_t temp_value; 15758c2ecf20Sopenharmony_ci uint8_t cmd; 15768c2ecf20Sopenharmony_ci int ret; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci ret = drm_object_property_set_value(&connector->base, property, val); 15798c2ecf20Sopenharmony_ci if (ret) 15808c2ecf20Sopenharmony_ci return ret; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (property == dev_priv->force_audio_property) { 15838c2ecf20Sopenharmony_ci int i = val; 15848c2ecf20Sopenharmony_ci bool has_audio; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if (i == psb_intel_sdvo_connector->force_audio) 15878c2ecf20Sopenharmony_ci return 0; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->force_audio = i; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (i == 0) 15928c2ecf20Sopenharmony_ci has_audio = psb_intel_sdvo_detect_hdmi_audio(connector); 15938c2ecf20Sopenharmony_ci else 15948c2ecf20Sopenharmony_ci has_audio = i > 0; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if (has_audio == psb_intel_sdvo->has_hdmi_audio) 15978c2ecf20Sopenharmony_ci return 0; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci psb_intel_sdvo->has_hdmi_audio = has_audio; 16008c2ecf20Sopenharmony_ci goto done; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci if (property == dev_priv->broadcast_rgb_property) { 16048c2ecf20Sopenharmony_ci if (val == !!psb_intel_sdvo->color_range) 16058c2ecf20Sopenharmony_ci return 0; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci psb_intel_sdvo->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0; 16088c2ecf20Sopenharmony_ci goto done; 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci#define CHECK_PROPERTY(name, NAME) \ 16128c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->name == property) { \ 16138c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->cur_##name == temp_value) return 0; \ 16148c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->max_##name < temp_value) return -EINVAL; \ 16158c2ecf20Sopenharmony_ci cmd = SDVO_CMD_SET_##NAME; \ 16168c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->cur_##name = temp_value; \ 16178c2ecf20Sopenharmony_ci goto set_value; \ 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (property == psb_intel_sdvo_connector->tv_format) { 16218c2ecf20Sopenharmony_ci if (val >= ARRAY_SIZE(tv_format_names)) 16228c2ecf20Sopenharmony_ci return -EINVAL; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci if (psb_intel_sdvo->tv_format_index == 16258c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->tv_format_supported[val]) 16268c2ecf20Sopenharmony_ci return 0; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci psb_intel_sdvo->tv_format_index = psb_intel_sdvo_connector->tv_format_supported[val]; 16298c2ecf20Sopenharmony_ci goto done; 16308c2ecf20Sopenharmony_ci } else if (IS_TV_OR_LVDS(psb_intel_sdvo_connector)) { 16318c2ecf20Sopenharmony_ci temp_value = val; 16328c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->left == property) { 16338c2ecf20Sopenharmony_ci drm_object_property_set_value(&connector->base, 16348c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->right, val); 16358c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->left_margin == temp_value) 16368c2ecf20Sopenharmony_ci return 0; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left_margin = temp_value; 16398c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->right_margin = temp_value; 16408c2ecf20Sopenharmony_ci temp_value = psb_intel_sdvo_connector->max_hscan - 16418c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left_margin; 16428c2ecf20Sopenharmony_ci cmd = SDVO_CMD_SET_OVERSCAN_H; 16438c2ecf20Sopenharmony_ci goto set_value; 16448c2ecf20Sopenharmony_ci } else if (psb_intel_sdvo_connector->right == property) { 16458c2ecf20Sopenharmony_ci drm_object_property_set_value(&connector->base, 16468c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left, val); 16478c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->right_margin == temp_value) 16488c2ecf20Sopenharmony_ci return 0; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left_margin = temp_value; 16518c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->right_margin = temp_value; 16528c2ecf20Sopenharmony_ci temp_value = psb_intel_sdvo_connector->max_hscan - 16538c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left_margin; 16548c2ecf20Sopenharmony_ci cmd = SDVO_CMD_SET_OVERSCAN_H; 16558c2ecf20Sopenharmony_ci goto set_value; 16568c2ecf20Sopenharmony_ci } else if (psb_intel_sdvo_connector->top == property) { 16578c2ecf20Sopenharmony_ci drm_object_property_set_value(&connector->base, 16588c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->bottom, val); 16598c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->top_margin == temp_value) 16608c2ecf20Sopenharmony_ci return 0; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top_margin = temp_value; 16638c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->bottom_margin = temp_value; 16648c2ecf20Sopenharmony_ci temp_value = psb_intel_sdvo_connector->max_vscan - 16658c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top_margin; 16668c2ecf20Sopenharmony_ci cmd = SDVO_CMD_SET_OVERSCAN_V; 16678c2ecf20Sopenharmony_ci goto set_value; 16688c2ecf20Sopenharmony_ci } else if (psb_intel_sdvo_connector->bottom == property) { 16698c2ecf20Sopenharmony_ci drm_object_property_set_value(&connector->base, 16708c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top, val); 16718c2ecf20Sopenharmony_ci if (psb_intel_sdvo_connector->bottom_margin == temp_value) 16728c2ecf20Sopenharmony_ci return 0; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top_margin = temp_value; 16758c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->bottom_margin = temp_value; 16768c2ecf20Sopenharmony_ci temp_value = psb_intel_sdvo_connector->max_vscan - 16778c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top_margin; 16788c2ecf20Sopenharmony_ci cmd = SDVO_CMD_SET_OVERSCAN_V; 16798c2ecf20Sopenharmony_ci goto set_value; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci CHECK_PROPERTY(hpos, HPOS) 16828c2ecf20Sopenharmony_ci CHECK_PROPERTY(vpos, VPOS) 16838c2ecf20Sopenharmony_ci CHECK_PROPERTY(saturation, SATURATION) 16848c2ecf20Sopenharmony_ci CHECK_PROPERTY(contrast, CONTRAST) 16858c2ecf20Sopenharmony_ci CHECK_PROPERTY(hue, HUE) 16868c2ecf20Sopenharmony_ci CHECK_PROPERTY(brightness, BRIGHTNESS) 16878c2ecf20Sopenharmony_ci CHECK_PROPERTY(sharpness, SHARPNESS) 16888c2ecf20Sopenharmony_ci CHECK_PROPERTY(flicker_filter, FLICKER_FILTER) 16898c2ecf20Sopenharmony_ci CHECK_PROPERTY(flicker_filter_2d, FLICKER_FILTER_2D) 16908c2ecf20Sopenharmony_ci CHECK_PROPERTY(flicker_filter_adaptive, FLICKER_FILTER_ADAPTIVE) 16918c2ecf20Sopenharmony_ci CHECK_PROPERTY(tv_chroma_filter, TV_CHROMA_FILTER) 16928c2ecf20Sopenharmony_ci CHECK_PROPERTY(tv_luma_filter, TV_LUMA_FILTER) 16938c2ecf20Sopenharmony_ci CHECK_PROPERTY(dot_crawl, DOT_CRAWL) 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci return -EINVAL; /* unknown property */ 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ciset_value: 16998c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_value(psb_intel_sdvo, cmd, &temp_value, 2)) 17008c2ecf20Sopenharmony_ci return -EIO; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_cidone: 17048c2ecf20Sopenharmony_ci if (psb_intel_sdvo->base.base.crtc) { 17058c2ecf20Sopenharmony_ci struct drm_crtc *crtc = psb_intel_sdvo->base.base.crtc; 17068c2ecf20Sopenharmony_ci drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, 17078c2ecf20Sopenharmony_ci crtc->y, crtc->primary->fb); 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci return 0; 17118c2ecf20Sopenharmony_ci#undef CHECK_PROPERTY 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_save(struct drm_connector *connector) 17158c2ecf20Sopenharmony_ci{ 17168c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 17178c2ecf20Sopenharmony_ci struct gma_encoder *gma_encoder = gma_attached_encoder(connector); 17188c2ecf20Sopenharmony_ci struct psb_intel_sdvo *sdvo = to_psb_intel_sdvo(&gma_encoder->base); 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci sdvo->saveSDVO = REG_READ(sdvo->sdvo_reg); 17218c2ecf20Sopenharmony_ci} 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_restore(struct drm_connector *connector) 17248c2ecf20Sopenharmony_ci{ 17258c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 17268c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &gma_attached_encoder(connector)->base; 17278c2ecf20Sopenharmony_ci struct psb_intel_sdvo *sdvo = to_psb_intel_sdvo(encoder); 17288c2ecf20Sopenharmony_ci struct drm_crtc *crtc = encoder->crtc; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci REG_WRITE(sdvo->sdvo_reg, sdvo->saveSDVO); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci /* Force a full mode set on the crtc. We're supposed to have the 17338c2ecf20Sopenharmony_ci mode_config lock already. */ 17348c2ecf20Sopenharmony_ci if (connector->status == connector_status_connected) 17358c2ecf20Sopenharmony_ci drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, 17368c2ecf20Sopenharmony_ci NULL); 17378c2ecf20Sopenharmony_ci} 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs psb_intel_sdvo_helper_funcs = { 17408c2ecf20Sopenharmony_ci .dpms = psb_intel_sdvo_dpms, 17418c2ecf20Sopenharmony_ci .mode_fixup = psb_intel_sdvo_mode_fixup, 17428c2ecf20Sopenharmony_ci .prepare = gma_encoder_prepare, 17438c2ecf20Sopenharmony_ci .mode_set = psb_intel_sdvo_mode_set, 17448c2ecf20Sopenharmony_ci .commit = gma_encoder_commit, 17458c2ecf20Sopenharmony_ci}; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs psb_intel_sdvo_connector_funcs = { 17488c2ecf20Sopenharmony_ci .dpms = drm_helper_connector_dpms, 17498c2ecf20Sopenharmony_ci .detect = psb_intel_sdvo_detect, 17508c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 17518c2ecf20Sopenharmony_ci .set_property = psb_intel_sdvo_set_property, 17528c2ecf20Sopenharmony_ci .destroy = psb_intel_sdvo_destroy, 17538c2ecf20Sopenharmony_ci}; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs psb_intel_sdvo_connector_helper_funcs = { 17568c2ecf20Sopenharmony_ci .get_modes = psb_intel_sdvo_get_modes, 17578c2ecf20Sopenharmony_ci .mode_valid = psb_intel_sdvo_mode_valid, 17588c2ecf20Sopenharmony_ci .best_encoder = gma_best_encoder, 17598c2ecf20Sopenharmony_ci}; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_cistatic void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder) 17628c2ecf20Sopenharmony_ci{ 17638c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (psb_intel_sdvo->sdvo_lvds_fixed_mode != NULL) 17668c2ecf20Sopenharmony_ci drm_mode_destroy(encoder->dev, 17678c2ecf20Sopenharmony_ci psb_intel_sdvo->sdvo_lvds_fixed_mode); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci i2c_del_adapter(&psb_intel_sdvo->ddc); 17708c2ecf20Sopenharmony_ci gma_encoder_destroy(encoder); 17718c2ecf20Sopenharmony_ci} 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs psb_intel_sdvo_enc_funcs = { 17748c2ecf20Sopenharmony_ci .destroy = psb_intel_sdvo_enc_destroy, 17758c2ecf20Sopenharmony_ci}; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_cistatic void 17788c2ecf20Sopenharmony_cipsb_intel_sdvo_guess_ddc_bus(struct psb_intel_sdvo *sdvo) 17798c2ecf20Sopenharmony_ci{ 17808c2ecf20Sopenharmony_ci /* FIXME: At the moment, ddc_bus = 2 is the only thing that works. 17818c2ecf20Sopenharmony_ci * We need to figure out if this is true for all available poulsbo 17828c2ecf20Sopenharmony_ci * hardware, or if we need to fiddle with the guessing code above. 17838c2ecf20Sopenharmony_ci * The problem might go away if we can parse sdvo mappings from bios */ 17848c2ecf20Sopenharmony_ci sdvo->ddc_bus = 2; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci#if 0 17878c2ecf20Sopenharmony_ci uint16_t mask = 0; 17888c2ecf20Sopenharmony_ci unsigned int num_bits; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci /* Make a mask of outputs less than or equal to our own priority in the 17918c2ecf20Sopenharmony_ci * list. 17928c2ecf20Sopenharmony_ci */ 17938c2ecf20Sopenharmony_ci switch (sdvo->controlled_output) { 17948c2ecf20Sopenharmony_ci case SDVO_OUTPUT_LVDS1: 17958c2ecf20Sopenharmony_ci mask |= SDVO_OUTPUT_LVDS1; 17968c2ecf20Sopenharmony_ci case SDVO_OUTPUT_LVDS0: 17978c2ecf20Sopenharmony_ci mask |= SDVO_OUTPUT_LVDS0; 17988c2ecf20Sopenharmony_ci case SDVO_OUTPUT_TMDS1: 17998c2ecf20Sopenharmony_ci mask |= SDVO_OUTPUT_TMDS1; 18008c2ecf20Sopenharmony_ci case SDVO_OUTPUT_TMDS0: 18018c2ecf20Sopenharmony_ci mask |= SDVO_OUTPUT_TMDS0; 18028c2ecf20Sopenharmony_ci case SDVO_OUTPUT_RGB1: 18038c2ecf20Sopenharmony_ci mask |= SDVO_OUTPUT_RGB1; 18048c2ecf20Sopenharmony_ci case SDVO_OUTPUT_RGB0: 18058c2ecf20Sopenharmony_ci mask |= SDVO_OUTPUT_RGB0; 18068c2ecf20Sopenharmony_ci break; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci /* Count bits to find what number we are in the priority list. */ 18108c2ecf20Sopenharmony_ci mask &= sdvo->caps.output_flags; 18118c2ecf20Sopenharmony_ci num_bits = hweight16(mask); 18128c2ecf20Sopenharmony_ci /* If more than 3 outputs, default to DDC bus 3 for now. */ 18138c2ecf20Sopenharmony_ci if (num_bits > 3) 18148c2ecf20Sopenharmony_ci num_bits = 3; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci /* Corresponds to SDVO_CONTROL_BUS_DDCx */ 18178c2ecf20Sopenharmony_ci sdvo->ddc_bus = 1 << num_bits; 18188c2ecf20Sopenharmony_ci#endif 18198c2ecf20Sopenharmony_ci} 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci/** 18228c2ecf20Sopenharmony_ci * Choose the appropriate DDC bus for control bus switch command for this 18238c2ecf20Sopenharmony_ci * SDVO output based on the controlled output. 18248c2ecf20Sopenharmony_ci * 18258c2ecf20Sopenharmony_ci * DDC bus number assignment is in a priority order of RGB outputs, then TMDS 18268c2ecf20Sopenharmony_ci * outputs, then LVDS outputs. 18278c2ecf20Sopenharmony_ci */ 18288c2ecf20Sopenharmony_cistatic void 18298c2ecf20Sopenharmony_cipsb_intel_sdvo_select_ddc_bus(struct drm_psb_private *dev_priv, 18308c2ecf20Sopenharmony_ci struct psb_intel_sdvo *sdvo, u32 reg) 18318c2ecf20Sopenharmony_ci{ 18328c2ecf20Sopenharmony_ci struct sdvo_device_mapping *mapping; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci if (IS_SDVOB(reg)) 18358c2ecf20Sopenharmony_ci mapping = &(dev_priv->sdvo_mappings[0]); 18368c2ecf20Sopenharmony_ci else 18378c2ecf20Sopenharmony_ci mapping = &(dev_priv->sdvo_mappings[1]); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci if (mapping->initialized) 18408c2ecf20Sopenharmony_ci sdvo->ddc_bus = 1 << ((mapping->ddc_pin & 0xf0) >> 4); 18418c2ecf20Sopenharmony_ci else 18428c2ecf20Sopenharmony_ci psb_intel_sdvo_guess_ddc_bus(sdvo); 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_cistatic void 18468c2ecf20Sopenharmony_cipsb_intel_sdvo_select_i2c_bus(struct drm_psb_private *dev_priv, 18478c2ecf20Sopenharmony_ci struct psb_intel_sdvo *sdvo, u32 reg) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci struct sdvo_device_mapping *mapping; 18508c2ecf20Sopenharmony_ci u8 pin, speed; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci if (IS_SDVOB(reg)) 18538c2ecf20Sopenharmony_ci mapping = &dev_priv->sdvo_mappings[0]; 18548c2ecf20Sopenharmony_ci else 18558c2ecf20Sopenharmony_ci mapping = &dev_priv->sdvo_mappings[1]; 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci pin = GMBUS_PORT_DPB; 18588c2ecf20Sopenharmony_ci speed = GMBUS_RATE_1MHZ >> 8; 18598c2ecf20Sopenharmony_ci if (mapping->initialized) { 18608c2ecf20Sopenharmony_ci pin = mapping->i2c_pin; 18618c2ecf20Sopenharmony_ci speed = mapping->i2c_speed; 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (pin < GMBUS_NUM_PORTS) { 18658c2ecf20Sopenharmony_ci sdvo->i2c = &dev_priv->gmbus[pin].adapter; 18668c2ecf20Sopenharmony_ci gma_intel_gmbus_set_speed(sdvo->i2c, speed); 18678c2ecf20Sopenharmony_ci gma_intel_gmbus_force_bit(sdvo->i2c, true); 18688c2ecf20Sopenharmony_ci } else 18698c2ecf20Sopenharmony_ci sdvo->i2c = &dev_priv->gmbus[GMBUS_PORT_DPB].adapter; 18708c2ecf20Sopenharmony_ci} 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_cistatic bool 18738c2ecf20Sopenharmony_cipsb_intel_sdvo_is_hdmi_connector(struct psb_intel_sdvo *psb_intel_sdvo, int device) 18748c2ecf20Sopenharmony_ci{ 18758c2ecf20Sopenharmony_ci return psb_intel_sdvo_check_supp_encode(psb_intel_sdvo); 18768c2ecf20Sopenharmony_ci} 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_cistatic u8 18798c2ecf20Sopenharmony_cipsb_intel_sdvo_get_slave_addr(struct drm_device *dev, int sdvo_reg) 18808c2ecf20Sopenharmony_ci{ 18818c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 18828c2ecf20Sopenharmony_ci struct sdvo_device_mapping *my_mapping, *other_mapping; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci if (IS_SDVOB(sdvo_reg)) { 18858c2ecf20Sopenharmony_ci my_mapping = &dev_priv->sdvo_mappings[0]; 18868c2ecf20Sopenharmony_ci other_mapping = &dev_priv->sdvo_mappings[1]; 18878c2ecf20Sopenharmony_ci } else { 18888c2ecf20Sopenharmony_ci my_mapping = &dev_priv->sdvo_mappings[1]; 18898c2ecf20Sopenharmony_ci other_mapping = &dev_priv->sdvo_mappings[0]; 18908c2ecf20Sopenharmony_ci } 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci /* If the BIOS described our SDVO device, take advantage of it. */ 18938c2ecf20Sopenharmony_ci if (my_mapping->slave_addr) 18948c2ecf20Sopenharmony_ci return my_mapping->slave_addr; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci /* If the BIOS only described a different SDVO device, use the 18978c2ecf20Sopenharmony_ci * address that it isn't using. 18988c2ecf20Sopenharmony_ci */ 18998c2ecf20Sopenharmony_ci if (other_mapping->slave_addr) { 19008c2ecf20Sopenharmony_ci if (other_mapping->slave_addr == 0x70) 19018c2ecf20Sopenharmony_ci return 0x72; 19028c2ecf20Sopenharmony_ci else 19038c2ecf20Sopenharmony_ci return 0x70; 19048c2ecf20Sopenharmony_ci } 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci /* No SDVO device info is found for another DVO port, 19078c2ecf20Sopenharmony_ci * so use mapping assumption we had before BIOS parsing. 19088c2ecf20Sopenharmony_ci */ 19098c2ecf20Sopenharmony_ci if (IS_SDVOB(sdvo_reg)) 19108c2ecf20Sopenharmony_ci return 0x70; 19118c2ecf20Sopenharmony_ci else 19128c2ecf20Sopenharmony_ci return 0x72; 19138c2ecf20Sopenharmony_ci} 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_cistatic void 19168c2ecf20Sopenharmony_cipsb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector, 19178c2ecf20Sopenharmony_ci struct psb_intel_sdvo *encoder) 19188c2ecf20Sopenharmony_ci{ 19198c2ecf20Sopenharmony_ci drm_connector_init(encoder->base.base.dev, 19208c2ecf20Sopenharmony_ci &connector->base.base, 19218c2ecf20Sopenharmony_ci &psb_intel_sdvo_connector_funcs, 19228c2ecf20Sopenharmony_ci connector->base.base.connector_type); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci drm_connector_helper_add(&connector->base.base, 19258c2ecf20Sopenharmony_ci &psb_intel_sdvo_connector_helper_funcs); 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci connector->base.base.interlace_allowed = 0; 19288c2ecf20Sopenharmony_ci connector->base.base.doublescan_allowed = 0; 19298c2ecf20Sopenharmony_ci connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci connector->base.save = psb_intel_sdvo_save; 19328c2ecf20Sopenharmony_ci connector->base.restore = psb_intel_sdvo_restore; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci gma_connector_attach_encoder(&connector->base, &encoder->base); 19358c2ecf20Sopenharmony_ci drm_connector_register(&connector->base.base); 19368c2ecf20Sopenharmony_ci} 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_cistatic void 19398c2ecf20Sopenharmony_cipsb_intel_sdvo_add_hdmi_properties(struct psb_intel_sdvo_connector *connector) 19408c2ecf20Sopenharmony_ci{ 19418c2ecf20Sopenharmony_ci /* FIXME: We don't support HDMI at the moment 19428c2ecf20Sopenharmony_ci struct drm_device *dev = connector->base.base.dev; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci intel_attach_force_audio_property(&connector->base.base); 19458c2ecf20Sopenharmony_ci intel_attach_broadcast_rgb_property(&connector->base.base); 19468c2ecf20Sopenharmony_ci */ 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_cistatic bool 19508c2ecf20Sopenharmony_cipsb_intel_sdvo_dvi_init(struct psb_intel_sdvo *psb_intel_sdvo, int device) 19518c2ecf20Sopenharmony_ci{ 19528c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &psb_intel_sdvo->base.base; 19538c2ecf20Sopenharmony_ci struct drm_connector *connector; 19548c2ecf20Sopenharmony_ci struct gma_connector *intel_connector; 19558c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci psb_intel_sdvo_connector = kzalloc(sizeof(struct psb_intel_sdvo_connector), GFP_KERNEL); 19588c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector) 19598c2ecf20Sopenharmony_ci return false; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci if (device == 0) { 19628c2ecf20Sopenharmony_ci psb_intel_sdvo->controlled_output |= SDVO_OUTPUT_TMDS0; 19638c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->output_flag = SDVO_OUTPUT_TMDS0; 19648c2ecf20Sopenharmony_ci } else if (device == 1) { 19658c2ecf20Sopenharmony_ci psb_intel_sdvo->controlled_output |= SDVO_OUTPUT_TMDS1; 19668c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->output_flag = SDVO_OUTPUT_TMDS1; 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci intel_connector = &psb_intel_sdvo_connector->base; 19708c2ecf20Sopenharmony_ci connector = &intel_connector->base; 19718c2ecf20Sopenharmony_ci // connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; 19728c2ecf20Sopenharmony_ci encoder->encoder_type = DRM_MODE_ENCODER_TMDS; 19738c2ecf20Sopenharmony_ci connector->connector_type = DRM_MODE_CONNECTOR_DVID; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci if (psb_intel_sdvo_is_hdmi_connector(psb_intel_sdvo, device)) { 19768c2ecf20Sopenharmony_ci connector->connector_type = DRM_MODE_CONNECTOR_HDMIA; 19778c2ecf20Sopenharmony_ci psb_intel_sdvo->is_hdmi = true; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci psb_intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) | 19808c2ecf20Sopenharmony_ci (1 << INTEL_ANALOG_CLONE_BIT)); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci psb_intel_sdvo_connector_init(psb_intel_sdvo_connector, psb_intel_sdvo); 19838c2ecf20Sopenharmony_ci if (psb_intel_sdvo->is_hdmi) 19848c2ecf20Sopenharmony_ci psb_intel_sdvo_add_hdmi_properties(psb_intel_sdvo_connector); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci return true; 19878c2ecf20Sopenharmony_ci} 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_cistatic bool 19908c2ecf20Sopenharmony_cipsb_intel_sdvo_tv_init(struct psb_intel_sdvo *psb_intel_sdvo, int type) 19918c2ecf20Sopenharmony_ci{ 19928c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &psb_intel_sdvo->base.base; 19938c2ecf20Sopenharmony_ci struct drm_connector *connector; 19948c2ecf20Sopenharmony_ci struct gma_connector *intel_connector; 19958c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector; 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci psb_intel_sdvo_connector = kzalloc(sizeof(struct psb_intel_sdvo_connector), GFP_KERNEL); 19988c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector) 19998c2ecf20Sopenharmony_ci return false; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci intel_connector = &psb_intel_sdvo_connector->base; 20028c2ecf20Sopenharmony_ci connector = &intel_connector->base; 20038c2ecf20Sopenharmony_ci encoder->encoder_type = DRM_MODE_ENCODER_TVDAC; 20048c2ecf20Sopenharmony_ci connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci psb_intel_sdvo->controlled_output |= type; 20078c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->output_flag = type; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci psb_intel_sdvo->is_tv = true; 20108c2ecf20Sopenharmony_ci psb_intel_sdvo->base.needs_tv_clock = true; 20118c2ecf20Sopenharmony_ci psb_intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT; 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci psb_intel_sdvo_connector_init(psb_intel_sdvo_connector, psb_intel_sdvo); 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_tv_create_property(psb_intel_sdvo, psb_intel_sdvo_connector, type)) 20168c2ecf20Sopenharmony_ci goto err; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_create_enhance_property(psb_intel_sdvo, psb_intel_sdvo_connector)) 20198c2ecf20Sopenharmony_ci goto err; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci return true; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_cierr: 20248c2ecf20Sopenharmony_ci psb_intel_sdvo_destroy(connector); 20258c2ecf20Sopenharmony_ci return false; 20268c2ecf20Sopenharmony_ci} 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_cistatic bool 20298c2ecf20Sopenharmony_cipsb_intel_sdvo_analog_init(struct psb_intel_sdvo *psb_intel_sdvo, int device) 20308c2ecf20Sopenharmony_ci{ 20318c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &psb_intel_sdvo->base.base; 20328c2ecf20Sopenharmony_ci struct drm_connector *connector; 20338c2ecf20Sopenharmony_ci struct gma_connector *intel_connector; 20348c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci psb_intel_sdvo_connector = kzalloc(sizeof(struct psb_intel_sdvo_connector), GFP_KERNEL); 20378c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector) 20388c2ecf20Sopenharmony_ci return false; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci intel_connector = &psb_intel_sdvo_connector->base; 20418c2ecf20Sopenharmony_ci connector = &intel_connector->base; 20428c2ecf20Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_CONNECT; 20438c2ecf20Sopenharmony_ci encoder->encoder_type = DRM_MODE_ENCODER_DAC; 20448c2ecf20Sopenharmony_ci connector->connector_type = DRM_MODE_CONNECTOR_VGA; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci if (device == 0) { 20478c2ecf20Sopenharmony_ci psb_intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB0; 20488c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB0; 20498c2ecf20Sopenharmony_ci } else if (device == 1) { 20508c2ecf20Sopenharmony_ci psb_intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB1; 20518c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1; 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci psb_intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) | 20558c2ecf20Sopenharmony_ci (1 << INTEL_ANALOG_CLONE_BIT)); 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci psb_intel_sdvo_connector_init(psb_intel_sdvo_connector, 20588c2ecf20Sopenharmony_ci psb_intel_sdvo); 20598c2ecf20Sopenharmony_ci return true; 20608c2ecf20Sopenharmony_ci} 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_cistatic bool 20638c2ecf20Sopenharmony_cipsb_intel_sdvo_lvds_init(struct psb_intel_sdvo *psb_intel_sdvo, int device) 20648c2ecf20Sopenharmony_ci{ 20658c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &psb_intel_sdvo->base.base; 20668c2ecf20Sopenharmony_ci struct drm_connector *connector; 20678c2ecf20Sopenharmony_ci struct gma_connector *intel_connector; 20688c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector; 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci psb_intel_sdvo_connector = kzalloc(sizeof(struct psb_intel_sdvo_connector), GFP_KERNEL); 20718c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector) 20728c2ecf20Sopenharmony_ci return false; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci intel_connector = &psb_intel_sdvo_connector->base; 20758c2ecf20Sopenharmony_ci connector = &intel_connector->base; 20768c2ecf20Sopenharmony_ci encoder->encoder_type = DRM_MODE_ENCODER_LVDS; 20778c2ecf20Sopenharmony_ci connector->connector_type = DRM_MODE_CONNECTOR_LVDS; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci if (device == 0) { 20808c2ecf20Sopenharmony_ci psb_intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS0; 20818c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS0; 20828c2ecf20Sopenharmony_ci } else if (device == 1) { 20838c2ecf20Sopenharmony_ci psb_intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS1; 20848c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1; 20858c2ecf20Sopenharmony_ci } 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci psb_intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) | 20888c2ecf20Sopenharmony_ci (1 << INTEL_SDVO_LVDS_CLONE_BIT)); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci psb_intel_sdvo_connector_init(psb_intel_sdvo_connector, psb_intel_sdvo); 20918c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_create_enhance_property(psb_intel_sdvo, psb_intel_sdvo_connector)) 20928c2ecf20Sopenharmony_ci goto err; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci return true; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_cierr: 20978c2ecf20Sopenharmony_ci psb_intel_sdvo_destroy(connector); 20988c2ecf20Sopenharmony_ci return false; 20998c2ecf20Sopenharmony_ci} 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_cistatic bool 21028c2ecf20Sopenharmony_cipsb_intel_sdvo_output_setup(struct psb_intel_sdvo *psb_intel_sdvo, uint16_t flags) 21038c2ecf20Sopenharmony_ci{ 21048c2ecf20Sopenharmony_ci psb_intel_sdvo->is_tv = false; 21058c2ecf20Sopenharmony_ci psb_intel_sdvo->base.needs_tv_clock = false; 21068c2ecf20Sopenharmony_ci psb_intel_sdvo->is_lvds = false; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci /* SDVO requires XXX1 function may not exist unless it has XXX0 function.*/ 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci if (flags & SDVO_OUTPUT_TMDS0) 21118c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_dvi_init(psb_intel_sdvo, 0)) 21128c2ecf20Sopenharmony_ci return false; 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci if ((flags & SDVO_TMDS_MASK) == SDVO_TMDS_MASK) 21158c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_dvi_init(psb_intel_sdvo, 1)) 21168c2ecf20Sopenharmony_ci return false; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci /* TV has no XXX1 function block */ 21198c2ecf20Sopenharmony_ci if (flags & SDVO_OUTPUT_SVID0) 21208c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_tv_init(psb_intel_sdvo, SDVO_OUTPUT_SVID0)) 21218c2ecf20Sopenharmony_ci return false; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci if (flags & SDVO_OUTPUT_CVBS0) 21248c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_tv_init(psb_intel_sdvo, SDVO_OUTPUT_CVBS0)) 21258c2ecf20Sopenharmony_ci return false; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci if (flags & SDVO_OUTPUT_RGB0) 21288c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_analog_init(psb_intel_sdvo, 0)) 21298c2ecf20Sopenharmony_ci return false; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci if ((flags & SDVO_RGB_MASK) == SDVO_RGB_MASK) 21328c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_analog_init(psb_intel_sdvo, 1)) 21338c2ecf20Sopenharmony_ci return false; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci if (flags & SDVO_OUTPUT_LVDS0) 21368c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_lvds_init(psb_intel_sdvo, 0)) 21378c2ecf20Sopenharmony_ci return false; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci if ((flags & SDVO_LVDS_MASK) == SDVO_LVDS_MASK) 21408c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_lvds_init(psb_intel_sdvo, 1)) 21418c2ecf20Sopenharmony_ci return false; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci if ((flags & SDVO_OUTPUT_MASK) == 0) { 21448c2ecf20Sopenharmony_ci unsigned char bytes[2]; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci psb_intel_sdvo->controlled_output = 0; 21478c2ecf20Sopenharmony_ci memcpy(bytes, &psb_intel_sdvo->caps.output_flags, 2); 21488c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%s: Unknown SDVO output type (0x%02x%02x)\n", 21498c2ecf20Sopenharmony_ci SDVO_NAME(psb_intel_sdvo), 21508c2ecf20Sopenharmony_ci bytes[0], bytes[1]); 21518c2ecf20Sopenharmony_ci return false; 21528c2ecf20Sopenharmony_ci } 21538c2ecf20Sopenharmony_ci psb_intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci return true; 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_tv_create_property(struct psb_intel_sdvo *psb_intel_sdvo, 21598c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector, 21608c2ecf20Sopenharmony_ci int type) 21618c2ecf20Sopenharmony_ci{ 21628c2ecf20Sopenharmony_ci struct drm_device *dev = psb_intel_sdvo->base.base.dev; 21638c2ecf20Sopenharmony_ci struct psb_intel_sdvo_tv_format format; 21648c2ecf20Sopenharmony_ci uint32_t format_map, i; 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_target_output(psb_intel_sdvo, type)) 21678c2ecf20Sopenharmony_ci return false; 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(format) != 6); 21708c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, 21718c2ecf20Sopenharmony_ci SDVO_CMD_GET_SUPPORTED_TV_FORMATS, 21728c2ecf20Sopenharmony_ci &format, sizeof(format))) 21738c2ecf20Sopenharmony_ci return false; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci memcpy(&format_map, &format, min(sizeof(format_map), sizeof(format))); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci if (format_map == 0) 21788c2ecf20Sopenharmony_ci return false; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->format_supported_num = 0; 21818c2ecf20Sopenharmony_ci for (i = 0 ; i < ARRAY_SIZE(tv_format_names); i++) 21828c2ecf20Sopenharmony_ci if (format_map & (1 << i)) 21838c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->tv_format_supported[psb_intel_sdvo_connector->format_supported_num++] = i; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->tv_format = 21878c2ecf20Sopenharmony_ci drm_property_create(dev, DRM_MODE_PROP_ENUM, 21888c2ecf20Sopenharmony_ci "mode", psb_intel_sdvo_connector->format_supported_num); 21898c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector->tv_format) 21908c2ecf20Sopenharmony_ci return false; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci for (i = 0; i < psb_intel_sdvo_connector->format_supported_num; i++) 21938c2ecf20Sopenharmony_ci drm_property_add_enum( 21948c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->tv_format, 21958c2ecf20Sopenharmony_ci i, tv_format_names[psb_intel_sdvo_connector->tv_format_supported[i]]); 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci psb_intel_sdvo->tv_format_index = psb_intel_sdvo_connector->tv_format_supported[0]; 21988c2ecf20Sopenharmony_ci drm_object_attach_property(&psb_intel_sdvo_connector->base.base.base, 21998c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->tv_format, 0); 22008c2ecf20Sopenharmony_ci return true; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci} 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci#define ENHANCEMENT(name, NAME) do { \ 22058c2ecf20Sopenharmony_ci if (enhancements.name) { \ 22068c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_MAX_##NAME, &data_value, 4) || \ 22078c2ecf20Sopenharmony_ci !psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_##NAME, &response, 2)) \ 22088c2ecf20Sopenharmony_ci return false; \ 22098c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->max_##name = data_value[0]; \ 22108c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->cur_##name = response; \ 22118c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->name = \ 22128c2ecf20Sopenharmony_ci drm_property_create_range(dev, 0, #name, 0, data_value[0]); \ 22138c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector->name) return false; \ 22148c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, \ 22158c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->name, \ 22168c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->cur_##name); \ 22178c2ecf20Sopenharmony_ci DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \ 22188c2ecf20Sopenharmony_ci data_value[0], data_value[1], response); \ 22198c2ecf20Sopenharmony_ci } \ 22208c2ecf20Sopenharmony_ci} while(0) 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_cistatic bool 22238c2ecf20Sopenharmony_cipsb_intel_sdvo_create_enhance_property_tv(struct psb_intel_sdvo *psb_intel_sdvo, 22248c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector, 22258c2ecf20Sopenharmony_ci struct psb_intel_sdvo_enhancements_reply enhancements) 22268c2ecf20Sopenharmony_ci{ 22278c2ecf20Sopenharmony_ci struct drm_device *dev = psb_intel_sdvo->base.base.dev; 22288c2ecf20Sopenharmony_ci struct drm_connector *connector = &psb_intel_sdvo_connector->base.base; 22298c2ecf20Sopenharmony_ci uint16_t response, data_value[2]; 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci /* when horizontal overscan is supported, Add the left/right property */ 22328c2ecf20Sopenharmony_ci if (enhancements.overscan_h) { 22338c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, 22348c2ecf20Sopenharmony_ci SDVO_CMD_GET_MAX_OVERSCAN_H, 22358c2ecf20Sopenharmony_ci &data_value, 4)) 22368c2ecf20Sopenharmony_ci return false; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, 22398c2ecf20Sopenharmony_ci SDVO_CMD_GET_OVERSCAN_H, 22408c2ecf20Sopenharmony_ci &response, 2)) 22418c2ecf20Sopenharmony_ci return false; 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->max_hscan = data_value[0]; 22448c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left_margin = data_value[0] - response; 22458c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->right_margin = psb_intel_sdvo_connector->left_margin; 22468c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left = 22478c2ecf20Sopenharmony_ci drm_property_create_range(dev, 0, "left_margin", 0, data_value[0]); 22488c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector->left) 22498c2ecf20Sopenharmony_ci return false; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 22528c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left, 22538c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->left_margin); 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->right = 22568c2ecf20Sopenharmony_ci drm_property_create_range(dev, 0, "right_margin", 0, data_value[0]); 22578c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector->right) 22588c2ecf20Sopenharmony_ci return false; 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 22618c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->right, 22628c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->right_margin); 22638c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("h_overscan: max %d, " 22648c2ecf20Sopenharmony_ci "default %d, current %d\n", 22658c2ecf20Sopenharmony_ci data_value[0], data_value[1], response); 22668c2ecf20Sopenharmony_ci } 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci if (enhancements.overscan_v) { 22698c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, 22708c2ecf20Sopenharmony_ci SDVO_CMD_GET_MAX_OVERSCAN_V, 22718c2ecf20Sopenharmony_ci &data_value, 4)) 22728c2ecf20Sopenharmony_ci return false; 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, 22758c2ecf20Sopenharmony_ci SDVO_CMD_GET_OVERSCAN_V, 22768c2ecf20Sopenharmony_ci &response, 2)) 22778c2ecf20Sopenharmony_ci return false; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->max_vscan = data_value[0]; 22808c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top_margin = data_value[0] - response; 22818c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->bottom_margin = psb_intel_sdvo_connector->top_margin; 22828c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top = 22838c2ecf20Sopenharmony_ci drm_property_create_range(dev, 0, "top_margin", 0, data_value[0]); 22848c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector->top) 22858c2ecf20Sopenharmony_ci return false; 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 22888c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top, 22898c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->top_margin); 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->bottom = 22928c2ecf20Sopenharmony_ci drm_property_create_range(dev, 0, "bottom_margin", 0, data_value[0]); 22938c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector->bottom) 22948c2ecf20Sopenharmony_ci return false; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 22978c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->bottom, 22988c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->bottom_margin); 22998c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("v_overscan: max %d, " 23008c2ecf20Sopenharmony_ci "default %d, current %d\n", 23018c2ecf20Sopenharmony_ci data_value[0], data_value[1], response); 23028c2ecf20Sopenharmony_ci } 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci ENHANCEMENT(hpos, HPOS); 23058c2ecf20Sopenharmony_ci ENHANCEMENT(vpos, VPOS); 23068c2ecf20Sopenharmony_ci ENHANCEMENT(saturation, SATURATION); 23078c2ecf20Sopenharmony_ci ENHANCEMENT(contrast, CONTRAST); 23088c2ecf20Sopenharmony_ci ENHANCEMENT(hue, HUE); 23098c2ecf20Sopenharmony_ci ENHANCEMENT(sharpness, SHARPNESS); 23108c2ecf20Sopenharmony_ci ENHANCEMENT(brightness, BRIGHTNESS); 23118c2ecf20Sopenharmony_ci ENHANCEMENT(flicker_filter, FLICKER_FILTER); 23128c2ecf20Sopenharmony_ci ENHANCEMENT(flicker_filter_adaptive, FLICKER_FILTER_ADAPTIVE); 23138c2ecf20Sopenharmony_ci ENHANCEMENT(flicker_filter_2d, FLICKER_FILTER_2D); 23148c2ecf20Sopenharmony_ci ENHANCEMENT(tv_chroma_filter, TV_CHROMA_FILTER); 23158c2ecf20Sopenharmony_ci ENHANCEMENT(tv_luma_filter, TV_LUMA_FILTER); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci if (enhancements.dot_crawl) { 23188c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_value(psb_intel_sdvo, SDVO_CMD_GET_DOT_CRAWL, &response, 2)) 23198c2ecf20Sopenharmony_ci return false; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->max_dot_crawl = 1; 23228c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->cur_dot_crawl = response & 0x1; 23238c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->dot_crawl = 23248c2ecf20Sopenharmony_ci drm_property_create_range(dev, 0, "dot_crawl", 0, 1); 23258c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_connector->dot_crawl) 23268c2ecf20Sopenharmony_ci return false; 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 23298c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->dot_crawl, 23308c2ecf20Sopenharmony_ci psb_intel_sdvo_connector->cur_dot_crawl); 23318c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("dot crawl: current %d\n", response); 23328c2ecf20Sopenharmony_ci } 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci return true; 23358c2ecf20Sopenharmony_ci} 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_cistatic bool 23388c2ecf20Sopenharmony_cipsb_intel_sdvo_create_enhance_property_lvds(struct psb_intel_sdvo *psb_intel_sdvo, 23398c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector, 23408c2ecf20Sopenharmony_ci struct psb_intel_sdvo_enhancements_reply enhancements) 23418c2ecf20Sopenharmony_ci{ 23428c2ecf20Sopenharmony_ci struct drm_device *dev = psb_intel_sdvo->base.base.dev; 23438c2ecf20Sopenharmony_ci struct drm_connector *connector = &psb_intel_sdvo_connector->base.base; 23448c2ecf20Sopenharmony_ci uint16_t response, data_value[2]; 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci ENHANCEMENT(brightness, BRIGHTNESS); 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci return true; 23498c2ecf20Sopenharmony_ci} 23508c2ecf20Sopenharmony_ci#undef ENHANCEMENT 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_cistatic bool psb_intel_sdvo_create_enhance_property(struct psb_intel_sdvo *psb_intel_sdvo, 23538c2ecf20Sopenharmony_ci struct psb_intel_sdvo_connector *psb_intel_sdvo_connector) 23548c2ecf20Sopenharmony_ci{ 23558c2ecf20Sopenharmony_ci union { 23568c2ecf20Sopenharmony_ci struct psb_intel_sdvo_enhancements_reply reply; 23578c2ecf20Sopenharmony_ci uint16_t response; 23588c2ecf20Sopenharmony_ci } enhancements; 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(enhancements) != 2); 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci enhancements.response = 0; 23638c2ecf20Sopenharmony_ci psb_intel_sdvo_get_value(psb_intel_sdvo, 23648c2ecf20Sopenharmony_ci SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS, 23658c2ecf20Sopenharmony_ci &enhancements, sizeof(enhancements)); 23668c2ecf20Sopenharmony_ci if (enhancements.response == 0) { 23678c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("No enhancement is supported\n"); 23688c2ecf20Sopenharmony_ci return true; 23698c2ecf20Sopenharmony_ci } 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci if (IS_TV(psb_intel_sdvo_connector)) 23728c2ecf20Sopenharmony_ci return psb_intel_sdvo_create_enhance_property_tv(psb_intel_sdvo, psb_intel_sdvo_connector, enhancements.reply); 23738c2ecf20Sopenharmony_ci else if(IS_LVDS(psb_intel_sdvo_connector)) 23748c2ecf20Sopenharmony_ci return psb_intel_sdvo_create_enhance_property_lvds(psb_intel_sdvo, psb_intel_sdvo_connector, enhancements.reply); 23758c2ecf20Sopenharmony_ci else 23768c2ecf20Sopenharmony_ci return true; 23778c2ecf20Sopenharmony_ci} 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_cistatic int psb_intel_sdvo_ddc_proxy_xfer(struct i2c_adapter *adapter, 23808c2ecf20Sopenharmony_ci struct i2c_msg *msgs, 23818c2ecf20Sopenharmony_ci int num) 23828c2ecf20Sopenharmony_ci{ 23838c2ecf20Sopenharmony_ci struct psb_intel_sdvo *sdvo = adapter->algo_data; 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus)) 23868c2ecf20Sopenharmony_ci return -EIO; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci return sdvo->i2c->algo->master_xfer(sdvo->i2c, msgs, num); 23898c2ecf20Sopenharmony_ci} 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_cistatic u32 psb_intel_sdvo_ddc_proxy_func(struct i2c_adapter *adapter) 23928c2ecf20Sopenharmony_ci{ 23938c2ecf20Sopenharmony_ci struct psb_intel_sdvo *sdvo = adapter->algo_data; 23948c2ecf20Sopenharmony_ci return sdvo->i2c->algo->functionality(sdvo->i2c); 23958c2ecf20Sopenharmony_ci} 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_cistatic const struct i2c_algorithm psb_intel_sdvo_ddc_proxy = { 23988c2ecf20Sopenharmony_ci .master_xfer = psb_intel_sdvo_ddc_proxy_xfer, 23998c2ecf20Sopenharmony_ci .functionality = psb_intel_sdvo_ddc_proxy_func 24008c2ecf20Sopenharmony_ci}; 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_cistatic bool 24038c2ecf20Sopenharmony_cipsb_intel_sdvo_init_ddc_proxy(struct psb_intel_sdvo *sdvo, 24048c2ecf20Sopenharmony_ci struct drm_device *dev) 24058c2ecf20Sopenharmony_ci{ 24068c2ecf20Sopenharmony_ci sdvo->ddc.owner = THIS_MODULE; 24078c2ecf20Sopenharmony_ci sdvo->ddc.class = I2C_CLASS_DDC; 24088c2ecf20Sopenharmony_ci snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy"); 24098c2ecf20Sopenharmony_ci sdvo->ddc.dev.parent = &dev->pdev->dev; 24108c2ecf20Sopenharmony_ci sdvo->ddc.algo_data = sdvo; 24118c2ecf20Sopenharmony_ci sdvo->ddc.algo = &psb_intel_sdvo_ddc_proxy; 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci return i2c_add_adapter(&sdvo->ddc) == 0; 24148c2ecf20Sopenharmony_ci} 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_cibool psb_intel_sdvo_init(struct drm_device *dev, int sdvo_reg) 24178c2ecf20Sopenharmony_ci{ 24188c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 24198c2ecf20Sopenharmony_ci struct gma_encoder *gma_encoder; 24208c2ecf20Sopenharmony_ci struct psb_intel_sdvo *psb_intel_sdvo; 24218c2ecf20Sopenharmony_ci int i; 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci psb_intel_sdvo = kzalloc(sizeof(struct psb_intel_sdvo), GFP_KERNEL); 24248c2ecf20Sopenharmony_ci if (!psb_intel_sdvo) 24258c2ecf20Sopenharmony_ci return false; 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_ci psb_intel_sdvo->sdvo_reg = sdvo_reg; 24288c2ecf20Sopenharmony_ci psb_intel_sdvo->slave_addr = psb_intel_sdvo_get_slave_addr(dev, sdvo_reg) >> 1; 24298c2ecf20Sopenharmony_ci psb_intel_sdvo_select_i2c_bus(dev_priv, psb_intel_sdvo, sdvo_reg); 24308c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_init_ddc_proxy(psb_intel_sdvo, dev)) { 24318c2ecf20Sopenharmony_ci kfree(psb_intel_sdvo); 24328c2ecf20Sopenharmony_ci return false; 24338c2ecf20Sopenharmony_ci } 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci /* encoder type will be decided later */ 24368c2ecf20Sopenharmony_ci gma_encoder = &psb_intel_sdvo->base; 24378c2ecf20Sopenharmony_ci gma_encoder->type = INTEL_OUTPUT_SDVO; 24388c2ecf20Sopenharmony_ci drm_encoder_init(dev, &gma_encoder->base, &psb_intel_sdvo_enc_funcs, 24398c2ecf20Sopenharmony_ci 0, NULL); 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci /* Read the regs to test if we can talk to the device */ 24428c2ecf20Sopenharmony_ci for (i = 0; i < 0x40; i++) { 24438c2ecf20Sopenharmony_ci u8 byte; 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, i, &byte)) { 24468c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("No SDVO device found on SDVO%c\n", 24478c2ecf20Sopenharmony_ci IS_SDVOB(sdvo_reg) ? 'B' : 'C'); 24488c2ecf20Sopenharmony_ci goto err; 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci } 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci if (IS_SDVOB(sdvo_reg)) 24538c2ecf20Sopenharmony_ci dev_priv->hotplug_supported_mask |= SDVOB_HOTPLUG_INT_STATUS; 24548c2ecf20Sopenharmony_ci else 24558c2ecf20Sopenharmony_ci dev_priv->hotplug_supported_mask |= SDVOC_HOTPLUG_INT_STATUS; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci drm_encoder_helper_add(&gma_encoder->base, &psb_intel_sdvo_helper_funcs); 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci /* In default case sdvo lvds is false */ 24608c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_capabilities(psb_intel_sdvo, &psb_intel_sdvo->caps)) 24618c2ecf20Sopenharmony_ci goto err; 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci if (psb_intel_sdvo_output_setup(psb_intel_sdvo, 24648c2ecf20Sopenharmony_ci psb_intel_sdvo->caps.output_flags) != true) { 24658c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SDVO output failed to setup on SDVO%c\n", 24668c2ecf20Sopenharmony_ci IS_SDVOB(sdvo_reg) ? 'B' : 'C'); 24678c2ecf20Sopenharmony_ci goto err; 24688c2ecf20Sopenharmony_ci } 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci psb_intel_sdvo_select_ddc_bus(dev_priv, psb_intel_sdvo, sdvo_reg); 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci /* Set the input timing to the screen. Assume always input 0. */ 24738c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_set_target_input(psb_intel_sdvo)) 24748c2ecf20Sopenharmony_ci goto err; 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci if (!psb_intel_sdvo_get_input_pixel_clock_range(psb_intel_sdvo, 24778c2ecf20Sopenharmony_ci &psb_intel_sdvo->pixel_clock_min, 24788c2ecf20Sopenharmony_ci &psb_intel_sdvo->pixel_clock_max)) 24798c2ecf20Sopenharmony_ci goto err; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%s device VID/DID: %02X:%02X.%02X, " 24828c2ecf20Sopenharmony_ci "clock range %dMHz - %dMHz, " 24838c2ecf20Sopenharmony_ci "input 1: %c, input 2: %c, " 24848c2ecf20Sopenharmony_ci "output 1: %c, output 2: %c\n", 24858c2ecf20Sopenharmony_ci SDVO_NAME(psb_intel_sdvo), 24868c2ecf20Sopenharmony_ci psb_intel_sdvo->caps.vendor_id, psb_intel_sdvo->caps.device_id, 24878c2ecf20Sopenharmony_ci psb_intel_sdvo->caps.device_rev_id, 24888c2ecf20Sopenharmony_ci psb_intel_sdvo->pixel_clock_min / 1000, 24898c2ecf20Sopenharmony_ci psb_intel_sdvo->pixel_clock_max / 1000, 24908c2ecf20Sopenharmony_ci (psb_intel_sdvo->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N', 24918c2ecf20Sopenharmony_ci (psb_intel_sdvo->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N', 24928c2ecf20Sopenharmony_ci /* check currently supported outputs */ 24938c2ecf20Sopenharmony_ci psb_intel_sdvo->caps.output_flags & 24948c2ecf20Sopenharmony_ci (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N', 24958c2ecf20Sopenharmony_ci psb_intel_sdvo->caps.output_flags & 24968c2ecf20Sopenharmony_ci (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N'); 24978c2ecf20Sopenharmony_ci return true; 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_cierr: 25008c2ecf20Sopenharmony_ci drm_encoder_cleanup(&gma_encoder->base); 25018c2ecf20Sopenharmony_ci i2c_del_adapter(&psb_intel_sdvo->ddc); 25028c2ecf20Sopenharmony_ci kfree(psb_intel_sdvo); 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci return false; 25058c2ecf20Sopenharmony_ci} 2506