* Copyright 2006-2018, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
*/
#include "driver.h"
#include "device.h"
#include "intel_extreme.h"
#include "utility.h"
#include <OS.h>
#include <KernelExport.h>
#include <Drivers.h>
#include <PCI.h>
#include <SupportDefs.h>
#include <graphic_driver.h>
#include <image.h>
#include <stdlib.h>
#include <string.h>
#define DEBUG_COMMANDS
#define TRACE_DEVICE
#ifdef TRACE_DEVICE
# define TRACE(x...) dprintf("intel_extreme: " x)
#else
# define TRACE(x) ;
#endif
#define ERROR(x...) dprintf("intel_extreme: " x)
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
static status_t device_open(const char* name, uint32 flags, void** _cookie);
static status_t device_close(void* data);
static status_t device_free(void* data);
static status_t device_ioctl(void* data, uint32 opcode, void* buffer,
size_t length);
static status_t device_read(void* data, off_t offset, void* buffer,
size_t* length);
static status_t device_write(void* data, off_t offset, const void* buffer,
size_t* length);
device_hooks gDeviceHooks = {
device_open,
device_close,
device_free,
device_ioctl,
device_read,
device_write,
NULL,
NULL,
NULL,
NULL
};
#ifdef DEBUG_COMMANDS
static int
getset_register(int argc, char** argv)
{
if (argc < 2 || argc > 3) {
kprintf("usage: %s <register> [set-to-value]\n", argv[0]);
return 0;
}
uint32 reg = parse_expression(argv[1]);
uint32 value = 0;
bool set = argc == 3;
if (set)
value = parse_expression(argv[2]);
kprintf("intel_extreme register %#" B_PRIx32 "\n", reg);
intel_info &info = *gDeviceInfo[0];
uint32 oldValue = read32(info, reg);
kprintf(" %svalue: %#" B_PRIx32 " (%" B_PRIu32 ")\n", set ? "old " : "",
oldValue, oldValue);
if (set) {
write32(info, reg, value);
value = read32(info, reg);
kprintf(" new value: %#" B_PRIx32 " (%" B_PRIu32 ")\n", value, value);
}
return 0;
}
static int
dump_pipe_info(int argc, char** argv)
{
int pipeOffset = 0;
if (argc > 2) {
kprintf("usage: %s [pipe index]\n", argv[0]);
return 0;
}
if (argc > 1) {
uint32 pipe = parse_expression(argv[1]);
if (pipe != 0)
pipeOffset = INTEL_DISPLAY_OFFSET;
}
intel_info &info = *gDeviceInfo[0];
uint32 value;
kprintf("intel_extreme pipe configuration:\n");
value = read32(info, INTEL_DISPLAY_A_HTOTAL + pipeOffset);
kprintf(" HTOTAL start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_DISPLAY_A_HBLANK + pipeOffset);
kprintf(" HBLANK start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_DISPLAY_A_HSYNC + pipeOffset);
kprintf(" HSYNC start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_DISPLAY_A_VTOTAL + pipeOffset);
kprintf(" VTOTAL start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_DISPLAY_A_VBLANK + pipeOffset);
kprintf(" VBLANK start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_DISPLAY_A_VSYNC + pipeOffset);
kprintf(" VSYNC start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_DISPLAY_A_PIPE_SIZE + pipeOffset);
kprintf(" SIZE %" B_PRIu32 "x%" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
if (info.pch_info != INTEL_PCH_NONE) {
kprintf("intel_extreme transcoder configuration:\n");
value = read32(info, INTEL_TRANSCODER_A_HTOTAL + pipeOffset);
kprintf(" HTOTAL start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_TRANSCODER_A_HBLANK + pipeOffset);
kprintf(" HBLANK start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_TRANSCODER_A_HSYNC + pipeOffset);
kprintf(" HSYNC start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_TRANSCODER_A_VTOTAL + pipeOffset);
kprintf(" VTOTAL start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_TRANSCODER_A_VBLANK + pipeOffset);
kprintf(" VBLANK start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_TRANSCODER_A_VSYNC + pipeOffset);
kprintf(" VSYNC start %" B_PRIu32 " end %" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
value = read32(info, INTEL_TRANSCODER_A_IMAGE_SIZE + pipeOffset);
kprintf(" SIZE %" B_PRIu32 "x%" B_PRIu32 "\n",
(value & 0xFFFF) + 1, (value >> 16) + 1);
}
kprintf("intel_extreme display plane configuration:\n");
value = read32(info, INTEL_DISPLAY_A_CONTROL + pipeOffset);
kprintf(" CONTROL: %" B_PRIx32 "\n", value);
value = read32(info, INTEL_DISPLAY_A_BASE + pipeOffset);
kprintf(" BASE: %" B_PRIx32 "\n", value);
value = read32(info, INTEL_DISPLAY_A_BYTES_PER_ROW + pipeOffset);
kprintf(" BYTES_PER_ROW: %" B_PRIx32 "\n", value);
value = read32(info, INTEL_DISPLAY_A_SURFACE + pipeOffset);
kprintf(" SURFACE: %" B_PRIx32 "\n", value);
return 0;
}
#endif
static status_t
device_open(const char* name, uint32 , void** _cookie)
{
CALLED();
int32 id;
{
char* thisName;
for (id = 0; (thisName = gDeviceNames[id]) != NULL; id++) {
if (!strcmp(name, thisName))
break;
}
if (!thisName)
return B_BAD_VALUE;
}
intel_info* info = gDeviceInfo[id];
mutex_lock(&gLock);
if (info->open_count == 0) {
info->init_status = intel_extreme_init(*info);
if (info->init_status == B_OK) {
#ifdef DEBUG_COMMANDS
add_debugger_command("ie_reg", getset_register,
"dumps or sets the specified intel_extreme register");
add_debugger_command("ie_pipe", dump_pipe_info,
"show pipe configuration information");
#endif
}
}
if (info->init_status == B_OK) {
info->open_count++;
*_cookie = info;
} else
ERROR("%s: initialization failed!\n", __func__);
mutex_unlock(&gLock);
return info->init_status;
}
static status_t
device_close(void* )
{
CALLED();
return B_OK;
}
static status_t
device_free(void* data)
{
struct intel_info* info = (intel_info*)data;
mutex_lock(&gLock);
if (info->open_count-- == 1) {
info->init_status = B_NO_INIT;
intel_extreme_uninit(*info);
#ifdef DEBUG_COMMANDS
remove_debugger_command("ie_reg", getset_register);
remove_debugger_command("ie_pipe", dump_pipe_info);
#endif
}
mutex_unlock(&gLock);
return B_OK;
}
static status_t
device_ioctl(void* data, uint32 op, void* buffer, size_t bufferLength)
{
struct intel_info* info = (intel_info*)data;
switch (op) {
case B_GET_ACCELERANT_SIGNATURE:
TRACE("accelerant: %s\n", INTEL_ACCELERANT_NAME);
if (user_strlcpy((char*)buffer, INTEL_ACCELERANT_NAME,
bufferLength) < B_OK)
return B_BAD_ADDRESS;
return B_OK;
case INTEL_GET_PRIVATE_DATA:
{
intel_get_private_data data;
if (user_memcpy(&data, buffer, sizeof(intel_get_private_data)) < B_OK)
return B_BAD_ADDRESS;
if (data.magic == INTEL_PRIVATE_DATA_MAGIC) {
data.shared_info_area = info->shared_area;
return user_memcpy(buffer, &data,
sizeof(intel_get_private_data));
}
break;
}
case INTEL_GET_DEVICE_NAME:
if (user_strlcpy((char* )buffer, gDeviceNames[info->id],
bufferLength) < B_OK)
return B_BAD_ADDRESS;
return B_OK;
case INTEL_ALLOCATE_GRAPHICS_MEMORY:
{
intel_allocate_graphics_memory allocMemory;
if (user_memcpy(&allocMemory, buffer,
sizeof(intel_allocate_graphics_memory)) < B_OK)
return B_BAD_ADDRESS;
if (allocMemory.magic != INTEL_PRIVATE_DATA_MAGIC)
return B_BAD_VALUE;
status_t status = intel_allocate_memory(*info, allocMemory.size,
allocMemory.alignment, allocMemory.flags,
&allocMemory.buffer_base);
if (status == B_OK) {
if (user_memcpy(buffer, &allocMemory,
sizeof(intel_allocate_graphics_memory)) < B_OK)
return B_BAD_ADDRESS;
}
return status;
}
case INTEL_FREE_GRAPHICS_MEMORY:
{
intel_free_graphics_memory freeMemory;
if (user_memcpy(&freeMemory, buffer,
sizeof(intel_free_graphics_memory)) < B_OK)
return B_BAD_ADDRESS;
if (freeMemory.magic == INTEL_PRIVATE_DATA_MAGIC)
return intel_free_memory(*info, freeMemory.buffer_base);
break;
}
case INTEL_GET_BRIGHTNESS_LEGACY:
case INTEL_SET_BRIGHTNESS_LEGACY:
{
intel_brightness_legacy brightnessLegacy;
if (user_memcpy(&brightnessLegacy, buffer,
sizeof(brightnessLegacy)) < B_OK)
return B_BAD_ADDRESS;
if (brightnessLegacy.magic != INTEL_PRIVATE_DATA_MAGIC)
break;
if (op == INTEL_GET_BRIGHTNESS_LEGACY) {
brightnessLegacy.lpc = get_pci_config(info->pci, LEGACY_BACKLIGHT_BRIGHTNESS, 1);
if (user_memcpy(buffer, &brightnessLegacy, sizeof(brightnessLegacy)) < B_OK)
return B_BAD_ADDRESS;
} else {
set_pci_config(info->pci, LEGACY_BACKLIGHT_BRIGHTNESS, 1, brightnessLegacy.lpc);
}
return B_OK;
}
default:
ERROR("ioctl() unknown message %" B_PRIu32 " (length = %"
B_PRIuSIZE ")\n", op, bufferLength);
break;
}
return B_DEV_INVALID_IOCTL;
}
static status_t
device_read(void* , off_t , void* , size_t* _length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
static status_t
device_write(void* , off_t , const void* ,
size_t* _length)
{
*_length = 0;
return B_NOT_ALLOWED;
}