18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright © 2006 Intel Corporation
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
138c2ecf20Sopenharmony_ci * Software.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
218c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Authors:
248c2ecf20Sopenharmony_ci *    Eric Anholt <eric@anholt.net>
258c2ecf20Sopenharmony_ci *    Thomas Richter <thor@math.tu-berlin.de>
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * Minor modifications (Dithering enable):
288c2ecf20Sopenharmony_ci *    Thomas Richter <thor@math.tu-berlin.de>
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include "intel_display_types.h"
338c2ecf20Sopenharmony_ci#include "intel_dvo_dev.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * register definitions for the i82807aa.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * Documentation on this chipset can be found in datasheet #29069001 at
398c2ecf20Sopenharmony_ci * intel.com.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * VCH Revision & GMBus Base Addr
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ci#define VR00		0x00
468c2ecf20Sopenharmony_ci# define VR00_BASE_ADDRESS_MASK		0x007f
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Functionality Enable
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_ci#define VR01		0x01
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * Enable the panel fitter
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci# define VR01_PANEL_FIT_ENABLE		(1 << 3)
578c2ecf20Sopenharmony_ci/*
588c2ecf20Sopenharmony_ci * Enables the LCD display.
598c2ecf20Sopenharmony_ci *
608c2ecf20Sopenharmony_ci * This must not be set while VR01_DVO_BYPASS_ENABLE is set.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_ci# define VR01_LCD_ENABLE		(1 << 2)
638c2ecf20Sopenharmony_ci/* Enables the DVO repeater. */
648c2ecf20Sopenharmony_ci# define VR01_DVO_BYPASS_ENABLE		(1 << 1)
658c2ecf20Sopenharmony_ci/* Enables the DVO clock */
668c2ecf20Sopenharmony_ci# define VR01_DVO_ENABLE		(1 << 0)
678c2ecf20Sopenharmony_ci/* Enable dithering for 18bpp panels. Not documented. */
688c2ecf20Sopenharmony_ci# define VR01_DITHER_ENABLE             (1 << 4)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/*
718c2ecf20Sopenharmony_ci * LCD Interface Format
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_ci#define VR10		0x10
748c2ecf20Sopenharmony_ci/* Enables LVDS output instead of CMOS */
758c2ecf20Sopenharmony_ci# define VR10_LVDS_ENABLE		(1 << 4)
768c2ecf20Sopenharmony_ci/* Enables 18-bit LVDS output. */
778c2ecf20Sopenharmony_ci# define VR10_INTERFACE_1X18		(0 << 2)
788c2ecf20Sopenharmony_ci/* Enables 24-bit LVDS or CMOS output */
798c2ecf20Sopenharmony_ci# define VR10_INTERFACE_1X24		(1 << 2)
808c2ecf20Sopenharmony_ci/* Enables 2x18-bit LVDS or CMOS output. */
818c2ecf20Sopenharmony_ci# define VR10_INTERFACE_2X18		(2 << 2)
828c2ecf20Sopenharmony_ci/* Enables 2x24-bit LVDS output */
838c2ecf20Sopenharmony_ci# define VR10_INTERFACE_2X24		(3 << 2)
848c2ecf20Sopenharmony_ci/* Mask that defines the depth of the pipeline */
858c2ecf20Sopenharmony_ci# define VR10_INTERFACE_DEPTH_MASK      (3 << 2)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/*
888c2ecf20Sopenharmony_ci * VR20 LCD Horizontal Display Size
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_ci#define VR20	0x20
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/*
938c2ecf20Sopenharmony_ci * LCD Vertical Display Size
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci#define VR21	0x21
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/*
988c2ecf20Sopenharmony_ci * Panel power down status
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_ci#define VR30		0x30
1018c2ecf20Sopenharmony_ci/* Read only bit indicating that the panel is not in a safe poweroff state. */
1028c2ecf20Sopenharmony_ci# define VR30_PANEL_ON			(1 << 15)
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define VR40		0x40
1058c2ecf20Sopenharmony_ci# define VR40_STALL_ENABLE		(1 << 13)
1068c2ecf20Sopenharmony_ci# define VR40_VERTICAL_INTERP_ENABLE	(1 << 12)
1078c2ecf20Sopenharmony_ci# define VR40_ENHANCED_PANEL_FITTING	(1 << 11)
1088c2ecf20Sopenharmony_ci# define VR40_HORIZONTAL_INTERP_ENABLE	(1 << 10)
1098c2ecf20Sopenharmony_ci# define VR40_AUTO_RATIO_ENABLE		(1 << 9)
1108c2ecf20Sopenharmony_ci# define VR40_CLOCK_GATING_ENABLE	(1 << 8)
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci * Panel Fitting Vertical Ratio
1148c2ecf20Sopenharmony_ci * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2
1158c2ecf20Sopenharmony_ci */
1168c2ecf20Sopenharmony_ci#define VR41		0x41
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/*
1198c2ecf20Sopenharmony_ci * Panel Fitting Horizontal Ratio
1208c2ecf20Sopenharmony_ci * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_ci#define VR42		0x42
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/*
1258c2ecf20Sopenharmony_ci * Horizontal Image Size
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_ci#define VR43		0x43
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* VR80 GPIO 0
1308c2ecf20Sopenharmony_ci */
1318c2ecf20Sopenharmony_ci#define VR80	    0x80
1328c2ecf20Sopenharmony_ci#define VR81	    0x81
1338c2ecf20Sopenharmony_ci#define VR82	    0x82
1348c2ecf20Sopenharmony_ci#define VR83	    0x83
1358c2ecf20Sopenharmony_ci#define VR84	    0x84
1368c2ecf20Sopenharmony_ci#define VR85	    0x85
1378c2ecf20Sopenharmony_ci#define VR86	    0x86
1388c2ecf20Sopenharmony_ci#define VR87	    0x87
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/* VR88 GPIO 8
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_ci#define VR88	    0x88
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/* Graphics BIOS scratch 0
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_ci#define VR8E	    0x8E
1478c2ecf20Sopenharmony_ci# define VR8E_PANEL_TYPE_MASK		(0xf << 0)
1488c2ecf20Sopenharmony_ci# define VR8E_PANEL_INTERFACE_CMOS	(0 << 4)
1498c2ecf20Sopenharmony_ci# define VR8E_PANEL_INTERFACE_LVDS	(1 << 4)
1508c2ecf20Sopenharmony_ci# define VR8E_FORCE_DEFAULT_PANEL	(1 << 5)
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* Graphics BIOS scratch 1
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_ci#define VR8F	    0x8F
1558c2ecf20Sopenharmony_ci# define VR8F_VCH_PRESENT		(1 << 0)
1568c2ecf20Sopenharmony_ci# define VR8F_DISPLAY_CONN		(1 << 1)
1578c2ecf20Sopenharmony_ci# define VR8F_POWER_MASK		(0x3c)
1588c2ecf20Sopenharmony_ci# define VR8F_POWER_POS			(2)
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/* Some Bios implementations do not restore the DVO state upon
1618c2ecf20Sopenharmony_ci * resume from standby. Thus, this driver has to handle it
1628c2ecf20Sopenharmony_ci * instead. The following list contains all registers that
1638c2ecf20Sopenharmony_ci * require saving.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic const u16 backup_addresses[] = {
1668c2ecf20Sopenharmony_ci	0x11, 0x12,
1678c2ecf20Sopenharmony_ci	0x18, 0x19, 0x1a, 0x1f,
1688c2ecf20Sopenharmony_ci	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
1698c2ecf20Sopenharmony_ci	0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
1708c2ecf20Sopenharmony_ci	0x8e, 0x8f,
1718c2ecf20Sopenharmony_ci	0x10		/* this must come last */
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistruct ivch_priv {
1768c2ecf20Sopenharmony_ci	bool quiet;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	u16 width, height;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* Register backup */
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	u16 reg_backup[ARRAY_SIZE(backup_addresses)];
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic void ivch_dump_regs(struct intel_dvo_device *dvo);
1878c2ecf20Sopenharmony_ci/*
1888c2ecf20Sopenharmony_ci * Reads a register on the ivch.
1898c2ecf20Sopenharmony_ci *
1908c2ecf20Sopenharmony_ci * Each of the 256 registers are 16 bits long.
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_cistatic bool ivch_read(struct intel_dvo_device *dvo, int addr, u16 *data)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct ivch_priv *priv = dvo->dev_priv;
1958c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = dvo->i2c_bus;
1968c2ecf20Sopenharmony_ci	u8 out_buf[1];
1978c2ecf20Sopenharmony_ci	u8 in_buf[2];
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	struct i2c_msg msgs[] = {
2008c2ecf20Sopenharmony_ci		{
2018c2ecf20Sopenharmony_ci			.addr = dvo->slave_addr,
2028c2ecf20Sopenharmony_ci			.flags = I2C_M_RD,
2038c2ecf20Sopenharmony_ci			.len = 0,
2048c2ecf20Sopenharmony_ci		},
2058c2ecf20Sopenharmony_ci		{
2068c2ecf20Sopenharmony_ci			.addr = 0,
2078c2ecf20Sopenharmony_ci			.flags = I2C_M_NOSTART,
2088c2ecf20Sopenharmony_ci			.len = 1,
2098c2ecf20Sopenharmony_ci			.buf = out_buf,
2108c2ecf20Sopenharmony_ci		},
2118c2ecf20Sopenharmony_ci		{
2128c2ecf20Sopenharmony_ci			.addr = dvo->slave_addr,
2138c2ecf20Sopenharmony_ci			.flags = I2C_M_RD | I2C_M_NOSTART,
2148c2ecf20Sopenharmony_ci			.len = 2,
2158c2ecf20Sopenharmony_ci			.buf = in_buf,
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci	};
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	out_buf[0] = addr;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (i2c_transfer(adapter, msgs, 3) == 3) {
2228c2ecf20Sopenharmony_ci		*data = (in_buf[1] << 8) | in_buf[0];
2238c2ecf20Sopenharmony_ci		return true;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (!priv->quiet) {
2278c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("Unable to read register 0x%02x from "
2288c2ecf20Sopenharmony_ci				"%s:%02x.\n",
2298c2ecf20Sopenharmony_ci			  addr, adapter->name, dvo->slave_addr);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci	return false;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci/* Writes a 16-bit register on the ivch */
2358c2ecf20Sopenharmony_cistatic bool ivch_write(struct intel_dvo_device *dvo, int addr, u16 data)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct ivch_priv *priv = dvo->dev_priv;
2388c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = dvo->i2c_bus;
2398c2ecf20Sopenharmony_ci	u8 out_buf[3];
2408c2ecf20Sopenharmony_ci	struct i2c_msg msg = {
2418c2ecf20Sopenharmony_ci		.addr = dvo->slave_addr,
2428c2ecf20Sopenharmony_ci		.flags = 0,
2438c2ecf20Sopenharmony_ci		.len = 3,
2448c2ecf20Sopenharmony_ci		.buf = out_buf,
2458c2ecf20Sopenharmony_ci	};
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	out_buf[0] = addr;
2488c2ecf20Sopenharmony_ci	out_buf[1] = data & 0xff;
2498c2ecf20Sopenharmony_ci	out_buf[2] = data >> 8;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (i2c_transfer(adapter, &msg, 1) == 1)
2528c2ecf20Sopenharmony_ci		return true;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (!priv->quiet) {
2558c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
2568c2ecf20Sopenharmony_ci			  addr, adapter->name, dvo->slave_addr);
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return false;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/* Probes the given bus and slave address for an ivch */
2638c2ecf20Sopenharmony_cistatic bool ivch_init(struct intel_dvo_device *dvo,
2648c2ecf20Sopenharmony_ci		      struct i2c_adapter *adapter)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct ivch_priv *priv;
2678c2ecf20Sopenharmony_ci	u16 temp;
2688c2ecf20Sopenharmony_ci	int i;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
2718c2ecf20Sopenharmony_ci	if (priv == NULL)
2728c2ecf20Sopenharmony_ci		return false;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	dvo->i2c_bus = adapter;
2758c2ecf20Sopenharmony_ci	dvo->dev_priv = priv;
2768c2ecf20Sopenharmony_ci	priv->quiet = true;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (!ivch_read(dvo, VR00, &temp))
2798c2ecf20Sopenharmony_ci		goto out;
2808c2ecf20Sopenharmony_ci	priv->quiet = false;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/* Since the identification bits are probably zeroes, which doesn't seem
2838c2ecf20Sopenharmony_ci	 * very unique, check that the value in the base address field matches
2848c2ecf20Sopenharmony_ci	 * the address it's responding on.
2858c2ecf20Sopenharmony_ci	 */
2868c2ecf20Sopenharmony_ci	if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
2878c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("ivch detect failed due to address mismatch "
2888c2ecf20Sopenharmony_ci			  "(%d vs %d)\n",
2898c2ecf20Sopenharmony_ci			  (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
2908c2ecf20Sopenharmony_ci		goto out;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	ivch_read(dvo, VR20, &priv->width);
2948c2ecf20Sopenharmony_ci	ivch_read(dvo, VR21, &priv->height);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* Make a backup of the registers to be able to restore them
2978c2ecf20Sopenharmony_ci	 * upon suspend.
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(backup_addresses); i++)
3008c2ecf20Sopenharmony_ci		ivch_read(dvo, backup_addresses[i], priv->reg_backup + i);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	ivch_dump_regs(dvo);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return true;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ciout:
3078c2ecf20Sopenharmony_ci	kfree(priv);
3088c2ecf20Sopenharmony_ci	return false;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	return connector_status_connected;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
3178c2ecf20Sopenharmony_ci					    struct drm_display_mode *mode)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	if (mode->clock > 112000)
3208c2ecf20Sopenharmony_ci		return MODE_CLOCK_HIGH;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return MODE_OK;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci/* Restore the DVO registers after a resume
3268c2ecf20Sopenharmony_ci * from RAM. Registers have been saved during
3278c2ecf20Sopenharmony_ci * the initialization.
3288c2ecf20Sopenharmony_ci */
3298c2ecf20Sopenharmony_cistatic void ivch_reset(struct intel_dvo_device *dvo)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct ivch_priv *priv = dvo->dev_priv;
3328c2ecf20Sopenharmony_ci	int i;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("Resetting the IVCH registers\n");
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	ivch_write(dvo, VR10, 0x0000);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(backup_addresses); i++)
3398c2ecf20Sopenharmony_ci		ivch_write(dvo, backup_addresses[i], priv->reg_backup[i]);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/* Sets the power state of the panel connected to the ivch */
3438c2ecf20Sopenharmony_cistatic void ivch_dpms(struct intel_dvo_device *dvo, bool enable)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	int i;
3468c2ecf20Sopenharmony_ci	u16 vr01, vr30, backlight;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ivch_reset(dvo);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/* Set the new power state of the panel. */
3518c2ecf20Sopenharmony_ci	if (!ivch_read(dvo, VR01, &vr01))
3528c2ecf20Sopenharmony_ci		return;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (enable)
3558c2ecf20Sopenharmony_ci		backlight = 1;
3568c2ecf20Sopenharmony_ci	else
3578c2ecf20Sopenharmony_ci		backlight = 0;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	ivch_write(dvo, VR80, backlight);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (enable)
3628c2ecf20Sopenharmony_ci		vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
3638c2ecf20Sopenharmony_ci	else
3648c2ecf20Sopenharmony_ci		vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	ivch_write(dvo, VR01, vr01);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* Wait for the panel to make its state transition */
3698c2ecf20Sopenharmony_ci	for (i = 0; i < 100; i++) {
3708c2ecf20Sopenharmony_ci		if (!ivch_read(dvo, VR30, &vr30))
3718c2ecf20Sopenharmony_ci			break;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		if (((vr30 & VR30_PANEL_ON) != 0) == enable)
3748c2ecf20Sopenharmony_ci			break;
3758c2ecf20Sopenharmony_ci		udelay(1000);
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	/* wait some more; vch may fail to resync sometimes without this */
3788c2ecf20Sopenharmony_ci	udelay(16 * 1000);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic bool ivch_get_hw_state(struct intel_dvo_device *dvo)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	u16 vr01;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	ivch_reset(dvo);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* Set the new power state of the panel. */
3888c2ecf20Sopenharmony_ci	if (!ivch_read(dvo, VR01, &vr01))
3898c2ecf20Sopenharmony_ci		return false;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (vr01 & VR01_LCD_ENABLE)
3928c2ecf20Sopenharmony_ci		return true;
3938c2ecf20Sopenharmony_ci	else
3948c2ecf20Sopenharmony_ci		return false;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic void ivch_mode_set(struct intel_dvo_device *dvo,
3988c2ecf20Sopenharmony_ci			  const struct drm_display_mode *mode,
3998c2ecf20Sopenharmony_ci			  const struct drm_display_mode *adjusted_mode)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct ivch_priv *priv = dvo->dev_priv;
4028c2ecf20Sopenharmony_ci	u16 vr40 = 0;
4038c2ecf20Sopenharmony_ci	u16 vr01 = 0;
4048c2ecf20Sopenharmony_ci	u16 vr10;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	ivch_reset(dvo);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	vr10 = priv->reg_backup[ARRAY_SIZE(backup_addresses) - 1];
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* Enable dithering for 18 bpp pipelines */
4118c2ecf20Sopenharmony_ci	vr10 &= VR10_INTERFACE_DEPTH_MASK;
4128c2ecf20Sopenharmony_ci	if (vr10 == VR10_INTERFACE_2X18 || vr10 == VR10_INTERFACE_1X18)
4138c2ecf20Sopenharmony_ci		vr01 = VR01_DITHER_ENABLE;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
4168c2ecf20Sopenharmony_ci		VR40_HORIZONTAL_INTERP_ENABLE);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (mode->hdisplay != adjusted_mode->crtc_hdisplay ||
4198c2ecf20Sopenharmony_ci	    mode->vdisplay != adjusted_mode->crtc_vdisplay) {
4208c2ecf20Sopenharmony_ci		u16 x_ratio, y_ratio;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		vr01 |= VR01_PANEL_FIT_ENABLE;
4238c2ecf20Sopenharmony_ci		vr40 |= VR40_CLOCK_GATING_ENABLE;
4248c2ecf20Sopenharmony_ci		x_ratio = (((mode->hdisplay - 1) << 16) /
4258c2ecf20Sopenharmony_ci			   (adjusted_mode->crtc_hdisplay - 1)) >> 2;
4268c2ecf20Sopenharmony_ci		y_ratio = (((mode->vdisplay - 1) << 16) /
4278c2ecf20Sopenharmony_ci			   (adjusted_mode->crtc_vdisplay - 1)) >> 2;
4288c2ecf20Sopenharmony_ci		ivch_write(dvo, VR42, x_ratio);
4298c2ecf20Sopenharmony_ci		ivch_write(dvo, VR41, y_ratio);
4308c2ecf20Sopenharmony_ci	} else {
4318c2ecf20Sopenharmony_ci		vr01 &= ~VR01_PANEL_FIT_ENABLE;
4328c2ecf20Sopenharmony_ci		vr40 &= ~VR40_CLOCK_GATING_ENABLE;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci	vr40 &= ~VR40_AUTO_RATIO_ENABLE;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	ivch_write(dvo, VR01, vr01);
4378c2ecf20Sopenharmony_ci	ivch_write(dvo, VR40, vr40);
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic void ivch_dump_regs(struct intel_dvo_device *dvo)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	u16 val;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	ivch_read(dvo, VR00, &val);
4458c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR00: 0x%04x\n", val);
4468c2ecf20Sopenharmony_ci	ivch_read(dvo, VR01, &val);
4478c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR01: 0x%04x\n", val);
4488c2ecf20Sopenharmony_ci	ivch_read(dvo, VR10, &val);
4498c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR10: 0x%04x\n", val);
4508c2ecf20Sopenharmony_ci	ivch_read(dvo, VR30, &val);
4518c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR30: 0x%04x\n", val);
4528c2ecf20Sopenharmony_ci	ivch_read(dvo, VR40, &val);
4538c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR40: 0x%04x\n", val);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/* GPIO registers */
4568c2ecf20Sopenharmony_ci	ivch_read(dvo, VR80, &val);
4578c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR80: 0x%04x\n", val);
4588c2ecf20Sopenharmony_ci	ivch_read(dvo, VR81, &val);
4598c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR81: 0x%04x\n", val);
4608c2ecf20Sopenharmony_ci	ivch_read(dvo, VR82, &val);
4618c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR82: 0x%04x\n", val);
4628c2ecf20Sopenharmony_ci	ivch_read(dvo, VR83, &val);
4638c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR83: 0x%04x\n", val);
4648c2ecf20Sopenharmony_ci	ivch_read(dvo, VR84, &val);
4658c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR84: 0x%04x\n", val);
4668c2ecf20Sopenharmony_ci	ivch_read(dvo, VR85, &val);
4678c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR85: 0x%04x\n", val);
4688c2ecf20Sopenharmony_ci	ivch_read(dvo, VR86, &val);
4698c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR86: 0x%04x\n", val);
4708c2ecf20Sopenharmony_ci	ivch_read(dvo, VR87, &val);
4718c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR87: 0x%04x\n", val);
4728c2ecf20Sopenharmony_ci	ivch_read(dvo, VR88, &val);
4738c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR88: 0x%04x\n", val);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/* Scratch register 0 - AIM Panel type */
4768c2ecf20Sopenharmony_ci	ivch_read(dvo, VR8E, &val);
4778c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR8E: 0x%04x\n", val);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* Scratch register 1 - Status register */
4808c2ecf20Sopenharmony_ci	ivch_read(dvo, VR8F, &val);
4818c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VR8F: 0x%04x\n", val);
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic void ivch_destroy(struct intel_dvo_device *dvo)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct ivch_priv *priv = dvo->dev_priv;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (priv) {
4898c2ecf20Sopenharmony_ci		kfree(priv);
4908c2ecf20Sopenharmony_ci		dvo->dev_priv = NULL;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ciconst struct intel_dvo_dev_ops ivch_ops = {
4958c2ecf20Sopenharmony_ci	.init = ivch_init,
4968c2ecf20Sopenharmony_ci	.dpms = ivch_dpms,
4978c2ecf20Sopenharmony_ci	.get_hw_state = ivch_get_hw_state,
4988c2ecf20Sopenharmony_ci	.mode_valid = ivch_mode_valid,
4998c2ecf20Sopenharmony_ci	.mode_set = ivch_mode_set,
5008c2ecf20Sopenharmony_ci	.detect = ivch_detect,
5018c2ecf20Sopenharmony_ci	.dump_regs = ivch_dump_regs,
5028c2ecf20Sopenharmony_ci	.destroy = ivch_destroy,
5038c2ecf20Sopenharmony_ci};
504