/*
*/
/*
* Copyright (c) 2006-2013 Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*
*/
/** @file
* Integrated TV-out support for the 915GM and 945GM.
*/
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_edid.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
enum tv_margin {
};
/** Private structure for the integrated TV support */
struct intel_tv {
int type;
const char *tv_format;
};
struct video_levels {
};
struct color_conversion {
};
0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
0x28003100, 0x28002F00, 0x00003100, 0x36403000,
0x2D002CC0, 0x30003640, 0x2D0036C0,
0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
0x28003100, 0x28002F00, 0x00003100,
};
/*
* Color conversion values have 3 separate fixed point formats:
*
* 10 bit fields (ay, au)
* 1.9 fixed point (b.bbbbbbbbb)
* 11 bit fields (ry, by, ru, gu, gv)
* exp.mantissa (ee.mmmmmmmmm)
* ee = 00 = 10^-1 (0.mmmmmmmmm)
* ee = 01 = 10^-2 (0.0mmmmmmmmm)
* ee = 10 = 10^-3 (0.00mmmmmmmmm)
* ee = 11 = 10^-4 (0.000mmmmmmmmm)
* 12 bit fields (gy, rv, bu)
* exp.mantissa (eee.mmmmmmmmm)
* eee = 000 = 10^-1 (0.mmmmmmmmm)
* eee = 001 = 10^-2 (0.0mmmmmmmmm)
* eee = 010 = 10^-3 (0.00mmmmmmmmm)
* eee = 011 = 10^-4 (0.000mmmmmmmmm)
* eee = 100 = reserved
* eee = 101 = reserved
* eee = 110 = reserved
* eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
*
* Saturation and contrast are 8 bits, with their own representation:
* 8 bit field (saturation, contrast)
* exp.mantissa (ee.mmmmmm)
* ee = 00 = 10^-1 (0.mmmmmm)
* ee = 01 = 10^0 (m.mmmmm)
* ee = 10 = 10^1 (mm.mmmm)
* ee = 11 = 10^2 (mmm.mmm)
*
* Simple conversion function:
*
* static u32
* float_to_csc_11(float f)
* {
* u32 exp;
* u32 mant;
* u32 ret;
*
* if (f < 0)
* f = -f;
*
* if (f >= 1) {
* exp = 0x7;
* mant = 1 << 8;
* } else {
* for (exp = 0; exp < 3 && f < 0.5; exp++)
* f *= 2.0;
* mant = (f * (1 << 9) + 0.5);
* if (mant >= (1 << 9))
* mant = (1 << 9) - 1;
* }
* ret = (exp << 9) | mant;
* return ret;
* }
*/
/*
* Behold, magic numbers! If we plant them they might grow a big
* s-video cable to the sky... or something.
*
* Pre-converted to appropriate hex value.
*/
/*
* PAL & NTSC values for composite & s-video connections
*/
};
};
};
};
};
};
};
};
};
};
};
};
};
};
};
};
};
};
};
};
/*
* Component connections
*/
};
};
};
};
};
struct tv_mode {
const char *name;
int clock;
bool veq_ena;
bool burst_ena;
/*
* subcarrier programming
*/
bool pal_burst;
/*
*/
int max_srcw;
};
/*
* Sub carrier DDA
*
* I think this works as follows:
*
* subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
*
* Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
*
* So,
* dda1_ideal = subcarrier/pixel * 4096
* dda1_inc = floor (dda1_ideal)
* dda2 = dda1_ideal - dda1_inc
*
* then pick a ratio for dda2 that gives the closest approximation. If
* you can't get close enough, you can play with dda3 as well. This
* seems likely to happen when dda2 is small as the jumps would be larger
*
* To invert this,
*
* pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
*
* The constants below were all computed using a 107.520MHz clock
*/
/**
* Register programming values for TV modes.
*
* These values account for -1s required.
*/
{
.name = "NTSC-M",
.clock = 108000,
.refresh = 59940,
.component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
.progressive = false, .trilevel_sync = false,
.vsync_len = 6,
.veq_ena = true, .veq_start_f1 = 0,
.nbr_end = 240,
.burst_ena = true,
/* desired 3.5800000 actual 3.5800000 clock 107.52 */
.dda1_inc = 135,
.pal_burst = false,
},
{
.name = "NTSC-443",
.clock = 108000,
.refresh = 59940,
.component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
.progressive = false, .trilevel_sync = false,
.vsync_len = 6,
.veq_ena = true, .veq_start_f1 = 0,
.nbr_end = 240,
.burst_ena = true,
/* desired 4.4336180 actual 4.4336180 clock 107.52 */
.dda1_inc = 168,
.pal_burst = false,
},
{
.name = "NTSC-J",
.clock = 108000,
.refresh = 59940,
.component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
.progressive = false, .trilevel_sync = false,
.vsync_len = 6,
.veq_ena = true, .veq_start_f1 = 0,
.nbr_end = 240,
.burst_ena = true,
/* desired 3.5800000 actual 3.5800000 clock 107.52 */
.dda1_inc = 135,
.pal_burst = false,
},
{
.name = "PAL-M",
.clock = 108000,
.refresh = 59940,
.component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
.progressive = false, .trilevel_sync = false,
.vsync_len = 6,
.veq_ena = true, .veq_start_f1 = 0,
.nbr_end = 240,
.burst_ena = true,
/* desired 3.5800000 actual 3.5800000 clock 107.52 */
.dda1_inc = 135,
.pal_burst = true,
},
{
/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
.name = "PAL-N",
.clock = 108000,
.refresh = 50000,
.component_only = 0,
.progressive = false, .trilevel_sync = false,
.vsync_len = 6,
.veq_ena = true, .veq_start_f1 = 0,
.nbr_end = 286,
.burst_ena = true,
/* desired 4.4336180 actual 4.4336180 clock 107.52 */
.dda1_inc = 135,
.pal_burst = true,
},
{
/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
.name = "PAL",
.clock = 108000,
.refresh = 50000,
.component_only = 0,
.progressive = false, .trilevel_sync = false,
.vsync_len = 5,
.veq_ena = true, .veq_start_f1 = 0,
.nbr_end = 286,
.burst_ena = true,
/* desired 4.4336180 actual 4.4336180 clock 107.52 */
.dda1_inc = 168,
.pal_burst = true,
},
{
.name = "480p",
.clock = 107520,
.refresh = 59940,
.component_only = 1,
.progressive = true, .trilevel_sync = false,
.vsync_len = 12,
.veq_ena = false,
.nbr_end = 479,
.burst_ena = false,
},
{
.name = "576p",
.clock = 107520,
.refresh = 50000,
.component_only = 1,
.progressive = true, .trilevel_sync = false,
.vsync_len = 10,
.veq_ena = false,
.nbr_end = 575,
.burst_ena = false,
},
{
.name = "720p@60Hz",
.clock = 148800,
.refresh = 60000,
.component_only = 1,
.progressive = true, .trilevel_sync = true,
.vsync_len = 10,
.veq_ena = false,
.nbr_end = 719,
.burst_ena = false,
},
{
.name = "720p@50Hz",
.clock = 148800,
.refresh = 50000,
.component_only = 1,
.progressive = true, .trilevel_sync = true,
.vsync_len = 10,
.veq_ena = false,
.nbr_end = 719,
.burst_ena = false,
.max_srcw = 800
},
{
.name = "1080i@50Hz",
.clock = 148800,
.refresh = 50000,
.component_only = 1,
.progressive = false, .trilevel_sync = true,
.vsync_len = 10,
.nbr_end = 539,
.burst_ena = false,
},
{
.name = "1080i@60Hz",
.clock = 148800,
.refresh = 60000,
.component_only = 1,
.progressive = false, .trilevel_sync = true,
.vsync_len = 10,
.nbr_end = 539,
.burst_ena = false,
},
};
{
}
{
struct intel_tv,
base);
}
static bool
{
if (!(tmp & TV_ENC_ENABLE))
return false;
return true;
}
static void
{
}
static void
{
}
static const struct tv_mode *
{
int i;
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
return tv_mode;
}
return NULL;
}
static const struct tv_mode *
{
}
static int /* OSOL_i915 */
struct drm_display_mode *mode)
{
/* Ensure TV refresh is close to desired refresh */
< 1000)
return MODE_OK;
return MODE_CLOCK_RANGE;
}
static bool
struct intel_crtc_config *pipe_config)
{
if (!tv_mode)
return false;
DRM_DEBUG_KMS("forcing bpc to 8 for TV\n");
return true;
}
static void
/* LINTED */
/* LINTED */
struct drm_display_mode *adjusted_mode)
{
int i, j;
bool burst_ena;
if (!tv_mode)
return; /* can't happen (mode_prepare prevents this) */
tv_ctl &= TV_CTL_SAVE;
default:
break;
else
burst_ena = false;
break;
break;
}
if (burst_ena)
hctl2 |= TV_BURST_ENA;
vctl3 |= TV_EQUAL_ENA;
if (tv_mode->progressive)
tv_ctl |= TV_PROGRESSIVE;
if (tv_mode->trilevel_sync)
tv_ctl |= TV_PAL_BURST;
scctl1 = 0;
scctl1 |= TV_SC_DDA1_EN;
scctl1 |= TV_SC_DDA2_EN;
scctl1 |= TV_SC_DDA3_EN;
if (video_levels)
/* Enable two fixes for the chips that need them. */
if (color_conversion) {
}
else
if (video_levels)
{
/* Pipe must be off here */
/* Wait for vblank for the disable to take effect */
/* Wait for vblank for the disable to take effect. */
/* Filter ctl must be set before TV_WIN_SIZE */
if (tv_mode->progressive)
else
}
j = 0;
for (i = 0; i < 60; i++)
for (i = 0; i < 60; i++)
for (i = 0; i < 43; i++)
for (i = 0; i < 43; i++)
}
{
.name = "NTSC 480i",
.clock = 107520,
.hdisplay = 1280,
.hsync_start = 1368,
.hsync_end = 1496,
.htotal = 1712,
.vdisplay = 1024,
.vsync_start = 1027,
.vsync_end = 1034,
.vtotal = 1104,
},
};
/**
* Detects TV presence by checking for load.
*
* Requires that the current pipe's DPLL is active.
* \return true if TV is connected.
* \return false if TV is disconnected.
*/
static int
struct drm_connector *connector)
{
unsigned long irqflags;
int type;
/* Disable TV interrupts around load detect or we'll recurse */
}
/* Poll for TV detection */
else
tv_dac |= (TVDAC_STATE_CHG_EN |
/*
* The TV sense state should be cleared to zero on cantiga platform. Otherwise
* the TV is misdetected. This is hardware requirement.
*/
type = -1;
/*
* A B C
* 0 1 1 Composite
* 1 0 X svideo
* 0 0 0 Component
*/
DRM_DEBUG_KMS("Detected Composite TV connection\n");
DRM_DEBUG_KMS("Detected S-Video TV connection\n");
} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
DRM_DEBUG_KMS("Detected Component TV connection\n");
} else {
DRM_DEBUG_KMS("Unrecognised TV connection\n");
type = -1;
}
/* For unknown reasons the hw barfs if we don't do this vblank wait. */
/* Restore interrupt config */
}
return type;
}
/*
* Here we set accurate tv format according to connector type
* i.e Component TV should not be assigned by NTSC or PAL
*/
{
int i;
return;
break;
}
}
/**
* Detect the TV connection.
*
* Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
* we have a pipe programmed in order to probe the TV.
*/
static enum drm_connector_status
{
int type;
mode = reported_modes[0];
if (force) {
} else
return connector_status_unknown;
} else
if (type < 0)
return connector_status_disconnected;
return connector_status_connected;
}
static const struct input_res {
const char *name;
int w, h;
} input_res_table[] = {
{"640x480", 640, 480},
{"800x600", 800, 600},
{"1024x768", 1024, 768},
{"1280x1024", 1280, 1024},
{"848x480", 848, 480},
{"1280x720", 1280, 720},
{"1920x1080", 1920, 1080},
};
/*
* Chose preferred mode according to line number of TV format
*/
static void
struct drm_display_mode *mode_ptr)
{
}
}
/**
* Stub get_modes function.
*
* This should probably return a set of fixed modes, unless we can figure out
* how to probe modes off of TV connections.
*/
static int
{
int j, count = 0;
return 0;
for (j = 0; j < ARRAY_SIZE(input_res_table);
j++) {
continue;
&& !tv_mode->component_only))
continue;
if (!mode_ptr)
continue;
count++;
}
return count;
}
static void
{
}
static int
{
int ret = 0;
bool changed = false;
if (ret < 0)
goto out;
changed = true;
changed = true;
changed = true;
changed = true;
goto out;
}
goto out;
changed = true;
} else {
goto out;
}
out:
return ret;
}
};
};
};
};
/*
* Enumerate the child dev array parsed from VBT to check whether
* the integrated TV is present.
* If it is present, return 1.
* If it is not present, return false.
* If no child dev is parsed from VBT, it assumes that the TV is present.
*/
{
int i, ret;
return 1;
ret = 0;
/*
* If the device type is not TV, continue.
*/
continue;
/* Only when the addin_offset is non-zero, it is regarded
* as present.
*/
if (p_child->addin_offset) {
ret = 1;
break;
}
}
return ret;
}
void
{
int i, initial_mode = 0;
return;
if (!tv_is_present_in_vbt(dev)) {
DRM_DEBUG_KMS("Integrated TV is not present.\n");
return;
}
/* Even if we have an encoder we may not have a connector */
return;
/*
* Sanity check the TV output by checking to see if the
* DAC register holds a value
*/
/*
* If the register does not hold the state change enable
* bit, (either as a 0 or a 1), assume it doesn't really
* exist
*/
if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
(tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
return;
if (!intel_tv) {
return;
}
if (!intel_connector) {
return;
}
/* The documentation, for the older chipsets at least, recommend
* using a polling method rather than hotplug detection for TVs.
* This is because in order to perform the hotplug detection, the PLLs
* for the TV must be kept alive increasing power drain and starving
* bandwidth from other encoders. Notably for instance, it causes
* pipe underruns on Crestline when this encoder is supposedly idle.
*
* More recent chipsets favour HDMI rather than integrated S-Video.
*/
intel_encoder->cloneable = false;
/* BIOS margin values */
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
/* Create TV properties then attach current values */
for (i = 0; i < ARRAY_SIZE(tv_modes); i++)
(void) drm_mode_create_tv_properties(dev,
}