* Separated I2C from DDC a bit more.
* i2c_bus now contains a i2c_timing structure, so that you don't need both to talk to the I2C bus. * Therefore, there is now a void ddc2_init_timing() function to get the the timing DDC needs. * Cleanup in radeon's monitor_detection.c, and updated it to work with the DDC/I2C changes. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22265 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,21 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2003, Thomas Kurschel
|
* Copyright (c) 2003, Thomas Kurschel
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
|
||||||
Part of DDC driver
|
|
||||||
|
|
||||||
Main DDC communication
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _DDC_H
|
#ifndef _DDC_H
|
||||||
#define _DDC_H
|
#define _DDC_H
|
||||||
|
|
||||||
|
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "edid.h"
|
#include "edid.h"
|
||||||
|
|
||||||
|
|
||||||
|
void ddc2_init_timing(i2c_bus *bus);
|
||||||
|
|
||||||
// read EDID and VDIF from monitor via ddc2
|
// read EDID and VDIF from monitor via ddc2
|
||||||
// (currently, *vdif and *vdif_len is always set to null)
|
// (currently, *vdif and *vdif_len is always set to null)
|
||||||
status_t ddc2_read_edid1(const i2c_bus *bus, edid1_info *edid,
|
status_t ddc2_read_edid1(const i2c_bus *bus, edid1_info *edid,
|
||||||
void **vdif, size_t *vdif_len );
|
void **vdif, size_t *vdifLength);
|
||||||
|
|
||||||
#endif
|
#endif /* _DDC_H */
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2003, Thomas Kurschel
|
* Copyright 2003, Thomas Kurschel. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
|
||||||
Part of DDC driver
|
|
||||||
|
|
||||||
I2C protocoll
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _I2C_H
|
#ifndef _I2C_H
|
||||||
#define _I2C_H
|
#define _I2C_H
|
||||||
|
|
||||||
|
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
// timing for i2c bus
|
// timing for i2c bus
|
||||||
typedef struct i2c_timing {
|
typedef struct i2c_timing {
|
||||||
// general timing as defined by standard
|
// general timing as defined by standard
|
||||||
@@ -39,25 +36,23 @@ typedef struct i2c_timing {
|
|||||||
|
|
||||||
|
|
||||||
// set signals on bus
|
// set signals on bus
|
||||||
typedef status_t (*i2c_set_signals)( void *cookie, int scl, int sda );
|
typedef status_t (*i2c_set_signals)(void *cookie, int clock, int data);
|
||||||
// read signals from bus
|
// read signals from bus
|
||||||
typedef status_t (*i2c_get_signals)( void *cookie, int *scl, int *sda );
|
typedef status_t (*i2c_get_signals)(void *cookie, int *clock, int *data);
|
||||||
|
|
||||||
|
|
||||||
// i2c bus definition
|
// i2c bus definition
|
||||||
typedef struct i2c_bus {
|
typedef struct i2c_bus {
|
||||||
void *cookie; // user-defined cookie
|
void *cookie; // user-defined cookie
|
||||||
|
i2c_timing timing;
|
||||||
i2c_set_signals set_signals; // callback to set signals
|
i2c_set_signals set_signals; // callback to set signals
|
||||||
i2c_get_signals get_signals; // callback to detect signals
|
i2c_get_signals get_signals; // callback to detect signals
|
||||||
} i2c_bus;
|
} i2c_bus;
|
||||||
|
|
||||||
|
|
||||||
// send and receive data via i2c bus
|
// send and receive data via i2c bus
|
||||||
status_t i2c_send_receive( const i2c_bus *bus, const i2c_timing *timing,
|
status_t i2c_send_receive(const i2c_bus *bus, int slave_address,
|
||||||
int slave_address,
|
const uint8 *writeBuffer, size_t writeLength, uint8 *readBuffer,
|
||||||
const uint8 *write_buffer, size_t write_len,
|
size_t readLength);
|
||||||
uint8 *read_buffer, size_t read_len );
|
|
||||||
|
|
||||||
|
|
||||||
// fill <timing> with standard 100kHz bus timing
|
// fill <timing> with standard 100kHz bus timing
|
||||||
void i2c_get100k_timing(i2c_timing *timing);
|
void i2c_get100k_timing(i2c_timing *timing);
|
||||||
@@ -66,4 +61,4 @@ void i2c_get100k_timing( i2c_timing *timing );
|
|||||||
// (as timing resolution is 1 microsecond, we cannot reach full speed!)
|
// (as timing resolution is 1 microsecond, we cannot reach full speed!)
|
||||||
void i2c_get400k_timing(i2c_timing *timing);
|
void i2c_get400k_timing(i2c_timing *timing);
|
||||||
|
|
||||||
#endif
|
#endif /* _I2C_H */
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Part of DDC driver
|
DDC communication
|
||||||
Main DDC communication
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -56,36 +54,26 @@ verify_checksum(const uint8 *data, size_t len)
|
|||||||
|
|
||||||
//! Read ddc2 data from monitor
|
//! Read ddc2 data from monitor
|
||||||
static status_t
|
static status_t
|
||||||
ddc2_read(const i2c_bus *bus, int start, uint8 *buffer, size_t len)
|
ddc2_read(const i2c_bus *bus, int start, uint8 *buffer, size_t length)
|
||||||
{
|
{
|
||||||
uint8 write_buffer[2];
|
status_t status = B_OK;
|
||||||
i2c_timing timing;
|
uint8 writeBuffer[2];
|
||||||
int i;
|
int i;
|
||||||
status_t res = B_OK;
|
|
||||||
|
|
||||||
write_buffer[0] = start & 0xff;
|
writeBuffer[0] = start & 0xff;
|
||||||
write_buffer[1] = (start >> 8) & 0xff;
|
writeBuffer[1] = (start >> 8) & 0xff;
|
||||||
|
|
||||||
i2c_get100k_timing(&timing);
|
|
||||||
|
|
||||||
timing.start_timeout = 550;
|
|
||||||
timing.byte_timeout = 2200;
|
|
||||||
timing.bit_timeout = 40;
|
|
||||||
timing.ack_start_timeout = 40;
|
|
||||||
timing.ack_timeout = 40;
|
|
||||||
|
|
||||||
for (i = 0; i < READ_RETRIES; ++i) {
|
for (i = 0; i < READ_RETRIES; ++i) {
|
||||||
res = i2c_send_receive(bus, &timing,
|
status = i2c_send_receive(bus, 0xa0, writeBuffer,
|
||||||
0xa0, write_buffer, start < 0x100 ? 1 : 2,
|
start < 0x100 ? 1 : 2, buffer, length);
|
||||||
buffer, len);
|
|
||||||
// don't verify checksum - it's often broken
|
// don't verify checksum - it's often broken
|
||||||
if (res == B_OK /*&& verify_checksum( buffer, len ) == B_OK*/)
|
if (status == B_OK /*&& verify_checksum( buffer, len ) == B_OK*/)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
res = B_ERROR;
|
status = B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -133,29 +121,41 @@ ddc2_read_vdif(const i2c_bus *bus, int start,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ddc2_init_timing(i2c_bus *bus)
|
||||||
|
{
|
||||||
|
i2c_get100k_timing(&bus->timing);
|
||||||
|
|
||||||
|
// VESA standard
|
||||||
|
bus->timing.start_timeout = 550;
|
||||||
|
bus->timing.byte_timeout = 2200;
|
||||||
|
bus->timing.bit_timeout = 40;
|
||||||
|
bus->timing.ack_start_timeout = 40;
|
||||||
|
bus->timing.ack_timeout = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//! Read EDID and VDIF from monitor via ddc2
|
//! Read EDID and VDIF from monitor via ddc2
|
||||||
status_t
|
status_t
|
||||||
ddc2_read_edid1(const i2c_bus *bus, edid1_info *edid,
|
ddc2_read_edid1(const i2c_bus *bus, edid1_info *edid,
|
||||||
void **vdif, size_t *vdif_len)
|
void **vdif, size_t *vdifLength)
|
||||||
{
|
{
|
||||||
status_t res;
|
|
||||||
edid1_raw raw;
|
edid1_raw raw;
|
||||||
|
status_t status = ddc2_read(bus, 0, (uint8 *)&raw, sizeof(raw));
|
||||||
res = ddc2_read(bus, 0, (uint8 *)&raw, sizeof(raw));
|
if (status != B_OK)
|
||||||
if (res != B_OK)
|
return status;
|
||||||
return res;
|
|
||||||
|
|
||||||
edid_decode(edid, &raw);
|
edid_decode(edid, &raw);
|
||||||
|
|
||||||
*vdif = NULL;
|
*vdif = NULL;
|
||||||
*vdif_len = 0;
|
*vdifLength = 0;
|
||||||
|
|
||||||
// skip vdif as long as it's not tested
|
// skip vdif as long as it's not tested
|
||||||
#if 0
|
#if 0
|
||||||
res = ddc2_read_vdif(bus, sizeof(raw) * (edid->num_sections + 1),
|
status = ddc2_read_vdif(bus, sizeof(raw) * (edid->num_sections + 1),
|
||||||
vdif, vdif_len);
|
vdif, vdif_len);
|
||||||
if (res != B_OK)
|
if (status != B_OK)
|
||||||
return res;
|
return status;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|||||||
@@ -1,398 +1,30 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2007, Axel Dörfler, [email protected]. All Rights Reserved.
|
||||||
* Copyright 2003, Thomas Kurschel. All Rights Reserved.
|
* Copyright 2003, Thomas Kurschel. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Part of DDC driver
|
I2C protocol
|
||||||
I2C protocoll
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ddc_int.h"
|
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
|
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
/*!
|
//#define TRACE_I2C
|
||||||
There's no spin in user space, but we need it to wait a couple
|
#ifdef TRACE_I2C
|
||||||
of microseconds only
|
extern "C" void _sPrintf(const char *format, ...);
|
||||||
(in this case, snooze has much too much overhead)
|
# define TRACE(x...) _sPrintf("I2C: " x)
|
||||||
*/
|
#else
|
||||||
void
|
# define TRACE(x...) ;
|
||||||
spin(bigtime_t delay)
|
#endif
|
||||||
{
|
|
||||||
bigtime_t start_time = system_time();
|
|
||||||
|
|
||||||
while (system_time() - start_time < delay)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Wait until slave releases clock signal ("clock stretching")
|
|
||||||
static status_t
|
|
||||||
wait_for_clk(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
bigtime_t timeout)
|
|
||||||
{
|
|
||||||
bigtime_t start_time;
|
|
||||||
|
|
||||||
// wait for clock signal to raise
|
|
||||||
spin(timing->r);
|
|
||||||
|
|
||||||
start_time = system_time();
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int clk, data;
|
|
||||||
|
|
||||||
bus->get_signals(bus->cookie, &clk, &data);
|
|
||||||
if (clk != 0)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
if (system_time() - start_time > timeout)
|
|
||||||
return B_TIMEOUT;
|
|
||||||
|
|
||||||
spin(timing->r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send start or repeated start condition
|
|
||||||
static status_t
|
|
||||||
send_start_condition(const i2c_bus *bus, const i2c_timing *timing)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 1, 1);
|
|
||||||
|
|
||||||
res = wait_for_clk(bus, timing, timing->start_timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout sending start condition");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin(timing->su_sta);
|
|
||||||
bus->set_signals(bus->cookie, 1, 0);
|
|
||||||
spin(timing->hd_sta);
|
|
||||||
bus->set_signals(bus->cookie, 0, 0);
|
|
||||||
spin(timing->f);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send stop condition
|
|
||||||
static status_t
|
|
||||||
send_stop_condition(const i2c_bus *bus, const i2c_timing *timing)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 0, 0);
|
|
||||||
spin(timing->r);
|
|
||||||
bus->set_signals(bus->cookie, 1, 0);
|
|
||||||
|
|
||||||
// a slave may wait for us, so let elapse the acknowledge timeout
|
|
||||||
// to make the slave release bus control
|
|
||||||
res = wait_for_clk(bus, timing, timing->ack_timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout sending stop condition");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin(timing->su_sto);
|
|
||||||
bus->set_signals(bus->cookie, 1, 1);
|
|
||||||
spin(timing->buf);
|
|
||||||
|
|
||||||
SHOW_FLOW0(3, "");
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send one bit
|
|
||||||
static status_t
|
|
||||||
send_bit(const i2c_bus *bus, const i2c_timing *timing, bool bit, int timeout)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
//SHOW_FLOW( 3, "%d", bit & 1 );
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 0, bit & 1);
|
|
||||||
spin(timing->su_dat);
|
|
||||||
bus->set_signals(bus->cookie, 1, bit & 1);
|
|
||||||
|
|
||||||
res = wait_for_clk(bus, timing, timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout when sending next bit");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin(timing->high);
|
|
||||||
bus->set_signals(bus->cookie, 0, bit & 1);
|
|
||||||
spin(timing->f + timing->low);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send acknowledge and wait for reply
|
|
||||||
static status_t
|
|
||||||
send_acknowledge(const i2c_bus *bus, const i2c_timing *timing)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
bigtime_t start_time;
|
|
||||||
|
|
||||||
// release data so slave can modify it
|
|
||||||
bus->set_signals(bus->cookie, 0, 1);
|
|
||||||
spin(timing->su_dat);
|
|
||||||
bus->set_signals(bus->cookie, 1, 1);
|
|
||||||
|
|
||||||
res = wait_for_clk(bus, timing, timing->ack_start_timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout when sending acknowledge");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// data and clock is high, now wait for slave to pull data low
|
|
||||||
// (according to spec, this can happen any time once clock is high)
|
|
||||||
start_time = system_time();
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int clk, data;
|
|
||||||
|
|
||||||
bus->get_signals(bus->cookie, &clk, &data);
|
|
||||||
|
|
||||||
if (data == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (system_time() - start_time > timing->ack_timeout) {
|
|
||||||
SHOW_FLOW0(3, "Slave didn't acknowledge byte");
|
|
||||||
return B_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin(timing->r);
|
|
||||||
}
|
|
||||||
|
|
||||||
SHOW_FLOW0(4, "Success!");
|
|
||||||
|
|
||||||
// make sure we've waited at least t_high
|
|
||||||
spin(timing->high);
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 0, 1);
|
|
||||||
spin(timing->f + timing->low);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send byte and wait for acknowledge if <ackowledge> is true
|
|
||||||
static status_t
|
|
||||||
send_byte(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
uint8 byte, bool acknowledge)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
SHOW_FLOW( 3, "%x ", byte );
|
|
||||||
|
|
||||||
for (i = 7; i >= 0; --i) {
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = send_bit(bus, timing, byte >> i,
|
|
||||||
i == 7 ? timing->byte_timeout : timing->bit_timeout);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (acknowledge)
|
|
||||||
return send_acknowledge(bus, timing);
|
|
||||||
else
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send slave address, obeying 10-bit addresses and general call addresses
|
|
||||||
static status_t
|
|
||||||
send_slave_address( const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
int slave_address, bool is_write )
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = send_byte(bus, timing, (slave_address & 0xfe) | !is_write, true);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
// there are the following special cases if the first byte looks like:
|
|
||||||
// - 0000 0000 - general call address (second byte with address follows)
|
|
||||||
// - 0000 0001 - start byte
|
|
||||||
// - 0000 001x - CBus address
|
|
||||||
// - 0000 010x - address reserved for different bus format
|
|
||||||
// - 0000 011x |
|
|
||||||
// - 0000 1xxx |-> reserved
|
|
||||||
// - 1111 1xxx |
|
|
||||||
// - 1111 0xxx - 10 bit address (second byte contains remaining 8 bits)
|
|
||||||
|
|
||||||
// the lsb is 0 for write and 1 for read (except for general call address)
|
|
||||||
if ((slave_address & 0xff) != 0 && (slave_address & 0xf8) != 0xf0)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
return send_byte(bus, timing, slave_address >> 8, true);
|
|
||||||
// send second byte if required
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Receive one bit
|
|
||||||
static status_t
|
|
||||||
receive_bit(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
bool *bit, int timeout)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
int clk, data;
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 1, 1);
|
|
||||||
// release clock
|
|
||||||
|
|
||||||
// wait for slave to raise clock
|
|
||||||
res = wait_for_clk(bus, timing, timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout waiting for bit sent by slave");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bus->get_signals(bus->cookie, &clk, &data);
|
|
||||||
// sample data
|
|
||||||
|
|
||||||
spin(timing->high);
|
|
||||||
// leave clock high for minimal time
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 0, 1);
|
|
||||||
// pull clock low so slave waits for us before next bit
|
|
||||||
|
|
||||||
spin(timing->f + timing->low);
|
|
||||||
// let it settle and leave it low for minimal time
|
|
||||||
// to make sure slave has finished bit transmission too
|
|
||||||
|
|
||||||
*bit = data;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! receive byte
|
|
||||||
Send positive acknowledge afterwards if <acknowledge> is true,
|
|
||||||
else send negative one
|
|
||||||
*/
|
|
||||||
static status_t
|
|
||||||
receive_byte(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
uint8 *res_byte, bool acknowledge)
|
|
||||||
{
|
|
||||||
uint8 byte = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// pull clock low to let slave wait for us
|
|
||||||
bus->set_signals(bus->cookie, 0, 1);
|
|
||||||
|
|
||||||
for (i = 7; i >= 0; --i) {
|
|
||||||
status_t res;
|
|
||||||
bool bit;
|
|
||||||
|
|
||||||
res = receive_bit(bus, timing, &bit,
|
|
||||||
i == 7 ? timing->byte_timeout : timing->bit_timeout);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
byte = (byte << 1) | bit;
|
|
||||||
}
|
|
||||||
|
|
||||||
//SHOW_FLOW( 3, "%x ", byte );
|
|
||||||
|
|
||||||
*res_byte = byte;
|
|
||||||
|
|
||||||
return send_bit(bus, timing, acknowledge ? 0 : 1, timing->bit_timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send multiple bytes
|
|
||||||
static status_t
|
|
||||||
send_bytes(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
const uint8 *write_buffer, ssize_t write_len)
|
|
||||||
{
|
|
||||||
SHOW_FLOW( 3, "len=%ld", write_len );
|
|
||||||
|
|
||||||
for (; write_len > 0; --write_len, ++write_buffer) {
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = send_byte(bus, timing, *write_buffer, true);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Receive multiple bytes
|
|
||||||
static status_t
|
|
||||||
receive_bytes(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
uint8 *read_buffer, ssize_t read_len)
|
|
||||||
{
|
|
||||||
SHOW_FLOW(3, "len=%ld", read_len);
|
|
||||||
|
|
||||||
for (; read_len > 0; --read_len, ++read_buffer) {
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = receive_byte(bus, timing, read_buffer, read_len > 1);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Combined i2c send+receive format
|
|
||||||
status_t
|
|
||||||
i2c_send_receive(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
int slave_address, const uint8 *write_buffer, size_t write_len,
|
|
||||||
uint8 *read_buffer, size_t read_len)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = send_start_condition(bus, timing);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res = send_slave_address(bus, timing, slave_address, true);
|
|
||||||
if (res != B_OK)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
res = send_bytes(bus, timing, write_buffer, write_len);
|
|
||||||
if (res != B_OK)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
res = send_start_condition(bus, timing);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res = send_slave_address(bus, timing, slave_address, false);
|
|
||||||
if (res != B_OK)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
res = receive_bytes(bus, timing, read_buffer, read_len);
|
|
||||||
if (res != B_OK)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
res = send_stop_condition(bus, timing);
|
|
||||||
return res;
|
|
||||||
|
|
||||||
err:
|
|
||||||
SHOW_FLOW0(3, "Cancelling transmission");
|
|
||||||
send_stop_condition(bus, timing);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Timining for 100kHz bus (fractional parts are rounded up)
|
//! Timining for 100kHz bus (fractional parts are rounded up)
|
||||||
i2c_timing i2c_timing_100k = {
|
const static i2c_timing kTiming100k = {
|
||||||
buf : 5,
|
buf : 5,
|
||||||
hd_sta : 4,
|
hd_sta : 4,
|
||||||
low : 5,
|
low : 5,
|
||||||
@@ -414,7 +46,7 @@ i2c_timing i2c_timing_100k = {
|
|||||||
|
|
||||||
// timing for 400 kHz bus
|
// timing for 400 kHz bus
|
||||||
// (argh! heavy up-rounding here)
|
// (argh! heavy up-rounding here)
|
||||||
i2c_timing i2c_timing_400k = {
|
const static i2c_timing kTiming400k = {
|
||||||
buf : 2,
|
buf : 2,
|
||||||
hd_sta : 1,
|
hd_sta : 1,
|
||||||
low : 2,
|
low : 2,
|
||||||
@@ -426,7 +58,7 @@ i2c_timing i2c_timing_400k = {
|
|||||||
f : 1,
|
f : 1,
|
||||||
su_sto : 1,
|
su_sto : 1,
|
||||||
|
|
||||||
// see i2c_timing_100k
|
// see kTiming100k
|
||||||
start_timeout : 2,
|
start_timeout : 2,
|
||||||
byte_timeout : 2,
|
byte_timeout : 2,
|
||||||
bit_timeout : 2,
|
bit_timeout : 2,
|
||||||
@@ -435,15 +67,373 @@ i2c_timing i2c_timing_400k = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
There's no spin in user space, but we need it to wait a couple
|
||||||
|
of microseconds only
|
||||||
|
(in this case, snooze has much too much overhead)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
spin(bigtime_t delay)
|
||||||
|
{
|
||||||
|
bigtime_t startTime = system_time();
|
||||||
|
|
||||||
|
while (system_time() - startTime < delay)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Wait until slave releases clock signal ("clock stretching")
|
||||||
|
static status_t
|
||||||
|
wait_for_clk(const i2c_bus *bus, bigtime_t timeout)
|
||||||
|
{
|
||||||
|
bigtime_t startTime;
|
||||||
|
|
||||||
|
// wait for clock signal to raise
|
||||||
|
spin(bus->timing.r);
|
||||||
|
|
||||||
|
startTime = system_time();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int clk, data;
|
||||||
|
|
||||||
|
bus->get_signals(bus->cookie, &clk, &data);
|
||||||
|
if (clk != 0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
if (system_time() - startTime > timeout)
|
||||||
|
return B_TIMEOUT;
|
||||||
|
|
||||||
|
spin(bus->timing.r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send start or repeated start condition
|
||||||
|
static status_t
|
||||||
|
send_start_condition(const i2c_bus *bus)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 1, 1);
|
||||||
|
|
||||||
|
status = wait_for_clk(bus, bus->timing.start_timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("send_start_condition(): Timeout sending start condition\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin(bus->timing.su_sta);
|
||||||
|
bus->set_signals(bus->cookie, 1, 0);
|
||||||
|
spin(bus->timing.hd_sta);
|
||||||
|
bus->set_signals(bus->cookie, 0, 0);
|
||||||
|
spin(bus->timing.f);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send stop condition
|
||||||
|
static status_t
|
||||||
|
send_stop_condition(const i2c_bus *bus)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 0, 0);
|
||||||
|
spin(bus->timing.r);
|
||||||
|
bus->set_signals(bus->cookie, 1, 0);
|
||||||
|
|
||||||
|
// a slave may wait for us, so let elapse the acknowledge timeout
|
||||||
|
// to make the slave release bus control
|
||||||
|
status = wait_for_clk(bus, bus->timing.ack_timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("send_stop_condition(): Timeout sending stop condition\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin(bus->timing.su_sto);
|
||||||
|
bus->set_signals(bus->cookie, 1, 1);
|
||||||
|
spin(bus->timing.buf);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send one bit
|
||||||
|
static status_t
|
||||||
|
send_bit(const i2c_bus *bus, bool bit, int timeout)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
//TRACE("send_bit(bit = %d)\n", bit & 1);
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 0, bit & 1);
|
||||||
|
spin(bus->timing.su_dat);
|
||||||
|
bus->set_signals(bus->cookie, 1, bit & 1);
|
||||||
|
|
||||||
|
status = wait_for_clk(bus, timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("send_bit(): Timeout when sending next bit\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin(bus->timing.high);
|
||||||
|
bus->set_signals(bus->cookie, 0, bit & 1);
|
||||||
|
spin(bus->timing.f + bus->timing.low);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send acknowledge and wait for reply
|
||||||
|
static status_t
|
||||||
|
send_acknowledge(const i2c_bus *bus)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
bigtime_t startTime;
|
||||||
|
|
||||||
|
// release data so slave can modify it
|
||||||
|
bus->set_signals(bus->cookie, 0, 1);
|
||||||
|
spin(bus->timing.su_dat);
|
||||||
|
bus->set_signals(bus->cookie, 1, 1);
|
||||||
|
|
||||||
|
status = wait_for_clk(bus, bus->timing.ack_start_timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("send_acknowledge(): Timeout when sending acknowledge\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// data and clock is high, now wait for slave to pull data low
|
||||||
|
// (according to spec, this can happen any time once clock is high)
|
||||||
|
startTime = system_time();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int clk, data;
|
||||||
|
|
||||||
|
bus->get_signals(bus->cookie, &clk, &data);
|
||||||
|
|
||||||
|
if (data == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (system_time() - startTime > bus->timing.ack_timeout) {
|
||||||
|
TRACE("send_acknowledge(): Slave didn't acknowledge byte\n");
|
||||||
|
return B_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin(bus->timing.r);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("send_acknowledge(): Success!\n");
|
||||||
|
|
||||||
|
// make sure we've waited at least t_high
|
||||||
|
spin(bus->timing.high);
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 0, 1);
|
||||||
|
spin(bus->timing.f + bus->timing.low);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send byte and wait for acknowledge if <ackowledge> is true
|
||||||
|
static status_t
|
||||||
|
send_byte(const i2c_bus *bus, uint8 byte, bool acknowledge)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
//TRACE("send_byte(byte = %x)\n", byte);
|
||||||
|
|
||||||
|
for (i = 7; i >= 0; --i) {
|
||||||
|
status_t status = send_bit(bus, byte >> i,
|
||||||
|
i == 7 ? bus->timing.byte_timeout : bus->timing.bit_timeout);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acknowledge)
|
||||||
|
return send_acknowledge(bus);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send slave address, obeying 10-bit addresses and general call addresses
|
||||||
|
static status_t
|
||||||
|
send_slave_address(const i2c_bus *bus, int slaveAddress, bool isWrite)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
status = send_byte(bus, (slaveAddress & 0xfe) | !isWrite, true);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// there are the following special cases if the first byte looks like:
|
||||||
|
// - 0000 0000 - general call address (second byte with address follows)
|
||||||
|
// - 0000 0001 - start byte
|
||||||
|
// - 0000 001x - CBus address
|
||||||
|
// - 0000 010x - address reserved for different bus format
|
||||||
|
// - 0000 011x |
|
||||||
|
// - 0000 1xxx |-> reserved
|
||||||
|
// - 1111 1xxx |
|
||||||
|
// - 1111 0xxx - 10 bit address (second byte contains remaining 8 bits)
|
||||||
|
|
||||||
|
// the lsb is 0 for write and 1 for read (except for general call address)
|
||||||
|
if ((slaveAddress & 0xff) != 0 && (slaveAddress & 0xf8) != 0xf0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
return send_byte(bus, slaveAddress >> 8, true);
|
||||||
|
// send second byte if required
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Receive one bit
|
||||||
|
static status_t
|
||||||
|
receive_bit(const i2c_bus *bus, bool *bit, int timeout)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
int clk, data;
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 1, 1);
|
||||||
|
// release clock
|
||||||
|
|
||||||
|
// wait for slave to raise clock
|
||||||
|
status = wait_for_clk(bus, timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("receive_bit(): Timeout waiting for bit sent by slave\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
bus->get_signals(bus->cookie, &clk, &data);
|
||||||
|
// sample data
|
||||||
|
|
||||||
|
spin(bus->timing.high);
|
||||||
|
// leave clock high for minimal time
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 0, 1);
|
||||||
|
// pull clock low so slave waits for us before next bit
|
||||||
|
|
||||||
|
spin(bus->timing.f + bus->timing.low);
|
||||||
|
// let it settle and leave it low for minimal time
|
||||||
|
// to make sure slave has finished bit transmission too
|
||||||
|
|
||||||
|
*bit = data;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Send positive acknowledge afterwards if <acknowledge> is true,
|
||||||
|
else send negative one
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
receive_byte(const i2c_bus *bus, uint8 *resultByte, bool acknowledge)
|
||||||
|
{
|
||||||
|
uint8 byte = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// pull clock low to let slave wait for us
|
||||||
|
bus->set_signals(bus->cookie, 0, 1);
|
||||||
|
|
||||||
|
for (i = 7; i >= 0; i--) {
|
||||||
|
bool bit;
|
||||||
|
|
||||||
|
status_t status = receive_bit(bus, &bit,
|
||||||
|
i == 7 ? bus->timing.byte_timeout : bus->timing.bit_timeout);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
byte = (byte << 1) | bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
//SHOW_FLOW(3, "%x ", byte);
|
||||||
|
|
||||||
|
*resultByte = byte;
|
||||||
|
|
||||||
|
return send_bit(bus, acknowledge ? 0 : 1, bus->timing.bit_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send multiple bytes
|
||||||
|
static status_t
|
||||||
|
send_bytes(const i2c_bus *bus, const uint8 *writeBuffer, ssize_t writeLength)
|
||||||
|
{
|
||||||
|
TRACE("send_bytes(length = %ld)\n", writeLength);
|
||||||
|
|
||||||
|
for (; writeLength > 0; --writeLength, ++writeBuffer) {
|
||||||
|
status_t status = send_byte(bus, *writeBuffer, true);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Receive multiple bytes
|
||||||
|
static status_t
|
||||||
|
receive_bytes(const i2c_bus *bus, uint8 *readBuffer, ssize_t readLength)
|
||||||
|
{
|
||||||
|
TRACE("receive_bytes(length = %ld)\n", writeLength);
|
||||||
|
|
||||||
|
for (; readLength > 0; --readLength, ++readBuffer) {
|
||||||
|
status_t status = receive_byte(bus, readBuffer, readLength > 1);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - exported functions
|
||||||
|
|
||||||
|
|
||||||
|
//! Combined i2c send+receive format
|
||||||
|
status_t
|
||||||
|
i2c_send_receive(const i2c_bus *bus, int slaveAddress, const uint8 *writeBuffer,
|
||||||
|
size_t writeLength, uint8 *readBuffer, size_t readLength)
|
||||||
|
{
|
||||||
|
status_t status = send_start_condition(bus);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
status = send_slave_address(bus, slaveAddress, true);
|
||||||
|
if (status != B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
status = send_bytes(bus, writeBuffer, writeLength);
|
||||||
|
if (status != B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
status = send_start_condition(bus);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
status = send_slave_address(bus, slaveAddress, false);
|
||||||
|
if (status != B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
status = receive_bytes(bus, readBuffer, readLength);
|
||||||
|
if (status != B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
return send_stop_condition(bus);
|
||||||
|
|
||||||
|
err:
|
||||||
|
TRACE("i2c_send_receive(): Cancelling transmission\n");
|
||||||
|
send_stop_condition(bus);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
i2c_get100k_timing(i2c_timing *timing)
|
i2c_get100k_timing(i2c_timing *timing)
|
||||||
{
|
{
|
||||||
*timing = i2c_timing_100k;
|
*timing = kTiming100k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
i2c_get400k_timing(i2c_timing *timing)
|
i2c_get400k_timing(i2c_timing *timing)
|
||||||
{
|
{
|
||||||
*timing = i2c_timing_400k;
|
*timing = kTiming400k;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2002-2004 Thomas Kurschel
|
* Copyright 2002-2004, Thomas Kurschel. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
|
||||||
Part of Radeon accelerant
|
|
||||||
|
|
||||||
Monitor detection
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Radeon monitor detection
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "radeon_accelerant.h"
|
#include "radeon_accelerant.h"
|
||||||
#include "mmio.h"
|
#include "mmio.h"
|
||||||
#include "crtc_regs.h"
|
#include "crtc_regs.h"
|
||||||
@@ -21,8 +24,7 @@
|
|||||||
#include "theatre_regs.h"
|
#include "theatre_regs.h"
|
||||||
#include "set_mode.h"
|
#include "set_mode.h"
|
||||||
#include "ddc.h"
|
#include "ddc.h"
|
||||||
#include <malloc.h>
|
|
||||||
#include "string.h"
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
accelerator_info *ai;
|
accelerator_info *ai;
|
||||||
@@ -30,15 +32,15 @@ typedef struct {
|
|||||||
} ddc_port_info;
|
} ddc_port_info;
|
||||||
|
|
||||||
|
|
||||||
// get I2C signals
|
//! Get I2C signals
|
||||||
static status_t get_signals( void *cookie, int *clk, int *data )
|
static status_t
|
||||||
|
get_signals(void *cookie, int *clk, int *data)
|
||||||
{
|
{
|
||||||
ddc_port_info *info = (ddc_port_info *)cookie;
|
ddc_port_info *info = (ddc_port_info *)cookie;
|
||||||
vuint8 *regs = info->ai->regs;
|
vuint8 *regs = info->ai->regs;
|
||||||
uint32 value;
|
uint32 value;
|
||||||
|
|
||||||
value = INREG(regs, info->port);
|
value = INREG(regs, info->port);
|
||||||
|
|
||||||
*clk = (value >> RADEON_GPIO_Y_SHIFT_1) & 1;
|
*clk = (value >> RADEON_GPIO_Y_SHIFT_1) & 1;
|
||||||
*data = (value >> RADEON_GPIO_Y_SHIFT_0) & 1;
|
*data = (value >> RADEON_GPIO_Y_SHIFT_0) & 1;
|
||||||
|
|
||||||
@@ -46,8 +48,9 @@ static status_t get_signals( void *cookie, int *clk, int *data )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// set I2C signals
|
//! Set I2C signals
|
||||||
static status_t set_signals( void *cookie, int clk, int data )
|
static status_t
|
||||||
|
set_signals(void *cookie, int clk, int data)
|
||||||
{
|
{
|
||||||
ddc_port_info *info = (ddc_port_info *)cookie;
|
ddc_port_info *info = (ddc_port_info *)cookie;
|
||||||
vuint8 *regs = info->ai->regs;
|
vuint8 *regs = info->ai->regs;
|
||||||
@@ -56,75 +59,73 @@ static status_t set_signals( void *cookie, int clk, int data )
|
|||||||
value = INREG(regs, info->port);
|
value = INREG(regs, info->port);
|
||||||
value &= ~(RADEON_GPIO_A_1 | RADEON_GPIO_A_0);
|
value &= ~(RADEON_GPIO_A_1 | RADEON_GPIO_A_0);
|
||||||
value &= ~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1);
|
value &= ~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1);
|
||||||
value |= ((1-clk) << RADEON_GPIO_EN_SHIFT_1) | ((1-data) << RADEON_GPIO_EN_SHIFT_0);
|
value |= ((1-clk) << RADEON_GPIO_EN_SHIFT_1)
|
||||||
|
| ((1-data) << RADEON_GPIO_EN_SHIFT_0);
|
||||||
|
|
||||||
OUTREG(regs, info->port, value);
|
OUTREG(regs, info->port, value);
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// read EDID information from monitor
|
/*! Read EDID information from monitor
|
||||||
// ddc_port - register to use for DDC2 communication
|
ddc_port - register to use for DDC2 communication
|
||||||
bool Radeon_ReadEDID( accelerator_info *ai, uint32 ddc_port, edid1_info *edid )
|
*/
|
||||||
|
bool
|
||||||
|
Radeon_ReadEDID(accelerator_info *ai, uint32 ddcPort, edid1_info *edid)
|
||||||
{
|
{
|
||||||
i2c_bus bus;
|
i2c_bus bus;
|
||||||
ddc_port_info info;
|
ddc_port_info info;
|
||||||
void *vdif;
|
void *vdif;
|
||||||
size_t vdif_len;
|
size_t vdifLength;
|
||||||
status_t res;
|
|
||||||
|
|
||||||
info.ai = ai;
|
info.ai = ai;
|
||||||
info.port = ddc_port;
|
info.port = ddcPort;
|
||||||
|
|
||||||
|
ddc2_init_timing(&bus);
|
||||||
bus.cookie = &info;
|
bus.cookie = &info;
|
||||||
bus.set_signals = &set_signals;
|
bus.set_signals = &set_signals;
|
||||||
bus.get_signals = &get_signals;
|
bus.get_signals = &get_signals;
|
||||||
|
|
||||||
res = ddc2_read_edid1( &bus, edid, &vdif, &vdif_len );
|
if (ddc2_read_edid1(&bus, edid, &vdif, &vdifLength) != B_OK)
|
||||||
if( res != B_OK )
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SHOW_FLOW( 2, "Found DDC-capable monitor @0x%04x", ddc_port );
|
SHOW_FLOW(2, "Found DDC-capable monitor @0x%04x", ddcPort);
|
||||||
|
|
||||||
if( vdif != NULL )
|
|
||||||
free(vdif);
|
free(vdif);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// search for display connect to CRT DAC
|
// search for display connect to CRT DAC
|
||||||
// colour - true, if only a colour monitor is to be accepted
|
// colour - true, if only a colour monitor is to be accepted
|
||||||
static bool Radeon_DetectCRTInt( accelerator_info *ai, bool colour )
|
static bool
|
||||||
|
Radeon_DetectCRTInt(accelerator_info *ai, bool colour)
|
||||||
{
|
{
|
||||||
vuint8 *regs = ai->regs;
|
vuint8 *regs = ai->regs;
|
||||||
uint32 old_crtc_ext_cntl, old_dac_ext_cntl, old_dac_cntl, tmp;
|
uint32 old_crtc_ext_cntl, old_dac_ext_cntl, old_dac_cntl, value;
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
// makes sure there is a signal
|
// makes sure there is a signal
|
||||||
old_crtc_ext_cntl = INREG(regs, RADEON_CRTC_EXT_CNTL);
|
old_crtc_ext_cntl = INREG(regs, RADEON_CRTC_EXT_CNTL);
|
||||||
|
|
||||||
tmp = old_crtc_ext_cntl | RADEON_CRTC_CRT_ON;
|
value = old_crtc_ext_cntl | RADEON_CRTC_CRT_ON;
|
||||||
OUTREG( regs, RADEON_CRTC_EXT_CNTL, tmp );
|
OUTREG(regs, RADEON_CRTC_EXT_CNTL, value);
|
||||||
|
|
||||||
// force DAC to output constant voltage
|
// force DAC to output constant voltage
|
||||||
// for colour monitors, RGB is tested, for B/W only G
|
// for colour monitors, RGB is tested, for B/W only G
|
||||||
old_dac_ext_cntl = INREG(regs, RADEON_DAC_EXT_CNTL);
|
old_dac_ext_cntl = INREG(regs, RADEON_DAC_EXT_CNTL);
|
||||||
|
|
||||||
tmp =
|
value = RADEON_DAC_FORCE_BLANK_OFF_EN | RADEON_DAC_FORCE_DATA_EN
|
||||||
RADEON_DAC_FORCE_BLANK_OFF_EN |
|
| (colour ? RADEON_DAC_FORCE_DATA_SEL_RGB : RADEON_DAC_FORCE_DATA_SEL_G)
|
||||||
RADEON_DAC_FORCE_DATA_EN |
|
| (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT);
|
||||||
(colour ? RADEON_DAC_FORCE_DATA_SEL_RGB : RADEON_DAC_FORCE_DATA_SEL_G) |
|
OUTREG(regs, RADEON_DAC_EXT_CNTL, value);
|
||||||
(0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT);
|
|
||||||
OUTREG( regs, RADEON_DAC_EXT_CNTL, tmp );
|
|
||||||
|
|
||||||
// enable DAC and tell is to use VGA signals
|
// enable DAC and tell it to use VGA signals
|
||||||
old_dac_cntl = INREG(regs, RADEON_DAC_CNTL);
|
old_dac_cntl = INREG(regs, RADEON_DAC_CNTL);
|
||||||
|
|
||||||
tmp = old_dac_cntl & ~(RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_PDWN);
|
value = old_dac_cntl & ~(RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_PDWN);
|
||||||
tmp |= RADEON_DAC_RANGE_CNTL_PS2 | RADEON_DAC_CMP_EN;
|
value |= RADEON_DAC_RANGE_CNTL_PS2 | RADEON_DAC_CMP_EN;
|
||||||
OUTREG( regs, RADEON_DAC_CNTL, tmp );
|
OUTREG(regs, RADEON_DAC_CNTL, value);
|
||||||
|
|
||||||
// specs says that we should wait 1µs before checking but sample
|
// specs says that we should wait 1µs before checking but sample
|
||||||
// code uses 2 ms; we use long delay to be on safe side
|
// code uses 2 ms; we use long delay to be on safe side
|
||||||
@@ -134,9 +135,10 @@ static bool Radeon_DetectCRTInt( accelerator_info *ai, bool colour )
|
|||||||
|
|
||||||
// let's see whether there is some
|
// let's see whether there is some
|
||||||
found = (INREG(regs, RADEON_DAC_CNTL) & RADEON_DAC_CMP_OUTPUT) != 0;
|
found = (INREG(regs, RADEON_DAC_CNTL) & RADEON_DAC_CMP_OUTPUT) != 0;
|
||||||
|
if (found) {
|
||||||
if( found )
|
SHOW_INFO(2, "Found %s CRT connected to CRT-DAC",
|
||||||
SHOW_INFO( 2, "Found %s CRT connected to CRT-DAC", colour ? "colour" : "b/w" );
|
colour ? "colour" : "b/w");
|
||||||
|
}
|
||||||
|
|
||||||
OUTREG(regs, RADEON_DAC_CNTL, old_dac_cntl);
|
OUTREG(regs, RADEON_DAC_CNTL, old_dac_cntl);
|
||||||
OUTREG(regs, RADEON_DAC_EXT_CNTL, old_dac_ext_cntl);
|
OUTREG(regs, RADEON_DAC_EXT_CNTL, old_dac_ext_cntl);
|
||||||
@@ -146,75 +148,70 @@ static bool Radeon_DetectCRTInt( accelerator_info *ai, bool colour )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// check whethere there is a CRT connected to CRT DAC
|
//! Check whethere there is a CRT connected to CRT DAC
|
||||||
static bool Radeon_DetectCRT( accelerator_info *ai )
|
static bool
|
||||||
|
Radeon_DetectCRT(accelerator_info *ai)
|
||||||
{
|
{
|
||||||
vuint32 old_vclk_ecp_cntl, tmp;
|
vuint32 old_vclk_ecp_cntl, value;
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
// enforce clock so the DAC gets activated
|
// enforce clock so the DAC gets activated
|
||||||
old_vclk_ecp_cntl = Radeon_INPLL( ai->regs, ai->si->asic, RADEON_VCLK_ECP_CNTL );
|
old_vclk_ecp_cntl = Radeon_INPLL(ai->regs, ai->si->asic,
|
||||||
|
RADEON_VCLK_ECP_CNTL);
|
||||||
|
|
||||||
tmp = old_vclk_ecp_cntl &
|
value = old_vclk_ecp_cntl
|
||||||
~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb);
|
& ~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb);
|
||||||
Radeon_OUTPLL( ai->regs, ai->si->asic, RADEON_VCLK_ECP_CNTL, tmp );
|
Radeon_OUTPLL(ai->regs, ai->si->asic, RADEON_VCLK_ECP_CNTL, value);
|
||||||
|
|
||||||
// search first for colour, then for B/W monitor
|
// search first for colour, then for B/W monitor
|
||||||
found = Radeon_DetectCRTInt(ai, true) || Radeon_DetectCRTInt(ai, false);
|
found = Radeon_DetectCRTInt(ai, true) || Radeon_DetectCRTInt(ai, false);
|
||||||
|
|
||||||
Radeon_OUTPLL( ai->regs, ai->si->asic, RADEON_VCLK_ECP_CNTL, old_vclk_ecp_cntl );
|
Radeon_OUTPLL(ai->regs, ai->si->asic, RADEON_VCLK_ECP_CNTL,
|
||||||
|
old_vclk_ecp_cntl);
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// CRT on TV-DAC detection for rv200 and below
|
//! CRT on TV-DAC detection for rv200 and below checked for rv200
|
||||||
// checked for rv200
|
static bool
|
||||||
static bool Radeon_DetectTVCRT_RV200( accelerator_info *ai )
|
Radeon_DetectTVCRT_RV200(accelerator_info *ai)
|
||||||
{
|
{
|
||||||
vuint8 *regs = ai->regs;
|
vuint8 *regs = ai->regs;
|
||||||
uint32 old_crtc2_gen_cntl, old_tv_dac_cntl, old_dac_cntl2, tmp;
|
uint32 old_crtc2_gen_cntl, old_tv_dac_cntl, old_dac_cntl2, value;
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
// enable CRTC2, setting 8 bpp (we just pick any valid value)
|
// enable CRTC2, setting 8 bpp (we just pick any valid value)
|
||||||
old_crtc2_gen_cntl = INREG(regs, RADEON_CRTC2_GEN_CNTL);
|
old_crtc2_gen_cntl = INREG(regs, RADEON_CRTC2_GEN_CNTL);
|
||||||
|
|
||||||
tmp = old_crtc2_gen_cntl & ~RADEON_CRTC2_PIX_WIDTH_MASK;
|
value = old_crtc2_gen_cntl & ~RADEON_CRTC2_PIX_WIDTH_MASK;
|
||||||
tmp |=
|
value |= RADEON_CRTC2_CRT2_ON | (2 << RADEON_CRTC2_PIX_WIDTH_SHIFT);
|
||||||
RADEON_CRTC2_CRT2_ON |
|
OUTREG(regs, RADEON_CRTC2_GEN_CNTL, value);
|
||||||
(2 << RADEON_CRTC2_PIX_WIDTH_SHIFT);
|
|
||||||
OUTREG( regs, RADEON_CRTC2_GEN_CNTL, tmp );
|
|
||||||
|
|
||||||
// enable TV-DAC, choosing VGA signal level
|
// enable TV-DAC, choosing VGA signal level
|
||||||
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
|
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
|
||||||
|
|
||||||
tmp =
|
value = RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD
|
||||||
RADEON_TV_DAC_CNTL_NBLANK |
|
| RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_STD_PS2;
|
||||||
RADEON_TV_DAC_CNTL_NHOLD |
|
OUTREG(regs, RADEON_TV_DAC_CNTL, value);
|
||||||
RADEON_TV_DAC_CNTL_DETECT |
|
|
||||||
RADEON_TV_DAC_CNTL_STD_PS2;
|
|
||||||
OUTREG( regs, RADEON_TV_DAC_CNTL, tmp );
|
|
||||||
|
|
||||||
// enforce constant DAC output voltage on RGB
|
// enforce constant DAC output voltage on RGB
|
||||||
tmp =
|
value = RADEON_DAC2_FORCE_BLANK_OFF_EN | RADEON_DAC2_FORCE_DATA_EN
|
||||||
RADEON_DAC2_FORCE_BLANK_OFF_EN |
|
| RADEON_DAC_FORCE_DATA_SEL_RGB
|
||||||
RADEON_DAC2_FORCE_DATA_EN |
|
| (0x180 << RADEON_DAC_FORCE_DATA_SHIFT);
|
||||||
RADEON_DAC_FORCE_DATA_SEL_RGB |
|
OUTREG(regs, RADEON_DAC_EXT_CNTL, value);
|
||||||
(0x180 << RADEON_DAC_FORCE_DATA_SHIFT);
|
|
||||||
OUTREG( regs, RADEON_DAC_EXT_CNTL, tmp );
|
|
||||||
|
|
||||||
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
|
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
|
||||||
|
|
||||||
// set DAC in CRT mode and enable detection
|
// set DAC in CRT mode and enable detection
|
||||||
// TODO: make sure we really use CRTC2 - this is ASIC dependant
|
// TODO: make sure we really use CRTC2 - this is ASIC dependant
|
||||||
tmp = old_dac_cntl2 | RADEON_DAC2_CLK_SEL_CRT | RADEON_DAC2_CMP_EN;
|
value = old_dac_cntl2 | RADEON_DAC2_CLK_SEL_CRT | RADEON_DAC2_CMP_EN;
|
||||||
OUTREG( regs, RADEON_DAC_CNTL2, tmp );
|
OUTREG(regs, RADEON_DAC_CNTL2, value);
|
||||||
|
|
||||||
snooze(10000);
|
snooze(10000);
|
||||||
|
|
||||||
// let's see what we've got!
|
// let's see what we've got!
|
||||||
found = (INREG(regs, RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUTPUT) != 0;
|
found = (INREG(regs, RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUTPUT) != 0;
|
||||||
|
|
||||||
if (found)
|
if (found)
|
||||||
SHOW_INFO0(2, "Found CRT connected to TV-DAC, i.e. DVI port");
|
SHOW_INFO0(2, "Found CRT connected to TV-DAC, i.e. DVI port");
|
||||||
|
|
||||||
@@ -226,12 +223,13 @@ static bool Radeon_DetectTVCRT_RV200( accelerator_info *ai )
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRT on TV-DAC detection for r300
|
|
||||||
// checked for r300
|
//! CRT on TV-DAC detection for r300 checked for r300
|
||||||
static bool Radeon_DetectTVCRT_R300( accelerator_info *ai )
|
static bool
|
||||||
|
Radeon_DetectTVCRT_R300(accelerator_info *ai)
|
||||||
{
|
{
|
||||||
vuint8 *regs = ai->regs;
|
vuint8 *regs = ai->regs;
|
||||||
uint32 old_crtc2_gen_cntl, old_tv_dac_cntl, old_dac_cntl2, tmp;
|
uint32 old_crtc2_gen_cntl, old_tv_dac_cntl, old_dac_cntl2, value;
|
||||||
uint32 old_radeon_gpiopad_a;
|
uint32 old_radeon_gpiopad_a;
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
@@ -247,34 +245,29 @@ static bool Radeon_DetectTVCRT_R300( accelerator_info *ai )
|
|||||||
// enable DAC, choose valid pixel format and enable DPMS
|
// enable DAC, choose valid pixel format and enable DPMS
|
||||||
// as usual, the code doesn't take into account whether the TV-DAC
|
// as usual, the code doesn't take into account whether the TV-DAC
|
||||||
// does really use CRTC2
|
// does really use CRTC2
|
||||||
tmp = old_crtc2_gen_cntl;
|
value = old_crtc2_gen_cntl;
|
||||||
tmp &= ~RADEON_CRTC2_PIX_WIDTH_MASK;
|
value &= ~RADEON_CRTC2_PIX_WIDTH_MASK;
|
||||||
tmp |=
|
value |= (2 << RADEON_CRTC2_PIX_WIDTH_SHIFT) | RADEON_CRTC2_CRT2_ON
|
||||||
(2 << RADEON_CRTC2_PIX_WIDTH_SHIFT) |
|
| RADEON_CRTC2_VSYNC_TRISTAT;
|
||||||
RADEON_CRTC2_CRT2_ON | RADEON_CRTC2_VSYNC_TRISTAT;
|
OUTREG(regs, RADEON_CRTC2_GEN_CNTL, value);
|
||||||
|
|
||||||
OUTREG( regs, RADEON_CRTC2_GEN_CNTL, tmp );
|
|
||||||
|
|
||||||
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
|
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
|
||||||
|
|
||||||
// enable TV-DAC
|
// enable TV-DAC
|
||||||
OUTREG( regs, RADEON_TV_DAC_CNTL,
|
OUTREG(regs, RADEON_TV_DAC_CNTL, RADEON_TV_DAC_CNTL_NBLANK
|
||||||
RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD |
|
| RADEON_TV_DAC_CNTL_NHOLD | RADEON_TV_DAC_CNTL_DETECT
|
||||||
RADEON_TV_DAC_CNTL_DETECT |
|
| RADEON_TV_DAC_CNTL_STD_PS2);
|
||||||
RADEON_TV_DAC_CNTL_STD_PS2 );
|
|
||||||
|
|
||||||
// force constant voltage output of DAC for impedance test
|
// force constant voltage output of DAC for impedance test
|
||||||
OUTREG( regs, RADEON_DAC_EXT_CNTL,
|
OUTREG(regs, RADEON_DAC_EXT_CNTL, RADEON_DAC2_FORCE_BLANK_OFF_EN
|
||||||
RADEON_DAC2_FORCE_BLANK_OFF_EN | RADEON_DAC2_FORCE_DATA_EN |
|
| RADEON_DAC2_FORCE_DATA_EN | RADEON_DAC_FORCE_DATA_SEL_RGB
|
||||||
RADEON_DAC_FORCE_DATA_SEL_RGB |
|
| (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT));
|
||||||
(0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT ));
|
|
||||||
|
|
||||||
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
|
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
|
||||||
|
|
||||||
// enable CRT mode of TV-DAC and enable comparator
|
// enable CRT mode of TV-DAC and enable comparator
|
||||||
tmp = old_dac_cntl2 | RADEON_DAC2_CLK_SEL_CRT | RADEON_DAC2_CMP_EN;
|
OUTREG(regs, RADEON_DAC_CNTL2, old_dac_cntl2 | RADEON_DAC2_CLK_SEL_CRT
|
||||||
|
| RADEON_DAC2_CMP_EN);
|
||||||
OUTREG( regs, RADEON_DAC_CNTL2, tmp );
|
|
||||||
|
|
||||||
snooze(10000);
|
snooze(10000);
|
||||||
|
|
||||||
@@ -293,8 +286,9 @@ static bool Radeon_DetectTVCRT_R300( accelerator_info *ai )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// check whether there is a CRT connected to TV-DAC
|
//! Check whether there is a CRT connected to TV-DAC
|
||||||
static bool Radeon_DetectTVCRT( accelerator_info *ai )
|
static bool
|
||||||
|
Radeon_DetectTVCRT(accelerator_info *ai)
|
||||||
{
|
{
|
||||||
if (ai->si->is_mobility)
|
if (ai->si->is_mobility)
|
||||||
return dd_none;
|
return dd_none;
|
||||||
@@ -333,14 +327,14 @@ static bool Radeon_DetectTVCRT( accelerator_info *ai )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TV detection for rv200 and below
|
//! TV detection for rv200 and below should work for M6 and RV200
|
||||||
// should work for M6 and RV200
|
static display_device_e
|
||||||
static display_device_e Radeon_DetectTV_RV200( accelerator_info *ai, bool tv_crt_found )
|
Radeon_DetectTV_RV200(accelerator_info *ai, bool tv_crt_found)
|
||||||
{
|
{
|
||||||
vuint8 *regs = ai->regs;
|
vuint8 *regs = ai->regs;
|
||||||
uint32
|
uint32 value, old_dac_cntl2, old_crtc_ext_cntl, old_crtc2_gen_cntl;
|
||||||
tmp, old_dac_cntl2, old_crtc_ext_cntl, old_crtc2_gen_cntl, old_tv_master_cntl,
|
uint32 old_tv_master_cntl, old_tv_dac_cntl, old_pre_dac_mux_cntl;
|
||||||
old_tv_dac_cntl, old_pre_dac_mux_cntl, config_cntl;
|
uint32 config_cntl;
|
||||||
display_device_e displays = dd_none;
|
display_device_e displays = dd_none;
|
||||||
|
|
||||||
// give up if there is a CRT connected to TV-DAC
|
// give up if there is a CRT connected to TV-DAC
|
||||||
@@ -349,60 +343,60 @@ static display_device_e Radeon_DetectTV_RV200( accelerator_info *ai, bool tv_crt
|
|||||||
|
|
||||||
// enable TV mode
|
// enable TV mode
|
||||||
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
|
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
|
||||||
tmp = old_dac_cntl2 & ~RADEON_DAC2_CLK_SEL_CRT;
|
value = old_dac_cntl2 & ~RADEON_DAC2_CLK_SEL_CRT;
|
||||||
OUTREG( regs, RADEON_DAC_CNTL2, tmp );
|
OUTREG(regs, RADEON_DAC_CNTL2, value);
|
||||||
|
|
||||||
old_crtc_ext_cntl = INREG(regs, RADEON_CRTC_EXT_CNTL);
|
old_crtc_ext_cntl = INREG(regs, RADEON_CRTC_EXT_CNTL);
|
||||||
old_crtc2_gen_cntl = INREG(regs, RADEON_CRTC2_GEN_CNTL);
|
old_crtc2_gen_cntl = INREG(regs, RADEON_CRTC2_GEN_CNTL);
|
||||||
old_tv_master_cntl = INREG(regs, RADEON_TV_MASTER_CNTL);
|
old_tv_master_cntl = INREG(regs, RADEON_TV_MASTER_CNTL);
|
||||||
|
|
||||||
// enable TV output
|
// enable TV output
|
||||||
tmp = old_tv_master_cntl | RADEON_TV_MASTER_CNTL_TV_ON;
|
value = old_tv_master_cntl | RADEON_TV_MASTER_CNTL_TV_ON;
|
||||||
tmp &= ~(
|
value &= ~(
|
||||||
RADEON_TV_MASTER_CNTL_TV_ASYNC_RST |
|
RADEON_TV_MASTER_CNTL_TV_ASYNC_RST |
|
||||||
RADEON_TV_MASTER_CNTL_RESTART_PHASE_FIX |
|
RADEON_TV_MASTER_CNTL_RESTART_PHASE_FIX |
|
||||||
RADEON_TV_MASTER_CNTL_CRT_FIFO_CE_EN |
|
RADEON_TV_MASTER_CNTL_CRT_FIFO_CE_EN |
|
||||||
RADEON_TV_MASTER_CNTL_TV_FIFO_CE_EN |
|
RADEON_TV_MASTER_CNTL_TV_FIFO_CE_EN |
|
||||||
RADEON_TV_MASTER_CNTL_RE_SYNC_NOW_SEL_MASK);
|
RADEON_TV_MASTER_CNTL_RE_SYNC_NOW_SEL_MASK);
|
||||||
tmp |=
|
value |=
|
||||||
RADEON_TV_MASTER_CNTL_TV_FIFO_ASYNC_RST |
|
RADEON_TV_MASTER_CNTL_TV_FIFO_ASYNC_RST |
|
||||||
RADEON_TV_MASTER_CNTL_CRT_ASYNC_RST;
|
RADEON_TV_MASTER_CNTL_CRT_ASYNC_RST;
|
||||||
OUTREG( regs, RADEON_TV_MASTER_CNTL, tmp );
|
OUTREG(regs, RADEON_TV_MASTER_CNTL, value);
|
||||||
|
|
||||||
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
|
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
|
||||||
|
|
||||||
config_cntl = INREG(regs, RADEON_CONFIG_CNTL);
|
config_cntl = INREG(regs, RADEON_CONFIG_CNTL);
|
||||||
|
|
||||||
// unlock TV DAC
|
// unlock TV DAC
|
||||||
tmp =
|
value =
|
||||||
RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD |
|
RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD |
|
||||||
RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_STD_NTSC |
|
RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_STD_NTSC |
|
||||||
(8 << RADEON_TV_DAC_CNTL_BGADJ_SHIFT) |
|
(8 << RADEON_TV_DAC_CNTL_BGADJ_SHIFT) |
|
||||||
((((config_cntl & RADEON_CFG_ATI_REV_ID_MASK) == 0) ? 8 : 4) << RADEON_TV_DAC_CNTL_DACADJ_SHIFT);
|
((((config_cntl & RADEON_CFG_ATI_REV_ID_MASK) == 0) ? 8 : 4) << RADEON_TV_DAC_CNTL_DACADJ_SHIFT);
|
||||||
OUTREG( regs, RADEON_TV_DAC_CNTL, tmp );
|
OUTREG(regs, RADEON_TV_DAC_CNTL, value);
|
||||||
|
|
||||||
old_pre_dac_mux_cntl = INREG(regs, RADEON_TV_PRE_DAC_MUX_CNTL);
|
old_pre_dac_mux_cntl = INREG(regs, RADEON_TV_PRE_DAC_MUX_CNTL);
|
||||||
|
|
||||||
// force constant DAC output voltage
|
// force constant DAC output voltage
|
||||||
tmp =
|
value =
|
||||||
RADEON_TV_PRE_DAC_MUX_CNTL_C_GRN_EN | RADEON_TV_PRE_DAC_MUX_CNTL_CMP_BLU_EN |
|
RADEON_TV_PRE_DAC_MUX_CNTL_C_GRN_EN | RADEON_TV_PRE_DAC_MUX_CNTL_CMP_BLU_EN |
|
||||||
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_RED_MX_SHIFT) |
|
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_RED_MX_SHIFT) |
|
||||||
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_GRN_MX_SHIFT) |
|
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_GRN_MX_SHIFT) |
|
||||||
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_BLU_MX_SHIFT) |
|
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_BLU_MX_SHIFT) |
|
||||||
(0x109 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT);
|
(0x109 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT);
|
||||||
OUTREG( regs, RADEON_TV_PRE_DAC_MUX_CNTL, tmp );
|
OUTREG(regs, RADEON_TV_PRE_DAC_MUX_CNTL, value);
|
||||||
|
|
||||||
// let things settle a bit
|
// let things settle a bit
|
||||||
snooze(3000);
|
snooze(3000);
|
||||||
|
|
||||||
// now see which wires are connected
|
// now see which wires are connected
|
||||||
tmp = INREG( regs, RADEON_TV_DAC_CNTL );
|
value = INREG(regs, RADEON_TV_DAC_CNTL);
|
||||||
if( (tmp & RADEON_TV_DAC_CNTL_GDACDET) != 0 ) {
|
if ((value & RADEON_TV_DAC_CNTL_GDACDET) != 0) {
|
||||||
displays |= dd_stv;
|
displays |= dd_stv;
|
||||||
SHOW_INFO0(2, "S-Video TV-Out is connected");
|
SHOW_INFO0(2, "S-Video TV-Out is connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
if( (tmp & RADEON_TV_DAC_CNTL_BDACDET) != 0 ) {
|
if ((value & RADEON_TV_DAC_CNTL_BDACDET) != 0) {
|
||||||
displays |= dd_ctv;
|
displays |= dd_ctv;
|
||||||
SHOW_INFO0(2, "Composite TV-Out is connected");
|
SHOW_INFO0(2, "Composite TV-Out is connected");
|
||||||
}
|
}
|
||||||
@@ -553,34 +547,37 @@ static bool readTVDetect( accelerator_info *ai )
|
|||||||
} while (system_time() - start_time < 1000000);
|
} while (system_time() - start_time < 1000000);
|
||||||
|
|
||||||
SHOW_FLOW0(2, "timeout");
|
SHOW_FLOW0(2, "timeout");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// detect TV connected to external Theatre-Out
|
//! Detect TV connected to external Theatre-Out
|
||||||
static display_device_e Radeon_DetectTV_Theatre( accelerator_info *ai )
|
static display_device_e
|
||||||
|
Radeon_DetectTV_Theatre(accelerator_info *ai)
|
||||||
{
|
{
|
||||||
uint32
|
uint32 old_tv_dac_cntl, old_pre_dac_mux_cntl, old_modulator_cntl1;
|
||||||
old_tv_dac_cntl, old_pre_dac_mux_cntl, old_modulator_cntl1, old_master_cntl;
|
uint32 old_master_cntl;
|
||||||
uint32
|
uint32 uv_adr, old_last_fifo_entry, old_mid_fifo_entry, last_fifo_addr;
|
||||||
uv_adr, old_last_fifo_entry, old_mid_fifo_entry, last_fifo_addr;
|
|
||||||
display_device_e displays = dd_none;
|
display_device_e displays = dd_none;
|
||||||
|
|
||||||
if (ai->si->tv_chip != tc_external_rt1)
|
if (ai->si->tv_chip != tc_external_rt1)
|
||||||
return dd_none;
|
return dd_none;
|
||||||
|
|
||||||
// save previous values (TV-Out may be running)
|
// save previous values (TV-Out may be running)
|
||||||
Radeon_VIPRead( ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL, &old_tv_dac_cntl );
|
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
|
||||||
|
&old_tv_dac_cntl);
|
||||||
|
|
||||||
// enable DAC and comparators
|
// enable DAC and comparators
|
||||||
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
|
||||||
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT |
|
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT
|
||||||
RADEON_TV_DAC_CNTL_NHOLD | RADEON_TV_DAC_CNTL_NBLANK );
|
| RADEON_TV_DAC_CNTL_NHOLD | RADEON_TV_DAC_CNTL_NBLANK);
|
||||||
|
|
||||||
Radeon_VIPRead( ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL, &old_pre_dac_mux_cntl );
|
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
|
||||||
Radeon_VIPRead( ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1, &old_modulator_cntl1 );
|
&old_pre_dac_mux_cntl);
|
||||||
Radeon_VIPRead( ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL, &old_master_cntl );
|
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1,
|
||||||
|
&old_modulator_cntl1);
|
||||||
|
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
|
||||||
|
&old_master_cntl);
|
||||||
|
|
||||||
// save output timing
|
// save output timing
|
||||||
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_UV_ADR, &uv_adr);
|
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_UV_ADR, &uv_adr);
|
||||||
@@ -595,26 +592,26 @@ static display_device_e Radeon_DetectTV_Theatre( accelerator_info *ai )
|
|||||||
|
|
||||||
// stop TV-Out to savely program it
|
// stop TV-Out to savely program it
|
||||||
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
|
||||||
RADEON_TV_MASTER_CNTL_TV_FIFO_ASYNC_RST | RADEON_TV_MASTER_CNTL_TV_ASYNC_RST );
|
RADEON_TV_MASTER_CNTL_TV_FIFO_ASYNC_RST
|
||||||
|
| RADEON_TV_MASTER_CNTL_TV_ASYNC_RST);
|
||||||
|
|
||||||
// set constant base level
|
// set constant base level
|
||||||
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1,
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1,
|
||||||
(0x2c << RADEON_TV_MODULATOR_CNTL1_SET_UP_LEVEL_SHIFT) |
|
(0x2c << RADEON_TV_MODULATOR_CNTL1_SET_UP_LEVEL_SHIFT)
|
||||||
(0x2c << RADEON_TV_MODULATOR_CNTL1_BLANK_LEVEL_SHIFT) );
|
| (0x2c << RADEON_TV_MODULATOR_CNTL1_BLANK_LEVEL_SHIFT));
|
||||||
|
|
||||||
// enable output
|
// enable output
|
||||||
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
|
||||||
RADEON_TV_MASTER_CNTL_TV_ASYNC_RST);
|
RADEON_TV_MASTER_CNTL_TV_ASYNC_RST);
|
||||||
|
|
||||||
Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL, 0);
|
||||||
0 );
|
|
||||||
|
|
||||||
// set constant Composite output
|
// set constant Composite output
|
||||||
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
|
||||||
RADEON_TV_PRE_DAC_MUX_CNTL_CMP_BLU_EN |
|
RADEON_TV_PRE_DAC_MUX_CNTL_CMP_BLU_EN
|
||||||
RADEON_TV_PRE_DAC_MUX_CNTL_DAC_DITHER_EN |
|
| RADEON_TV_PRE_DAC_MUX_CNTL_DAC_DITHER_EN
|
||||||
(9 << RADEON_TV_PRE_DAC_MUX_CNTL_BLU_MX_SHIFT) |
|
| (9 << RADEON_TV_PRE_DAC_MUX_CNTL_BLU_MX_SHIFT)
|
||||||
(0xa8 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT) );
|
| (0xa8 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT));
|
||||||
|
|
||||||
// check for S-Video connection
|
// check for S-Video connection
|
||||||
if (readTVDetect(ai)) {
|
if (readTVDetect(ai)) {
|
||||||
@@ -624,15 +621,15 @@ static display_device_e Radeon_DetectTV_Theatre( accelerator_info *ai )
|
|||||||
|
|
||||||
// enable output changes
|
// enable output changes
|
||||||
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
|
||||||
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_NBLANK |
|
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT
|
||||||
RADEON_TV_DAC_CNTL_NHOLD );
|
| RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD);
|
||||||
|
|
||||||
// set constant Y-output of S-Video adapter
|
// set constant Y-output of S-Video adapter
|
||||||
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
|
||||||
RADEON_TV_PRE_DAC_MUX_CNTL_Y_RED_EN |
|
RADEON_TV_PRE_DAC_MUX_CNTL_Y_RED_EN
|
||||||
RADEON_TV_PRE_DAC_MUX_CNTL_DAC_DITHER_EN |
|
| RADEON_TV_PRE_DAC_MUX_CNTL_DAC_DITHER_EN
|
||||||
(9 << RADEON_TV_PRE_DAC_MUX_CNTL_RED_MX_SHIFT) |
|
| (9 << RADEON_TV_PRE_DAC_MUX_CNTL_RED_MX_SHIFT)
|
||||||
(0xa8 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT) );
|
| (0xa8 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT));
|
||||||
|
|
||||||
// check for composite connection
|
// check for composite connection
|
||||||
if (readTVDetect(ai)) {
|
if (readTVDetect(ai)) {
|
||||||
@@ -644,17 +641,25 @@ static display_device_e Radeon_DetectTV_Theatre( accelerator_info *ai )
|
|||||||
Radeon_TheatreWriteFIFO(ai, last_fifo_addr, old_last_fifo_entry);
|
Radeon_TheatreWriteFIFO(ai, last_fifo_addr, old_last_fifo_entry);
|
||||||
Radeon_TheatreWriteFIFO(ai, 0x18f, old_mid_fifo_entry);
|
Radeon_TheatreWriteFIFO(ai, 0x18f, old_mid_fifo_entry);
|
||||||
|
|
||||||
Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL, old_master_cntl );
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
|
||||||
Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1, old_modulator_cntl1 );
|
old_master_cntl);
|
||||||
Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL, old_pre_dac_mux_cntl );
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1,
|
||||||
Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL, old_tv_dac_cntl );
|
old_modulator_cntl1);
|
||||||
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
|
||||||
|
old_pre_dac_mux_cntl);
|
||||||
|
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
|
||||||
|
old_tv_dac_cntl);
|
||||||
|
|
||||||
return displays;
|
return displays;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether there is a TV connected to TV-DAC
|
|
||||||
// returns bit set, i.e. there can be S-Video or composite or both
|
/*!
|
||||||
static display_device_e Radeon_DetectTV( accelerator_info *ai, bool tv_crt_found )
|
Check whether there is a TV connected to TV-DAC
|
||||||
|
returns bit set, i.e. there can be S-Video or composite or both
|
||||||
|
*/
|
||||||
|
static display_device_e
|
||||||
|
Radeon_DetectTV(accelerator_info *ai, bool tv_crt_found)
|
||||||
{
|
{
|
||||||
switch (ai->si->asic) {
|
switch (ai->si->asic) {
|
||||||
case rt_r100:
|
case rt_r100:
|
||||||
@@ -683,9 +688,10 @@ static display_device_e Radeon_DetectTV( accelerator_info *ai, bool tv_crt_found
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// get native monitor timing, using Detailed Monitor Description
|
//! Get native monitor timing, using Detailed Monitor Description
|
||||||
static void Radeon_FindFPTiming_DetailedMonitorDesc(
|
static void
|
||||||
const edid1_info *edid, fp_info *fp, uint32 *max_hsize, uint32 *max_vsize )
|
Radeon_FindFPTiming_DetailedMonitorDesc(const edid1_info *edid, fp_info *fp,
|
||||||
|
uint32 *max_hsize, uint32 *max_vsize)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -718,17 +724,19 @@ static void Radeon_FindFPTiming_DetailedMonitorDesc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get native monitor timing, using Standard Timing table;
|
|
||||||
// this table doesn't contain the actual timing, so we try to find a
|
/*! Get native monitor timing, using Standard Timing table;
|
||||||
// appropriate VESA modes for the resolutions given in the table
|
this table doesn't contain the actual timing, so we try to find a
|
||||||
static void Radeon_FindFPTiming_StandardTiming(
|
appropriate VESA modes for the resolutions given in the table
|
||||||
const edid1_info *edid, fp_info *fp, uint32 *max_hsize, uint32 *max_vsize )
|
*/
|
||||||
|
static void
|
||||||
|
Radeon_FindFPTiming_StandardTiming(const edid1_info *edid, fp_info *fp,
|
||||||
|
uint32 *max_hsize, uint32 *max_vsize)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < EDID1_NUM_STD_TIMING; ++i) {
|
for (i = 0; i < EDID1_NUM_STD_TIMING; ++i) {
|
||||||
const edid1_std_timing *std_timing = &edid->std_timing[i];
|
const edid1_std_timing *std_timing = &edid->std_timing[i];
|
||||||
|
|
||||||
int best_fit = -1;
|
int best_fit = -1;
|
||||||
int best_refresh_deviation = 10000;
|
int best_refresh_deviation = 10000;
|
||||||
int j;
|
int j;
|
||||||
@@ -739,20 +747,20 @@ static void Radeon_FindFPTiming_StandardTiming(
|
|||||||
for (j = 0; j < (int)vesa_mode_list_count; ++j) {
|
for (j = 0; j < (int)vesa_mode_list_count; ++j) {
|
||||||
int refresh_rate, cur_refresh_deviation;
|
int refresh_rate, cur_refresh_deviation;
|
||||||
|
|
||||||
if( vesa_mode_list[j].h_display != std_timing->h_size ||
|
if (vesa_mode_list[j].h_display != std_timing->h_size
|
||||||
vesa_mode_list[j].v_display != std_timing->v_size )
|
|| vesa_mode_list[j].v_display != std_timing->v_size)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// take pixel_clock times 1000 because is is in kHz
|
// take pixel_clock times 1000 because is is in kHz
|
||||||
// further, take it times 1000 again, to get 1/1000 frames
|
// further, take it times 1000 again, to get 1/1000 frames
|
||||||
// as refresh rate
|
// as refresh rate
|
||||||
refresh_rate = (int64)vesa_mode_list[j].pixel_clock * 1000*1000 /
|
refresh_rate = (int64)vesa_mode_list[j].pixel_clock * 1000*1000
|
||||||
(vesa_mode_list[j].h_total * vesa_mode_list[j].v_total);
|
/ (vesa_mode_list[j].h_total * vesa_mode_list[j].v_total);
|
||||||
|
|
||||||
// standard timing is in frames, so multiple by it to get 1/1000 frames
|
// standard timing is in frames, so multiple by it to get 1/1000 frames
|
||||||
// result is scaled by 100 to get difference in percentage;
|
// result is scaled by 100 to get difference in percentage;
|
||||||
cur_refresh_deviation =
|
cur_refresh_deviation = (100 * (refresh_rate - std_timing->refresh
|
||||||
(100 * (refresh_rate - std_timing->refresh * 1000)) / refresh_rate;
|
* 1000)) / refresh_rate;
|
||||||
|
|
||||||
if (cur_refresh_deviation < 0)
|
if (cur_refresh_deviation < 0)
|
||||||
cur_refresh_deviation = -cur_refresh_deviation;
|
cur_refresh_deviation = -cur_refresh_deviation;
|
||||||
@@ -760,9 +768,8 @@ static void Radeon_FindFPTiming_StandardTiming(
|
|||||||
// less then 1 percent difference is (hopefully) OK,
|
// less then 1 percent difference is (hopefully) OK,
|
||||||
// if there are multiple, we take best one
|
// if there are multiple, we take best one
|
||||||
// (if the screen is that picky, it should have defined an enhanced timing)
|
// (if the screen is that picky, it should have defined an enhanced timing)
|
||||||
if( cur_refresh_deviation < 1 &&
|
if (cur_refresh_deviation < 1
|
||||||
cur_refresh_deviation < best_refresh_deviation )
|
&& cur_refresh_deviation < best_refresh_deviation) {
|
||||||
{
|
|
||||||
best_fit = j;
|
best_fit = j;
|
||||||
best_refresh_deviation = cur_refresh_deviation;
|
best_refresh_deviation = cur_refresh_deviation;
|
||||||
}
|
}
|
||||||
@@ -799,8 +806,10 @@ static void Radeon_FindFPTiming_StandardTiming(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read edid data of flat panel and setup its timing accordingly
|
|
||||||
static status_t Radeon_StoreFPEDID( accelerator_info *ai, int port, const edid1_info *edid )
|
//! Read edid data of flat panel and setup its timing accordingly
|
||||||
|
static status_t
|
||||||
|
Radeon_StoreFPEDID(accelerator_info *ai, int port, const edid1_info *edid)
|
||||||
{
|
{
|
||||||
fp_info *fp = &ai->si->flatpanels[port];
|
fp_info *fp = &ai->si->flatpanels[port];
|
||||||
uint32 max_hsize, max_vsize;
|
uint32 max_hsize, max_vsize;
|
||||||
@@ -810,15 +819,12 @@ static status_t Radeon_StoreFPEDID( accelerator_info *ai, int port, const edid1_
|
|||||||
|
|
||||||
// find detailed timing with maximum resolution
|
// find detailed timing with maximum resolution
|
||||||
max_hsize = max_vsize = 0;
|
max_hsize = max_vsize = 0;
|
||||||
|
|
||||||
Radeon_FindFPTiming_DetailedMonitorDesc(edid, fp, &max_hsize, &max_vsize);
|
Radeon_FindFPTiming_DetailedMonitorDesc(edid, fp, &max_hsize, &max_vsize);
|
||||||
|
|
||||||
if (max_hsize == 0) {
|
if (max_hsize == 0) {
|
||||||
SHOW_FLOW0(2, "Timing is not explicitely defined in DDC - checking standard modes");
|
SHOW_FLOW0(2, "Timing is not explicitely defined in DDC - checking standard modes");
|
||||||
|
|
||||||
Radeon_FindFPTiming_StandardTiming(
|
Radeon_FindFPTiming_StandardTiming(edid, fp, &max_hsize, &max_vsize);
|
||||||
edid, fp, &max_hsize, &max_vsize );
|
|
||||||
|
|
||||||
if (max_hsize == 0) {
|
if (max_hsize == 0) {
|
||||||
SHOW_FLOW0(2, "Still found no valid native mode, disabling DVI");
|
SHOW_FLOW0(2, "Still found no valid native mode, disabling DVI");
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@@ -834,62 +840,71 @@ static status_t Radeon_StoreFPEDID( accelerator_info *ai, int port, const edid1_
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Radeon_ConnectorInfo( accelerator_info *ai, int port, disp_entity* ptr_entity )
|
|
||||||
|
static void
|
||||||
|
Radeon_ConnectorInfo(accelerator_info *ai, int port, disp_entity* ptr_entity)
|
||||||
{
|
{
|
||||||
const char* mon;
|
const char* mon;
|
||||||
const char* ddc = ptr_entity->port_info[port].ddc_type == ddc_none_detected ? "None" :
|
const char* ddc = ptr_entity->port_info[port].ddc_type == ddc_none_detected
|
||||||
ptr_entity->port_info[port].ddc_type == ddc_monid ? "Mon ID" :
|
? "None" : ptr_entity->port_info[port].ddc_type == ddc_monid
|
||||||
ptr_entity->port_info[port].ddc_type == ddc_dvi ? "DVI DDC" :
|
? "Mon ID" : ptr_entity->port_info[port].ddc_type == ddc_dvi
|
||||||
ptr_entity->port_info[port].ddc_type == ddc_vga ? "VGA DDC" :
|
? "DVI DDC" : ptr_entity->port_info[port].ddc_type == ddc_vga
|
||||||
ptr_entity->port_info[port].ddc_type == ddc_crt2 ? "CRT2 DDC" : "Error";
|
? "VGA DDC" : ptr_entity->port_info[port].ddc_type == ddc_crt2
|
||||||
|
? "CRT2 DDC" : "Error";
|
||||||
const char* tmds = ptr_entity->port_info[port].tmds_type == tmds_unknown ? "None" :
|
const char* tmds = ptr_entity->port_info[port].tmds_type == tmds_unknown
|
||||||
ptr_entity->port_info[port].tmds_type == tmds_int ? "Internal" :
|
? "None" : ptr_entity->port_info[port].tmds_type == tmds_int
|
||||||
ptr_entity->port_info[port].tmds_type == tmds_ext ? "External" : "??? ";
|
? "Internal" : ptr_entity->port_info[port].tmds_type == tmds_ext
|
||||||
|
? "External" : "??? ";
|
||||||
const char* dac = ptr_entity->port_info[port].dac_type == dac_unknown ? "Unknown" :
|
const char* dac = ptr_entity->port_info[port].dac_type == dac_unknown
|
||||||
ptr_entity->port_info[port].dac_type == dac_primary ? "Primary" :
|
? "Unknown" : ptr_entity->port_info[port].dac_type == dac_primary
|
||||||
ptr_entity->port_info[port].dac_type == dac_tvdac ? "TV / External" : "Error";
|
? "Primary" : ptr_entity->port_info[port].dac_type == dac_tvdac
|
||||||
|
? "TV / External" : "Error";
|
||||||
const char* con;
|
const char* con;
|
||||||
|
|
||||||
if (ai->si->is_atombios) {
|
if (ai->si->is_atombios) {
|
||||||
con = ptr_entity->port_info[port].connector_type == connector_none_atom ? "None" :
|
con = ptr_entity->port_info[port].connector_type == connector_none_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_vga_atom ? "VGA" :
|
? "None" : ptr_entity->port_info[port].connector_type == connector_vga_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_dvi_i_atom ? "DVI-I" :
|
? "VGA" : ptr_entity->port_info[port].connector_type == connector_dvi_i_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_dvi_d_atom ? "DVI-D" :
|
? "DVI-I" : ptr_entity->port_info[port].connector_type == connector_dvi_d_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_dvi_a_atom ? "DVI-A" :
|
? "DVI-D" : ptr_entity->port_info[port].connector_type == connector_dvi_a_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_stv_atom ? "S-Video TV" :
|
? "DVI-A" : ptr_entity->port_info[port].connector_type == connector_stv_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_ctv_atom ? "Composite TV" :
|
? "S-Video TV" : ptr_entity->port_info[port].connector_type == connector_ctv_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_lvds_atom ? "LVDS" :
|
? "Composite TV" : ptr_entity->port_info[port].connector_type == connector_lvds_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_digital_atom ? "Digital" :
|
? "LVDS" : ptr_entity->port_info[port].connector_type == connector_digital_atom
|
||||||
ptr_entity->port_info[port].connector_type == connector_unsupported_atom ? "N/A " : "Err ";
|
? "Digital" : ptr_entity->port_info[port].connector_type == connector_unsupported_atom
|
||||||
|
? "N/A " : "Err ";
|
||||||
} else {
|
} else {
|
||||||
con = ptr_entity->port_info[port].connector_type == connector_none ? "None" :
|
con = ptr_entity->port_info[port].connector_type == connector_none
|
||||||
ptr_entity->port_info[port].connector_type == connector_crt ? "VGA" :
|
? "None" : ptr_entity->port_info[port].connector_type == connector_crt
|
||||||
ptr_entity->port_info[port].connector_type == connector_dvi_i ? "DVI-I" :
|
? "VGA" : ptr_entity->port_info[port].connector_type == connector_dvi_i
|
||||||
ptr_entity->port_info[port].connector_type == connector_dvi_d ? "DVI-D" :
|
? "DVI-I" : ptr_entity->port_info[port].connector_type == connector_dvi_d
|
||||||
ptr_entity->port_info[port].connector_type == connector_proprietary ? "Proprietary" :
|
? "DVI-D" : ptr_entity->port_info[port].connector_type == connector_proprietary
|
||||||
ptr_entity->port_info[port].connector_type == connector_stv ? "S-Video TV" :
|
? "Proprietary" : ptr_entity->port_info[port].connector_type == connector_stv
|
||||||
ptr_entity->port_info[port].connector_type == connector_ctv ? "Composite TV" :
|
? "S-Video TV" : ptr_entity->port_info[port].connector_type == connector_ctv
|
||||||
ptr_entity->port_info[port].connector_type == connector_unsupported ? "N/A" : "Err";
|
? "Composite TV" : ptr_entity->port_info[port].connector_type == connector_unsupported
|
||||||
|
? "N/A" : "Err";
|
||||||
}
|
}
|
||||||
|
|
||||||
mon = ptr_entity->port_info[port].mon_type == mt_unknown ? "???" :
|
mon = ptr_entity->port_info[port].mon_type == mt_unknown ? "???"
|
||||||
ptr_entity->port_info[port].mon_type == mt_none ? "None" :
|
: ptr_entity->port_info[port].mon_type == mt_none ? "None"
|
||||||
ptr_entity->port_info[port].mon_type == mt_crt ? "CRT " :
|
: ptr_entity->port_info[port].mon_type == mt_crt ? "CRT "
|
||||||
ptr_entity->port_info[port].mon_type == mt_lcd ? "LCD " :
|
: ptr_entity->port_info[port].mon_type == mt_lcd ? "LCD "
|
||||||
ptr_entity->port_info[port].mon_type == mt_dfp ? "DVI " :
|
: ptr_entity->port_info[port].mon_type == mt_dfp ? "DVI "
|
||||||
ptr_entity->port_info[port].mon_type == mt_ctv ? "Composite TV" :
|
: ptr_entity->port_info[port].mon_type == mt_ctv ? "Composite TV"
|
||||||
ptr_entity->port_info[port].mon_type == mt_stv ? "S-Video TV" : "Err ?";
|
: ptr_entity->port_info[port].mon_type == mt_stv ? "S-Video TV"
|
||||||
|
: "Err ?";
|
||||||
|
|
||||||
SHOW_INFO(2, "Port %d:- \nMonitor: %s\nConn Type: %s\nDDC Port: %s\nTMDS Type: %s\nDAC Type: %s",
|
SHOW_INFO(2, "Port %d:- \nMonitor: %s\nConn Type: %s\nDDC Port: %s\nTMDS Type: %s\nDAC Type: %s",
|
||||||
port, mon, con, ddc, tmds, dac);
|
port, mon, con, ddc, tmds, dac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// detect connected displays devices
|
/*!
|
||||||
// whished_num_heads - how many heads the requested display mode needs
|
Detect connected displays devices
|
||||||
void Radeon_DetectDisplays( accelerator_info *ai )
|
whished_num_heads - how many heads the requested display mode needs
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
Radeon_DetectDisplays(accelerator_info *ai)
|
||||||
{
|
{
|
||||||
shared_info *si = ai->si;
|
shared_info *si = ai->si;
|
||||||
|
|
||||||
@@ -911,23 +926,19 @@ void Radeon_DetectDisplays( accelerator_info *ai )
|
|||||||
|
|
||||||
// alwats make TMDS_INT port first
|
// alwats make TMDS_INT port first
|
||||||
if (routes->port_info[1].tmds_type == tmds_int) {
|
if (routes->port_info[1].tmds_type == tmds_int) {
|
||||||
|
|
||||||
radeon_connector swap_entity;
|
radeon_connector swap_entity;
|
||||||
swap_entity = routes->port_info[0];
|
swap_entity = routes->port_info[0];
|
||||||
routes->port_info[0] = routes->port_info[1];
|
routes->port_info[0] = routes->port_info[1];
|
||||||
routes->port_info[1] = swap_entity;
|
routes->port_info[1] = swap_entity;
|
||||||
SHOW_FLOW0(2, "Swapping TMDS_INT to first port");
|
SHOW_FLOW0(2, "Swapping TMDS_INT to first port");
|
||||||
}
|
} else if (routes->port_info[0].tmds_type != tmds_int
|
||||||
else if ( routes->port_info[0].tmds_type != tmds_int &&
|
&& routes->port_info[1].tmds_type != tmds_int) {
|
||||||
routes->port_info[1].tmds_type != tmds_int ) {
|
|
||||||
|
|
||||||
// no TMDS_INT port, make primary DAC port first
|
// no TMDS_INT port, make primary DAC port first
|
||||||
// On my Inspiron 8600 both internal and external ports are
|
// On my Inspiron 8600 both internal and external ports are
|
||||||
// marked DAC_PRIMARY in BIOS. So be extra careful - only
|
// marked DAC_PRIMARY in BIOS. So be extra careful - only
|
||||||
// swap when the first port is not DAC_PRIMARY
|
// swap when the first port is not DAC_PRIMARY
|
||||||
if ( routes->port_info[1].dac_type == dac_primary &&
|
if (routes->port_info[1].dac_type == dac_primary
|
||||||
routes->port_info[0].dac_type != dac_primary ) {
|
&& routes->port_info[0].dac_type != dac_primary) {
|
||||||
|
|
||||||
radeon_connector swap_entity;
|
radeon_connector swap_entity;
|
||||||
swap_entity = routes->port_info[0];
|
swap_entity = routes->port_info[0];
|
||||||
routes->port_info[0] = routes->port_info[1];
|
routes->port_info[0] = routes->port_info[1];
|
||||||
@@ -936,9 +947,10 @@ void Radeon_DetectDisplays( accelerator_info *ai )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( si->asic == rt_rs300 ) // RS300 only has single Dac of TV type
|
if (si->asic == rt_rs300) {
|
||||||
{
|
// RS300 only has single Dac of TV type
|
||||||
// For RS300/RS350/RS400 chips, there is no primary DAC. Force VGA port to use TVDAC
|
// For RS300/RS350/RS400 chips, there is no primary DAC.
|
||||||
|
// Force VGA port to use TVDAC
|
||||||
if (routes->port_info[0].connector_type == connector_crt) {
|
if (routes->port_info[0].connector_type == connector_crt) {
|
||||||
routes->port_info[0].dac_type = dac_tvdac;
|
routes->port_info[0].dac_type = dac_tvdac;
|
||||||
routes->port_info[1].dac_type = dac_primary;
|
routes->port_info[1].dac_type = dac_primary;
|
||||||
@@ -947,43 +959,41 @@ void Radeon_DetectDisplays( accelerator_info *ai )
|
|||||||
routes->port_info[0].dac_type = dac_tvdac;
|
routes->port_info[0].dac_type = dac_tvdac;
|
||||||
}
|
}
|
||||||
} else if (si->num_crtc == 1) {
|
} else if (si->num_crtc == 1) {
|
||||||
|
|
||||||
routes->port_info[0].dac_type = dac_primary;
|
routes->port_info[0].dac_type = dac_primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
// use DDC to detect monitors - if we can read DDC, there must be a monitor
|
// use DDC to detect monitors - if we can read DDC, there must be
|
||||||
for ( i = 0; i < 2; i++ )
|
// a monitor
|
||||||
{
|
for (i = 0; i < 2; i++) {
|
||||||
//TODO could skip edid reading instead if we already have it, but what
|
//TODO could skip edid reading instead if we already have it, but what
|
||||||
//if monitors have been hot swapped? Also rely on edid for DVI-D detection
|
//if monitors have been hot swapped? Also rely on edid for DVI-D detection
|
||||||
//if (routes->port_info[i].mon_type != mt_unknown) {
|
//if (routes->port_info[i].mon_type != mt_unknown) {
|
||||||
// SHOW_FLOW0(2, "known type, skpping detection");
|
// SHOW_FLOW0(2, "known type, skpping detection");
|
||||||
// continue;
|
// continue;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
memset(&routes->port_info[i].edid , 0, sizeof(edid1_info));
|
memset(&routes->port_info[i].edid , 0, sizeof(edid1_info));
|
||||||
switch (routes->port_info[i].ddc_type) {
|
switch (routes->port_info[i].ddc_type) {
|
||||||
case ddc_monid:
|
case ddc_monid:
|
||||||
case ddc_dvi:
|
case ddc_dvi:
|
||||||
case ddc_vga:
|
case ddc_vga:
|
||||||
case ddc_crt2:
|
case ddc_crt2:
|
||||||
if ( Radeon_ReadEDID( ai, edid_regs[routes->port_info[i].ddc_type], &routes->port_info[i].edid ))
|
if (Radeon_ReadEDID(ai,
|
||||||
{
|
edid_regs[routes->port_info[i].ddc_type],
|
||||||
|
&routes->port_info[i].edid)) {
|
||||||
routes->port_info[i].edid_valid = true;
|
routes->port_info[i].edid_valid = true;
|
||||||
SHOW_FLOW(2, "Edid Data for CRTC %d on line %d", i, routes->port_info[i].ddc_type);
|
SHOW_FLOW(2, "Edid Data for CRTC %d on line %d", i, routes->port_info[i].ddc_type);
|
||||||
edid_dump(&routes->port_info[i].edid);
|
edid_dump(&routes->port_info[i].edid);
|
||||||
} else {
|
} else {
|
||||||
routes->port_info[i].mon_type = mt_none;
|
routes->port_info[i].mon_type = mt_none;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
SHOW_FLOW(2, "No Edid Pin Assigned to CRTC %d ", i);
|
SHOW_FLOW(2, "No Edid Pin Assigned to CRTC %d ", i);
|
||||||
routes->port_info[i].mon_type = mt_none;
|
routes->port_info[i].mon_type = mt_none;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (routes->port_info[i].edid_valid) {
|
if (routes->port_info[i].edid_valid) {
|
||||||
|
|
||||||
if (routes->port_info[i].edid.display.input_type == 1) {
|
if (routes->port_info[i].edid.display.input_type == 1) {
|
||||||
SHOW_FLOW0(2, "Must be a DVI monitor");
|
SHOW_FLOW0(2, "Must be a DVI monitor");
|
||||||
|
|
||||||
@@ -1038,7 +1048,6 @@ void Radeon_DetectDisplays( accelerator_info *ai )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!routes->port_info[0].edid_valid) {
|
if (!routes->port_info[0].edid_valid) {
|
||||||
SHOW_INFO0(2, "Searching port 0");
|
SHOW_INFO0(2, "Searching port 0");
|
||||||
if (si->is_mobility && (INREG(ai->regs, RADEON_BIOS_4_SCRATCH) & 4)) {
|
if (si->is_mobility && (INREG(ai->regs, RADEON_BIOS_4_SCRATCH) & 4)) {
|
||||||
@@ -1049,7 +1058,6 @@ void Radeon_DetectDisplays( accelerator_info *ai )
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!routes->port_info[1].edid_valid) {
|
if (!routes->port_info[1].edid_valid) {
|
||||||
|
|
||||||
if (si->is_mobility && (INREG(ai->regs, RADEON_FP2_GEN_CNTL) & RADEON_FP2_FPON)) {
|
if (si->is_mobility && (INREG(ai->regs, RADEON_FP2_GEN_CNTL) & RADEON_FP2_FPON)) {
|
||||||
SHOW_INFO0(2, "Found Ext Laptop DVI");
|
SHOW_INFO0(2, "Found Ext Laptop DVI");
|
||||||
routes->port_info[1].mon_type = mt_dfp;
|
routes->port_info[1].mon_type = mt_dfp;
|
||||||
@@ -1057,8 +1065,7 @@ void Radeon_DetectDisplays( accelerator_info *ai )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( routes->port_info[0].mon_type == mt_none )
|
if (routes->port_info[0].mon_type == mt_none) {
|
||||||
{
|
|
||||||
if (routes->port_info[1].mon_type == mt_none) {
|
if (routes->port_info[1].mon_type == mt_none) {
|
||||||
routes->port_info[0].mon_type = mt_crt;
|
routes->port_info[0].mon_type = mt_crt;
|
||||||
} else {
|
} else {
|
||||||
@@ -1077,8 +1084,6 @@ void Radeon_DetectDisplays( accelerator_info *ai )
|
|||||||
routes->reversed_DAC = true;
|
routes->reversed_DAC = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// we may have overseen monitors if they don't support DDC or
|
// we may have overseen monitors if they don't support DDC or
|
||||||
// have broken DDC data (like mine);
|
// have broken DDC data (like mine);
|
||||||
// time to do a physical wire test; this test is more reliable, but it
|
// time to do a physical wire test; this test is more reliable, but it
|
||||||
@@ -1088,16 +1093,15 @@ void Radeon_DetectDisplays( accelerator_info *ai )
|
|||||||
// even know the native resolution of the panel!
|
// even know the native resolution of the panel!
|
||||||
|
|
||||||
// all versions have a standard VGA port
|
// all versions have a standard VGA port
|
||||||
if( (displays & dd_crt) == 0 &&
|
if ((displays & dd_crt) == 0 && (controlled_displays & dd_crt) != 0
|
||||||
(controlled_displays & dd_crt) != 0 &&
|
&& Radeon_DetectCRT(ai))
|
||||||
Radeon_DetectCRT( ai ))
|
|
||||||
displays |= dd_crt;
|
displays |= dd_crt;
|
||||||
|
|
||||||
// check VGA signal routed to DVI port
|
// check VGA signal routed to DVI port
|
||||||
// (the detection code checks whether there is hardware for that)
|
// (the detection code checks whether there is hardware for that)
|
||||||
if( (displays & dd_tv_crt) == 0 &&
|
if ((displays & dd_tv_crt) == 0
|
||||||
(controlled_displays & dd_tv_crt) != 0 &&
|
&& (controlled_displays & dd_tv_crt) != 0
|
||||||
Radeon_DetectTVCRT( ai ))
|
&& Radeon_DetectTVCRT(ai))
|
||||||
displays |= dd_tv_crt;
|
displays |= dd_tv_crt;
|
||||||
|
|
||||||
// check TV-out connector
|
// check TV-out connector
|
||||||
|
|||||||
Reference in New Issue
Block a user