diff options
Diffstat (limited to 'drivers')
232 files changed, 19277 insertions, 15400 deletions
diff --git a/drivers/barcode_emul/Makefile b/drivers/barcode_emul/Makefile deleted file mode 100755 index 058a97c..0000000 --- a/drivers/barcode_emul/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# makefile for Barcode Emulator -# - -obj-$(CONFIG_BARCODE_EMUL_ICE4) += barcode_emul_ice4.o diff --git a/drivers/barcode_emul/barcode_emul_ice4.c b/drivers/barcode_emul/barcode_emul_ice4.c deleted file mode 100755 index 7032e6e..0000000 --- a/drivers/barcode_emul/barcode_emul_ice4.c +++ /dev/null @@ -1,626 +0,0 @@ -/* - * driver/barcode_emul Barcode emulator driver - * - * Copyright (C) 2012 Samsung Electronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/workqueue.h> -#include <linux/device.h> -#include <linux/earlysuspend.h> -#include <linux/spinlock.h> -#include <linux/gpio.h> -#include <linux/uaccess.h> -#include <linux/fs.h> -#include "barcode_emul_ice4.h" - -#if defined(TEST_DEBUG) -#define pr_barcode pr_emerg -#else -#define pr_barcode pr_info -#endif - -#define LOOP_BACK 48 -#define TEST_CODE1 49 -#define TEST_CODE2 50 -#define FW_VER_ADDR 0x80 -#define BEAM_STATUS_ADDR 0xFE -#define BARCODE_EMUL_MAX_FW_PATH 255 -#define BARCODE_EMUL_FW_FILENAME "i2c_top_bitmap.bin" - -struct barcode_emul_data { - struct i2c_client *client; -}; - -static struct workqueue_struct *barcode_init_wq; -static void barcode_init_workfunc(struct work_struct *work); -static DECLARE_WORK(barcode_init_work, barcode_init_workfunc); -/* - * Send barcode emulator firmware data thougth spi communication - */ -static int barcode_send_firmware_data(unsigned char *data) -{ - unsigned int i,j; - unsigned char spibit; - - i=0; - while (i < CONFIGURATION_SIZE) { - j=0; - spibit = data[i]; - while (j < 8) { - gpio_set_value(GPIO_FPGA_SPI_CLK,GPIO_LEVEL_LOW); - - if (spibit & 0x80) - { - gpio_set_value(GPIO_FPGA_SPI_SI,GPIO_LEVEL_HIGH); - } - else - { - gpio_set_value(GPIO_FPGA_SPI_SI,GPIO_LEVEL_LOW); - } - - j = j+1; - gpio_set_value(GPIO_FPGA_SPI_CLK,GPIO_LEVEL_HIGH); - spibit = spibit<<1; - } - i = i+1; - } - - i = 0; - while (i < 200) { - gpio_set_value(GPIO_FPGA_SPI_CLK,GPIO_LEVEL_LOW); - i = i+1; - gpio_set_value(GPIO_FPGA_SPI_CLK,GPIO_LEVEL_HIGH); - } - return 0; -} - -static int check_fpga_cdone(void) -{ - /* Device in Operation when CDONE='1'; Device Failed when CDONE='0'. */ - if (gpio_get_value(GPIO_FPGA_CDONE) != 1) { - pr_barcode("CDONE_FAIL\n"); - return 0; - } else { - pr_barcode("CDONE_SUCCESS\n"); - return 1; - } -} - -static int barcode_fpga_fimrware_update_start(unsigned char *data) -{ - pr_barcode("%s\n", __func__); - - gpio_request_one(GPIO_FPGA_CDONE, GPIOF_IN, "FPGA_CDONE"); - gpio_request_one(GPIO_FPGA_SPI_CLK, GPIOF_OUT_INIT_LOW, "FPGA_SPI_CLK"); - gpio_request_one(GPIO_FPGA_SPI_SI, GPIOF_OUT_INIT_LOW, "FPGA_SPI_SI"); - gpio_request_one(GPIO_FPGA_SPI_EN, GPIOF_OUT_INIT_LOW, "FPGA_SPI_EN"); - gpio_request_one(GPIO_FPGA_CRESET_B, GPIOF_OUT_INIT_HIGH, "FPGA_CRESET_B"); - gpio_request_one(GPIO_FPGA_RST_N, GPIOF_OUT_INIT_LOW, "FPGA_RST_N"); - - gpio_set_value(GPIO_FPGA_CRESET_B, GPIO_LEVEL_LOW); - udelay(30); - - gpio_set_value(GPIO_FPGA_CRESET_B, GPIO_LEVEL_HIGH); - udelay(1000); - - check_fpga_cdone(); - - barcode_send_firmware_data(data); - udelay(50); - - if (check_fpga_cdone()) { - udelay(5); - gpio_set_value(GPIO_FPGA_RST_N, GPIO_LEVEL_HIGH); - pr_barcode("barcode firmware update success\n"); - } else { - pr_barcode("barcode firmware update fail\n"); - } - return 0; -} - -void barcode_fpga_firmware_update(void) -{ - barcode_fpga_fimrware_update_start(spiword); -} - -static ssize_t barcode_emul_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - struct barcode_emul_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - - if (gpio_get_value(GPIO_FPGA_CDONE) != 1) { - pr_err("%s: cdone fail !!\n", __func__); - return 0; - } - - ret = i2c_master_send(client, buf, size); - if (ret < 0) { - pr_err("%s: i2c err1 %d\n", __func__, ret); - ret = i2c_master_send(client, buf, size); - if (ret < 0) - pr_err("%s: i2c err2 %d\n", __func__, ret); - } - - return size; -} - -static ssize_t barcode_emul_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return strlen(buf); -} -static DEVICE_ATTR(barcode_send, 0664, barcode_emul_show, barcode_emul_store); - -static ssize_t barcode_emul_fw_update_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - struct file *fp = NULL; - long fsize = 0, nread = 0; - const u8 *buff = 0; - char fw_path[BARCODE_EMUL_MAX_FW_PATH+1]; - mm_segment_t old_fs = get_fs(); - - pr_barcode("%s\n", __func__); - - old_fs = get_fs(); - set_fs(get_ds()); - - snprintf(fw_path, BARCODE_EMUL_MAX_FW_PATH, - "/sdcard/%s", BARCODE_EMUL_FW_FILENAME); - - fp = filp_open(fw_path, O_RDONLY, 0); - if (IS_ERR(fp)) { - pr_err("file %s open error:%d\n", - fw_path, (s32)fp); - goto err_open; - } - - fsize = fp->f_path.dentry->d_inode->i_size; - pr_barcode("barcode firmware size: %ld\n", fsize); - - buff = kzalloc((size_t)fsize, GFP_KERNEL); - if (!buff) { - pr_err("fail to alloc buffer for fw\n"); - goto err_alloc; - } - - nread = vfs_read(fp, (char __user *)buff, fsize, &fp->f_pos); - if (nread != fsize) { - pr_err("fail to read file %s (nread = %ld)\n", - fw_path, nread); - goto err_fw_size; - } - - barcode_fpga_fimrware_update_start((unsigned char *)buff); - - if (fp != NULL) { - err_fw_size: - kfree(buff); - err_alloc: - filp_close(fp, NULL); - err_open: - set_fs(old_fs); - } - - return size; -} - -static ssize_t barcode_emul_fw_update_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return strlen(buf); -} -static DEVICE_ATTR(barcode_fw_update, 0664, barcode_emul_fw_update_show, barcode_emul_fw_update_store); - -static int barcode_emul_read(struct i2c_client *client, u16 addr, u8 length, u8 *value) -{ - - struct i2c_msg msg[2]; - int ret; - - msg[0].addr = client->addr; - msg[0].flags = 0x00; - msg[0].len = 1; - msg[0].buf = (u8 *) &addr; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = length; - msg[1].buf = (u8 *) value; - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret == 2) { - return 0; - } else { - pr_barcode("%s: err1 %d\n", __func__, ret); - return -EIO; - } -} - -static ssize_t barcode_emul_test_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret, i; - struct barcode_emul_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - struct { - unsigned char addr; - unsigned char data[20]; - } i2c_block_transfer; - unsigned char barcode_data[14] = {0xFF, 0xAC, 0xDB, 0x36, 0x42, 0x85, 0x0A, 0xA8, 0xD1, 0xA3, 0x46, 0xC5, 0xDA, 0xFF}; - - if (gpio_get_value(GPIO_FPGA_CDONE) != 1) { - pr_err("%s: cdone fail !!\n", __func__); - return 0; - } - - if (buf[0] == LOOP_BACK) { - for (i = 0; i < size; i++) - i2c_block_transfer.data[i] = *buf++; - - i2c_block_transfer.addr = 0x01; - - pr_barcode("%s: write addr: %d, value: %d\n", __func__, - i2c_block_transfer.addr, i2c_block_transfer.data[0]); - - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) { - pr_barcode("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) - pr_barcode("%s: err2 %d\n", __func__, ret); - } - - } else if (buf[0] == TEST_CODE1) { - unsigned char BSR_data[6] =\ - {0xC8, 0x00, 0x32, 0x01, 0x00, 0x32}; - - pr_barcode("barcode test code start\n"); - - /* send NH */ - i2c_block_transfer.addr = 0x80; - i2c_block_transfer.data[0] = 0x05; - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - /* setup BSR data */ - for (i = 0; i < 6; i++) - i2c_block_transfer.data[i+1] = BSR_data[i]; - - /* send BSR1 => NS 1= 200, ISD 1 = 100us, IPD 1 = 200us, BL 1=14, BW 1=4MHZ */ - i2c_block_transfer.addr = 0x81; - i2c_block_transfer.data[0] = 0x00; - - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - /* send BSR2 => NS 2= 200, ISD 2 = 100us, IPD 2 = 200us, BL 2=14, BW 2=2MHZ*/ - i2c_block_transfer.addr = 0x88; - i2c_block_transfer.data[0] = 0x01; - - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - - /* send BSR3 => NS 3= 200, ISD 3 = 100us, IPD 3 = 200us, BL 3=14, BW 3=1MHZ*/ - i2c_block_transfer.addr = 0x8F; - i2c_block_transfer.data[0] = 0x02; - - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - /* send BSR4 => NS 4= 200, ISD 4 = 100us, IPD 4 = 200us, BL 4=14, BW 4=500KHZ*/ - i2c_block_transfer.addr = 0x96; - i2c_block_transfer.data[0] = 0x04; - - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - /* send BSR5 => NS 5= 200, ISD 5 = 100us, IPD 5 = 200us, BL 5=14, BW 5=250KHZ*/ - i2c_block_transfer.addr = 0x9D; - i2c_block_transfer.data[0] = 0x08; - - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 8); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - /* send barcode data */ - i2c_block_transfer.addr = 0x00; - for (i = 0; i < 14; i++) - i2c_block_transfer.data[i] = barcode_data[i]; - - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 15); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 15); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - /* send START */ - i2c_block_transfer.addr = 0xFF; - i2c_block_transfer.data[0] = 0x0E; - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - } else if (buf[0] == TEST_CODE2) { - pr_barcode("barcode test code stop\n"); - - i2c_block_transfer.addr = 0xFF; - i2c_block_transfer.data[0] = 0x00; - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - /* - pr_barcode("barcode test code send 02\n"); - - i2c_block_transfer.addr = 0x00; - i2c_block_transfer.data[0] = 0x00; - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - i2c_block_transfer.addr = 0x01; - i2c_block_transfer.data[0] = 0x01; - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - i2c_block_transfer.addr = 0x02; - for (i = 0; i < 14; i++) - i2c_block_transfer.data[i] = barcode_data[i]; - - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 15); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 15); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - - i2c_block_transfer.addr = 0x00; - i2c_block_transfer.data[0] = 0x01; - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) { - pr_err("%s: err1 %d\n", __func__, ret); - ret = i2c_master_send(client, (unsigned char *) &i2c_block_transfer, 2); - if (ret < 0) - pr_err("%s: err2 %d\n", __func__, ret); - } - */ - } - return size; -} - -static ssize_t barcode_emul_test_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return strlen(buf); -} -static DEVICE_ATTR(barcode_test_send, 0664, barcode_emul_test_show, barcode_emul_test_store); - -static ssize_t barcode_ver_check_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct barcode_emul_data *data = dev_get_drvdata(dev); - u8 fw_ver; - - barcode_emul_read(data->client, FW_VER_ADDR, 1, &fw_ver); - fw_ver = (fw_ver >> 5) & 0x7; - return sprintf(buf, "%d\n", fw_ver+14); - -} -static DEVICE_ATTR(barcode_ver_check, 0664, barcode_ver_check_show, NULL); - -static ssize_t barcode_led_status_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct barcode_emul_data *data = dev_get_drvdata(dev); - u8 status; - - barcode_emul_read(data->client, BEAM_STATUS_ADDR, 1, &status); - status = status & 0x1; - return sprintf(buf, "%d\n", status); - -} -static DEVICE_ATTR(barcode_led_status, 0664, barcode_led_status_show, NULL); - -static int __devinit barcode_emul_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct barcode_emul_data *data; - struct device *barcode_emul_dev; - int error; - - pr_barcode("%s probe!\n", __func__); - - if (gpio_get_value(GPIO_FPGA_CDONE) != 1) { - pr_err("%s: cdone fail !!\n", __func__); - return 0; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) - return -EIO; - - data = kzalloc(sizeof(struct barcode_emul_data), GFP_KERNEL); - if (NULL == data) { - pr_err("Failed to data allocate %s\n", __func__); - error = -ENOMEM; - goto err_free_mem; - } - data->client = client; - i2c_set_clientdata(client, data); - - barcode_emul_dev = device_create(sec_class, NULL, 0, data, "sec_barcode_emul"); - - if (IS_ERR(barcode_emul_dev)) - pr_err("Failed to create barcode_emul_dev device\n"); - - if (device_create_file(barcode_emul_dev, &dev_attr_barcode_send) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_barcode_send.attr.name); - if (device_create_file(barcode_emul_dev, &dev_attr_barcode_test_send) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_barcode_test_send.attr.name); - if (device_create_file(barcode_emul_dev, &dev_attr_barcode_fw_update) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_barcode_fw_update.attr.name); - if (device_create_file(barcode_emul_dev, &dev_attr_barcode_ver_check) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_barcode_ver_check.attr.name); - if (device_create_file(barcode_emul_dev, &dev_attr_barcode_led_status) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_barcode_led_status.attr.name); - - return 0; - -err_free_mem: - kfree(data); - return error; -} - -static int __devexit barcode_emul_remove(struct i2c_client *client) -{ - struct barcode_emul_data *data = i2c_get_clientdata(client); - - i2c_set_clientdata(client, NULL); - kfree(data); - return 0; -} - -#ifdef CONFIG_PM -static int barcode_emul_suspend(struct device *dev) -{ - gpio_set_value(GPIO_FPGA_RST_N, GPIO_LEVEL_LOW); - return 0; -} - -static int barcode_emul_resume(struct device *dev) -{ - gpio_set_value(GPIO_FPGA_RST_N, GPIO_LEVEL_HIGH); - return 0; -} - -static const struct dev_pm_ops barcode_emul_pm_ops = { - .suspend = barcode_emul_suspend, - .resume = barcode_emul_resume, -}; -#endif - -static const struct i2c_device_id ice4_id[] = { - {"ice4", 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, ice4_id); - -static struct i2c_driver ice4_i2c_driver = { - .driver = { - .name = "ice4", -#ifdef CONFIG_PM - .pm = &barcode_emul_pm_ops, -#endif - }, - .probe = barcode_emul_probe, - .remove = __devexit_p(barcode_emul_remove), - .id_table = ice4_id, -}; - -static void barcode_init_workfunc(struct work_struct *work) -{ - barcode_fpga_firmware_update(); - i2c_add_driver(&ice4_i2c_driver); -} - -static int __init barcode_emul_init(void) -{ - barcode_init_wq = create_singlethread_workqueue("barcode_init"); - queue_work(barcode_init_wq, &barcode_init_work); - - return 0; -} -late_initcall(barcode_emul_init); - -static void __exit barcode_emul_exit(void) -{ - i2c_del_driver(&ice4_i2c_driver); - destroy_workqueue(barcode_init_wq); -} -module_exit(barcode_emul_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SEC Barcode emulator"); diff --git a/drivers/barcode_emul/barcode_emul_ice4.h b/drivers/barcode_emul/barcode_emul_ice4.h deleted file mode 100755 index 2008496..0000000 --- a/drivers/barcode_emul/barcode_emul_ice4.h +++ /dev/null @@ -1,2042 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _BARCODE_EMULATOR_H_ -#define _BARCODE_EMULATOR_H_ - -extern struct class *sec_class; - -#define CONFIGURATION_SIZE 32216 -unsigned char spiword [] = { - 0x7E, 0xAA, 0x99, 0x7E, 0x51, 0x00, 0x01, 0x05, 0x92, 0x00, 0x20, 0x62, 0x01, 0x4B, 0x72, 0x00, - 0x90, 0x82, 0x00, 0x00, 0x11, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xC5, 0xA0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xEF, 0x04, 0x02, 0x00, 0x00, 0x00, 0x02, - 0xBC, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x0A, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xF0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1C, 0x03, 0xEC, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0A, 0x50, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x10, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, - 0x14, 0xFB, 0x14, 0x0A, 0x00, 0x00, 0x00, 0xF2, 0xE9, 0x10, 0x08, 0x00, 0x00, 0x03, 0x4F, 0xA4, - 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x30, 0xC0, 0x00, 0x00, 0x00, - 0x03, 0x26, 0x63, 0x00, 0x00, 0x00, 0x00, 0x14, 0x99, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xA0, 0x02, 0x00, 0x1C, 0xAA, 0x34, 0x0A, 0x80, 0x08, 0x00, 0xBB, 0xA0, 0x10, 0x0A, 0x00, 0x0E, - 0x05, 0xDB, 0xB1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x3C, 0x01, 0x5F, 0x03, 0xC0, - 0x20, 0x00, 0xF0, 0xE3, 0x2B, 0x97, 0x00, 0x80, 0x00, 0x23, 0x5C, 0x83, 0x0C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x9C, 0xC0, 0x52, 0x00, 0x00, 0x10, 0x00, 0x83, 0x49, 0x48, - 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x02, - 0xE0, 0xA5, 0x00, 0x00, 0x00, 0x50, 0x03, 0x28, 0x68, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0xC5, 0xA0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x3E, 0x96, 0x80, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00, 0x28, 0x04, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x42, 0xC0, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x80, 0xDB, 0x16, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x16, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, - 0xC0, 0x50, 0x08, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0xD0, 0xA5, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x60, 0x0C, 0xC5, 0xA0, 0x00, 0x00, 0x0D, 0x80, 0xBB, 0x16, 0x88, 0x00, 0x00, 0x26, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xE0, 0x0A, 0x01, 0x00, - 0x00, 0x50, 0x05, 0x80, 0x28, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x86, 0x89, 0x40, 0x4C, - 0x02, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x00, 0x00, 0x0B, 0x30, 0x68, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0xF0, 0x00, - 0x28, 0x0F, 0x9D, 0xB8, 0x00, 0x80, 0x00, 0x00, 0x00, 0xEF, 0x04, 0x02, 0x00, 0x00, 0x00, 0x03, - 0xBC, 0x10, 0x00, 0xA0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x10, 0x00, 0x00, 0x30, 0xE5, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x00, 0xF0, 0x40, 0x40, 0x00, 0x00, 0x00, 0x03, 0xC1, 0x00, 0x00, 0xE0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x80, 0x00, 0x1C, 0x30, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x02, 0x0E, 0x5F, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x82, 0x40, 0x1C, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x00, 0x00, 0x58, 0x07, 0x20, 0x01, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1E, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x5A, 0x05, 0x60, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x80, 0x5C, 0x3C, 0x18, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0xF0, 0x0B, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x04, 0xA0, 0x00, 0x0E, 0x05, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x10, 0x01, 0x00, 0xC0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1C, - 0x5F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x24, 0x0A, 0xFC, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x30, - 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x70, 0x0D, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, - 0x0B, 0x8E, 0xF8, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xF0, 0x8D, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x38, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x82, - 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x20, 0x07, 0x00, 0x90, 0x00, 0xC4, 0x01, 0xC0, 0x00, 0x00, 0x00, - 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x00, 0x09, 0x02, 0x0C, 0x2A, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x40, 0x08, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x7B, 0xC1, - 0x00, 0x00, 0x40, 0x00, 0x1C, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, - 0x2F, 0xCA, 0x44, 0x02, 0x00, 0x00, 0x00, 0x03, 0x83, 0xC0, 0x08, 0x00, 0x00, 0x20, 0x1E, 0xF0, - 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x18, 0x00, 0x00, 0x3C, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x98, 0xC0, 0x00, 0x00, 0x50, - 0x00, 0x40, 0x3C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x04, 0x00, 0x00, 0x00, 0x28, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x1C, 0x85, 0xFE, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, - 0xB0, 0x01, 0x40, 0x3D, 0xCB, 0x14, 0x02, 0x88, 0x51, 0x00, 0x83, 0x2C, 0x40, 0x86, 0x80, 0x40, - 0x20, 0x1E, 0xF0, 0x40, 0x00, 0x00, 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0xC0, 0xF5, 0xC0, 0x00, 0x00, 0x08, 0x93, 0x00, 0x08, 0x00, 0x36, 0x00, 0xCD, 0x30, 0xC0, - 0x20, 0x02, 0x08, 0x03, 0x41, 0xC8, 0x04, 0x0C, 0x08, 0x06, 0x40, 0x0F, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x54, 0x80, 0x5E, 0x00, 0x00, 0x80, - 0x08, 0x00, 0x00, 0x00, 0x41, 0xC2, 0xBD, 0xB8, 0x80, 0x08, 0x01, 0x00, 0x00, 0x37, 0x68, 0xD0, - 0x0C, 0x41, 0x40, 0x00, 0x18, 0xF0, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x23, 0xE0, 0x50, 0x40, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x40, 0x00, 0x6F, - 0xFB, 0x11, 0x00, 0x00, 0x00, 0x00, 0x01, 0x78, 0x0F, 0x00, 0x40, 0x48, 0x00, 0x01, 0x0F, 0x04, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x54, 0x80, 0x5E, - 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0xE2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x23, 0x80, - 0xF8, 0x49, 0x00, 0x40, 0x0C, 0x00, 0x00, 0x0E, 0xF0, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xE0, 0x50, 0x40, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x02, - 0x40, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x08, 0x01, 0xF0, 0x05, 0x22, 0x94, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x0F, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x13, - 0x1E, 0x80, 0x5C, 0x10, 0x00, 0x49, 0x08, 0x80, 0x00, 0x0A, 0x01, 0xC3, 0x8C, 0xA8, 0x80, 0x08, - 0x10, 0x05, 0x01, 0x9E, 0x96, 0x80, 0x00, 0x00, 0x00, 0x04, 0x1C, 0xF0, 0x40, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xC0, 0x50, 0x40, 0x00, 0x02, 0x79, - 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x01, 0x5B, 0x11, 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x14, 0x80, - 0x20, 0x00, 0x03, 0x80, 0x0F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x16, 0x80, 0x5E, 0x00, 0x20, 0x00, 0x08, 0x80, 0x00, 0x00, 0x03, 0xE3, 0x1F, - 0x98, 0x80, 0x08, 0x00, 0x21, 0x88, 0x17, 0xEC, 0x50, 0x00, 0x00, 0x04, 0x04, 0x18, 0xF0, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0xC0, 0x50, 0x40, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x24, 0x41, 0xC8, 0x11, 0x00, 0x00, 0x04, 0x01, 0x07, - 0x3C, 0xC3, 0x00, 0x00, 0x00, 0x43, 0x41, 0x0F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x56, 0x80, 0x5E, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, - 0x08, 0xF0, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x67, - 0xE0, 0x50, 0x40, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0xD0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x01, 0x0F, 0x04, 0x00, 0x00, 0x00, 0x10, - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x02, 0x06, 0x1E, 0x80, 0x5C, 0x03, 0x00, 0x01, 0x00, - 0x80, 0x00, 0x42, 0x01, 0xC3, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xF0, 0x49, 0x00, 0x0C, - 0x00, 0x04, 0x00, 0x1C, 0xF0, 0x40, 0x00, 0x00, 0x01, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x04, 0x02, 0xE0, 0x50, 0x48, 0x30, 0x00, 0x08, 0x0B, 0x00, 0x0C, 0x80, 0x02, 0x40, 0x00, - 0x00, 0x00, 0x31, 0x02, 0x08, 0xE5, 0xA2, 0x94, 0x40, 0x40, 0x03, 0xE0, 0x01, 0x0F, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x80, 0x5C, 0x00, - 0x10, 0x20, 0x0E, 0x18, 0x08, 0x80, 0x00, 0x00, 0x00, 0xCF, 0x04, 0x02, 0x00, 0x03, 0x80, 0x13, - 0xA9, 0x10, 0x08, 0x40, 0x1C, 0x01, 0x4C, 0xA4, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x50, 0x64, 0x00, 0x24, 0x70, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xF0, 0x60, 0x00, 0x00, 0x18, 0x0B, 0x22, 0x63, 0x00, 0x08, 0x00, 0x00, 0x1D, - 0xC9, 0x8C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0xB2, 0x81, 0x00, 0xE0, 0x00, 0x00, 0x1C, - 0x80, 0x5C, 0x01, 0x80, 0x03, 0x8F, 0x10, 0x00, 0x60, 0x03, 0xE0, 0x00, 0xEF, 0x06, 0x02, 0x80, - 0x27, 0x80, 0x73, 0x6C, 0x50, 0x0A, 0x00, 0xA0, 0x03, 0xCA, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0F, 0x0C, 0x28, 0x10, 0x10, 0x00, 0x00, 0x01, 0xC0, 0x50, 0x50, 0x00, 0x00, 0x90, 0xF3, - 0x00, 0x00, 0x00, 0x1C, 0x00, 0x10, 0xF0, 0x40, 0x20, 0x05, 0x00, 0x01, 0x68, 0xC3, 0x00, 0x80, - 0x04, 0xC2, 0x6C, 0xDE, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x02, 0x06, 0x54, 0x80, 0x5C, 0x00, 0x00, 0x50, 0x00, 0xF0, 0x00, 0x40, 0x00, 0x07, 0xA0, 0x0A, - 0x54, 0x00, 0x02, 0x79, 0x8A, 0xFE, 0x62, 0x00, 0x00, 0x00, 0x36, 0x03, 0xFD, 0xCC, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x20, 0xE0, 0x50, 0x41, 0x00, - 0x00, 0x00, 0x0B, 0x00, 0x0C, 0x0E, 0x00, 0x01, 0xC0, 0x5A, 0x40, 0x00, 0x07, 0x99, 0xB7, 0xBC, - 0x88, 0x04, 0x00, 0x01, 0x40, 0x3C, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x60, 0x14, 0x80, 0x5E, 0x00, 0x01, 0x87, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x10, 0x20, 0x0A, 0x54, 0x00, 0x00, 0x07, 0x00, 0xBA, 0xA2, 0x00, 0x01, 0x01, 0x5E, 0x28, 0x7B, - 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, - 0x50, 0x40, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x02, 0x00, 0x00, 0x02, 0xC0, 0x5A, 0x40, 0x08, 0x00, - 0x98, 0x03, 0x6C, 0x88, 0x04, 0x30, 0x08, 0x02, 0xBF, 0xB2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x80, 0x5C, 0x10, 0x00, 0x03, 0x8E, 0x10, - 0x00, 0x00, 0x01, 0xC7, 0x01, 0x8F, 0x04, 0x00, 0x01, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x34, 0x73, 0xCD, 0x88, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x2C, 0x00, 0xC0, 0x50, 0x40, 0x08, 0x00, 0x78, 0xFB, 0x82, 0x02, 0x00, 0x01, 0x40, 0x00, 0xF0, - 0x40, 0x08, 0x38, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x2A, 0x00, 0xC6, 0xAC, 0xC1, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x00, 0x16, 0x80, 0x5C, 0x10, 0x00, - 0x08, 0x1C, 0x58, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x0A, 0x54, 0x00, 0x00, 0x03, 0x80, 0x3A, 0xA2, - 0x00, 0x00, 0x4A, 0x14, 0x72, 0xDE, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x4C, 0x9C, 0x01, 0xC0, 0x50, 0x40, 0x00, 0x00, 0x31, 0x9B, 0x00, 0x00, 0x00, 0x03, - 0x02, 0xC0, 0x5A, 0x40, 0x00, 0x00, 0x78, 0x8B, 0xA8, 0x44, 0x00, 0x08, 0x02, 0x42, 0x7E, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x20, 0x60, 0x1E, 0x80, - 0x54, 0x00, 0x00, 0x08, 0x00, 0x98, 0x00, 0x04, 0x00, 0x03, 0x81, 0xCF, 0x04, 0x00, 0x00, 0x09, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2C, 0x00, 0xE0, 0x50, 0x60, 0x00, 0x00, 0x30, 0x0F, 0x80, - 0x40, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x40, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x43, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x02, - 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0xB8, 0x00, 0x40, 0x01, 0xC7, 0x01, 0xEF, 0x04, - 0x01, 0x00, 0x07, 0x8F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x8E, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x01, 0xCB, 0x01, 0x04, 0x00, 0x00, 0x40, 0x10, 0xF0, 0x40, 0x30, 0x00, 0x01, 0xE0, 0x00, 0x00, - 0x00, 0x40, 0x11, 0x66, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF2, 0x81, - 0x00, 0x00, 0x00, 0x60, 0x46, 0xC1, 0x04, 0x00, 0x01, 0x00, 0x0E, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x80, 0x06, 0x3C, 0x10, 0x00, 0x00, 0x20, 0x28, 0x01, 0x00, - 0x80, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x28, 0x10, 0x00, 0x00, 0x34, 0x25, 0x68, 0x52, - 0x41, 0x02, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x90, 0x43, 0xC1, 0x00, 0x00, 0x02, 0x40, 0x00, 0xE0, 0x00, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x02, 0x8C, 0xCA, 0x54, 0x01, 0x80, 0x00, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x08, 0x1C, 0x06, 0xBC, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x40, 0x5A, 0x6A, 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xB1, 0x00, 0x43, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x35, 0x80, 0xF4, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x0A, 0x52, 0x00, 0x40, 0xB7, 0x00, 0x05, 0x00, 0x40, 0x00, 0x03, 0x3C, 0x10, - 0x00, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x36, 0x41, 0x50, 0x0A, 0x40, 0x00, 0x02, 0x01, 0x0D, 0x00, 0x00, 0x00, 0x8C, 0x32, - 0xD8, 0xB0, 0x00, 0x10, 0x02, 0x00, 0x00, 0x03, 0xC1, 0x00, 0x00, 0x30, 0x06, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x0E, 0xFB, 0x06, - 0x08, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x85, 0xA0, 0x00, 0x00, 0x05, 0x8E, - 0x06, 0xBC, 0x10, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x5C, 0xF1, 0xC0, 0x00, 0x04, 0x70, 0x09, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x60, 0x05, 0x10, 0x00, 0x00, 0x08, 0x00, 0x03, 0xC1, 0x00, 0x00, 0x01, 0x40, - 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x60, - 0x35, 0xDE, 0x54, 0x18, 0x20, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x07, 0x00, 0x10, 0x29, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x38, 0x4F, 0x4A, 0xC0, 0x00, 0x00, 0x00, - 0x9D, 0x00, 0x00, 0xA0, 0x02, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x95, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0xC0, 0x65, 0x81, 0x04, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x07, 0x40, - 0x00, 0x00, 0x00, 0x28, 0x40, 0x00, 0x03, 0x3C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x22, 0x6A, 0xA1, 0x40, - 0x02, 0x00, 0x10, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x03, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, 0x01, 0xC6, 0x35, 0xCE, 0x54, 0x10, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xBC, 0x10, 0x00, 0xC0, 0x00, 0x00, - 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0xCE, 0x4A, 0x40, 0x00, 0x10, 0x00, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xF0, 0x43, 0xC1, 0x00, 0x04, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x47, 0x35, 0xC1, 0x04, 0x03, 0x00, 0x03, 0x81, - 0x00, 0x00, 0x08, 0x00, 0x06, 0xBF, 0xC8, 0x80, 0x11, 0x00, 0x00, 0x00, 0x07, 0x3C, 0x10, 0x00, - 0x02, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0C, - 0x00, 0x02, 0x42, 0x7A, 0xA1, 0x40, 0x10, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x40, 0x00, 0x02, 0xFC, - 0x11, 0x00, 0x10, 0x02, 0x00, 0x00, 0x43, 0xC1, 0x00, 0x00, 0x00, 0x20, 0x00, 0xB0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x01, 0xFD, 0xE4, 0x81, 0x00, 0x00, 0x20, 0x06, 0x1D, 0xBF, 0xF1, 0x00, - 0x00, 0x0B, 0x8E, 0x30, 0x23, 0x80, 0x04, 0x07, 0xDD, 0xDA, 0x44, 0x0A, 0x00, 0x50, 0x00, 0x07, - 0x3C, 0x10, 0x00, 0xA1, 0x40, 0x00, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x10, 0x05, 0x09, 0x76, - 0x4E, 0x00, 0x00, 0x00, 0x40, 0x3E, 0xCD, 0xF7, 0x00, 0x01, 0x00, 0x78, 0x0F, 0x00, 0x00, 0x00, - 0x1C, 0x76, 0x5D, 0x98, 0xC0, 0x00, 0x12, 0x00, 0x00, 0x43, 0xC1, 0x00, 0x04, 0x08, 0x00, 0x00, - 0x0F, 0x00, 0x50, 0x00, 0x00, 0x18, 0x00, 0x00, 0xCD, 0xE0, 0x05, 0x00, 0x60, 0x00, 0x00, 0x40, - 0xA0, 0xC0, 0x21, 0x80, 0x00, 0x0E, 0x70, 0x2A, 0xA0, 0xA2, 0x60, 0x04, 0x9B, 0x14, 0x12, 0x80, - 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x02, 0xEF, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x0F, 0x0E, 0x57, 0x81, 0xC0, 0x00, 0x00, 0x1C, 0x20, 0x10, 0x0C, 0x00, 0x00, 0x00, 0xF1, 0x01, - 0x02, 0x08, 0xA0, 0x26, 0x01, 0xDB, 0x30, 0xC0, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x2E, 0xE3, 0x41, 0x10, 0x00, 0x00, 0x10, 0x00, 0x18, 0xED, 0xE9, 0x01, 0x00, 0x00, - 0x00, 0x02, 0xA5, 0xC0, 0x84, 0x20, 0x00, 0x00, 0x00, 0xD0, 0x0A, 0x00, 0x06, 0x60, 0x1F, 0x98, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x10, 0x10, 0xDA, 0x92, 0x80, 0x00, 0x00, 0x34, 0x01, 0xE0, 0x18, 0x60, 0x00, - 0x20, 0xF0, 0x0B, 0x01, 0x00, 0x08, 0x27, 0x03, 0xE8, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x99, 0x8B, 0x60, - 0x1D, 0x00, 0x00, 0x02, 0x03, 0xAC, 0x80, 0x00, 0x00, 0x02, 0x87, 0x1F, 0xD8, 0x00, 0x00, 0x00, - 0x02, 0x16, 0xA8, 0x80, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x9E, 0x5E, 0x40, 0x40, 0x00, 0x00, 0x3C, 0x6F, 0xDC, - 0x40, 0x00, 0x00, 0x05, 0x09, 0xCD, 0x00, 0x70, 0x00, 0x00, 0x3C, 0xEA, 0x11, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x95, 0xB4, 0x81, 0x00, 0x00, 0x0B, 0xE0, 0x76, 0xAE, 0x30, 0x00, 0x00, 0x03, 0x8E, 0x30, - 0x09, 0x0C, 0x00, 0x40, 0x04, 0xFF, 0x52, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x08, 0x96, 0x4E, 0x00, 0x00, 0x80, - 0x3E, 0x3F, 0x69, 0xC3, 0x00, 0x02, 0x00, 0x08, 0x01, 0x81, 0x00, 0x00, 0x1C, 0x00, 0xFF, 0xF4, - 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x19, 0x64, 0x05, 0x00, 0x0A, 0x00, 0x60, 0x66, 0xCF, 0xE0, 0x00, 0x02, - 0x20, 0x0E, 0x58, 0x04, 0x00, 0x02, 0x06, 0x9E, 0xC0, 0x50, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, - 0x00, 0x00, 0x0E, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x57, 0x81, - 0xC0, 0x00, 0x4C, 0x00, 0x3D, 0x6C, 0xDA, 0x08, 0x00, 0x00, 0x00, 0x89, 0x81, 0x10, 0x00, 0x1C, - 0x7E, 0x60, 0x00, 0x00, 0x01, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0xC0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xED, 0xF4, 0x81, 0x00, 0x00, 0x08, 0x00, 0x5C, 0xE0, - 0x50, 0x00, 0x00, 0x07, 0x1A, 0xB8, 0x0C, 0x02, 0x02, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x50, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0xFC, 0x00, 0x41, 0x40, 0x00, 0x00, 0x00, 0x01, - 0x00, 0xB2, 0x4E, 0x00, 0x40, 0x01, 0x44, 0x3C, 0x20, 0x05, 0x00, 0x00, 0x38, 0x01, 0x0F, 0x80, - 0x10, 0x00, 0x14, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, - 0x00, 0x35, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x90, 0x07, 0x3B, 0xE8, 0x02, 0x40, 0x09, - 0xE0, 0x4E, 0xC0, 0x11, 0x23, 0x00, 0x27, 0x88, 0x98, 0x00, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x00, - 0x03, 0x10, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1C, 0x0F, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x08, 0x3E, 0x80, 0x04, 0x00, 0x02, 0x20, 0x20, 0x01, 0x00, 0x30, 0x01, - 0x00, 0x09, 0x03, 0x14, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0xA1, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xF0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x21, 0x43, - 0x00, 0x10, 0x03, 0x66, 0x56, 0x9C, 0x00, 0x00, 0x40, 0x47, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0xFF, 0x3C, 0x40, 0x00, 0x00, 0x1C, 0x6C, 0x40, 0xA5, - 0x50, 0x40, 0x00, 0x20, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x50, 0xC1, 0x00, 0x04, 0x29, 0x7E, 0x30, - 0x00, 0x04, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x28, 0x0B, 0x00, 0x02, 0x00, 0x00, 0xF0, - 0xC3, 0xBF, 0xC0, 0x20, 0x00, 0x00, 0x27, 0xC0, 0x0A, 0x50, 0x10, 0x00, 0x00, 0x18, 0x00, 0x80, - 0x0F, 0x21, 0x43, 0x00, 0x04, 0x02, 0x07, 0xB7, 0x9F, 0xE0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0E, 0x00, 0x08, 0x00, 0x00, 0x27, 0x00, 0x10, 0x29, 0x40, 0x06, 0x40, 0x00, - 0x00, 0x0A, 0xF0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, 0x01, 0x50, 0x40, 0xA0, 0x04, - 0x01, 0x4B, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x04, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0xC0, 0x0F, 0x00, 0x04, 0x20, 0x00, - 0x10, 0x00, 0x01, 0x9D, 0x21, 0x43, 0x04, 0x00, 0x03, 0xE6, 0x84, 0xA3, 0x90, 0x00, 0x00, 0x40, - 0x0E, 0x00, 0x00, 0x00, 0x00, 0x42, 0x80, 0x00, 0x08, 0x00, 0x00, 0x08, 0x01, 0xB7, 0xA0, 0x08, - 0x84, 0x00, 0x16, 0x04, 0x08, 0xF0, 0x60, 0x01, 0x00, 0x01, 0x00, 0x00, 0x08, 0x18, 0x01, 0x50, - 0x00, 0x04, 0x16, 0x02, 0x4F, 0xC6, 0x00, 0x00, 0x00, 0x01, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x28, 0x70, 0xE5, 0x00, 0x00, 0x00, 0x40, 0x27, 0xE3, 0x81, 0x0F, 0x00, - 0x84, 0x00, 0x03, 0x00, 0x00, 0x99, 0x9D, 0x21, 0x43, 0x00, 0x00, 0x03, 0x63, 0xD6, 0xEC, 0x00, - 0x20, 0x00, 0x07, 0x8E, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x13, 0x07, 0x00, - 0x06, 0x28, 0x02, 0x01, 0x00, 0x3E, 0x04, 0x18, 0xF0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x09, 0x08, - 0x58, 0x01, 0x54, 0x00, 0x00, 0x14, 0x6A, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, - 0x40, 0x00, 0x38, 0x0B, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xCD, 0x01, 0x40, 0x00, 0x10, 0x01, 0xC3, - 0x81, 0x0F, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0x43, 0x00, 0x04, 0x02, 0x07, - 0x84, 0xA7, 0xF0, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, - 0x00, 0x47, 0x80, 0x5A, 0xA0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0x40, 0x40, 0x03, 0xE0, - 0x00, 0x00, 0x00, 0x10, 0x01, 0x50, 0x00, 0x80, 0x2C, 0x42, 0x4B, 0xFF, 0x00, 0x00, 0x00, 0x08, - 0x07, 0x00, 0x00, 0x01, 0x40, 0x2C, 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xA1, 0x43, 0x01, - 0x00, 0x00, 0x00, 0x21, 0xAD, 0xF0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x00, 0x08, 0x00, 0x00, 0x09, 0x80, 0x12, 0x3D, 0x40, 0x00, 0x0C, 0x00, 0x02, 0x00, 0xA5, 0x41, - 0x40, 0x00, 0x00, 0x00, 0x07, 0x00, 0x50, 0x01, 0x50, 0x00, 0x00, 0x00, 0x01, 0x40, 0xEF, 0x00, - 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0E, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, - 0x43, 0xD4, 0x00, 0x04, 0x00, 0x00, 0x34, 0x05, 0xA0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x98, 0x0D, - 0xA1, 0x43, 0x00, 0x0A, 0x00, 0x62, 0x27, 0xA6, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0xDA, 0xBD, 0xC0, 0xC0, 0xA0, 0x00, 0x00, - 0x1A, 0xF0, 0x40, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x01, 0x50, 0x00, 0x80, 0x00, 0x38, - 0x6C, 0x0C, 0x00, 0x00, 0x00, 0x08, 0x9D, 0x00, 0x00, 0x02, 0x34, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x00, 0x11, 0x01, 0xC3, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x10, 0x00, 0x00, 0x30, - 0x00, 0x80, 0x0D, 0xA1, 0x41, 0x80, 0x00, 0x00, 0x67, 0x85, 0xE8, 0x00, 0x21, 0x03, 0x83, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x20, 0x18, 0x1C, 0x97, 0xE0, 0x08, 0x04, - 0x00, 0x00, 0x00, 0x00, 0xA5, 0x40, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x58, 0x01, 0x50, 0x00, - 0x00, 0x00, 0x43, 0xD0, 0x00, 0x00, 0x30, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x0E, - 0x00, 0x00, 0x01, 0x1A, 0x11, 0x95, 0xE0, 0x00, 0x80, 0xC0, 0x00, 0x00, 0x34, 0x05, 0xA0, 0x14, - 0x00, 0x00, 0x11, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0D, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x50, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x00, 0xB8, 0x02, 0x90, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x0F, 0x21, - 0x41, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0xF8, 0x01, 0x50, 0x00, 0x00, 0x1C, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xE8, - 0x55, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x98, 0xE5, 0xA1, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x0F, 0x04, 0x08, 0x00, 0x00, 0x00, 0xB0, 0x01, 0x50, 0x00, 0x00, - 0x00, 0x34, 0xFE, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x68, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xF0, - 0x40, 0x00, 0x00, 0x01, 0x85, 0xA1, 0x43, 0x00, 0x00, 0x00, 0xE0, 0x7F, 0xC7, 0xF0, 0x08, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xF0, 0x01, - 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0xE8, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xA1, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x02, 0x00, - 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0xB8, 0x01, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0xC8, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x15, 0x21, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0xF8, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x68, 0xF0, 0x80, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x95, 0x21, 0x43, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xC2, 0x82, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x01, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x34, 0x01, 0x48, 0xF0, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x80, - 0x07, 0x21, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x50, 0xC0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x61, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x10, 0x03, 0x80, 0x15, 0x02, 0xC0, 0x00, 0x42, 0x34, 0x90, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x3D, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x0A, 0xB3, 0x08, 0x40, - 0x0C, 0x00, 0x06, 0x02, 0xC0, 0x0A, 0x60, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x5A, 0x14, 0x10, - 0x0C, 0x00, 0x2E, 0x04, 0x4A, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2E, - 0x6E, 0x03, 0x02, 0x00, 0x00, 0x01, 0x9B, 0x66, 0x10, 0x00, 0xC0, 0x02, 0x40, 0x2C, 0x85, 0x04, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x05, 0x80, 0x15, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0xF4, - 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x80, 0xBF, 0x70, 0x00, 0x00, 0x20, 0x1A, - 0x1F, 0xE8, 0x40, 0x00, 0x00, 0x04, 0x00, 0xE0, 0x05, 0x40, 0x00, 0x00, 0x00, 0x40, 0x09, 0x98, - 0x5A, 0x14, 0x30, 0x00, 0x00, 0x00, 0x64, 0x1E, 0x0F, 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, 0x6D, 0xDC, 0xF7, 0x00, 0x00, 0x04, 0x00, 0xF9, 0x72, 0xB4, 0x00, 0x00, 0x02, 0x60, - 0x16, 0x85, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x15, 0x02, 0x00, 0x00, 0x00, - 0x0C, 0xF6, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x43, 0x0F, 0x00, 0x00, 0x80, - 0x00, 0x20, 0x00, 0xDC, 0x20, 0x06, 0x00, 0x00, 0x02, 0x00, 0xC0, 0x05, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x5A, 0x14, 0x30, 0x00, 0x00, 0x06, 0x02, 0x01, 0x96, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x07, 0x5A, 0x0A, 0x00, 0x00, 0x01, 0x58, 0xCF, 0xE8, 0x00, 0x08, - 0x00, 0x01, 0xE6, 0x36, 0x85, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0x05, 0x00, 0x15, 0x02, - 0x00, 0x80, 0x42, 0x25, 0xC3, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xAC, - 0xBF, 0xF0, 0x00, 0x00, 0x09, 0x0C, 0xBE, 0x80, 0x80, 0x00, 0x00, 0x04, 0x00, 0xC0, 0x05, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x80, 0x7A, 0x14, 0x30, 0x00, 0x00, 0x26, 0x65, 0xEE, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x03, 0xDC, 0xF7, 0x00, 0x00, 0x00, 0x81, 0xE3, - 0x3A, 0x50, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x85, 0x0C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, - 0x80, 0x15, 0x00, 0x00, 0x03, 0x60, 0x06, 0x03, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x0F, 0xCE, 0xE0, 0x00, 0x00, 0x0D, 0x08, 0xBB, 0xD8, 0xC0, 0x00, 0x40, 0x80, 0x20, - 0xE0, 0x05, 0x40, 0x00, 0x00, 0x30, 0x00, 0x01, 0x99, 0x72, 0x14, 0x30, 0x00, 0x00, 0x14, 0x01, - 0xCC, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xCB, 0x77, 0x00, 0x00, - 0x00, 0x01, 0xBF, 0x65, 0xB0, 0x00, 0x02, 0x10, 0x60, 0x74, 0x85, 0x0C, 0x80, 0x00, 0x00, 0x30, - 0x00, 0x09, 0x85, 0x80, 0x15, 0x00, 0x00, 0x00, 0x06, 0xB5, 0xA0, 0x00, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x0F, 0x0F, 0xF0, 0x90, 0x00, 0x00, 0x90, 0xF6, 0x77, 0x40, 0x00, - 0x00, 0x00, 0x20, 0xE0, 0x05, 0x40, 0x00, 0x00, 0x01, 0x00, 0x05, 0x99, 0x52, 0x14, 0x30, 0x00, - 0x00, 0x16, 0x2D, 0x7F, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x70, 0x4E, - 0xF7, 0x02, 0x00, 0x00, 0x78, 0xA1, 0xF5, 0xDC, 0x00, 0x00, 0x00, 0x06, 0x54, 0x85, 0x0C, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xB0, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x42, 0xCD, 0x09, 0xA4, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x3E, 0x0A, 0xF0, 0x00, 0x00, 0x05, 0x8B, 0x04, - 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x05, 0x50, 0x00, 0x00, 0x01, 0x80, 0x08, 0x00, 0x72, - 0x14, 0x30, 0x06, 0x00, 0x20, 0x00, 0x0C, 0x65, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x73, 0xFC, 0xF5, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x28, 0x08, 0x08, 0x60, 0x00, 0x00, 0x3C, - 0x85, 0x0C, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x05, 0x00, 0x15, 0x10, 0x00, 0x01, 0x62, 0xC6, - 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x40, 0x1E, 0x8F, 0xF0, 0x00, 0x00, - 0x0D, 0x18, 0x92, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x05, 0x52, 0x00, 0x00, 0x02, 0x40, - 0x00, 0x00, 0x72, 0x14, 0x30, 0x00, 0x00, 0x34, 0x6A, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x62, 0x6E, 0x33, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE8, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x1C, 0x85, 0x0C, 0x00, 0x00, 0x00, 0x30, 0x00, 0x18, 0x0F, 0x80, 0x15, 0x00, 0x00, - 0x00, 0x20, 0x03, 0x0F, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, - 0xC0, 0x01, 0x00, 0x0F, 0x0A, 0xBC, 0x32, 0xD2, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x09, 0x18, 0x7A, 0x14, 0x30, 0x00, 0x00, 0x1E, 0x21, 0xEA, 0xFB, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x0A, 0x10, 0x30, 0xD0, 0xAB, 0xBB, - 0x0D, 0x48, 0x40, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x80, - 0x15, 0x00, 0x02, 0x03, 0x40, 0x27, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x05, 0x08, 0xD8, 0x10, 0x40, 0x00, 0x00, 0x00, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x9B, 0x5A, 0x14, 0x30, 0x10, 0x40, 0x3C, 0x01, 0x58, - 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x02, 0x00, 0x00, - 0x01, 0xB7, 0x7A, 0x04, 0x08, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0F, 0x00, 0x15, 0x00, 0x00, 0x02, 0xE4, 0x26, 0x80, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x00, 0x01, 0x05, 0x0E, 0x04, 0x00, 0xC0, 0x00, 0x06, - 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x14, 0x30, 0x00, 0x00, - 0x2E, 0x38, 0x78, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x3E, 0x20, 0x1C, 0x0F, - 0x00, 0x00, 0x00, 0x01, 0x90, 0x70, 0x0C, 0x08, 0x00, 0x00, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x08, 0x05, 0x00, 0xBB, 0x00, 0x15, 0x00, 0x20, 0x00, 0x00, 0x25, 0x9A, 0xD2, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0xE2, 0x34, 0xCF, 0x24, 0x00, 0xC0, 0x00, 0x0A, 0xB4, 0x00, - 0x02, 0x02, 0x00, 0x0E, 0x3C, 0x50, 0xFA, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x7A, 0x14, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, - 0x06, 0xDD, 0xF0, 0x40, 0x00, 0x00, 0x50, 0xA5, 0x78, 0x04, 0x8A, 0x00, 0x01, 0x40, 0x46, 0xE0, - 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x80, 0x15, 0x00, 0x00, 0x00, 0x20, 0x35, 0x0A, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x23, 0xAD, 0x00, 0x00, 0x08, 0x00, 0x01, - 0x00, 0xB0, 0x01, 0x40, 0x00, 0x00, 0x26, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x01, - 0x81, 0x52, 0x14, 0x30, 0x10, 0x00, 0x1C, 0x00, 0xE8, 0x83, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1E, 0x72, 0x4C, 0xA0, 0x00, 0x00, 0x00, 0x81, 0x80, 0x78, 0x28, 0x00, 0x00, 0x1D, - 0xE0, 0x1E, 0xE0, 0x51, 0x80, 0x00, 0x00, 0x30, 0x04, 0x00, 0x8B, 0x80, 0x15, 0x00, 0x08, 0x03, - 0xE6, 0x94, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x2E, 0x0F, 0x04, - 0x00, 0x00, 0x01, 0x00, 0xB8, 0x09, 0x56, 0x04, 0x00, 0x00, 0x03, 0xCE, 0x10, 0x01, 0x00, 0x00, - 0x03, 0x00, 0x20, 0x19, 0x52, 0x14, 0x30, 0x00, 0x40, 0x3C, 0x31, 0xDC, 0x08, 0x00, 0x00, 0x38, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xF5, 0x40, 0x00, 0x00, 0x80, 0x8F, 0x68, 0x61, - 0x28, 0xC0, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x15, - 0x00, 0x08, 0x02, 0xC7, 0x34, 0x03, 0xC0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xC3, - 0x76, 0x9C, 0x24, 0x00, 0x00, 0x0F, 0x00, 0x0C, 0x17, 0xC0, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x72, 0x14, 0x30, 0x00, 0x00, 0x20, 0x7D, 0xFA, 0x1C, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x20, 0x75, 0xCD, 0xC0, 0x48, 0x1C, 0x00, 0x80, - 0x05, 0xB9, 0x7C, 0x00, 0x60, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x08, 0x00, 0x00, 0x23, 0x01, 0x0A, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x40, 0x07, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xC0, 0x00, 0x00, 0x00, 0x24, - 0x02, 0xDC, 0x3C, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0A, 0x10, 0x00, 0x20, 0x00, 0x00, 0x16, - 0x3D, 0x5C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xDB, 0x33, 0x00, - 0x00, 0x00, 0x00, 0x0B, 0x3C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x37, 0xC3, 0xD4, 0x10, 0x00, 0x00, - 0x30, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x67, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x44, 0x2E, 0x00, 0x54, 0x00, 0x00, 0x05, 0x00, 0x54, 0x3C, 0x00, - 0x00, 0x80, 0x00, 0x26, 0xD0, 0xF3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0F, 0x8A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x14, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x20, 0x7B, - 0x68, 0x50, 0x41, 0x00, 0x00, 0x80, 0x0F, 0x28, 0x3C, 0x00, 0x04, 0x01, 0x63, 0xFE, 0xAC, 0xF0, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE6, 0xF5, 0x0D, 0xA0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x40, 0x3E, 0x00, 0x54, 0x10, 0x00, 0x01, 0x00, - 0xB0, 0x08, 0x00, 0x00, 0x08, 0x24, 0x21, 0xD0, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0A, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x72, 0x4A, 0xA5, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x26, 0x03, 0x68, 0x50, 0xC0, 0x00, 0x00, 0xD8, 0x0F, 0xB8, 0x00, 0x88, 0x00, 0x03, 0x60, - 0x40, 0xEE, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, - 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x44, 0x2C, 0x00, 0x54, 0x10, - 0x00, 0x00, 0x0A, 0x54, 0x3F, 0x00, 0x00, 0x00, 0x26, 0x41, 0xDC, 0xBE, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x4E, 0x74, 0xEA, 0x80, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x00, 0x7B, 0x68, 0x50, 0xC0, 0x00, 0x00, 0x11, 0xBF, 0x28, 0xFC, 0x08, - 0x00, 0x02, 0x66, 0x0C, 0xCF, 0x30, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x23, 0x76, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x3C, - 0x00, 0x54, 0x10, 0x80, 0x09, 0x00, 0x13, 0x18, 0xC0, 0x02, 0x00, 0x16, 0x25, 0xD0, 0x69, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x14, 0x05, 0xCC, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE8, 0x50, 0xC0, 0x80, 0x00, 0x00, 0x01, - 0xAA, 0x70, 0x00, 0x00, 0x02, 0x60, 0x61, 0xC9, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0xF9, 0xED, 0x00, 0x00, 0x60, 0x04, 0x0E, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x08, 0x02, 0x2E, 0x00, 0x54, 0x00, 0x00, 0x07, 0x00, 0x38, 0x16, 0x80, 0x00, 0x00, 0x00, 0x20, - 0xDF, 0xFF, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFA, 0xC4, 0x90, 0x00, 0x01, 0x00, 0x7B, - 0xCF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x67, 0x48, 0x50, 0xC1, 0x00, - 0x00, 0xD1, 0x8D, 0xE3, 0x94, 0x09, 0x00, 0x00, 0x00, 0x2F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x87, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x02, 0x3E, 0x00, 0x54, 0x08, 0x00, 0x09, 0x09, 0x0C, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x02, 0xCC, 0x97, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x69, 0xE8, 0x80, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x67, 0x48, - 0x50, 0xC0, 0x00, 0x00, 0x80, 0xB3, 0xA9, 0x00, 0x08, 0x00, 0x00, 0x06, 0x0C, 0xEF, 0xE0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x1D, 0xE0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x54, 0x00, 0x02, 0x81, 0x0E, 0x54, - 0x03, 0xC0, 0x00, 0x00, 0x00, 0x41, 0xDC, 0xBF, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x60, 0x00, 0x01, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xA0, - 0x00, 0x03, 0xC8, 0x50, 0xC1, 0x00, 0x00, 0xD0, 0x15, 0xE3, 0xC0, 0x00, 0x00, 0xC0, 0x03, 0x00, - 0xC7, 0xF0, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x60, 0x36, - 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x7C, 0x00, 0x54, 0x20, 0x02, - 0x0F, 0x00, 0x96, 0x8C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x30, 0xA0, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3C, 0x61, 0xCC, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x07, 0xC8, 0x50, 0xC1, 0x00, 0x00, 0x00, 0x0D, 0x2F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0xAA, 0x00, 0x80, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0xC0, - 0x02, 0x42, 0x1D, 0x05, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x24, 0x16, 0x81, - 0x10, 0x01, 0x10, 0x01, 0x00, 0x98, 0x01, 0x50, 0x0C, 0x00, 0x00, 0x20, 0x10, 0xF0, 0x40, 0x00, - 0x00, 0x03, 0x00, 0x01, 0x80, 0x7E, 0x28, 0x00, 0x04, 0x00, 0x20, 0x07, 0xCC, 0x03, 0x42, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0E, 0x73, 0xFF, 0x88, 0x00, 0x10, 0x00, 0x80, 0x05, 0xA1, - 0x41, 0x00, 0x40, 0x01, 0x40, 0x40, 0xAF, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xE8, - 0x40, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x01, 0x10, 0xB8, 0x01, 0x50, 0x10, 0x01, 0x40, 0x28, 0x10, - 0xF0, 0x40, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x53, 0xC0, 0x00, 0x00, 0x00, 0x1E, 0x3C, 0x7C, - 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x7A, 0x0A, 0x00, 0x00, 0x00, - 0x99, 0xC5, 0xA1, 0x43, 0x00, 0x00, 0x08, 0x02, 0x81, 0x8F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0xAD, 0xC3, 0xDD, 0x05, 0x00, 0x00, 0x40, 0x37, 0x0F, 0xF0, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x37, 0x91, 0x10, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x50, 0x00, 0x40, - 0x14, 0x03, 0xEF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x81, 0xDF, 0x39, 0xD0, 0x80, 0x00, - 0x26, 0x62, 0x4C, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x48, 0x88, - 0x00, 0x00, 0x20, 0x00, 0x05, 0xA1, 0x43, 0x00, 0x08, 0x00, 0x60, 0x36, 0xCF, 0x70, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x50, 0xA7, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x03, 0xBD, 0x03, 0xC6, 0x80, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x00, 0x04, 0xA1, 0x10, 0x00, 0x00, 0x00, 0x09, 0xB0, 0x01, - 0x50, 0x00, 0x00, 0x94, 0x35, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0xD7, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xBC, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, - 0x00, 0x4A, 0x88, 0x00, 0x00, 0x00, 0x00, 0x07, 0xA1, 0x43, 0x00, 0x00, 0x05, 0x40, 0x2D, 0xCF, - 0x70, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x85, 0x41, 0x54, 0x00, 0x00, 0x00, 0x22, 0xFD, 0x0A, - 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x46, 0xB7, 0x00, 0x02, 0x00, 0x02, 0x80, - 0x08, 0x98, 0x01, 0x50, 0x10, 0x00, 0x02, 0x00, 0x10, 0xF5, 0x00, 0x00, 0x00, 0x30, 0x00, 0x05, - 0x99, 0x7E, 0x16, 0x80, 0x00, 0x00, 0x0E, 0x04, 0xEE, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x2B, 0xDC, 0x40, 0x02, 0x00, 0x00, 0x19, 0x95, 0x21, 0x43, 0x00, 0x00, 0x01, - 0xE6, 0x0E, 0xEF, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x81, 0x03, 0x95, 0x00, 0x00, 0x00, - 0x46, 0xB5, 0x0B, 0xD4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x62, 0xE4, 0xB0, 0x00, - 0x03, 0x00, 0x00, 0x08, 0xB8, 0x01, 0x50, 0x14, 0x00, 0x14, 0x43, 0x60, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x01, 0x9F, 0x10, 0x10, 0x80, 0x00, 0x20, 0x25, 0x5E, 0xFE, 0x62, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x28, 0x4A, 0x08, 0x00, 0x30, 0x00, 0x01, 0x95, 0x21, 0x43, - 0x00, 0x40, 0x02, 0x02, 0x3E, 0xA4, 0x40, 0x20, 0x00, 0x00, 0x00, 0x38, 0xF0, 0xEF, 0x2E, 0x04, - 0x00, 0x00, 0x01, 0x40, 0x36, 0xCF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x42, - 0xEC, 0x00, 0x00, 0x00, 0x03, 0x8F, 0x00, 0x90, 0x01, 0x50, 0x10, 0x00, 0x14, 0x00, 0x10, 0x55, - 0x00, 0x00, 0x00, 0x01, 0x80, 0x09, 0x81, 0x00, 0x10, 0x80, 0x06, 0x00, 0x14, 0x00, 0x59, 0x7F, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x34, 0x2A, 0x7A, 0x80, 0x12, 0x18, 0x00, 0x00, - 0x07, 0x21, 0x43, 0x00, 0x60, 0x02, 0x07, 0x86, 0xC5, 0x50, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x89, 0xA5, 0x69, 0x02, 0x00, 0x00, 0x02, 0xE4, 0xD8, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0B, 0x46, 0xA7, 0xB1, 0x10, 0x00, 0x03, 0xD0, 0x10, 0xB0, 0x01, 0x50, 0x90, 0x00, 0x00, - 0x20, 0x4D, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x97, 0x36, 0x9A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x2A, 0x7B, 0x88, 0x00, - 0x00, 0x02, 0x00, 0xA7, 0x21, 0x43, 0x00, 0x80, 0x00, 0x00, 0x20, 0x0D, 0xB0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x93, 0x80, 0xC4, 0x00, 0x40, 0x00, 0x26, 0x45, 0x05, 0x04, 0x07, 0x00, 0x00, - 0x80, 0x30, 0x00, 0x40, 0x0B, 0xC4, 0x2C, 0x05, 0xA4, 0x01, 0x08, 0x00, 0x10, 0x04, 0x3C, 0x10, - 0x00, 0x00, 0x54, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x84, 0x80, - 0x8C, 0x00, 0x14, 0x6E, 0x1E, 0xA0, 0x40, 0x30, 0x01, 0x70, 0x08, 0x00, 0x0C, 0x01, 0x00, 0x38, - 0x00, 0xA5, 0x41, 0x11, 0x30, 0x00, 0xC0, 0x2B, 0xC1, 0x01, 0x00, 0x42, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0xC3, 0x68, 0x00, 0x0A, 0x00, 0x02, 0x5C, 0x90, 0x40, - 0x00, 0x0A, 0x00, 0x90, 0x70, 0x00, 0x00, 0xE1, 0xC0, 0x00, 0x0F, 0x04, 0x00, 0x00, 0x47, 0x10, - 0x00, 0x3C, 0x10, 0x40, 0x00, 0x00, 0x23, 0x50, 0x21, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7E, 0xB0, 0x00, 0x80, 0x40, 0x00, 0x06, 0x6A, 0x00, 0x00, 0x01, 0x00, 0x70, 0x80, 0x00, 0x00, - 0x00, 0x20, 0x70, 0x1E, 0xF0, 0x40, 0x01, 0x3A, 0x81, 0xE0, 0x33, 0xC1, 0x00, 0x00, 0x00, 0x00, - 0x5F, 0xE8, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xA0, 0x00, 0x00, 0x00, 0x02, 0x40, - 0x02, 0x0F, 0x50, 0x00, 0x00, 0x09, 0x90, 0xB0, 0x00, 0x00, 0x00, 0x40, 0x01, 0x0F, 0x04, 0x00, - 0x40, 0x0F, 0x18, 0x00, 0x3C, 0x10, 0x40, 0x20, 0x00, 0x02, 0x40, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x8A, 0x93, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x5E, 0xF5, 0x01, 0x00, 0x00, 0x39, - 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xF0, 0x40, 0x00, 0x00, 0x99, 0xD0, 0x2B, 0xC1, 0x00, - 0x04, 0x28, 0x00, 0x15, 0xA5, 0xF0, 0x00, 0x00, 0x02, 0x00, 0x04, 0x18, 0xA0, 0x41, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x10, 0x10, 0x0F, 0x30, 0x00, 0x00, 0xA0, 0x20, 0x01, - 0x0F, 0x04, 0x00, 0x00, 0x47, 0x09, 0x04, 0x3C, 0x10, 0x00, 0x00, 0x00, 0x64, 0x7D, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x80, 0x1B, 0x94, 0x00, 0x40, 0x01, 0x00, 0x02, 0x18, 0x50, 0x00, - 0x00, 0x38, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x78, 0x08, 0xF0, 0x40, 0x00, 0x00, 0x78, 0x00, - 0x2B, 0xC1, 0x00, 0x00, 0x00, 0x03, 0xB6, 0xE0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, - 0xE3, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0A, 0x54, 0x00, 0x08, 0x51, 0x00, 0x50, 0x2A, 0x84, 0x00, 0x00, 0x3E, 0x35, - 0x60, 0x4A, 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x32, 0x7D, 0xC2, 0x00, 0x00, 0x00, 0x00, - 0x7E, 0x05, 0x10, 0x02, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA5, 0x40, 0x01, - 0x02, 0x81, 0x88, 0x71, 0x54, 0x00, 0x00, 0x43, 0xE0, 0x2D, 0x80, 0x00, 0x20, 0x00, 0x00, 0x04, - 0x00, 0x00, 0xB1, 0xE4, 0x00, 0x00, 0xD0, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, - 0xB0, 0x00, 0xC8, 0x00, 0x20, 0x00, 0x0F, 0x04, 0x01, 0x00, 0x00, 0x8E, 0xB2, 0xC0, 0x00, 0x00, - 0x00, 0x16, 0x2A, 0x50, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x43, 0x85, 0x0A, 0x3A, 0xC2, 0x04, 0x05, - 0x00, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x1E, 0x00, 0x0C, - 0xF0, 0x40, 0x30, 0x25, 0x78, 0xED, 0xF4, 0x20, 0x00, 0x00, 0x03, 0xE2, 0x94, 0xCA, 0x10, 0x20, - 0x00, 0x00, 0x10, 0x00, 0x08, 0xA3, 0x40, 0x00, 0x04, 0x00, 0x03, 0xC7, 0x81, 0x00, 0xA0, 0x10, - 0x00, 0x00, 0x90, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x04, 0x00, 0x20, 0x1B, 0x00, 0x08, - 0x2A, 0x80, 0x00, 0x00, 0x34, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x96, - 0x02, 0x00, 0x86, 0x00, 0x2C, 0x7C, 0x78, 0x0A, 0x00, 0x18, 0x00, 0x31, 0xE8, 0x00, 0x06, 0x00, - 0x00, 0x00, 0x0A, 0xF0, 0x40, 0x19, 0x15, 0x80, 0x07, 0x32, 0xA8, 0x02, 0x00, 0x00, 0x00, 0x04, - 0xA0, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0xF9, 0xA0, 0xC3, 0xD4, 0x01, 0x00, 0x00, 0x02, 0xE7, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x02, 0x44, 0x00, 0x0F, 0x04, 0x00, 0x00, - 0x20, 0x00, 0x37, 0xC0, 0x00, 0x00, 0x00, 0x14, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x8E, 0x3A, 0xBD, 0x40, 0x08, 0x00, 0x00, 0x03, 0xEF, 0x08, 0x00, 0x00, 0x38, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x78, 0x0E, 0xF0, 0x40, 0x20, 0x04, 0x00, 0x07, 0x74, 0x20, 0x00, 0x00, - 0x01, 0x43, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x01, 0x30, 0x00, 0x00, 0x40, - 0x00, 0x60, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x08, 0x2F, 0x00, 0x04, 0x3C, 0x10, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x08, 0x13, 0x44, 0x00, 0x0C, 0x01, 0x3E, 0x01, 0xF8, 0xA0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x01, 0xC0, 0x2B, - 0xC1, 0x80, 0xC4, 0x01, 0xE7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x43, - 0x05, 0x01, 0x00, 0x00, 0x23, 0x6C, 0xED, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x40, 0x00, 0x3C, 0x12, 0x00, 0x00, 0x00, 0x70, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x38, 0x94, 0xC0, 0x00, 0x04, 0x05, 0xEB, - 0x5A, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x7B, 0xC1, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x90, 0x85, 0x72, 0x00, 0x00, 0x02, 0x02, 0xC0, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x10, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x3C, 0x10, 0x00, 0x20, - 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1B, 0x07, 0x10, 0x00, 0x00, 0x20, - 0x20, 0x02, 0x4C, 0x0A, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x20, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x38, 0x00, 0x7B, 0xC1, 0x80, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x38, 0x51, 0x05, 0xC1, 0x40, 0x00, 0x00, 0x03, 0x42, 0x0F, 0xBD, 0x34, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xAF, 0x0D, 0x00, 0x3C, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x05, 0x1E, 0x37, 0x20, - 0x00, 0x80, 0x00, 0x00, 0x05, 0xD9, 0xC3, 0x48, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x10, 0x73, 0xC1, 0x40, 0x00, 0x09, 0xE0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x80, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0xFB, - 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x80, 0x00, 0x3C, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00, 0xF0, 0x40, 0x00, 0x00, 0x30, 0x00, 0x08, - 0x18, 0x13, 0x82, 0x80, 0x00, 0x00, 0x00, 0x78, 0x4C, 0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x78, 0x00, 0x33, 0xC1, 0x01, 0x0A, 0x20, - 0x00, 0x01, 0xEF, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x0F, 0x42, 0xDD, 0x40, 0x00, 0x81, - 0xC0, 0x0F, 0x0C, 0xB4, 0x83, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x19, 0x04, 0x3C, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x10, 0xF0, 0x50, 0x02, 0x00, - 0x00, 0x00, 0x09, 0x80, 0xDE, 0x29, 0xD0, 0x80, 0x00, 0x20, 0x01, 0xD8, 0xC3, 0x52, 0x18, 0x30, - 0x00, 0xE0, 0x00, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x80, 0x2B, 0xC1, - 0x00, 0xC0, 0x00, 0x00, 0x00, 0xEF, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x30, 0x08, - 0x00, 0x00, 0x03, 0xC2, 0xD6, 0x0A, 0x00, 0x00, 0x13, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, - 0x1E, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x04, 0x3C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0xF0, - 0x40, 0x00, 0x00, 0x01, 0x82, 0x80, 0x0A, 0x03, 0x80, 0x40, 0x06, 0x00, 0x20, 0x28, 0xD8, 0x00, - 0x00, 0x02, 0x81, 0x00, 0x00, 0x00, 0x10, 0x0A, 0x00, 0x01, 0x4E, 0x50, 0x00, 0x18, 0x00, 0x00, - 0x00, 0x33, 0xC1, 0x04, 0x62, 0x00, 0x00, 0x01, 0xEF, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0xD0, - 0x87, 0xAF, 0x0F, 0x01, 0x00, 0x01, 0x40, 0x0F, 0xEA, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC0, 0x00, 0x00, 0x0E, 0x00, 0x3C, 0x10, 0x01, 0x00, 0x80, - 0x00, 0x00, 0xF0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x07, 0x81, 0x5A, 0xF8, 0xD4, 0x00, 0x03, 0xA0, - 0x30, 0x4E, 0xA5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xF0, 0x20, - 0x20, 0x00, 0x00, 0x10, 0x3B, 0xC1, 0x80, 0x80, 0x04, 0x00, 0x00, 0xCF, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, - 0x94, 0x08, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x40, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x0F, 0x58, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xB0, 0x00, 0x00, 0x10, 0xF0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3D, 0x40, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x43, - 0xC0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x83, 0xC4, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x04, 0x3C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xC4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x83, 0xC6, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0xE2, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x55, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x40, 0x10, 0x01, 0x00, 0x0C, 0x02, 0xA5, 0x00, - 0x40, 0x78, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x05, 0x68, 0x0E, 0x00, 0xF0, 0x00, 0x80, 0x15, 0xA0, 0x30, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xB4, 0x8D, 0x00, 0x00, 0x00, - 0x02, 0x0F, 0x18, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0xFC, 0x7E, 0xF0, 0x00, 0x00, 0x00, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x43, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x83, 0xC6, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x2F, 0x40, 0x1D, - 0x1A, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3C, 0x20, 0xF0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x0D, 0xD0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3B, 0xAD, 0x90, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFE, 0x5A, 0x50, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x2F, 0x40, 0x10, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xF8, 0x0D, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xA9, 0x40, 0x10, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x68, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x39, 0xE0, 0x15, 0x80, 0x00, 0x02, 0x02, - 0x00, 0x3B, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x03, 0x6D, 0xCD, 0x08, 0x90, 0x00, 0x20, 0x00, 0xA0, 0x70, 0x02, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2B, 0xCF, 0x80, 0x18, - 0x00, 0x00, 0x02, 0x0F, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x42, 0xBC, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10, 0xF0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x10, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA9, 0x40, 0x80, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x87, 0x80, 0x00, 0x00, 0x00, 0x1A, - 0x94, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, - 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x80, 0xF0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x08, 0x00, 0xA0, 0x18, 0x13, 0xFA, 0x13, 0x80, 0x00, - 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x3C, - 0x20, 0x30, 0x04, 0x80, 0x00, 0x3A, 0xF0, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xD4, 0x00, 0x01, 0x00, 0x00, 0x03, 0xA0, - 0x12, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x44, 0x3C, 0x00, 0x00, 0xE0, 0x00, 0x08, 0x20, 0xA0, 0x7C, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0xA0, 0x12, 0xA0, 0x64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xA0, 0x74, 0x42, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0xA0, 0x16, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xA0, - 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x83, 0xC6, 0x00, 0x0F, 0x00, 0x00, 0x03, 0xA0, 0x16, 0xA0, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x04, 0x3C, 0x20, 0x01, 0xB0, 0x00, - 0x08, 0x20, 0xA0, 0x37, 0xC3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x83, 0xCE, 0x00, 0x00, 0x40, 0x00, 0x03, 0xA0, 0x16, 0x8E, 0x60, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x3C, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x60, 0xA0, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x83, 0xA0, 0x17, - 0x86, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0xC8, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0xA0, 0x74, 0x03, 0xC0, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x21, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xF4, 0x80, 0x1C, 0x00, 0x00, - 0x03, 0xA0, 0x13, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x3F, 0x68, 0xF1, 0xE0, 0x00, 0x02, 0x20, 0xA0, 0x3C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x99, 0x14, 0xE7, - 0x80, 0x00, 0x18, 0x07, 0xA0, 0x13, 0x80, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x60, 0x1C, 0x58, 0x02, 0x00, 0x00, 0x20, 0xA0, 0x34, - 0x03, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x90, 0x94, 0xB0, 0x1D, 0x00, 0x00, 0x07, 0xA0, 0x12, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8A, 0xEB, 0x71, 0x80, 0x00, 0x00, - 0x20, 0xA0, 0x7C, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xDF, 0x87, 0x00, 0x00, 0x00, 0x07, 0xA0, 0x12, 0x80, 0x60, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x3F, 0xDC, 0x00, - 0x00, 0x00, 0x00, 0x20, 0xA0, 0x74, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x90, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xF8, 0x18, 0x00, 0x00, 0x07, 0xA0, 0x17, 0x8C, - 0x00, 0x00, 0x02, 0x00, 0x44, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x22, 0x5E, 0x10, 0x00, 0x01, 0x00, 0x20, 0xA0, 0x3C, 0x00, 0x00, 0x04, 0x00, 0x22, 0x34, 0x8B, - 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xAB, 0xCE, 0xE0, - 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xC6, 0x98, 0x00, 0xA0, 0x00, 0x07, - 0xA0, 0x16, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xBC, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x03, 0x9D, 0xF9, 0xF0, 0x04, 0x00, 0x00, 0x20, 0xA0, 0x34, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x10, 0x2C, 0xF9, 0x01, - 0x00, 0x80, 0x02, 0xA0, 0x17, 0x8C, 0x64, 0x00, 0x02, 0x03, 0xF9, 0xEF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x82, 0xD9, 0x10, 0xE0, 0x14, 0x10, 0x20, 0xA0, 0x7C, 0x02, - 0x00, 0x04, 0x00, 0x3F, 0xEE, 0xD0, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x9F, 0xC0, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x70, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x00, 0x17, 0xDC, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x42, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2A, - 0x59, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3F, 0xB8, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x8F, 0xE0, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x45, 0xDE, 0xF8, 0x10, 0x04, 0x40, 0x00, 0x20, 0x88, 0x95, 0x12, 0x80, 0x00, 0x60, 0x02, 0x00, - 0x00, 0xD8, 0xF1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x01, 0xCD, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x22, 0x17, 0x8F, 0x89, 0x0C, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xC0, 0x06, 0x00, 0x00, 0x34, 0xC0, 0x00, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x15, 0xF1, 0x81, 0x80, 0x98, 0x02, 0x0A, 0x77, 0x00, 0x05, - 0x00, 0x60, 0x0A, 0x94, 0x01, 0x00, 0x81, 0xC1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, - 0x00, 0x00, 0x01, 0x02, 0xE8, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0xD9, 0x50, 0xE4, 0x00, 0x20, 0x05, 0xA0, 0x7F, 0x42, 0x00, 0x00, 0x00, 0x96, 0x80, 0xE0, 0x1F, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0x0E, 0x87, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xD0, 0x1A, 0x48, 0x00, 0x00, 0x50, - 0x76, 0xC0, 0x00, 0x04, 0x00, 0x08, 0x3D, 0xC0, 0x11, 0x91, 0x80, 0xC0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0xEB, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x20, 0x00, 0x83, - 0xC0, 0x03, 0x8D, 0x04, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x07, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1D, 0x0D, 0x90, 0x0F, 0xA0, - 0x00, 0x40, 0x2C, 0x1F, 0x40, 0x00, 0x00, 0x00, 0x00, 0x46, 0xDC, 0x01, 0xE0, 0x14, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0xD4, 0x3C, 0x01, 0x80, 0x00, 0x00, 0x03, 0xC0, 0xEB, 0x42, 0xC0, - 0x00, 0x00, 0x11, 0x06, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x14, 0x1D, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0xA3, 0x56, - 0xF7, 0x80, 0xA0, 0x00, 0xC2, 0x83, 0x5A, 0x40, 0x78, 0x00, 0x00, 0x01, 0x40, 0x40, 0x90, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3A, 0x00, 0xF0, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x03, 0x3C, 0x18, 0xB0, 0x00, 0x01, 0x08, 0x4C, 0x30, - 0xA0, 0x04, 0x00, 0x04, 0x00, 0x28, 0x02, 0xB3, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x01, 0x30, 0xB6, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x14, 0x0F, 0x88, 0x18, 0x00, 0x00, 0x00, 0x5A, 0x1F, 0xAE, 0x04, 0x00, 0x03, 0x01, 0x01, - 0xDA, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x03, 0x0E, - 0xE8, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x20, 0x10, 0x00, 0x00, - 0x00, 0x05, 0x00, 0x30, 0xC3, 0xC7, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x88, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0xFF, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0xB8, 0xFC, 0x10, 0x18, 0x00, 0x80, 0x48, 0xC1, 0x77, 0x60, 0x64, 0x05, - 0x01, 0x00, 0x0C, 0xE0, 0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, - 0x00, 0x23, 0x0C, 0xD8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4C, 0x4D, - 0xB0, 0xC0, 0x10, 0x00, 0x08, 0x10, 0xFD, 0xC3, 0x40, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x8D, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x0C, 0x05, 0xE0, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0xA1, 0x6E, 0x07, 0x8E, 0x00, 0x88, 0x80, 0x10, 0xF7, - 0xD6, 0x21, 0x00, 0x31, 0x03, 0x00, 0x5E, 0x38, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x28, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, - 0x01, 0x2A, 0x6E, 0x08, 0xB0, 0x00, 0xC0, 0x03, 0x8D, 0x74, 0x04, 0x08, 0x02, 0x01, 0x38, 0x03, - 0xD1, 0x8F, 0xA0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x7F, - 0xC0, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x14, 0x1C, 0xA5, 0x9A, 0x00, 0x00, - 0x00, 0x10, 0x1B, 0x80, 0x00, 0x00, 0x00, 0x08, 0x3D, 0x60, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x0A, 0x40, 0x02, 0xD9, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x38, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x0D, 0xF9, 0xC0, 0x00, 0x00, - 0x00, 0x83, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x45, 0xD0, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0F, 0xA0, - 0x00, 0x80, 0x00, 0x02, 0xA0, 0x10, 0x06, 0x04, 0x00, 0x60, 0x08, 0x3D, 0x40, 0x00, 0xE0, 0x01, - 0x80, 0x00, 0x00, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x70, 0x00, 0x00, 0x00, 0x45, 0x00, 0xBC, - 0x22, 0x00, 0x00, 0x00, 0x83, 0xC0, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x5E, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x16, 0x99, 0x80, 0x00, 0x20, 0x02, 0xA5, 0x73, 0x86, 0x00, 0x00, 0x00, 0x08, 0x3C, 0xC0, - 0x70, 0xC5, 0x88, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x50, 0x00, 0x00, 0x00, - 0x00, 0xA0, 0xA4, 0x00, 0x07, 0x00, 0x00, 0x83, 0xC0, 0x00, 0x80, 0x80, 0x48, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x29, 0x8E, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x02, 0x9D, 0xF0, 0x0F, 0x00, 0x00, 0x02, 0xA0, 0x12, 0xDC, 0x60, 0x00, 0x00, - 0x18, 0x3D, 0x40, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x64, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1F, 0x50, - 0xA0, 0x00, 0x00, 0x0F, 0x00, 0xF0, 0x06, 0xC0, 0x00, 0x00, 0x83, 0xC2, 0x00, 0x0F, 0xA1, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x10, 0x80, 0xCE, 0xA7, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x23, 0xDC, 0x00, 0x01, 0x00, 0x00, 0x02, 0x81, 0x00, 0x0E, - 0x28, 0x04, 0x00, 0x08, 0x3C, 0x60, 0x81, 0xEA, 0x14, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3C, - 0x01, 0x00, 0x00, 0x02, 0x8C, 0x0C, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, - 0x3C, 0x0C, 0x70, 0xD0, 0x00, 0x20, 0x04, 0x2D, 0x24, 0x20, 0x00, 0x00, 0x00, 0x83, 0xC0, 0x0B, - 0x19, 0x5C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x88, 0x00, 0x00, 0x16, 0x96, 0xDF, - 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x33, 0xD4, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x5E, 0x06, 0x70, 0x00, 0x00, 0x08, 0x3C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, 0xEF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x03, 0x3C, 0x20, 0x00, 0x90, 0x00, 0x00, 0x00, 0x23, 0x34, 0x02, 0x00, 0x20, 0x00, - 0x83, 0xC0, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0xA9, 0x9C, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x17, 0xF0, 0x18, - 0x00, 0x00, 0x42, 0x00, 0x3F, 0x20, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x94, 0x2D, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xF2, 0xE8, 0x00, 0x10, 0x08, 0x00, 0x6C, 0x42, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x30, 0xA5, 0xD8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x14, - 0x8C, 0xDB, 0x81, 0x00, 0x00, 0x00, 0x10, 0xDB, 0x80, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x0E, 0xFE, 0x70, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x12, 0x94, 0x6D, 0x18, 0xF0, 0x00, 0x00, 0x00, - 0x05, 0xBC, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x9D, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x85, 0x0D, 0xF1, 0x81, 0x00, 0x80, 0x00, 0x50, 0x77, 0x80, 0x30, 0x60, 0x00, 0x08, - 0x29, 0x48, 0x00, 0xC4, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3C, 0x3E, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x92, 0xCC, 0x00, 0x80, - 0x10, 0x00, 0x05, 0x00, 0x00, 0x03, 0x80, 0x00, 0x02, 0x90, 0x8E, 0xB0, 0x1B, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xB5, 0xE7, 0x8A, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x16, 0x80, 0x0F, 0x08, 0x18, 0x40, 0x80, 0x5A, 0x00, 0x00, - 0x00, 0x60, 0x1A, 0x9F, 0xCA, 0x92, 0xA0, 0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x29, 0xFE, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, - 0x20, 0x00, 0x80, 0x00, 0x00, 0x04, 0x20, 0xA8, 0x02, 0x80, 0x00, 0x40, 0x94, 0x9D, 0xB9, 0x99, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xB1, 0x46, 0xA1, 0x81, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3E, 0xFB, 0x80, 0x00, 0x00, 0x40, 0x81, - 0x5A, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xA0, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x14, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0xFD, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0xB0, 0x02, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, - 0x0E, 0x83, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x90, 0x96, 0xC1, 0x99, 0x00, - 0x00, 0x40, 0xCF, 0x58, 0x0E, 0xA2, 0x55, 0x00, 0x02, 0x80, 0xE9, 0x80, 0x05, 0xC0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x02, 0x2C, 0x09, 0x00, 0xA0, 0x00, 0x00, 0x8C, 0xF0, 0x01, 0x44, 0x30, - 0x00, 0x00, 0x00, 0x02, 0x8D, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x14, 0x9D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xA4, - 0xF3, 0x98, 0x00, 0x00, 0x02, 0x0D, 0xFE, 0x80, 0x00, 0x84, 0x00, 0x02, 0x9C, 0x7B, 0x00, 0x1A, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x20, 0x00, 0x1B, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xEB, 0xFD, 0x18, 0xF0, 0x00, 0x00, 0x0A, 0xDD, - 0x68, 0x02, 0x00, 0x20, 0x00, 0x00, 0xDF, 0x90, 0x0D, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x0E, 0x9F, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3F, 0x95, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x1D, 0xD6, 0xE0, 0x00, 0x20, 0x03, 0x10, 0x05, - 0x49, 0x80, 0x05, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, - 0x2E, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x93, 0x6D, 0xD0, 0xB0, 0x00, - 0x20, 0x4A, 0xD7, 0x6C, 0x43, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x07, 0x00, 0x92, 0x94, 0x9F, 0x98, 0x00, 0x00, 0x00, 0x0A, 0x47, 0xDB, 0xE0, 0x04, 0x00, - 0x00, 0x01, 0x60, 0x7B, 0x01, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x88, 0x28, - 0x70, 0x00, 0x00, 0x00, 0x04, 0x2B, 0xF0, 0x42, 0x83, 0x00, 0x00, 0x3F, 0x1F, 0x90, 0x10, 0x58, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x08, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xA9, 0x57, 0xD9, 0x1C, 0x00, 0x0E, 0x00, 0x10, 0x9F, - 0xD6, 0x04, 0x00, 0x21, 0x00, 0x90, 0x08, 0x00, 0x04, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x14, 0x2A, 0x10, 0x90, 0x00, 0xC0, 0x00, 0x07, 0x6C, 0x02, 0x00, 0x03, 0x00, 0x09, 0x00, - 0x00, 0x0B, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x83, 0xCE, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0xFF, 0x7B, 0xC6, 0xE0, 0x00, 0x00, 0x2B, 0x45, 0x6E, 0xD1, 0x90, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x05, 0xE1, 0xA6, 0x42, 0x80, 0x00, - 0x03, 0x11, 0xC6, 0xCD, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x83, 0xCC, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x80, 0x02, 0xA5, 0x00, 0x20, 0x24, 0x00, 0x60, 0x09, 0x94, 0xD8, 0x50, 0xB1, 0xC1, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xE0, 0x00, 0x00, 0x85, 0xA0, 0x3D, - 0xC7, 0xC4, 0x00, 0x42, 0x25, 0x44, 0x90, 0x0B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x83, 0xCC, 0x00, 0x00, 0xA0, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x10, 0x00, 0x38, 0x00, 0x20, 0x4B, 0xB7, 0x4C, - 0x70, 0x10, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x00, - 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x80, 0xF0, 0x80, 0x04, 0x02, 0x02, 0x20, 0x21, 0x0C, 0xC0, 0x0F, 0x00, 0x0C, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x16, 0xD5, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x38, 0x06, 0x00, 0x00, 0x02, - 0x6B, 0xB5, 0xCC, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3C, 0x3E, 0x00, - 0x00, 0x00, 0x00, 0x80, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x12, 0x3E, 0xD5, 0x08, 0x01, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xA5, 0x00, 0x40, - 0x02, 0x02, 0x04, 0x0A, 0xA5, 0xFA, 0xD9, 0xB0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x05, 0xA0, 0x2C, 0x00, 0x10, 0x50, 0x02, 0x1A, 0x8E, 0xD5, - 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x06, 0xA0, 0x00, 0x10, 0x00, 0x00, - 0x01, 0x5F, 0x20, 0x04, 0x20, 0x06, 0x49, 0x1E, 0x4D, 0xD9, 0x91, 0xC0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0C, 0x01, 0x40, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xBD, 0x42, 0x00, 0x00, 0x00, - 0x07, 0x4E, 0x91, 0x0D, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x1E, 0xB0, 0x00, - 0x00, 0x00, 0x48, 0x00, 0x1A, 0x60, 0x00, 0x00, 0x02, 0x1A, 0xA4, 0x7D, 0xB0, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x74, 0x43, - 0x80, 0x00, 0x00, 0x25, 0x4D, 0xDF, 0x08, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x10, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0F, 0x50, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x10, 0xC0, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, - 0xF0, 0x02, 0xC0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x34, 0x40, 0x00, 0x00, 0x11, 0x03, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0C, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xC9, 0xBA, 0x80, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2C, 0x6F, 0x04, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x1D, 0x0E, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x24, 0x05, 0xE7, 0x6A, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x0A, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x07, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x12, - 0x55, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0C, 0x43, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xA5, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x12, 0x18, 0x0E, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x25, 0x85, 0x38, 0x02, - 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x05, 0xF5, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x06, 0x00, 0x02, 0x8C, 0xE0, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x2F, 0xFA, 0xC0, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xF1, 0xA0, 0x06, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x96, 0x8C, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xAE, 0x06, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x5A, 0x76, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0xE0, 0x06, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0xC3, 0x3E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x2C, 0x6D, - 0xAC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xC4, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0A, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x51, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0B, 0x02, 0x6C, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0xED, 0x42, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x7A, - 0x82, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x85, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x4E, 0x02, 0x82, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x2A, 0xD7, 0x69, 0x40, 0x40, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x7E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE4, - 0x00, 0x00, 0x01, 0x20, 0x01, 0x55, 0xC0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x03, 0x00, 0x84, 0x14, 0xD0, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x0B, 0x5C, 0x40, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x0A, 0xB6, 0xEF, - 0x00, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0xAA, 0xD1, 0x74, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x2A, 0x13, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x04, 0xF0, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x89, 0x17, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x20, 0x0F, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xF0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x16, 0x80, 0xE0, 0x00, 0x00, 0x02, 0x02, 0x05, 0x92, 0xEC, - 0x34, 0x00, 0x00, 0x08, 0x3C, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x0F, 0x16, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0A, - 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xD5, 0xF6, 0x60, 0x40, 0x00, 0x00, 0x83, 0xD4, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x40, 0x03, 0xC0, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x34, 0x00, 0x40, 0x08, 0x3C, 0x00, 0x01, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x0F, 0x16, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x01, - 0x83, 0xCC, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0xC4, 0x00, 0x00, 0x00, 0x28, 0x3C, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x0F, 0x16, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x8B, 0x76, 0x06, - 0x00, 0x00, 0x00, 0x83, 0xCE, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC7, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xBB, 0x00, 0x00, 0x50, 0x00, 0x28, 0x3C, 0x00, 0x00, - 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x81, 0x0F, 0x12, 0x80, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x40, - 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x83, 0xD4, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, - 0xF0, 0x40, 0xC3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x17, 0xC0, 0x40, 0x00, 0x00, 0x08, - 0x3C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x0F, 0x12, 0x16, 0x00, 0x00, 0x60, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x81, 0x80, 0x00, 0x2B, 0x74, 0x02, 0x80, 0x06, 0x00, 0x83, 0xCC, 0x07, 0x00, 0x00, 0x18, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x41, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0xBB, 0x0E, 0x00, - 0x00, 0x00, 0x08, 0x3C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x80, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0F, 0xE0, 0x40, 0x00, 0x01, 0x00, 0x83, 0xC6, 0x00, 0x00, - 0x20, 0x04, 0x00, 0x00, 0x00, 0x08, 0x50, 0xE0, 0x20, 0x00, 0x00, 0x02, 0x14, 0x80, 0xD0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x84, 0x20, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x50, 0xB5, - 0x56, 0x14, 0x00, 0x02, 0x01, 0x02, 0xB5, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xFA, 0x46, 0x40, 0x00, 0x00, 0x30, - 0xC7, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xA8, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x73, 0x20, 0x64, 0x00, 0x00, 0x02, 0x0F, 0x49, 0xD8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x00, - 0x00, 0x00, 0x2A, 0x85, 0x80, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x3C, 0x02, 0x40, - 0x00, 0x00, 0x10, 0x02, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x21, 0xA6, 0xD0, 0x01, 0xF0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x64, 0x00, 0x01, 0x41, 0x01, 0xCC, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x00, 0x5A, 0x64, 0x85, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, - 0x3C, 0x22, 0x40, 0x00, 0x00, 0x0A, 0x87, 0xB0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xA5, - 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x10, 0x57, 0x46, 0x04, 0x00, 0x00, 0x01, 0x5D, - 0x49, 0x78, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x5A, 0x65, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0F, 0x10, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0xA9, 0x0A, 0xA0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0A, 0x0C, 0x24, 0x00, 0x80, - 0x00, 0x41, 0xA4, 0xC0, 0xD1, 0x00, 0x80, 0x00, 0x00, 0x30, 0x40, 0xF5, 0x57, 0x00, 0x01, 0x00, - 0x00, 0x20, 0x29, 0x5C, 0xF0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x40, 0x00, 0x20, 0xA1, 0x62, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x45, 0x88, 0x0E, 0xA0, - 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0xC0, 0x02, 0x00, 0x00, 0x00, 0x10, 0x82, 0xA5, 0x1A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x0A, - 0x40, 0x34, 0x00, 0x30, 0x41, 0xA5, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAF, 0x73, - 0x06, 0x2C, 0x00, 0x01, 0x20, 0x01, 0x7A, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x2A, 0x05, 0x80, 0x00, 0x40, 0x03, 0x00, 0x5A, 0x44, - 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x37, 0x40, 0x00, 0x00, 0x00, 0x0E, 0x66, - 0xB0, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x46, 0xD0, 0x00, 0x00, 0x00, - 0x02, 0xC0, 0x58, 0x24, 0x00, 0x00, 0x00, 0x41, 0xA4, 0xC0, 0x08, 0xB0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5A, 0x77, 0x00, 0x00, 0x00, 0x00, 0x03, 0x25, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xF3, 0x48, 0x00, 0xE0, 0x00, 0x00, 0x2C, 0x01, 0x82, 0x00, 0x00, 0x06, - 0x00, 0x5A, 0x65, 0xC5, 0x8D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x83, 0xF8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x80, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x12, 0xA5, 0x02, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x01, 0xD0, 0x30, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xE7, 0x73, 0x00, 0x60, 0x00, 0x00, 0x01, 0x41, 0x49, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x2E, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x21, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xC2, 0x00, 0x03, 0x00, 0xA8, 0x01, 0xE0, 0x1E, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x09, 0x99, 0x00, 0x00, 0x00, 0xD7, 0xD6, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xF0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x30, 0x08, 0x28, 0x5B, - 0x00, 0x90, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x90, 0x00, 0x00, - 0x07, 0xDD, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x03, 0xE0, 0xC3, 0x00, 0x00, 0x00, 0xA8, 0x01, 0xC0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x0E, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xF1, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x82, 0xAD, 0x7E, 0xE0, 0x00, 0x00, 0x00, - 0x0C, 0x28, 0x5B, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x91, 0x68, 0x18, - 0x00, 0x40, 0x00, 0x0F, 0xE5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x69, 0x11, 0x3C, 0x00, 0x00, 0x50, 0x00, 0xA8, 0x00, 0xE0, 0x0E, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x81, 0x8B, 0x19, 0x00, 0x00, 0x00, 0x17, 0x96, 0xBC, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x82, 0xC1, 0xB6, 0x00, - 0x00, 0x00, 0x00, 0x0C, 0x28, 0x5B, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x68, 0xCF, 0x81, 0x90, 0x00, 0x00, 0x0D, 0x41, 0xA2, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x62, 0xA3, 0x20, 0x03, 0xC0, 0x00, 0x00, 0xA8, 0x00, 0xC7, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x8F, 0xBB, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xCC, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x82, - 0x0D, 0x7B, 0xC0, 0x64, 0x00, 0x00, 0x0C, 0x28, 0x5B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0xDF, 0xDB, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x9D, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0x80, 0x42, 0x80, 0x00, 0x00, - 0xA8, 0x01, 0xE1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xC3, 0x05, 0x0F, - 0x00, 0x00, 0x00, 0xFF, 0x1E, 0x8C, 0x28, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x02, 0x00, - 0x00, 0x30, 0x00, 0x55, 0x12, 0xA0, 0x6C, 0x50, 0x00, 0x0C, 0x28, 0x4B, 0x99, 0x90, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7D, 0xFC, 0xD8, 0x10, 0x00, 0x00, 0x0F, 0xE9, 0x2E, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x00, 0xA8, 0x01, 0xC1, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, - 0x40, 0xA5, 0x0A, 0x00, 0x00, 0x00, 0xC3, 0xD6, 0x74, 0x6C, 0x00, 0x02, 0x02, 0xBC, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x5E, 0x06, 0x68, 0x00, 0x00, 0x0C, 0x28, 0x4B, 0x98, - 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0xFD, 0xFC, 0x81, 0xB4, 0x00, 0x00, 0x0C, - 0x65, 0x21, 0x42, 0x80, 0x00, 0x00, 0x2B, 0xDD, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x65, 0x43, 0x80, 0x00, 0x20, 0xA8, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3A, 0x3E, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x9E, 0x80, 0x30, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x5E, 0x76, 0x64, 0x00, 0x60, 0x0C, - 0x28, 0x4F, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x52, 0xC1, 0x01, 0xA4, - 0x00, 0x00, 0x0F, 0xE1, 0x28, 0x02, 0x80, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x02, 0x01, 0x00, 0x01, 0x2D, 0x43, 0x80, 0x00, 0x10, 0xA8, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0xE0, 0x10, 0x00, 0x00, 0x00, 0x3C, 0xD6, 0x56, 0x40, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x36, 0xA0, 0x04, - 0x00, 0x10, 0x0C, 0x28, 0x4F, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xBD, - 0x4B, 0x00, 0xA0, 0x00, 0x00, 0x03, 0x95, 0x21, 0x42, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x42, 0x02, 0xA0, - 0x07, 0xD4, 0x20, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xC8, 0x00, 0x20, 0xA1, 0x6C, 0x06, 0xC0, 0x13, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, - 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0xA0, 0x06, 0x00, 0x39, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xD5, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xA1, 0x6C, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x3D, 0x42, - 0xF0, 0x00, 0x40, 0x00, 0x02, 0xA0, 0x03, 0xDC, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x53, 0xE9, 0xE1, 0x00, 0x02, 0x01, 0x00, 0x30, 0xA1, - 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xA0, 0x02, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xC0, 0x60, 0x01, 0x80, 0x00, - 0x00, 0x30, 0xA1, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x02, 0xA0, 0x07, 0xC4, 0x00, 0x51, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x90, 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0xA1, 0x2E, 0x66, 0x05, 0x20, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x21, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x02, 0xA0, 0x06, - 0x04, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xA1, 0x2E, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x07, 0x80, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x42, 0xF0, 0x00, 0x00, 0x00, - 0x80, 0xF0, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0B, 0xE9, 0xE1, 0x01, 0xC0, 0x01, 0x80, 0x10, 0xF1, 0x70, 0x00, 0x40, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x3D, 0x42, 0xF0, - 0x00, 0x00, 0x00, 0xC0, 0xF0, 0x00, 0x00, 0x00, 0x41, 0x82, 0x20, 0x14, 0x0A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5B, 0xFD, 0xED, 0x00, 0x00, 0x00, 0x00, 0x10, 0xF1, 0x38, - 0x00, 0x00, 0x00, 0x00, 0xAB, 0xC7, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x3F, 0x72, - 0x00, 0x80, 0x11, 0x00, 0x82, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x80, 0x01, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x01, 0x82, 0x00, 0x00, 0x01, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x80, 0x01, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x02, 0x82, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x80, 0x01, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x03, 0x82, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x80, 0x01, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x22, 0x8A, 0x9A, 0x01, 0x06, 0x00 -}; - -#endif /* _BARCODE_EMULATOR_H_ */ diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index 08792a7..07d55df 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -24,6 +24,8 @@ * */ +#include <linux/module.h> + #include "ch7006_priv.h" /* DRM encoder functions */ diff --git a/drivers/gpu/drm/i2c/sil164_drv.c b/drivers/gpu/drm/i2c/sil164_drv.c index 0b67732..b7d45ab 100644 --- a/drivers/gpu/drm/i2c/sil164_drv.c +++ b/drivers/gpu/drm/i2c/sil164_drv.c @@ -24,6 +24,8 @@ * */ +#include <linux/module.h> + #include "drmP.h" #include "drm_crtc_helper.h" #include "drm_encoder_slave.h" diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index 6f98d05..d4266bd 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -30,6 +30,8 @@ * Gareth Hughes <gareth@valinux.com> */ +#include <linux/module.h> + #include "drmP.h" #include "drm.h" #include "i810_drm.h" diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c index d3e8c54..1ca799a 100644 --- a/drivers/gpu/drm/i915/dvo_ch7017.c +++ b/drivers/gpu/drm/i915/dvo_ch7017.c @@ -227,7 +227,7 @@ static bool ch7017_init(struct intel_dvo_device *dvo, default: DRM_DEBUG_KMS("ch701x not detected, got %d: from %s " "slave %d.\n", - val, adapter->name,dvo->slave_addr); + val, adapter->name, dvo->slave_addr); goto fail; } diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c index 7eaa94e..4a03660 100644 --- a/drivers/gpu/drm/i915/dvo_ch7xxx.c +++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c @@ -111,7 +111,7 @@ static char *ch7xxx_get_id(uint8_t vid) /** Reads an 8 bit register */ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) { - struct ch7xxx_priv *ch7xxx= dvo->dev_priv; + struct ch7xxx_priv *ch7xxx = dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; u8 out_buf[2]; u8 in_buf[2]; @@ -303,7 +303,7 @@ static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) for (i = 0; i < CH7xxx_NUM_REGS; i++) { uint8_t val; - if ((i % 8) == 0 ) + if ((i % 8) == 0) DRM_LOG_KMS("\n %02X: ", i); ch7xxx_readb(dvo, i, &val); DRM_LOG_KMS("%02X ", val); diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c index a12ed94..04f2893 100644 --- a/drivers/gpu/drm/i915/dvo_ivch.c +++ b/drivers/gpu/drm/i915/dvo_ivch.c @@ -344,8 +344,8 @@ static void ivch_mode_set(struct intel_dvo_device *dvo, (adjusted_mode->hdisplay - 1)) >> 2; y_ratio = (((mode->vdisplay - 1) << 16) / (adjusted_mode->vdisplay - 1)) >> 2; - ivch_write (dvo, VR42, x_ratio); - ivch_write (dvo, VR41, y_ratio); + ivch_write(dvo, VR42, x_ratio); + ivch_write(dvo, VR41, y_ratio); } else { vr01 &= ~VR01_PANEL_FIT_ENABLE; vr40 &= ~VR40_CLOCK_GATING_ENABLE; @@ -410,7 +410,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo) } } -struct intel_dvo_dev_ops ivch_ops= { +struct intel_dvo_dev_ops ivch_ops = { .init = ivch_init, .dpms = ivch_dpms, .mode_valid = ivch_mode_valid, diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c index e4b4091..a0b13a6 100644 --- a/drivers/gpu/drm/i915/dvo_sil164.c +++ b/drivers/gpu/drm/i915/dvo_sil164.c @@ -104,7 +104,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) { - struct sil164_priv *sil= dvo->dev_priv; + struct sil164_priv *sil = dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; uint8_t out_buf[2]; struct i2c_msg msg = { diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c index 8ab2855..aa2cd3e 100644 --- a/drivers/gpu/drm/i915/dvo_tfp410.c +++ b/drivers/gpu/drm/i915/dvo_tfp410.c @@ -56,7 +56,7 @@ #define TFP410_CTL_2_MDI (1<<0) #define TFP410_CTL_3 0x0A -#define TFP410_CTL_3_DK_MASK (0x7<<5) +#define TFP410_CTL_3_DK_MASK (0x7<<5) #define TFP410_CTL_3_DK (1<<5) #define TFP410_CTL_3_DKEN (1<<4) #define TFP410_CTL_3_CTL_MASK (0x7<<1) @@ -225,12 +225,12 @@ static void tfp410_mode_set(struct intel_dvo_device *dvo, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - /* As long as the basics are set up, since we don't have clock dependencies - * in the mode setup, we can just leave the registers alone and everything - * will work fine. - */ - /* don't do much */ - return; + /* As long as the basics are set up, since we don't have clock dependencies + * in the mode setup, we can just leave the registers alone and everything + * will work fine. + */ + /* don't do much */ + return; } /* set the tfp410 power state */ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 19bab81..9b4e5c6 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -29,6 +29,8 @@ #include <linux/seq_file.h> #include <linux/debugfs.h> #include <linux/slab.h> +#include <linux/export.h> +#include <generated/utsrelease.h> #include "drmP.h" #include "drm.h" #include "intel_drv.h" @@ -61,6 +63,7 @@ static int i915_capabilities(struct seq_file *m, void *data) const struct intel_device_info *info = INTEL_INFO(dev); seq_printf(m, "gen: %d\n", info->gen); + seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev)); #define B(x) seq_printf(m, #x ": %s\n", yesno(info->x)) B(is_mobile); B(is_i85x); @@ -98,12 +101,12 @@ static const char *get_pin_flag(struct drm_i915_gem_object *obj) static const char *get_tiling_flag(struct drm_i915_gem_object *obj) { - switch (obj->tiling_mode) { - default: - case I915_TILING_NONE: return " "; - case I915_TILING_X: return "X"; - case I915_TILING_Y: return "Y"; - } + switch (obj->tiling_mode) { + default: + case I915_TILING_NONE: return " "; + case I915_TILING_X: return "X"; + case I915_TILING_Y: return "Y"; + } } static const char *cache_level_str(int type) @@ -217,7 +220,7 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) ++mappable_count; \ } \ } \ -} while(0) +} while (0) static int i915_gem_object_info(struct seq_file *m, void* data) { @@ -337,7 +340,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) seq_printf(m, "No flip due on pipe %c (plane %c)\n", pipe, plane); } else { - if (!work->pending) { + if (atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) { seq_printf(m, "Flip queued on pipe %c (plane %c)\n", pipe, plane); } else { @@ -348,7 +351,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) seq_printf(m, "Stall check enabled, "); else seq_printf(m, "Stall check waiting for page flip ioctl, "); - seq_printf(m, "%d prepares\n", work->pending); + seq_printf(m, "%d prepares\n", atomic_read(&work->pending)); if (work->old_fb_obj) { struct drm_i915_gem_object *obj = work->old_fb_obj; @@ -499,7 +502,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) seq_printf(m, "Interrupts received: %d\n", atomic_read(&dev_priv->irq_received)); for (i = 0; i < I915_NUM_RINGS; i++) { - if (IS_GEN6(dev)) { + if (IS_GEN6(dev) || IS_GEN7(dev)) { seq_printf(m, "Graphics Interrupt mask (%s): %08x\n", dev_priv->ring[i].name, I915_READ_IMR(&dev_priv->ring[i])); @@ -635,11 +638,16 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; + int ret; ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; if (ring->size == 0) return 0; + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + seq_printf(m, "Ring %s:\n", ring->name); seq_printf(m, " Head : %08x\n", I915_READ_HEAD(ring) & HEAD_ADDR); seq_printf(m, " Tail : %08x\n", I915_READ_TAIL(ring) & TAIL_ADDR); @@ -653,6 +661,8 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data) seq_printf(m, " Control : %08x\n", I915_READ_CTL(ring)); seq_printf(m, " Start : %08x\n", I915_READ_START(ring)); + mutex_unlock(&dev->struct_mutex); + return 0; } @@ -746,6 +756,7 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); + seq_printf(m, "Kernel: " UTS_RELEASE "\n"); seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device); seq_printf(m, "EIR: 0x%08x\n", error->eir); seq_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er); @@ -841,7 +852,16 @@ static int i915_rstdby_delays(struct seq_file *m, void *unused) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; - u16 crstanddelay = I915_READ16(CRSTANDVID); + u16 crstanddelay; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + crstanddelay = I915_READ16(CRSTANDVID); + + mutex_unlock(&dev->struct_mutex); seq_printf(m, "w/ctx: %d, w/o ctx: %d\n", (crstanddelay >> 8) & 0x3f, (crstanddelay & 0x3f)); @@ -939,7 +959,11 @@ static int i915_delayfreq_table(struct seq_file *m, void *unused) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; u32 delayfreq; - int i; + int ret, i; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; for (i = 0; i < 16; i++) { delayfreq = I915_READ(PXVFREQ_BASE + i * 4); @@ -947,6 +971,8 @@ static int i915_delayfreq_table(struct seq_file *m, void *unused) (delayfreq & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT); } + mutex_unlock(&dev->struct_mutex); + return 0; } @@ -961,13 +987,19 @@ static int i915_inttoext_table(struct seq_file *m, void *unused) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; u32 inttoext; - int i; + int ret, i; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; for (i = 1; i <= 32; i++) { inttoext = I915_READ(INTTOEXT_BASE_ILK + i * 4); seq_printf(m, "INTTOEXT%02d: 0x%08x\n", i, inttoext); } + mutex_unlock(&dev->struct_mutex); + return 0; } @@ -976,9 +1008,19 @@ static int i915_drpc_info(struct seq_file *m, void *unused) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; - u32 rgvmodectl = I915_READ(MEMMODECTL); - u32 rstdbyctl = I915_READ(RSTDBYCTL); - u16 crstandvid = I915_READ16(CRSTANDVID); + u32 rgvmodectl, rstdbyctl; + u16 crstandvid; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + rgvmodectl = I915_READ(MEMMODECTL); + rstdbyctl = I915_READ(RSTDBYCTL); + crstandvid = I915_READ16(CRSTANDVID); + + mutex_unlock(&dev->struct_mutex); seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ? "yes" : "no"); @@ -1123,14 +1165,59 @@ static int i915_emon_status(struct seq_file *m, void *unused) return 0; } +static int i915_ring_freq_table(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + int gpu_freq, ia_freq; + + if (!(IS_GEN6(dev) || IS_GEN7(dev))) { + seq_printf(m, "unsupported on this chipset\n"); + return 0; + } + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n"); + + for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay; + gpu_freq++) { + I915_WRITE(GEN6_PCODE_DATA, gpu_freq); + I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | + GEN6_PCODE_READ_MIN_FREQ_TABLE); + if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & + GEN6_PCODE_READY) == 0, 10)) { + DRM_ERROR("pcode read of freq table timed out\n"); + continue; + } + ia_freq = I915_READ(GEN6_PCODE_DATA); + seq_printf(m, "%d\t\t%d\n", gpu_freq * 50, ia_freq * 100); + } + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + static int i915_gfxec(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; seq_printf(m, "GFXEC: %ld\n", (unsigned long)I915_READ(0x112f4)); + mutex_unlock(&dev->struct_mutex); + return 0; } @@ -1229,9 +1316,13 @@ static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; + unsigned forcewake_count; + + spin_lock_irq(&dev_priv->gt_lock); + forcewake_count = dev_priv->forcewake_count; + spin_unlock_irq(&dev_priv->gt_lock); - seq_printf(m, "forcewake count = %d\n", - atomic_read(&dev_priv->forcewake_count)); + seq_printf(m, "forcewake count = %u\n", forcewake_count); return 0; } @@ -1255,12 +1346,12 @@ i915_wedged_read(struct file *filp, char buf[80]; int len; - len = snprintf(buf, sizeof (buf), + len = snprintf(buf, sizeof(buf), "wedged : %d\n", atomic_read(&dev_priv->mm.wedged)); - if (len > sizeof (buf)) - len = sizeof (buf); + if (len > sizeof(buf)) + len = sizeof(buf); return simple_read_from_buffer(ubuf, max, ppos, buf, len); } @@ -1276,7 +1367,7 @@ i915_wedged_write(struct file *filp, int val = 1; if (cnt > 0) { - if (cnt > sizeof (buf) - 1) + if (cnt > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, ubuf, cnt)) @@ -1300,6 +1391,155 @@ static const struct file_operations i915_wedged_fops = { .llseek = default_llseek, }; +static int +i915_max_freq_open(struct inode *inode, + struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t +i915_max_freq_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[80]; + int len; + + len = snprintf(buf, sizeof(buf), + "max freq: %d\n", dev_priv->max_delay * 50); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_max_freq_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + char buf[20]; + int val = 1; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val); + + /* + * Turbo will still be enabled, but won't go above the set value. + */ + dev_priv->max_delay = val / 50; + + gen6_set_rps(dev, val / 50); + + return cnt; +} + +static const struct file_operations i915_max_freq_fops = { + .owner = THIS_MODULE, + .open = i915_max_freq_open, + .read = i915_max_freq_read, + .write = i915_max_freq_write, + .llseek = default_llseek, +}; + +static int +i915_cache_sharing_open(struct inode *inode, + struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t +i915_cache_sharing_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[80]; + u32 snpcr; + int len; + + mutex_lock(&dev_priv->dev->struct_mutex); + snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); + mutex_unlock(&dev_priv->dev->struct_mutex); + + len = snprintf(buf, sizeof(buf), + "%d\n", (snpcr & GEN6_MBC_SNPCR_MASK) >> + GEN6_MBC_SNPCR_SHIFT); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_cache_sharing_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + char buf[20]; + u32 snpcr; + int val = 1; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + if (val < 0 || val > 3) + return -EINVAL; + + DRM_DEBUG_DRIVER("Manually setting uncore sharing to %d\n", val); + + /* Update the cache sharing policy here as well */ + snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); + snpcr &= ~GEN6_MBC_SNPCR_MASK; + snpcr |= (val << GEN6_MBC_SNPCR_SHIFT); + I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr); + + return cnt; +} + +static const struct file_operations i915_cache_sharing_fops = { + .owner = THIS_MODULE, + .open = i915_cache_sharing_open, + .read = i915_cache_sharing_read, + .write = i915_cache_sharing_write, + .llseek = default_llseek, +}; + /* As the drm_debugfs_init() routines are called before dev->dev_private is * allocated we need to hook into the minor for release. */ static int @@ -1318,7 +1558,10 @@ drm_add_fake_info_node(struct drm_minor *minor, node->minor = minor; node->dent = ent; node->info_ent = (void *) key; - list_add(&node->list, &minor->debugfs_nodes.list); + + mutex_lock(&minor->debugfs_lock); + list_add(&node->list, &minor->debugfs_list); + mutex_unlock(&minor->debugfs_lock); return 0; } @@ -1399,6 +1642,36 @@ static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor) return drm_add_fake_info_node(minor, ent, &i915_forcewake_fops); } +static int i915_max_freq_create(struct dentry *root, struct drm_minor *minor) +{ + struct drm_device *dev = minor->dev; + struct dentry *ent; + + ent = debugfs_create_file("i915_max_freq", + S_IRUGO | S_IWUSR, + root, dev, + &i915_max_freq_fops); + if (IS_ERR(ent)) + return PTR_ERR(ent); + + return drm_add_fake_info_node(minor, ent, &i915_max_freq_fops); +} + +static int i915_cache_sharing_create(struct dentry *root, struct drm_minor *minor) +{ + struct drm_device *dev = minor->dev; + struct dentry *ent; + + ent = debugfs_create_file("i915_cache_sharing", + S_IRUGO | S_IWUSR, + root, dev, + &i915_cache_sharing_fops); + if (IS_ERR(ent)) + return PTR_ERR(ent); + + return drm_add_fake_info_node(minor, ent, &i915_cache_sharing_fops); +} + static struct drm_info_list i915_debugfs_list[] = { {"i915_capabilities", i915_capabilities, 0}, {"i915_gem_objects", i915_gem_object_info, 0}, @@ -1430,6 +1703,7 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_inttoext_table", i915_inttoext_table, 0}, {"i915_drpc_info", i915_drpc_info, 0}, {"i915_emon_status", i915_emon_status, 0}, + {"i915_ring_freq_table", i915_ring_freq_table, 0}, {"i915_gfxec", i915_gfxec, 0}, {"i915_fbc_status", i915_fbc_status, 0}, {"i915_sr_status", i915_sr_status, 0}, @@ -1451,6 +1725,12 @@ int i915_debugfs_init(struct drm_minor *minor) ret = i915_forcewake_create(minor->debugfs_root, minor); if (ret) return ret; + ret = i915_max_freq_create(minor->debugfs_root, minor); + if (ret) + return ret; + ret = i915_cache_sharing_create(minor->debugfs_root, minor); + if (ret) + return ret; return drm_debugfs_create_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, @@ -1465,6 +1745,10 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_max_freq_fops, + 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops, + 1, minor); } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ef16443..ca67338 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -41,6 +41,7 @@ #include <linux/pnp.h> #include <linux/vga_switcheroo.h> #include <linux/slab.h> +#include <linux/module.h> #include <acpi/video.h> static void i915_write_hws_pga(struct drm_device *dev) @@ -884,7 +885,7 @@ static int i915_get_bridge_dev(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0)); + dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); if (!dev_priv->bridge_dev) { DRM_ERROR("bridge device not found\n"); return -1; @@ -1071,6 +1072,9 @@ static void i915_setup_compression(struct drm_device *dev, int size) unsigned long cfb_base; unsigned long ll_base = 0; + /* Just in case the BIOS is doing something questionable. */ + intel_disable_fbc(dev); + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); if (compressed_fb) compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); @@ -1097,7 +1101,6 @@ static void i915_setup_compression(struct drm_device *dev, int size) dev_priv->cfb_size = size; - intel_disable_fbc(dev); dev_priv->compressed_fb = compressed_fb; if (HAS_PCH_SPLIT(dev)) I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); @@ -1738,10 +1741,10 @@ static DEFINE_SPINLOCK(mchdev_lock); */ unsigned long i915_read_mch_val(void) { - struct drm_i915_private *dev_priv; + struct drm_i915_private *dev_priv; unsigned long chipset_val, graphics_val, ret = 0; - spin_lock(&mchdev_lock); + spin_lock(&mchdev_lock); if (!i915_mch_dev) goto out_unlock; dev_priv = i915_mch_dev; @@ -1752,9 +1755,9 @@ unsigned long i915_read_mch_val(void) ret = chipset_val + graphics_val; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock(&mchdev_lock); - return ret; + return ret; } EXPORT_SYMBOL_GPL(i915_read_mch_val); @@ -1765,10 +1768,10 @@ EXPORT_SYMBOL_GPL(i915_read_mch_val); */ bool i915_gpu_raise(void) { - struct drm_i915_private *dev_priv; + struct drm_i915_private *dev_priv; bool ret = true; - spin_lock(&mchdev_lock); + spin_lock(&mchdev_lock); if (!i915_mch_dev) { ret = false; goto out_unlock; @@ -1779,9 +1782,9 @@ bool i915_gpu_raise(void) dev_priv->max_delay--; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock(&mchdev_lock); - return ret; + return ret; } EXPORT_SYMBOL_GPL(i915_gpu_raise); @@ -1793,10 +1796,10 @@ EXPORT_SYMBOL_GPL(i915_gpu_raise); */ bool i915_gpu_lower(void) { - struct drm_i915_private *dev_priv; + struct drm_i915_private *dev_priv; bool ret = true; - spin_lock(&mchdev_lock); + spin_lock(&mchdev_lock); if (!i915_mch_dev) { ret = false; goto out_unlock; @@ -1807,9 +1810,9 @@ bool i915_gpu_lower(void) dev_priv->max_delay++; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock(&mchdev_lock); - return ret; + return ret; } EXPORT_SYMBOL_GPL(i915_gpu_lower); @@ -1820,10 +1823,10 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower); */ bool i915_gpu_busy(void) { - struct drm_i915_private *dev_priv; + struct drm_i915_private *dev_priv; bool ret = false; - spin_lock(&mchdev_lock); + spin_lock(&mchdev_lock); if (!i915_mch_dev) goto out_unlock; dev_priv = i915_mch_dev; @@ -1831,9 +1834,9 @@ bool i915_gpu_busy(void) ret = dev_priv->busy; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock(&mchdev_lock); - return ret; + return ret; } EXPORT_SYMBOL_GPL(i915_gpu_busy); @@ -1845,10 +1848,10 @@ EXPORT_SYMBOL_GPL(i915_gpu_busy); */ bool i915_gpu_turbo_disable(void) { - struct drm_i915_private *dev_priv; + struct drm_i915_private *dev_priv; bool ret = true; - spin_lock(&mchdev_lock); + spin_lock(&mchdev_lock); if (!i915_mch_dev) { ret = false; goto out_unlock; @@ -1861,9 +1864,9 @@ bool i915_gpu_turbo_disable(void) ret = false; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock(&mchdev_lock); - return ret; + return ret; } EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable); @@ -1887,6 +1890,27 @@ ips_ping_for_i915_load(void) } } +static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) +{ + struct apertures_struct *ap; + struct pci_dev *pdev = dev_priv->dev->pdev; + bool primary; + + ap = alloc_apertures(1); + if (!ap) + return; + + ap->ranges[0].base = dev_priv->dev->agp->base; + ap->ranges[0].size = + dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + primary = + pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; + + remove_conflicting_framebuffers(ap, "inteldrmfb", primary); + + kfree(ap); +} + /** * i915_driver_load - setup chip and create an initial config * @dev: DRM device @@ -1924,6 +1948,15 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto free_priv; } + dev_priv->mm.gtt = intel_gtt_get(); + if (!dev_priv->mm.gtt) { + DRM_ERROR("Failed to initialize GTT\n"); + ret = -ENODEV; + goto put_bridge; + } + + i915_kick_out_firmware_fb(dev_priv); + /* overlay on gen2 is broken and can't address above 1G */ if (IS_GEN2(dev)) dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(30)); @@ -1947,16 +1980,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_bridge; } - dev_priv->mm.gtt = intel_gtt_get(); - if (!dev_priv->mm.gtt) { - DRM_ERROR("Failed to initialize GTT\n"); - ret = -ENODEV; - goto out_rmmap; - } - agp_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - dev_priv->mm.gtt_mapping = + dev_priv->mm.gtt_mapping = io_mapping_create_wc(dev->agp->base, agp_size); if (dev_priv->mm.gtt_mapping == NULL) { ret = -EIO; @@ -2039,11 +2065,14 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (!IS_I945G(dev) && !IS_I945GM(dev)) pci_enable_msi(dev->pdev); + spin_lock_init(&dev_priv->gt_lock); spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->error_lock); spin_lock_init(&dev_priv->rps_lock); - if (IS_MOBILE(dev) || !IS_GEN2(dev)) + if (IS_IVYBRIDGE(dev)) + dev_priv->num_pipe = 3; + else if (IS_MOBILE(dev) || !IS_GEN2(dev)) dev_priv->num_pipe = 2; else dev_priv->num_pipe = 1; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 111686a..452bc51 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -35,46 +35,79 @@ #include "intel_drv.h" #include <linux/console.h> +#include <linux/module.h> #include "drm_crtc_helper.h" -static int i915_modeset = -1; +static int i915_modeset __read_mostly = -1; module_param_named(modeset, i915_modeset, int, 0400); +MODULE_PARM_DESC(modeset, + "Use kernel modesetting [KMS] (0=DRM_I915_KMS from .config, " + "1=on, -1=force vga console preference [default])"); -unsigned int i915_fbpercrtc = 0; +unsigned int i915_fbpercrtc __always_unused = 0; module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400); -int i915_panel_ignore_lid = 0; +int i915_panel_ignore_lid __read_mostly = 0; module_param_named(panel_ignore_lid, i915_panel_ignore_lid, int, 0600); +MODULE_PARM_DESC(panel_ignore_lid, + "Override lid status (0=autodetect [default], 1=lid open, " + "-1=lid closed)"); -unsigned int i915_powersave = 1; +unsigned int i915_powersave __read_mostly = 1; module_param_named(powersave, i915_powersave, int, 0600); +MODULE_PARM_DESC(powersave, + "Enable powersavings, fbc, downclocking, etc. (default: true)"); -unsigned int i915_semaphores = 0; +int i915_semaphores __read_mostly = -1; module_param_named(semaphores, i915_semaphores, int, 0600); +MODULE_PARM_DESC(semaphores, + "Use semaphores for inter-ring sync (default: -1 (use per-chip defaults))"); -unsigned int i915_enable_rc6 = 0; -module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600); +int i915_enable_rc6 __read_mostly = -1; +module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0400); +MODULE_PARM_DESC(i915_enable_rc6, + "Enable power-saving render C-state 6 (default: -1 (use per-chip default)"); -unsigned int i915_enable_fbc = 0; +int i915_enable_fbc __read_mostly = -1; module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600); +MODULE_PARM_DESC(i915_enable_fbc, + "Enable frame buffer compression for power savings " + "(default: -1 (use per-chip default))"); -unsigned int i915_lvds_downclock = 0; +unsigned int i915_lvds_downclock __read_mostly = 0; module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400); +MODULE_PARM_DESC(lvds_downclock, + "Use panel (LVDS/eDP) downclocking for power savings " + "(default: false)"); -unsigned int i915_panel_use_ssc = 1; +int i915_panel_use_ssc __read_mostly = -1; module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600); +MODULE_PARM_DESC(lvds_use_ssc, + "Use Spread Spectrum Clock with panels [LVDS/eDP] " + "(default: auto from VBT)"); -int i915_vbt_sdvo_panel_type = -1; +int i915_vbt_sdvo_panel_type __read_mostly = -1; module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600); +MODULE_PARM_DESC(vbt_sdvo_panel_type, + "Override selection of SDVO panel mode in the VBT " + "(default: auto)"); -static bool i915_try_reset = true; +static bool i915_try_reset __read_mostly = true; module_param_named(reset, i915_try_reset, bool, 0600); +MODULE_PARM_DESC(reset, "Attempt GPU resets (default: true)"); + +bool i915_enable_hangcheck __read_mostly = true; +module_param_named(enable_hangcheck, i915_enable_hangcheck, bool, 0644); +MODULE_PARM_DESC(enable_hangcheck, + "Periodically check GPU activity for detecting hangs. " + "WARNING: Disabling this can cause system wide hangs. " + "(default: true)"); static struct drm_driver driver; extern int intel_agp_enabled; #define INTEL_VGA_DEVICE(id, info) { \ - .class = PCI_CLASS_DISPLAY_VGA << 8, \ + .class = PCI_BASE_CLASS_DISPLAY << 16, \ .class_mask = 0xff0000, \ .vendor = 0x8086, \ .device = id, \ @@ -181,6 +214,7 @@ static const struct intel_device_info intel_sandybridge_d_info = { .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, + .has_force_wake = 1, }; static const struct intel_device_info intel_sandybridge_m_info = { @@ -189,6 +223,7 @@ static const struct intel_device_info intel_sandybridge_m_info = { .has_fbc = 1, .has_bsd_ring = 1, .has_blt_ring = 1, + .has_force_wake = 1, }; static const struct intel_device_info intel_ivybridge_d_info = { @@ -196,6 +231,7 @@ static const struct intel_device_info intel_ivybridge_d_info = { .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, + .has_force_wake = 1, }; static const struct intel_device_info intel_ivybridge_m_info = { @@ -204,6 +240,7 @@ static const struct intel_device_info intel_ivybridge_m_info = { .has_fbc = 0, /* FBC is not enabled on Ivybridge mobile yet */ .has_bsd_ring = 1, .has_blt_ring = 1, + .has_force_wake = 1, }; static const struct pci_device_id pciidlist[] = { /* aka */ @@ -250,6 +287,7 @@ static const struct pci_device_id pciidlist[] = { /* aka */ INTEL_VGA_DEVICE(0x0152, &intel_ivybridge_d_info), /* GT1 desktop */ INTEL_VGA_DEVICE(0x0162, &intel_ivybridge_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x015a, &intel_ivybridge_d_info), /* GT1 server */ + INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */ {0, 0, 0} }; @@ -262,7 +300,7 @@ MODULE_DEVICE_TABLE(pci, pciidlist); #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 #define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00 -void intel_detect_pch (struct drm_device *dev) +void intel_detect_pch(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct pci_dev *pch; @@ -295,7 +333,7 @@ void intel_detect_pch (struct drm_device *dev) } } -static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) +void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { int count; @@ -311,6 +349,22 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) udelay(10); } +void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) +{ + int count; + + count = 0; + while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1)) + udelay(10); + + I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 1); + POSTING_READ(FORCEWAKE_MT); + + count = 0; + while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1) == 0) + udelay(10); +} + /* * Generally this is called implicitly by the register read function. However, * if some sequence requires the GT to not power down then this function should @@ -319,38 +373,52 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) */ void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { - WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); + unsigned long irqflags; - /* Forcewake is atomic in case we get in here without the lock */ - if (atomic_add_return(1, &dev_priv->forcewake_count) == 1) - __gen6_gt_force_wake_get(dev_priv); + spin_lock_irqsave(&dev_priv->gt_lock, irqflags); + if (dev_priv->forcewake_count++ == 0) + dev_priv->display.force_wake_get(dev_priv); + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); } -static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) +void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE, 0); POSTING_READ(FORCEWAKE); } +void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) +{ + I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 0); + POSTING_READ(FORCEWAKE_MT); +} + /* * see gen6_gt_force_wake_get() */ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { - WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); + unsigned long irqflags; - if (atomic_dec_and_test(&dev_priv->forcewake_count)) - __gen6_gt_force_wake_put(dev_priv); + spin_lock_irqsave(&dev_priv->gt_lock, irqflags); + if (--dev_priv->forcewake_count == 0) + dev_priv->display.force_wake_put(dev_priv); + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); } void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) { - int loop = 500; - u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); - while (fifo < 20 && loop--) { - udelay(10); - fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); + if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) { + int loop = 500; + u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); + while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) { + udelay(10); + fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); + } + WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES); + dev_priv->gt_fifo_count = fifo; } + dev_priv->gt_fifo_count--; } static int i915_drm_freeze(struct drm_device *dev) @@ -438,6 +506,9 @@ static int i915_drm_thaw(struct drm_device *dev) error = i915_gem_init_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); + if (HAS_PCH_SPLIT(dev)) + ironlake_init_pch_refclk(dev); + drm_mode_config_reset(dev); drm_irq_install(dev); @@ -572,6 +643,7 @@ int i915_reset(struct drm_device *dev, u8 flags) * need to */ bool need_display = true; + unsigned long irqflags; int ret; if (!i915_try_reset) @@ -590,8 +662,10 @@ int i915_reset(struct drm_device *dev, u8 flags) case 6: ret = gen6_do_reset(dev, flags); /* If reset with a user forcewake, try to restore */ - if (atomic_read(&dev_priv->forcewake_count)) - __gen6_gt_force_wake_get(dev_priv); + spin_lock_irqsave(&dev_priv->gt_lock, irqflags); + if (dev_priv->forcewake_count) + dev_priv->display.force_wake_get(dev_priv); + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); break; case 5: ret = ironlake_do_reset(dev, flags); @@ -742,12 +816,12 @@ static int i915_pm_poweroff(struct device *dev) } static const struct dev_pm_ops i915_pm_ops = { - .suspend = i915_pm_suspend, - .resume = i915_pm_resume, - .freeze = i915_pm_freeze, - .thaw = i915_pm_thaw, - .poweroff = i915_pm_poweroff, - .restore = i915_pm_resume, + .suspend = i915_pm_suspend, + .resume = i915_pm_resume, + .freeze = i915_pm_freeze, + .thaw = i915_pm_thaw, + .poweroff = i915_pm_poweroff, + .restore = i915_pm_resume, }; static struct vm_operations_struct i915_gem_vm_ops = { @@ -757,8 +831,8 @@ static struct vm_operations_struct i915_gem_vm_ops = { }; static struct drm_driver driver = { - /* don't use mtrr's here, the Xserver or user space app should - * deal with them for intel hardware. + /* Don't use MTRRs here; the Xserver or userspace app should + * deal with them for Intel hardware. */ .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ @@ -867,3 +941,44 @@ module_exit(i915_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL and additional rights"); + +/* We give fast paths for the really cool registers */ +#define NEEDS_FORCE_WAKE(dev_priv, reg) \ + ((HAS_FORCE_WAKE((dev_priv)->dev)) && \ + ((reg) < 0x40000) && \ + ((reg) != FORCEWAKE) && \ + ((reg) != ECOBUS)) + +#define __i915_read(x, y) \ +u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ + u##x val = 0; \ + if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ + gen6_gt_force_wake_get(dev_priv); \ + val = read##y(dev_priv->regs + reg); \ + gen6_gt_force_wake_put(dev_priv); \ + } else { \ + val = read##y(dev_priv->regs + reg); \ + } \ + trace_i915_reg_rw(false, reg, val, sizeof(val)); \ + return val; \ +} + +__i915_read(8, b) +__i915_read(16, w) +__i915_read(32, l) +__i915_read(64, q) +#undef __i915_read + +#define __i915_write(x, y) \ +void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ + trace_i915_reg_rw(true, reg, val, sizeof(val)); \ + if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ + __gen6_gt_wait_for_fifo(dev_priv); \ + } \ + write##y(val, dev_priv->regs + reg); \ +} +__i915_write(8, b) +__i915_write(16, w) +__i915_write(32, l) +__i915_write(64, q) +#undef __i915_write diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b570415..61274bf 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -36,6 +36,7 @@ #include <linux/io-mapping.h> #include <linux/i2c.h> #include <drm/intel-gtt.h> +#include <linux/backlight.h> /* General customization: */ @@ -106,6 +107,7 @@ struct opregion_header; struct opregion_acpi; struct opregion_swsci; struct opregion_asle; +struct drm_i915_private; struct intel_opregion { struct opregion_header *header; @@ -125,6 +127,9 @@ struct drm_i915_master_private { struct _drm_i915_sarea *sarea_priv; }; #define I915_FENCE_REG_NONE -1 +#define I915_MAX_NUM_FENCES 16 +/* 16 fences + sign bit for FENCE_REG_NONE */ +#define I915_MAX_NUM_FENCE_BITS 5 struct drm_i915_fence_reg { struct list_head lru_list; @@ -138,7 +143,6 @@ struct sdvo_device_mapping { u8 slave_addr; u8 dvo_wiring; u8 i2c_pin; - u8 i2c_speed; u8 ddc_pin; }; @@ -168,7 +172,7 @@ struct drm_i915_error_state { u32 instdone1; u32 seqno; u64 bbaddr; - u64 fence[16]; + u64 fence[I915_MAX_NUM_FENCES]; struct timeval time; struct drm_i915_error_object { int page_count; @@ -182,7 +186,7 @@ struct drm_i915_error_state { u32 gtt_offset; u32 read_domains; u32 write_domain; - s32 fence_reg:5; + s32 fence_reg:I915_MAX_NUM_FENCE_BITS; s32 pinned:2; u32 tiling:2; u32 dirty:1; @@ -208,12 +212,18 @@ struct drm_i915_display_funcs { struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb); + void (*write_eld)(struct drm_connector *connector, + struct drm_crtc *crtc); void (*fdi_link_train)(struct drm_crtc *crtc); void (*init_clock_gating)(struct drm_device *dev); void (*init_pch_clock_gating)(struct drm_device *dev); int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj); + int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y); + void (*force_wake_get)(struct drm_i915_private *dev_priv); + void (*force_wake_put)(struct drm_i915_private *dev_priv); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ @@ -223,26 +233,27 @@ struct drm_i915_display_funcs { struct intel_device_info { u8 gen; - u8 is_mobile : 1; - u8 is_i85x : 1; - u8 is_i915g : 1; - u8 is_i945gm : 1; - u8 is_g33 : 1; - u8 need_gfx_hws : 1; - u8 is_g4x : 1; - u8 is_pineview : 1; - u8 is_broadwater : 1; - u8 is_crestline : 1; - u8 is_ivybridge : 1; - u8 has_fbc : 1; - u8 has_pipe_cxsr : 1; - u8 has_hotplug : 1; - u8 cursor_needs_physical : 1; - u8 has_overlay : 1; - u8 overlay_needs_physical : 1; - u8 supports_tv : 1; - u8 has_bsd_ring : 1; - u8 has_blt_ring : 1; + u8 is_mobile:1; + u8 is_i85x:1; + u8 is_i915g:1; + u8 is_i945gm:1; + u8 is_g33:1; + u8 need_gfx_hws:1; + u8 is_g4x:1; + u8 is_pineview:1; + u8 is_broadwater:1; + u8 is_crestline:1; + u8 is_ivybridge:1; + u8 has_force_wake:1; + u8 has_fbc:1; + u8 has_pipe_cxsr:1; + u8 has_hotplug:1; + u8 cursor_needs_physical:1; + u8 has_overlay:1; + u8 overlay_needs_physical:1; + u8 supports_tv:1; + u8 has_bsd_ring:1; + u8 has_blt_ring:1; }; enum no_fbc_reason { @@ -263,8 +274,11 @@ enum intel_pch { #define QUIRK_PIPEA_FORCE (1<<0) #define QUIRK_LVDS_SSC_DISABLE (1<<1) +#define QUIRK_INVERT_BRIGHTNESS (1<<2) +#define QUIRK_NO_PCH_PWM_ENABLE (1<<3) struct intel_fbdev; +struct intel_fbc_work; typedef struct drm_i915_private { struct drm_device *dev; @@ -275,6 +289,13 @@ typedef struct drm_i915_private { int relative_constants_mode; void __iomem *regs; + /** gt_fifo_count and the subsequent register write are synchronized + * with dev->struct_mutex. */ + unsigned gt_fifo_count; + /** forcewake_count is protected by gt_lock */ + unsigned forcewake_count; + /** gt_lock is also taken in irq contexts. */ + struct spinlock gt_lock; struct intel_gmbus { struct i2c_adapter adapter; @@ -331,11 +352,10 @@ typedef struct drm_i915_private { uint32_t last_instdone1; unsigned long cfb_size; - unsigned long cfb_pitch; - unsigned long cfb_offset; - int cfb_fence; - int cfb_plane; + unsigned int cfb_fb; + enum plane cfb_plane; int cfb_y; + struct intel_fbc_work *fbc_work; struct intel_opregion opregion; @@ -345,7 +365,6 @@ typedef struct drm_i915_private { /* LVDS info */ int backlight_level; /* restore backlight to this value */ bool backlight_enabled; - struct drm_display_mode *panel_fixed_mode; struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ @@ -355,7 +374,10 @@ typedef struct drm_i915_private { unsigned int lvds_vbt:1; unsigned int int_crt_support:1; unsigned int lvds_use_ssc:1; + unsigned int display_clock_mode:1; int lvds_ssc_freq; + unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ + unsigned int lvds_val; /* used for checking LVDS channel mode */ struct { int rate; int lanes; @@ -372,7 +394,7 @@ typedef struct drm_i915_private { struct notifier_block lid_notifier; int crt_ddc_pin; - struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */ + struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* assume 965 */ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ int num_fence_regs; /* 8 on pre-965, 16 otherwise */ @@ -503,7 +525,7 @@ typedef struct drm_i915_private { u8 saveAR[21]; u8 saveDACMASK; u8 saveCR[37]; - uint64_t saveFENCE[16]; + uint64_t saveFENCE[I915_MAX_NUM_FENCES]; u32 saveCURACNTR; u32 saveCURAPOS; u32 saveCURABASE; @@ -670,10 +692,9 @@ typedef struct drm_i915_private { unsigned int lvds_border_bits; /* Panel fitter placement and size for Ironlake+ */ u32 pch_pf_pos, pch_pf_size; - int panel_t3, panel_t12; - struct drm_crtc *plane_to_crtc_mapping[2]; - struct drm_crtc *pipe_to_crtc_mapping[2]; + struct drm_crtc *plane_to_crtc_mapping[3]; + struct drm_crtc *pipe_to_crtc_mapping[3]; wait_queue_head_t pending_flip_queue; bool flip_pending_is_done; @@ -689,6 +710,7 @@ typedef struct drm_i915_private { int child_dev_num; struct child_device_config *child_dev; struct drm_connector *int_lvds_connector; + struct drm_connector *int_edp_connector; bool mchbar_need_disable; @@ -723,10 +745,10 @@ typedef struct drm_i915_private { /* list of fbdev register on this device */ struct intel_fbdev *fbdev; + struct backlight_device *backlight; + struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; - - atomic_t forcewake_count; } drm_i915_private_t; enum i915_cache_level { @@ -755,39 +777,37 @@ struct drm_i915_gem_object { * (has pending rendering), and is not set if it's on inactive (ready * to be unbound). */ - unsigned int active : 1; + unsigned int active:1; /** * This is set if the object has been written to since last bound * to the GTT */ - unsigned int dirty : 1; + unsigned int dirty:1; /** * This is set if the object has been written to since the last * GPU flush. */ - unsigned int pending_gpu_write : 1; + unsigned int pending_gpu_write:1; /** * Fence register bits (if any) for this object. Will be set * as needed when mapped into the GTT. * Protected by dev->struct_mutex. - * - * Size: 4 bits for 16 fences + sign (for FENCE_REG_NONE) */ - signed int fence_reg : 5; + signed int fence_reg:I915_MAX_NUM_FENCE_BITS; /** * Advice: are the backing pages purgeable? */ - unsigned int madv : 2; + unsigned int madv:2; /** * Current tiling mode for the object. */ - unsigned int tiling_mode : 2; - unsigned int tiling_changed : 1; + unsigned int tiling_mode:2; + unsigned int tiling_changed:1; /** How many users have pinned this object in GTT space. The following * users can each hold at most one reference: pwrite/pread, pin_ioctl @@ -798,22 +818,22 @@ struct drm_i915_gem_object { * * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3 * bits with absolutely no headroom. So use 4 bits. */ - unsigned int pin_count : 4; + unsigned int pin_count:4; #define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf /** * Is the object at the current location in the gtt mappable and * fenceable? Used to avoid costly recalculations. */ - unsigned int map_and_fenceable : 1; + unsigned int map_and_fenceable:1; /** * Whether the current gtt mapping needs to be mappable (and isn't just * mappable by accident). Track pin and fault separate for a more * accurate mappable working set. */ - unsigned int fault_mappable : 1; - unsigned int pin_mappable : 1; + unsigned int fault_mappable:1; + unsigned int pin_mappable:1; /* * Is the GPU currently using a fence to access this buffer, @@ -986,19 +1006,22 @@ struct drm_i915_file_private { #define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT) #define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX) +#define HAS_FORCE_WAKE(dev) (INTEL_INFO(dev)->has_force_wake) + #include "i915_trace.h" extern struct drm_ioctl_desc i915_ioctls[]; extern int i915_max_ioctl; -extern unsigned int i915_fbpercrtc; -extern int i915_panel_ignore_lid; -extern unsigned int i915_powersave; -extern unsigned int i915_semaphores; -extern unsigned int i915_lvds_downclock; -extern unsigned int i915_panel_use_ssc; -extern int i915_vbt_sdvo_panel_type; -extern unsigned int i915_enable_rc6; -extern unsigned int i915_enable_fbc; +extern unsigned int i915_fbpercrtc __always_unused; +extern int i915_panel_ignore_lid __read_mostly; +extern unsigned int i915_powersave __read_mostly; +extern int i915_semaphores __read_mostly; +extern unsigned int i915_lvds_downclock __read_mostly; +extern int i915_panel_use_ssc __read_mostly; +extern int i915_vbt_sdvo_panel_type __read_mostly; +extern int i915_enable_rc6 __read_mostly; +extern int i915_enable_fbc __read_mostly; +extern bool i915_enable_hangcheck __read_mostly; extern int i915_suspend(struct drm_device *dev, pm_message_t state); extern int i915_resume(struct drm_device *dev); @@ -1051,7 +1074,7 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); void i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); -void intel_enable_asle (struct drm_device *dev); +void intel_enable_asle(struct drm_device *dev); #ifdef CONFIG_DEBUG_FS extern void i915_destroy_error_state(struct drm_device *dev); @@ -1141,7 +1164,7 @@ int i915_gem_dumb_create(struct drm_file *file_priv, int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); int i915_gem_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, - uint32_t handle); + uint32_t handle); /** * Returns true if seq1 is later than seq2. */ @@ -1151,12 +1174,7 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2) return (int32_t)(seq1 - seq2) >= 0; } -static inline u32 -i915_gem_next_request_seqno(struct intel_ring_buffer *ring) -{ - drm_i915_private_t *dev_priv = ring->dev->dev_private; - return ring->outstanding_lazy_request = dev_priv->next_seqno; -} +u32 i915_gem_next_request_seqno(struct intel_ring_buffer *ring); int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj, struct intel_ring_buffer *pipelined); @@ -1168,7 +1186,7 @@ void i915_gem_clflush_object(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_set_domain(struct drm_i915_gem_object *obj, uint32_t read_domains, uint32_t write_domain); -int __must_check i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj); +int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj); int __must_check i915_gem_init_ringbuffer(struct drm_device *dev); void i915_gem_cleanup_ringbuffer(struct drm_device *dev); void i915_gem_do_init(struct drm_device *dev, @@ -1187,7 +1205,8 @@ int __must_check i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write); int __must_check -i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj, +i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, + u32 alignment, struct intel_ring_buffer *pipelined); int i915_gem_attach_phys_object(struct drm_device *dev, struct drm_i915_gem_object *obj, @@ -1203,9 +1222,14 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, uint32_t size, int tiling_mode); +int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level); + /* i915_gem_gtt.c */ void i915_gem_restore_gtt_mappings(struct drm_device *dev); int __must_check i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj); +void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level); void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj); /* i915_gem_evict.c */ @@ -1283,21 +1307,24 @@ static inline void intel_unregister_dsm_handler(void) { return; } #endif /* CONFIG_ACPI */ /* modesetting */ +extern void i915_redisable_vga(struct drm_device *dev); extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); -extern void i8xx_disable_fbc(struct drm_device *dev); -extern void g4x_disable_fbc(struct drm_device *dev); -extern void ironlake_disable_fbc(struct drm_device *dev); -extern void intel_disable_fbc(struct drm_device *dev); -extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval); extern bool intel_fbc_enabled(struct drm_device *dev); +extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); +extern void ironlake_init_pch_refclk(struct drm_device *dev); extern void ironlake_enable_rc6(struct drm_device *dev); extern void gen6_set_rps(struct drm_device *dev, u8 val); -extern void intel_detect_pch (struct drm_device *dev); -extern int intel_trans_dp_port_sel (struct drm_crtc *crtc); +extern void intel_detect_pch(struct drm_device *dev); +extern int intel_trans_dp_port_sel(struct drm_crtc *crtc); + +extern void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv); +extern void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv); +extern void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv); +extern void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv); /* overlay */ #ifdef CONFIG_DEBUG_FS @@ -1343,22 +1370,12 @@ void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv); /* We give fast paths for the really cool registers */ #define NEEDS_FORCE_WAKE(dev_priv, reg) \ (((dev_priv)->info->gen >= 6) && \ - ((reg) < 0x40000) && \ - ((reg) != FORCEWAKE)) + ((reg) < 0x40000) && \ + ((reg) != FORCEWAKE) && \ + ((reg) != ECOBUS)) #define __i915_read(x, y) \ -static inline u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ - u##x val = 0; \ - if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ - gen6_gt_force_wake_get(dev_priv); \ - val = read##y(dev_priv->regs + reg); \ - gen6_gt_force_wake_put(dev_priv); \ - } else { \ - val = read##y(dev_priv->regs + reg); \ - } \ - trace_i915_reg_rw(false, reg, val, sizeof(val)); \ - return val; \ -} + u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg); __i915_read(8, b) __i915_read(16, w) @@ -1367,13 +1384,8 @@ __i915_read(64, q) #undef __i915_read #define __i915_write(x, y) \ -static inline void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ - trace_i915_reg_rw(true, reg, val, sizeof(val)); \ - if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ - __gen6_gt_wait_for_fifo(dev_priv); \ - } \ - write##y(val, dev_priv->regs + reg); \ -} + void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val); + __i915_write(8, b) __i915_write(16, w) __i915_write(32, l) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 46e04a1..315a49e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -179,7 +179,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, mutex_unlock(&dev->struct_mutex); args->aper_size = dev_priv->mm.gtt_total; - args->aper_available_size = args->aper_size -pinned; + args->aper_available_size = args->aper_size - pinned; return 0; } @@ -195,6 +195,8 @@ i915_gem_create(struct drm_file *file, u32 handle; size = roundup(size, PAGE_SIZE); + if (size == 0) + return -EINVAL; /* Allocate the new object */ obj = i915_gem_alloc_object(dev, size); @@ -800,11 +802,11 @@ i915_gem_shmem_pwrite_fast(struct drm_device *dev, if (IS_ERR(page)) return PTR_ERR(page); - vaddr = kmap_atomic(page, KM_USER0); + vaddr = kmap_atomic(page); ret = __copy_from_user_inatomic(vaddr + page_offset, user_data, page_length); - kunmap_atomic(vaddr, KM_USER0); + kunmap_atomic(vaddr); set_page_dirty(page); mark_page_accessed(page); @@ -997,6 +999,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, if (obj->phys_obj) ret = i915_gem_phys_pwrite(dev, obj, args, file); else if (obj->gtt_space && + obj->tiling_mode == I915_TILING_NONE && obj->base.write_domain != I915_GEM_DOMAIN_CPU) { ret = i915_gem_object_pin(obj, 0, true); if (ret) @@ -1270,74 +1273,6 @@ out: } /** - * i915_gem_create_mmap_offset - create a fake mmap offset for an object - * @obj: obj in question - * - * GEM memory mapping works by handing back to userspace a fake mmap offset - * it can use in a subsequent mmap(2) call. The DRM core code then looks - * up the object based on the offset and sets up the various memory mapping - * structures. - * - * This routine allocates and attaches a fake offset for @obj. - */ -static int -i915_gem_create_mmap_offset(struct drm_i915_gem_object *obj) -{ - struct drm_device *dev = obj->base.dev; - struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list; - struct drm_local_map *map; - int ret = 0; - - /* Set the object up for mmap'ing */ - list = &obj->base.map_list; - list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL); - if (!list->map) - return -ENOMEM; - - map = list->map; - map->type = _DRM_GEM; - map->size = obj->base.size; - map->handle = obj; - - /* Get a DRM GEM mmap offset allocated... */ - list->file_offset_node = drm_mm_search_free(&mm->offset_manager, - obj->base.size / PAGE_SIZE, - 0, 0); - if (!list->file_offset_node) { - DRM_ERROR("failed to allocate offset for bo %d\n", - obj->base.name); - ret = -ENOSPC; - goto out_free_list; - } - - list->file_offset_node = drm_mm_get_block(list->file_offset_node, - obj->base.size / PAGE_SIZE, - 0); - if (!list->file_offset_node) { - ret = -ENOMEM; - goto out_free_list; - } - - list->hash.key = list->file_offset_node->start; - ret = drm_ht_insert_item(&mm->offset_hash, &list->hash); - if (ret) { - DRM_ERROR("failed to add to map hash\n"); - goto out_free_mm; - } - - return 0; - -out_free_mm: - drm_mm_put_block(list->file_offset_node); -out_free_list: - kfree(list->map); - list->map = NULL; - - return ret; -} - -/** * i915_gem_release_mmap - remove physical page mappings * @obj: obj in question * @@ -1365,19 +1300,6 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) obj->fault_mappable = false; } -static void -i915_gem_free_mmap_offset(struct drm_i915_gem_object *obj) -{ - struct drm_device *dev = obj->base.dev; - struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list = &obj->base.map_list; - - drm_ht_remove_item(&mm->offset_hash, &list->hash); - drm_mm_put_block(list->file_offset_node); - kfree(list->map); - list->map = NULL; -} - static uint32_t i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) { @@ -1490,7 +1412,7 @@ i915_gem_mmap_gtt(struct drm_file *file, } if (!obj->base.map_list.map) { - ret = i915_gem_create_mmap_offset(obj); + ret = drm_gem_create_mmap_offset(&obj->base); if (ret) goto out; } @@ -1562,7 +1484,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, obj->pages[i] = page; } - if (obj->tiling_mode != I915_TILING_NONE) + if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_do_bit_17_swizzle(obj); return 0; @@ -1584,7 +1506,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) BUG_ON(obj->madv == __I915_MADV_PURGED); - if (obj->tiling_mode != I915_TILING_NONE) + if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_save_bit_17_swizzle(obj); if (obj->madv == I915_MADV_DONTNEED) @@ -1627,16 +1549,19 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, list_move_tail(&obj->ring_list, &ring->active_list); obj->last_rendering_seqno = seqno; - if (obj->fenced_gpu_access) { - struct drm_i915_fence_reg *reg; - - BUG_ON(obj->fence_reg == I915_FENCE_REG_NONE); + if (obj->fenced_gpu_access) { obj->last_fenced_seqno = seqno; obj->last_fenced_ring = ring; - reg = &dev_priv->fence_regs[obj->fence_reg]; - list_move_tail(®->lru_list, &dev_priv->mm.fence_list); + /* Bump MRU to take account of the delayed flush */ + if (obj->fence_reg != I915_FENCE_REG_NONE) { + struct drm_i915_fence_reg *reg; + + reg = &dev_priv->fence_regs[obj->fence_reg]; + list_move_tail(®->lru_list, + &dev_priv->mm.fence_list); + } } } @@ -1645,6 +1570,7 @@ i915_gem_object_move_off_active(struct drm_i915_gem_object *obj) { list_del_init(&obj->ring_list); obj->last_rendering_seqno = 0; + obj->last_fenced_seqno = 0; } static void @@ -1673,6 +1599,7 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) BUG_ON(!list_empty(&obj->gpu_write_list)); BUG_ON(!obj->active); obj->ring = NULL; + obj->last_fenced_ring = NULL; i915_gem_object_move_off_active(obj); obj->fenced_gpu_access = false; @@ -1731,6 +1658,28 @@ i915_gem_process_flushing_list(struct intel_ring_buffer *ring, } } +static u32 +i915_gem_get_seqno(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 seqno = dev_priv->next_seqno; + + /* reserve 0 for non-seqno */ + if (++dev_priv->next_seqno == 0) + dev_priv->next_seqno = 1; + + return seqno; +} + +u32 +i915_gem_next_request_seqno(struct intel_ring_buffer *ring) +{ + if (ring->outstanding_lazy_request == 0) + ring->outstanding_lazy_request = i915_gem_get_seqno(ring->dev); + + return ring->outstanding_lazy_request; +} + int i915_add_request(struct intel_ring_buffer *ring, struct drm_file *file, @@ -1742,6 +1691,7 @@ i915_add_request(struct intel_ring_buffer *ring, int ret; BUG_ON(request == NULL); + seqno = i915_gem_next_request_seqno(ring); ret = ring->add_request(ring, &seqno); if (ret) @@ -1768,8 +1718,11 @@ i915_add_request(struct intel_ring_buffer *ring, ring->outstanding_lazy_request = false; if (!dev_priv->mm.suspended) { - mod_timer(&dev_priv->hangcheck_timer, - jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); + if (i915_enable_hangcheck) { + mod_timer(&dev_priv->hangcheck_timer, + jiffies + + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); + } if (was_empty) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); @@ -1826,7 +1779,7 @@ static void i915_gem_reset_fences(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int i; - for (i = 0; i < 16; i++) { + for (i = 0; i < dev_priv->num_fence_regs; i++) { struct drm_i915_fence_reg *reg = &dev_priv->fence_regs[i]; struct drm_i915_gem_object *obj = reg->obj; @@ -1858,7 +1811,7 @@ void i915_gem_reset(struct drm_device *dev) * lost bo to the inactive list. */ while (!list_empty(&dev_priv->mm.flushing_list)) { - obj= list_first_entry(&dev_priv->mm.flushing_list, + obj = list_first_entry(&dev_priv->mm.flushing_list, struct drm_i915_gem_object, mm_list); @@ -1924,7 +1877,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring) while (!list_empty(&ring->active_list)) { struct drm_i915_gem_object *obj; - obj= list_first_entry(&ring->active_list, + obj = list_first_entry(&ring->active_list, struct drm_i915_gem_object, ring_list); @@ -2140,6 +2093,30 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj) return 0; } +static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj) +{ + u32 old_write_domain, old_read_domains; + + /* Act a barrier for all accesses through the GTT */ + mb(); + + /* Force a pagefault for domain tracking on next user access */ + i915_gem_release_mmap(obj); + + if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) + return; + + old_read_domains = obj->base.read_domains; + old_write_domain = obj->base.write_domain; + + obj->base.read_domains &= ~I915_GEM_DOMAIN_GTT; + obj->base.write_domain &= ~I915_GEM_DOMAIN_GTT; + + trace_i915_gem_object_change_domain(obj, + old_read_domains, + old_write_domain); +} + /** * Unbinds an object from the GTT aperture. */ @@ -2156,23 +2133,28 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) return -EINVAL; } - /* blow away mappings if mapped through GTT */ - i915_gem_release_mmap(obj); - - /* Move the object to the CPU domain to ensure that - * any possible CPU writes while it's not in the GTT - * are flushed when we go to remap it. This will - * also ensure that all pending GPU writes are finished - * before we unbind. - */ - ret = i915_gem_object_set_to_cpu_domain(obj, 1); + ret = i915_gem_object_finish_gpu(obj); if (ret == -ERESTARTSYS) return ret; /* Continue on if we fail due to EIO, the GPU is hung so we * should be safe and we need to cleanup or else we might * cause memory corruption through use-after-free. */ + + i915_gem_object_finish_gtt(obj); + + /* Move the object to the CPU domain to ensure that + * any possible CPU writes while it's not in the GTT + * are flushed when we go to remap it. + */ + if (ret == 0) + ret = i915_gem_object_set_to_cpu_domain(obj, 1); + if (ret == -ERESTARTSYS) + return ret; if (ret) { + /* In the event of a disaster, abandon all caches and + * hope for the best. + */ i915_gem_clflush_object(obj); obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU; } @@ -2245,14 +2227,8 @@ int i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - bool lists_empty; int ret, i; - lists_empty = (list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->mm.active_list)); - if (lists_empty) - return 0; - /* Flush everything onto the inactive list. */ for (i = 0; i < I915_NUM_RINGS; i++) { ret = i915_ring_idle(&dev_priv->ring[i]); @@ -2272,6 +2248,13 @@ static int sandybridge_write_fence_reg(struct drm_i915_gem_object *obj, int regnum = obj->fence_reg; uint64_t val; + /* Adjust fence size to match tiled area */ + if (obj->tiling_mode != I915_TILING_NONE) { + uint32_t row_size = obj->stride * + (obj->tiling_mode == I915_TILING_Y ? 32 : 8); + size = (size / row_size) * row_size; + } + val = (uint64_t)((obj->gtt_offset + size - 4096) & 0xfffff000) << 32; val |= obj->gtt_offset & 0xfffff000; @@ -2309,6 +2292,13 @@ static int i965_write_fence_reg(struct drm_i915_gem_object *obj, int regnum = obj->fence_reg; uint64_t val; + /* Adjust fence size to match tiled area */ + if (obj->tiling_mode != I915_TILING_NONE) { + uint32_t row_size = obj->stride * + (obj->tiling_mode == I915_TILING_Y ? 32 : 8); + size = (size / row_size) * row_size; + } + val = (uint64_t)((obj->gtt_offset + size - 4096) & 0xfffff000) << 32; val |= obj->gtt_offset & 0xfffff000; @@ -2544,6 +2534,11 @@ i915_find_fence_reg(struct drm_device *dev, return avail; } +static void i915_gem_write_fence__ipi(void *data) +{ + wbinvd(); +} + /** * i915_gem_object_get_fence - set up a fence reg for an object * @obj: object to map through a fence reg @@ -2664,6 +2659,17 @@ update: switch (INTEL_INFO(dev)->gen) { case 7: case 6: + /* In order to fully serialize access to the fenced region and + * the update to the fence register we need to take extreme + * measures on SNB+. In theory, the write to the fence register + * flushes all memory transactions before, and coupled with the + * mb() placed around the register write we serialise all memory + * operations with respect to the changes in the tiler. Yet, on + * SNB+ we need to take a step further and emit an explicit wbinvd() + * on each processor in order to manually flush all memory + * transactions before updating the fence register. + */ + on_each_cpu(i915_gem_write_fence__ipi, NULL, 1); ret = sandybridge_write_fence_reg(obj, pipelined); break; case 5: @@ -2855,7 +2861,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, fenceable = obj->gtt_space->size == fence_size && - (obj->gtt_space->start & (fence_alignment -1)) == 0; + (obj->gtt_space->start & (fence_alignment - 1)) == 0; mappable = obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end; @@ -3001,51 +3007,139 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) return 0; } +int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level) +{ + int ret; + + if (obj->cache_level == cache_level) + return 0; + + if (obj->pin_count) { + DRM_DEBUG("can not change the cache level of pinned objects\n"); + return -EBUSY; + } + + if (obj->gtt_space) { + ret = i915_gem_object_finish_gpu(obj); + if (ret) + return ret; + + i915_gem_object_finish_gtt(obj); + + /* Before SandyBridge, you could not use tiling or fence + * registers with snooped memory, so relinquish any fences + * currently pointing to our region in the aperture. + */ + if (INTEL_INFO(obj->base.dev)->gen < 6) { + ret = i915_gem_object_put_fence(obj); + if (ret) + return ret; + } + + i915_gem_gtt_rebind_object(obj, cache_level); + } + + if (cache_level == I915_CACHE_NONE) { + u32 old_read_domains, old_write_domain; + + /* If we're coming from LLC cached, then we haven't + * actually been tracking whether the data is in the + * CPU cache or not, since we only allow one bit set + * in obj->write_domain and have been skipping the clflushes. + * Just set it to the CPU cache for now. + */ + WARN_ON(obj->base.write_domain & ~I915_GEM_DOMAIN_CPU); + WARN_ON(obj->base.read_domains & ~I915_GEM_DOMAIN_CPU); + + old_read_domains = obj->base.read_domains; + old_write_domain = obj->base.write_domain; + + obj->base.read_domains = I915_GEM_DOMAIN_CPU; + obj->base.write_domain = I915_GEM_DOMAIN_CPU; + + trace_i915_gem_object_change_domain(obj, + old_read_domains, + old_write_domain); + } + + obj->cache_level = cache_level; + return 0; +} + /* - * Prepare buffer for display plane. Use uninterruptible for possible flush - * wait, as in modesetting process we're not supposed to be interrupted. + * Prepare buffer for display plane (scanout, cursors, etc). + * Can be called from an uninterruptible phase (modesetting) and allows + * any flushes to be pipelined (for pageflips). + * + * For the display plane, we want to be in the GTT but out of any write + * domains. So in many ways this looks like set_to_gtt_domain() apart from the + * ability to pipeline the waits, pinning and any additional subtleties + * that may differentiate the display plane from ordinary buffers. */ int -i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj, +i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, + u32 alignment, struct intel_ring_buffer *pipelined) { - uint32_t old_read_domains; + u32 old_read_domains, old_write_domain; int ret; - /* Not valid to be called on unbound objects. */ - if (obj->gtt_space == NULL) - return -EINVAL; - ret = i915_gem_object_flush_gpu_write_domain(obj); if (ret) return ret; - - /* Currently, we are always called from an non-interruptible context. */ if (pipelined != obj->ring) { ret = i915_gem_object_wait_rendering(obj); - if (ret) + if (ret == -ERESTARTSYS) return ret; } + /* The display engine is not coherent with the LLC cache on gen6. As + * a result, we make sure that the pinning that is about to occur is + * done with uncached PTEs. This is lowest common denominator for all + * chipsets. + * + * However for gen6+, we could do better by using the GFDT bit instead + * of uncaching, which would allow us to flush all the LLC-cached data + * with that bit in the PTE to main memory with just one PIPE_CONTROL. + */ + ret = i915_gem_object_set_cache_level(obj, I915_CACHE_NONE); + if (ret) + return ret; + + /* As the user may map the buffer once pinned in the display plane + * (e.g. libkms for the bootup splash), we have to ensure that we + * always use map_and_fenceable for all scanout buffers. + */ + ret = i915_gem_object_pin(obj, alignment, true); + if (ret) + return ret; + i915_gem_object_flush_cpu_write_domain(obj); + old_write_domain = obj->base.write_domain; old_read_domains = obj->base.read_domains; + + /* It should now be out of any other write domains, and we can update + * the domain values for our changes. + */ + BUG_ON((obj->base.write_domain & ~I915_GEM_DOMAIN_GTT) != 0); obj->base.read_domains |= I915_GEM_DOMAIN_GTT; trace_i915_gem_object_change_domain(obj, old_read_domains, - obj->base.write_domain); + old_write_domain); return 0; } int -i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj) +i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj) { int ret; - if (!obj->active) + if ((obj->base.read_domains & I915_GEM_GPU_DOMAINS) == 0) return 0; if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { @@ -3054,7 +3148,13 @@ i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj) return ret; } - return i915_gem_object_wait_rendering(obj); + ret = i915_gem_object_wait_rendering(obj); + if (ret) + return ret; + + /* Ensure that we invalidate the GPU's caches and TLBs. */ + obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS; + return 0; } /** @@ -3276,6 +3376,10 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) if (ret == 0 && atomic_read(&dev_priv->mm.wedged)) ret = -EIO; + } else if (wait_for(i915_seqno_passed(ring->get_seqno(ring), + seqno) || + atomic_read(&dev_priv->mm.wedged), 3000)) { + ret = -EBUSY; } } @@ -3294,7 +3398,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, struct drm_i915_private *dev_priv = dev->dev_private; int ret; - BUG_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT); + if (WARN_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT)) + return -EBUSY; WARN_ON(i915_verify_lists(dev)); if (obj->gtt_space != NULL) { @@ -3381,14 +3486,15 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, goto out; } - obj->user_pin_count++; - obj->pin_filp = file; - if (obj->user_pin_count == 1) { + if (obj->user_pin_count == 0) { ret = i915_gem_object_pin(obj, args->alignment, true); if (ret) goto out; } + obj->user_pin_count++; + obj->pin_filp = file; + /* XXX - flush the CPU caches for pinned objects * as the X server doesn't manage domains yet */ @@ -3479,9 +3585,11 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, * so emit a request to do so. */ request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request) - ret = i915_add_request(obj->ring, NULL,request); - else + if (request) { + ret = i915_add_request(obj->ring, NULL, request); + if (ret) + kfree(request); + } else ret = -ENOMEM; } @@ -3505,7 +3613,7 @@ int i915_gem_throttle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - return i915_gem_ring_throttle(dev, file_priv); + return i915_gem_ring_throttle(dev, file_priv); } int @@ -3580,7 +3688,23 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, obj->base.write_domain = I915_GEM_DOMAIN_CPU; obj->base.read_domains = I915_GEM_DOMAIN_CPU; - obj->cache_level = I915_CACHE_NONE; + if (IS_GEN6(dev) || IS_GEN7(dev)) { + /* On Gen6, we can have the GPU use the LLC (the CPU + * cache) for about a 10% performance improvement + * compared to uncached. Graphics requests other than + * display scanout are coherent with the CPU in + * accessing this cache. This means in this mode we + * don't need to clflush on the CPU side, and on the + * GPU side we only need to flush internal caches to + * get data visible to the CPU. + * + * However, we maintain the display planes as UC, and so + * need to rebind when first used as such. + */ + obj->cache_level = I915_CACHE_LLC; + } else + obj->cache_level = I915_CACHE_NONE; + obj->base.driver_private = NULL; obj->fence_reg = I915_FENCE_REG_NONE; INIT_LIST_HEAD(&obj->mm_list); @@ -3618,7 +3742,7 @@ static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj) trace_i915_gem_object_destroy(obj); if (obj->base.map_list.map) - i915_gem_free_mmap_offset(obj); + drm_gem_free_mmap_offset(&obj->base); drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(dev_priv, obj->base.size); @@ -3828,7 +3952,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->mm.gtt_list); for (i = 0; i < I915_NUM_RINGS; i++) init_ring_lists(&dev_priv->ring[i]); - for (i = 0; i < 16; i++) + for (i = 0; i < I915_MAX_NUM_FENCES; i++) INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c index 8da1899..cc93cac 100644 --- a/drivers/gpu/drm/i915/i915_gem_debug.c +++ b/drivers/gpu/drm/i915/i915_gem_debug.c @@ -72,7 +72,7 @@ i915_verify_lists(struct drm_device *dev) break; } else if (!obj->active || (obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0 || - list_empty(&obj->gpu_write_list)){ + list_empty(&obj->gpu_write_list)) { DRM_ERROR("invalid flushing %p (a %d w %x gwl %d)\n", obj, obj->active, @@ -157,7 +157,7 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle) for (page = 0; page < obj->size / PAGE_SIZE; page++) { int i; - backing_map = kmap_atomic(obj->pages[page], KM_USER0); + backing_map = kmap_atomic(obj->pages[page]); if (backing_map == NULL) { DRM_ERROR("failed to map backing page\n"); @@ -181,13 +181,13 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle) } } } - kunmap_atomic(backing_map, KM_USER0); + kunmap_atomic(backing_map); backing_map = NULL; } out: if (backing_map != NULL) - kunmap_atomic(backing_map, KM_USER0); + kunmap_atomic(backing_map); iounmap(gtt_mapping); /* give syslog time to catch up */ diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index da05a26..ead5d00 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -122,7 +122,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, goto found; } list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { - if (! obj->base.write_domain || obj->pin_count) + if (!obj->base.write_domain || obj->pin_count) continue; if (mark_free(obj, &unwind_list)) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 1ca53ff..9b9f447 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -32,6 +32,7 @@ #include "i915_drv.h" #include "i915_trace.h" #include "intel_drv.h" +#include <linux/dma_remapping.h> struct change_domains { uint32_t invalidate_domains; @@ -286,14 +287,14 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, * exec_object list, so it should have a GTT space bound by now. */ if (unlikely(target_offset == 0)) { - DRM_ERROR("No GTT space found for object %d\n", + DRM_DEBUG("No GTT space found for object %d\n", reloc->target_handle); return ret; } /* Validate that the target is in a valid r/w GPU domain */ if (unlikely(reloc->write_domain & (reloc->write_domain - 1))) { - DRM_ERROR("reloc with multiple write domains: " + DRM_DEBUG("reloc with multiple write domains: " "obj %p target %d offset %d " "read %08x write %08x", obj, reloc->target_handle, @@ -303,7 +304,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, return ret; } if (unlikely((reloc->write_domain | reloc->read_domains) & I915_GEM_DOMAIN_CPU)) { - DRM_ERROR("reloc with read/write CPU domains: " + DRM_DEBUG("reloc with read/write CPU domains: " "obj %p target %d offset %d " "read %08x write %08x", obj, reloc->target_handle, @@ -314,7 +315,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, } if (unlikely(reloc->write_domain && target_obj->pending_write_domain && reloc->write_domain != target_obj->pending_write_domain)) { - DRM_ERROR("Write domain conflict: " + DRM_DEBUG("Write domain conflict: " "obj %p target %d offset %d " "new %08x old %08x\n", obj, reloc->target_handle, @@ -335,7 +336,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, /* Check that the relocation address is valid... */ if (unlikely(reloc->offset > obj->base.size - 4)) { - DRM_ERROR("Relocation beyond object bounds: " + DRM_DEBUG("Relocation beyond object bounds: " "obj %p target %d offset %d size %d.\n", obj, reloc->target_handle, (int) reloc->offset, @@ -343,7 +344,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, return ret; } if (unlikely(reloc->offset & 3)) { - DRM_ERROR("Relocation not 4-byte aligned: " + DRM_DEBUG("Relocation not 4-byte aligned: " "obj %p target %d offset %d.\n", obj, reloc->target_handle, (int) reloc->offset); @@ -573,7 +574,8 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, if (ret) break; } - obj->pending_fenced_gpu_access = need_fence; + obj->pending_fenced_gpu_access = + !!(entry->flags & EXEC_OBJECT_NEEDS_FENCE); } entry->offset = obj->gtt_offset; @@ -677,9 +679,9 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, * relocations were valid. */ for (j = 0; j < exec[i].relocation_count; j++) { - if (copy_to_user(&user_relocs[j].presumed_offset, - &invalid_offset, - sizeof(invalid_offset))) { + if (__copy_to_user(&user_relocs[j].presumed_offset, + &invalid_offset, + sizeof(invalid_offset))) { ret = -EFAULT; mutex_lock(&dev->struct_mutex); goto err; @@ -702,7 +704,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, obj = to_intel_bo(drm_gem_object_lookup(dev, file, exec[i].handle)); if (&obj->base == NULL) { - DRM_ERROR("Invalid object handle %d at index %d\n", + DRM_DEBUG("Invalid object handle %d at index %d\n", exec[i].handle, i); ret = -ENOENT; goto err; @@ -767,6 +769,22 @@ i915_gem_execbuffer_flush(struct drm_device *dev, return 0; } +static bool +intel_enable_semaphores(struct drm_device *dev) +{ + if (INTEL_INFO(dev)->gen < 6) + return 0; + + if (i915_semaphores >= 0) + return i915_semaphores; + + /* Disable semaphores on SNB */ + if (INTEL_INFO(dev)->gen == 6) + return 0; + + return 1; +} + static int i915_gem_execbuffer_sync_rings(struct drm_i915_gem_object *obj, struct intel_ring_buffer *to) @@ -779,7 +797,7 @@ i915_gem_execbuffer_sync_rings(struct drm_i915_gem_object *obj, return 0; /* XXX gpu semaphores are implicated in various hard hangs on SNB */ - if (INTEL_INFO(obj->base.dev)->gen < 6 || !i915_semaphores) + if (!intel_enable_semaphores(obj->base.dev)) return i915_gem_object_wait_rendering(obj); idx = intel_ring_sync_index(from, to); @@ -805,7 +823,8 @@ i915_gem_execbuffer_sync_rings(struct drm_i915_gem_object *obj, } from->sync_seqno[idx] = seqno; - return intel_ring_sync(to, from, seqno - 1); + + return to->sync_to(to, from, seqno - 1); } static int @@ -931,13 +950,13 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, u32 old_write = obj->base.write_domain; + obj->dirty = 1; /* be paranoid */ obj->base.read_domains = obj->base.pending_read_domains; obj->base.write_domain = obj->base.pending_write_domain; obj->fenced_gpu_access = obj->pending_fenced_gpu_access; i915_gem_object_move_to_active(obj, ring, seqno); if (obj->base.write_domain) { - obj->dirty = 1; obj->pending_gpu_write = true; list_move_tail(&obj->gpu_write_list, &ring->gpu_write_list); @@ -992,10 +1011,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct intel_ring_buffer *ring; u32 exec_start, exec_len; u32 seqno; + u32 mask; int ret, mode, i; if (!i915_gem_check_execbuffer(args)) { - DRM_ERROR("execbuf with invalid offset/length\n"); + DRM_DEBUG("execbuf with invalid offset/length\n"); return -EINVAL; } @@ -1010,25 +1030,26 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, break; case I915_EXEC_BSD: if (!HAS_BSD(dev)) { - DRM_ERROR("execbuf with invalid ring (BSD)\n"); + DRM_DEBUG("execbuf with invalid ring (BSD)\n"); return -EINVAL; } ring = &dev_priv->ring[VCS]; break; case I915_EXEC_BLT: if (!HAS_BLT(dev)) { - DRM_ERROR("execbuf with invalid ring (BLT)\n"); + DRM_DEBUG("execbuf with invalid ring (BLT)\n"); return -EINVAL; } ring = &dev_priv->ring[BCS]; break; default: - DRM_ERROR("execbuf with unknown ring: %d\n", + DRM_DEBUG("execbuf with unknown ring: %d\n", (int)(args->flags & I915_EXEC_RING_MASK)); return -EINVAL; } mode = args->flags & I915_EXEC_CONSTANTS_MASK; + mask = I915_EXEC_CONSTANTS_MASK; switch (mode) { case I915_EXEC_CONSTANTS_REL_GENERAL: case I915_EXEC_CONSTANTS_ABSOLUTE: @@ -1042,33 +1063,24 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, mode == I915_EXEC_CONSTANTS_REL_SURFACE) return -EINVAL; - ret = intel_ring_begin(ring, 4); - if (ret) - return ret; - - intel_ring_emit(ring, MI_NOOP); - intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); - intel_ring_emit(ring, INSTPM); - intel_ring_emit(ring, - I915_EXEC_CONSTANTS_MASK << 16 | mode); - intel_ring_advance(ring); - - dev_priv->relative_constants_mode = mode; + /* The HW changed the meaning on this bit on gen6 */ + if (INTEL_INFO(dev)->gen >= 6) + mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE; } break; default: - DRM_ERROR("execbuf with unknown constants: %d\n", mode); + DRM_DEBUG("execbuf with unknown constants: %d\n", mode); return -EINVAL; } if (args->buffer_count < 1) { - DRM_ERROR("execbuf with %d buffers\n", args->buffer_count); + DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count); return -EINVAL; } if (args->num_cliprects != 0) { if (ring != &dev_priv->ring[RCS]) { - DRM_ERROR("clip rectangles are only valid with the render ring\n"); + DRM_DEBUG("clip rectangles are only valid with the render ring\n"); return -EINVAL; } @@ -1118,7 +1130,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, obj = to_intel_bo(drm_gem_object_lookup(dev, file, exec[i].handle)); if (&obj->base == NULL) { - DRM_ERROR("Invalid object handle %d at index %d\n", + DRM_DEBUG("Invalid object handle %d at index %d\n", exec[i].handle, i); /* prevent error path from reading uninitialized data */ ret = -ENOENT; @@ -1126,7 +1138,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } if (!list_empty(&obj->exec_list)) { - DRM_ERROR("Object %p [handle %d, index %d] appears more than once in object list\n", + DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", obj, exec[i].handle, i); ret = -EINVAL; goto err; @@ -1164,7 +1176,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, /* Set the pending read domains for the batch buffer to COMMAND */ if (batch_obj->base.pending_write_domain) { - DRM_ERROR("Attempting to use self-modifying batch buffer\n"); + DRM_DEBUG("Attempting to use self-modifying batch buffer\n"); ret = -EINVAL; goto err; } @@ -1189,6 +1201,21 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } } + if (ring == &dev_priv->ring[RCS] && + mode != dev_priv->relative_constants_mode) { + ret = intel_ring_begin(ring, 4); + if (ret) + goto err; + + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit(ring, INSTPM); + intel_ring_emit(ring, mask << 16 | mode); + intel_ring_advance(ring); + + dev_priv->relative_constants_mode = mode; + } + trace_i915_gem_ring_dispatch(ring, seqno); exec_start = batch_obj->gtt_offset + args->batch_start_offset; @@ -1248,7 +1275,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, int ret, i; if (args->buffer_count < 1) { - DRM_ERROR("execbuf with %d buffers\n", args->buffer_count); + DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count); return -EINVAL; } @@ -1256,7 +1283,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, exec_list = drm_malloc_ab(sizeof(*exec_list), args->buffer_count); exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count); if (exec_list == NULL || exec2_list == NULL) { - DRM_ERROR("Failed to allocate exec list for %d buffers\n", + DRM_DEBUG("Failed to allocate exec list for %d buffers\n", args->buffer_count); drm_free_large(exec_list); drm_free_large(exec2_list); @@ -1267,7 +1294,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, (uintptr_t) args->buffers_ptr, sizeof(*exec_list) * args->buffer_count); if (ret != 0) { - DRM_ERROR("copy %d exec entries failed %d\n", + DRM_DEBUG("copy %d exec entries failed %d\n", args->buffer_count, ret); drm_free_large(exec_list); drm_free_large(exec2_list); @@ -1298,19 +1325,21 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, ret = i915_gem_do_execbuffer(dev, data, file, &exec2, exec2_list); if (!ret) { + struct drm_i915_gem_exec_object __user *user_exec_list = + (void __user *)(uintptr_t)args->buffers_ptr; + /* Copy the new buffer offsets back to the user's exec list. */ - for (i = 0; i < args->buffer_count; i++) - exec_list[i].offset = exec2_list[i].offset; - /* ... and back out to userspace */ - ret = copy_to_user((struct drm_i915_relocation_entry __user *) - (uintptr_t) args->buffers_ptr, - exec_list, - sizeof(*exec_list) * args->buffer_count); - if (ret) { - ret = -EFAULT; - DRM_ERROR("failed to copy %d exec entries " - "back to user (%d)\n", - args->buffer_count, ret); + for (i = 0; i < args->buffer_count; i++) { + ret = __copy_to_user(&user_exec_list[i].offset, + &exec2_list[i].offset, + sizeof(user_exec_list[i].offset)); + if (ret) { + ret = -EFAULT; + DRM_DEBUG("failed to copy %d exec entries " + "back to user (%d)\n", + args->buffer_count, ret); + break; + } } } @@ -1329,7 +1358,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, if (args->buffer_count < 1 || args->buffer_count > UINT_MAX / sizeof(*exec2_list)) { - DRM_ERROR("execbuf2 with %d buffers\n", args->buffer_count); + DRM_DEBUG("execbuf2 with %d buffers\n", args->buffer_count); return -EINVAL; } @@ -1339,7 +1368,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count); if (exec2_list == NULL) { - DRM_ERROR("Failed to allocate exec list for %d buffers\n", + DRM_DEBUG("Failed to allocate exec list for %d buffers\n", args->buffer_count); return -ENOMEM; } @@ -1348,7 +1377,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, (uintptr_t) args->buffers_ptr, sizeof(*exec2_list) * args->buffer_count); if (ret != 0) { - DRM_ERROR("copy %d exec entries failed %d\n", + DRM_DEBUG("copy %d exec entries failed %d\n", args->buffer_count, ret); drm_free_large(exec2_list); return -EFAULT; @@ -1357,15 +1386,21 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list); if (!ret) { /* Copy the new buffer offsets back to the user's exec list. */ - ret = copy_to_user((struct drm_i915_relocation_entry __user *) - (uintptr_t) args->buffers_ptr, - exec2_list, - sizeof(*exec2_list) * args->buffer_count); - if (ret) { - ret = -EFAULT; - DRM_ERROR("failed to copy %d exec entries " - "back to user (%d)\n", - args->buffer_count, ret); + struct drm_i915_gem_exec_object2 __user *user_exec_list = + (void __user *)(uintptr_t)args->buffers_ptr; + int i; + + for (i = 0; i < args->buffer_count; i++) { + ret = __copy_to_user(&user_exec_list[i].offset, + &exec2_list[i].offset, + sizeof(user_exec_list[i].offset)); + if (ret) { + ret = -EFAULT; + DRM_DEBUG("failed to copy %d exec entries " + "back to user\n", + args->buffer_count); + break; + } } } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index e46b645..6042c5e 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -49,6 +49,28 @@ static unsigned int cache_level_to_agp_type(struct drm_device *dev, } } +static bool do_idling(struct drm_i915_private *dev_priv) +{ + bool ret = dev_priv->mm.interruptible; + + if (unlikely(dev_priv->mm.gtt->do_idle_maps)) { + dev_priv->mm.interruptible = false; + if (i915_gpu_idle(dev_priv->dev)) { + DRM_ERROR("Couldn't idle GPU\n"); + /* Wait a bit, in hopes it avoids the hang */ + udelay(10); + } + } + + return ret; +} + +static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible) +{ + if (unlikely(dev_priv->mm.gtt->do_idle_maps)) + dev_priv->mm.interruptible = interruptible; +} + void i915_gem_restore_gtt_mappings(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -59,24 +81,8 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { - unsigned int agp_type = - cache_level_to_agp_type(dev, obj->cache_level); - i915_gem_clflush_object(obj); - - if (dev_priv->mm.gtt->needs_dmar) { - BUG_ON(!obj->sg_list); - - intel_gtt_insert_sg_entries(obj->sg_list, - obj->num_sg, - obj->gtt_space->start >> PAGE_SHIFT, - agp_type); - } else - intel_gtt_insert_pages(obj->gtt_space->start - >> PAGE_SHIFT, - obj->base.size >> PAGE_SHIFT, - obj->pages, - agp_type); + i915_gem_gtt_rebind_object(obj, obj->cache_level); } intel_gtt_chipset_flush(); @@ -110,8 +116,35 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj) return 0; } +void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level) +{ + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned int agp_type = cache_level_to_agp_type(dev, cache_level); + + if (dev_priv->mm.gtt->needs_dmar) { + BUG_ON(!obj->sg_list); + + intel_gtt_insert_sg_entries(obj->sg_list, + obj->num_sg, + obj->gtt_space->start >> PAGE_SHIFT, + agp_type); + } else + intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT, + obj->base.size >> PAGE_SHIFT, + obj->pages, + agp_type); +} + void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) { + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + bool interruptible; + + interruptible = do_idling(dev_priv); + intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, obj->base.size >> PAGE_SHIFT); @@ -119,4 +152,6 @@ void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) intel_gtt_unmap_memory(obj->sg_list, obj->num_sg); obj->sg_list = NULL; } + + undo_idling(dev_priv, interruptible); } diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 99c4faa..861223b 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -92,7 +92,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; - if (INTEL_INFO(dev)->gen >= 5) { + if (INTEL_INFO(dev)->gen >= 6) { + swizzle_x = I915_BIT_6_SWIZZLE_NONE; + swizzle_y = I915_BIT_6_SWIZZLE_NONE; + } else if (IS_GEN5(dev)) { /* On Ironlake whatever DRAM config, GPU always do * same swizzling setup. */ @@ -104,10 +107,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) */ swizzle_x = I915_BIT_6_SWIZZLE_NONE; swizzle_y = I915_BIT_6_SWIZZLE_NONE; - } else if (IS_MOBILE(dev)) { + } else if (IS_MOBILE(dev) || (IS_GEN3(dev) && !IS_G33(dev))) { uint32_t dcc; - /* On mobile 9xx chipsets, channel interleave by the CPU is + /* On 9xx chipsets, channel interleave by the CPU is * determined by DCC. For single-channel, neither the CPU * nor the GPU do swizzling. For dual channel interleaved, * the GPU's interleave is bit 9 and 10 for X tiled, and bit @@ -440,14 +443,9 @@ i915_gem_swizzle_page(struct page *page) void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - drm_i915_private_t *dev_priv = dev->dev_private; int page_count = obj->base.size >> PAGE_SHIFT; int i; - if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17) - return; - if (obj->bit_17 == NULL) return; @@ -464,14 +462,9 @@ i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj) void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - drm_i915_private_t *dev_priv = dev->dev_private; int page_count = obj->base.size >> PAGE_SHIFT; int i; - if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17) - return; - if (obj->bit_17 == NULL) { obj->bit_17 = kmalloc(BITS_TO_LONGS(page_count) * sizeof(long), GFP_KERNEL); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d05f03c..93e74fb 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -364,10 +364,12 @@ static void notify_ring(struct drm_device *dev, ring->irq_seqno = seqno; wake_up_all(&ring->irq_queue); - - dev_priv->hangcheck_count = 0; - mod_timer(&dev_priv->hangcheck_timer, - jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); + if (i915_enable_hangcheck) { + dev_priv->hangcheck_count = 0; + mod_timer(&dev_priv->hangcheck_timer, + jiffies + + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); + } } static void gen6_pm_rps_work(struct work_struct *work) @@ -381,6 +383,7 @@ static void gen6_pm_rps_work(struct work_struct *work) pm_iir = dev_priv->pm_iir; dev_priv->pm_iir = 0; pm_imr = I915_READ(GEN6_PMIMR); + I915_WRITE(GEN6_PMIMR, 0); spin_unlock_irq(&dev_priv->rps_lock); if (!pm_iir) @@ -418,10 +421,33 @@ static void gen6_pm_rps_work(struct work_struct *work) * an *extremely* unlikely race with gen6_rps_enable() that is prevented * by holding struct_mutex for the duration of the write. */ - I915_WRITE(GEN6_PMIMR, pm_imr & ~pm_iir); mutex_unlock(&dev_priv->dev->struct_mutex); } +static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, + u32 pm_iir) +{ + unsigned long flags; + + /* + * IIR bits should never already be set because IMR should + * prevent an interrupt from being shown in IIR. The warning + * displays a case where we've unsafely cleared + * dev_priv->pm_iir. Although missing an interrupt of the same + * type is not a problem, it displays a problem in the logic. + * + * The mask bit in IMR is cleared by rps_work. + */ + + spin_lock_irqsave(&dev_priv->rps_lock, flags); + dev_priv->pm_iir |= pm_iir; + I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir); + POSTING_READ(GEN6_PMIMR); + spin_unlock_irqrestore(&dev_priv->rps_lock, flags); + + queue_work(dev_priv->wq, &dev_priv->rps_work); +} + static void pch_irq_handler(struct drm_device *dev, u32 pch_iir) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -504,6 +530,12 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) if (de_iir & DE_GSE_IVB) intel_opregion_gse_intr(dev); + if (de_iir & DE_PIPEA_VBLANK_IVB) + drm_handle_vblank(dev, 0); + + if (de_iir & DE_PIPEB_VBLANK_IVB) + drm_handle_vblank(dev, 1); + if (de_iir & DE_PLANEA_FLIP_DONE_IVB) { intel_prepare_page_flip(dev, 0); intel_finish_page_flip_plane(dev, 0); @@ -514,12 +546,6 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) intel_finish_page_flip_plane(dev, 1); } - if (de_iir & DE_PIPEA_VBLANK_IVB) - drm_handle_vblank(dev, 0); - - if (de_iir & DE_PIPEB_VBLANK_IVB) - drm_handle_vblank(dev, 1); - /* check event from PCH */ if (de_iir & DE_PCH_EVENT_IVB) { if (pch_iir & SDE_HOTPLUG_MASK_CPT) @@ -527,15 +553,8 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) pch_irq_handler(dev, pch_iir); } - if (pm_iir & GEN6_PM_DEFERRED_EVENTS) { - unsigned long flags; - spin_lock_irqsave(&dev_priv->rps_lock, flags); - WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); - I915_WRITE(GEN6_PMIMR, pm_iir); - dev_priv->pm_iir |= pm_iir; - spin_unlock_irqrestore(&dev_priv->rps_lock, flags); - queue_work(dev_priv->wq, &dev_priv->rps_work); - } + if (pm_iir & GEN6_PM_DEFERRED_EVENTS) + gen6_queue_rps_work(dev_priv, pm_iir); /* should clear PCH hotplug event before clear CPU irq */ I915_WRITE(SDEIIR, pch_iir); @@ -603,6 +622,12 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) if (de_iir & DE_GSE) intel_opregion_gse_intr(dev); + if (de_iir & DE_PIPEA_VBLANK) + drm_handle_vblank(dev, 0); + + if (de_iir & DE_PIPEB_VBLANK) + drm_handle_vblank(dev, 1); + if (de_iir & DE_PLANEA_FLIP_DONE) { intel_prepare_page_flip(dev, 0); intel_finish_page_flip_plane(dev, 0); @@ -613,12 +638,6 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) intel_finish_page_flip_plane(dev, 1); } - if (de_iir & DE_PIPEA_VBLANK) - drm_handle_vblank(dev, 0); - - if (de_iir & DE_PIPEB_VBLANK) - drm_handle_vblank(dev, 1); - /* check event from PCH */ if (de_iir & DE_PCH_EVENT) { if (pch_iir & hotplug_mask) @@ -631,24 +650,8 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) i915_handle_rps_change(dev); } - if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) { - /* - * IIR bits should never already be set because IMR should - * prevent an interrupt from being shown in IIR. The warning - * displays a case where we've unsafely cleared - * dev_priv->pm_iir. Although missing an interrupt of the same - * type is not a problem, it displays a problem in the logic. - * - * The mask bit in IMR is cleared by rps_work. - */ - unsigned long flags; - spin_lock_irqsave(&dev_priv->rps_lock, flags); - WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); - I915_WRITE(GEN6_PMIMR, pm_iir); - dev_priv->pm_iir |= pm_iir; - spin_unlock_irqrestore(&dev_priv->rps_lock, flags); - queue_work(dev_priv->wq, &dev_priv->rps_work); - } + if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) + gen6_queue_rps_work(dev_priv, pm_iir); /* should clear PCH hotplug event before clear CPU irq */ I915_WRITE(SDEIIR, pch_iir); @@ -706,7 +709,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, page_count = src->base.size / PAGE_SIZE; - dst = kmalloc(sizeof(*dst) + page_count * sizeof (u32 *), GFP_ATOMIC); + dst = kmalloc(sizeof(*dst) + page_count * sizeof(u32 *), GFP_ATOMIC); if (dst == NULL) return NULL; @@ -1184,7 +1187,9 @@ static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) spin_lock_irqsave(&dev->event_lock, flags); work = intel_crtc->unpin_work; - if (work == NULL || work->pending || !work->enable_stall_check) { + if (work == NULL || + atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE || + !work->enable_stall_check) { /* Either the pending flip IRQ arrived, or we're too early. Don't check */ spin_unlock_irqrestore(&dev->event_lock, flags); return; @@ -1489,7 +1494,7 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe) spin_lock_irqsave(&dev_priv->irq_lock, irqflags); ironlake_enable_display_irq(dev_priv, (pipe == 0) ? - DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); + DE_PIPEA_VBLANK : DE_PIPEB_VBLANK); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); return 0; @@ -1537,7 +1542,7 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe) spin_lock_irqsave(&dev_priv->irq_lock, irqflags); ironlake_disable_display_irq(dev_priv, (pipe == 0) ? - DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); + DE_PIPEA_VBLANK : DE_PIPEB_VBLANK); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } @@ -1665,6 +1670,9 @@ void i915_hangcheck_elapsed(unsigned long data) uint32_t acthd, instdone, instdone1, acthd_bsd, acthd_blt; bool err = false; + if (!i915_enable_hangcheck) + return; + /* If all work is done then ACTHD clearly hasn't advanced. */ if (i915_hangcheck_ring_idle(&dev_priv->ring[RCS], &err) && i915_hangcheck_ring_idle(&dev_priv->ring[VCS], &err) && @@ -1777,6 +1785,26 @@ static void ironlake_irq_preinstall(struct drm_device *dev) POSTING_READ(SDEIER); } +/* + * Enable digital hotplug on the PCH, and configure the DP short pulse + * duration to 2ms (which is the minimum in the Display Port spec) + * + * This register is the same on all known PCH chips. + */ + +static void ironlake_enable_pch_hotplug(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug; + + hotplug = I915_READ(PCH_PORT_HOTPLUG); + hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK); + hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms; + hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms; + hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms; + I915_WRITE(PCH_PORT_HOTPLUG, hotplug); +} + static int ironlake_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -1839,6 +1867,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); + ironlake_enable_pch_hotplug(dev); + if (IS_IRONLAKE_M(dev)) { /* Clear & enable PCU event interrupts */ I915_WRITE(DEIIR, DE_PCU_EVENT); @@ -1896,6 +1926,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); + ironlake_enable_pch_hotplug(dev); + return 0; } @@ -1976,10 +2008,22 @@ static int i915_driver_irq_postinstall(struct drm_device *dev) hotplug_en |= HDMIC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) hotplug_en |= HDMID_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS) - hotplug_en |= SDVOC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS) - hotplug_en |= SDVOB_HOTPLUG_INT_EN; + if (IS_G4X(dev)) { + if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_G4X) + hotplug_en |= SDVOC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_G4X) + hotplug_en |= SDVOB_HOTPLUG_INT_EN; + } else if (IS_GEN4(dev)) { + if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I965) + hotplug_en |= SDVOC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I965) + hotplug_en |= SDVOB_HOTPLUG_INT_EN; + } else { + if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) + hotplug_en |= SDVOC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) + hotplug_en |= SDVOB_HOTPLUG_INT_EN; + } if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { hotplug_en |= CRT_HOTPLUG_INT_EN; @@ -2020,6 +2064,10 @@ static void ironlake_irq_uninstall(struct drm_device *dev) I915_WRITE(GTIMR, 0xffffffff); I915_WRITE(GTIER, 0x0); I915_WRITE(GTIIR, I915_READ(GTIIR)); + + I915_WRITE(SDEIMR, 0xffffffff); + I915_WRITE(SDEIER, 0x0); + I915_WRITE(SDEIIR, I915_READ(SDEIIR)); } static void i915_driver_irq_uninstall(struct drm_device * dev) @@ -2058,8 +2106,10 @@ void intel_irq_init(struct drm_device *dev) dev->driver->get_vblank_counter = gm45_get_vblank_counter; } - - dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp; + else + dev->driver->get_vblank_timestamp = NULL; dev->driver->get_scanout_position = i915_get_crtc_scanoutpos; if (IS_IVYBRIDGE(dev)) { diff --git a/drivers/gpu/drm/i915/i915_mem.c b/drivers/gpu/drm/i915/i915_mem.c index 83b7b81..cc8f6d4 100644 --- a/drivers/gpu/drm/i915/i915_mem.c +++ b/drivers/gpu/drm/i915/i915_mem.c @@ -202,7 +202,7 @@ static int init_heap(struct mem_block **heap, int start, int size) blocks->next = blocks->prev = *heap; memset(*heap, 0, sizeof(**heap)); - (*heap)->file_priv = (struct drm_file *) - 1; + (*heap)->file_priv = (struct drm_file *) -1; (*heap)->next = (*heap)->prev = blocks; return 0; } @@ -359,19 +359,19 @@ int i915_mem_init_heap(struct drm_device *dev, void *data, return init_heap(heap, initheap->start, initheap->size); } -int i915_mem_destroy_heap( struct drm_device *dev, void *data, - struct drm_file *file_priv ) +int i915_mem_destroy_heap(struct drm_device *dev, void *data, + struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_mem_destroy_heap_t *destroyheap = data; struct mem_block **heap; - if ( !dev_priv ) { - DRM_ERROR( "called with no initialization\n" ); + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); return -EINVAL; } - heap = get_heap( dev_priv, destroyheap->region ); + heap = get_heap(dev_priv, destroyheap->region); if (!heap) { DRM_ERROR("get_heap failed"); return -EFAULT; @@ -382,6 +382,6 @@ int i915_mem_destroy_heap( struct drm_device *dev, void *data, return -EFAULT; } - i915_mem_takedown( heap ); + i915_mem_takedown(heap); return 0; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5dc3b6d..ddb22e7 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -37,6 +37,7 @@ */ #define INTEL_GMCH_CTRL 0x52 #define INTEL_GMCH_VGA_DISABLE (1 << 1) +#define SNB_GMCH_CTRL 0x50 /* PCI config space */ @@ -80,6 +81,14 @@ #define GRDOM_RENDER (1<<2) #define GRDOM_MEDIA (3<<2) +#define GEN6_MBCUNIT_SNPCR 0x900c /* for LLC config */ +#define GEN6_MBC_SNPCR_SHIFT 21 +#define GEN6_MBC_SNPCR_MASK (3<<21) +#define GEN6_MBC_SNPCR_MAX (0<<21) +#define GEN6_MBC_SNPCR_MED (1<<21) +#define GEN6_MBC_SNPCR_LOW (2<<21) +#define GEN6_MBC_SNPCR_MIN (3<<21) /* only 1/16th of the cache is shared */ + #define GEN6_GDRST 0x941c #define GEN6_GRDOM_FULL (1 << 0) #define GEN6_GRDOM_RENDER (1 << 1) @@ -150,7 +159,7 @@ #define MI_SUSPEND_FLUSH MI_INSTR(0x0b, 0) #define MI_SUSPEND_FLUSH_EN (1<<0) #define MI_REPORT_HEAD MI_INSTR(0x07, 0) -#define MI_OVERLAY_FLIP MI_INSTR(0x11,0) +#define MI_OVERLAY_FLIP MI_INSTR(0x11, 0) #define MI_OVERLAY_CONTINUE (0x0<<21) #define MI_OVERLAY_ON (0x1<<21) #define MI_OVERLAY_OFF (0x2<<21) @@ -158,6 +167,14 @@ #define MI_DISPLAY_FLIP MI_INSTR(0x14, 2) #define MI_DISPLAY_FLIP_I915 MI_INSTR(0x14, 1) #define MI_DISPLAY_FLIP_PLANE(n) ((n) << 20) +/* IVB has funny definitions for which plane to flip. */ +#define MI_DISPLAY_FLIP_IVB_PLANE_A (0 << 19) +#define MI_DISPLAY_FLIP_IVB_PLANE_B (1 << 19) +#define MI_DISPLAY_FLIP_IVB_SPRITE_A (2 << 19) +#define MI_DISPLAY_FLIP_IVB_SPRITE_B (3 << 19) +#define MI_DISPLAY_FLIP_IVB_PLANE_C (4 << 19) +#define MI_DISPLAY_FLIP_IVB_SPRITE_C (5 << 19) + #define MI_SET_CONTEXT MI_INSTR(0x18, 0) #define MI_MM_SPACE_GTT (1<<8) #define MI_MM_SPACE_PHYSICAL (0<<8) @@ -188,6 +205,13 @@ #define MI_SEMAPHORE_UPDATE (1<<21) #define MI_SEMAPHORE_COMPARE (1<<20) #define MI_SEMAPHORE_REGISTER (1<<18) +#define MI_SEMAPHORE_SYNC_RV (2<<16) +#define MI_SEMAPHORE_SYNC_RB (0<<16) +#define MI_SEMAPHORE_SYNC_VR (0<<16) +#define MI_SEMAPHORE_SYNC_VB (2<<16) +#define MI_SEMAPHORE_SYNC_BR (2<<16) +#define MI_SEMAPHORE_SYNC_BV (0<<16) +#define MI_SEMAPHORE_SYNC_INVALID (1<<0) /* * 3D instructions used by the kernel */ @@ -229,16 +253,22 @@ #define ASYNC_FLIP (1<<22) #define DISPLAY_PLANE_A (0<<20) #define DISPLAY_PLANE_B (1<<20) -#define GFX_OP_PIPE_CONTROL ((0x3<<29)|(0x3<<27)|(0x2<<24)|2) -#define PIPE_CONTROL_QW_WRITE (1<<14) -#define PIPE_CONTROL_DEPTH_STALL (1<<13) -#define PIPE_CONTROL_WC_FLUSH (1<<12) -#define PIPE_CONTROL_IS_FLUSH (1<<11) /* MBZ on Ironlake */ -#define PIPE_CONTROL_TC_FLUSH (1<<10) /* GM45+ only */ -#define PIPE_CONTROL_ISP_DIS (1<<9) -#define PIPE_CONTROL_NOTIFY (1<<8) +#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2)) +#define PIPE_CONTROL_CS_STALL (1<<20) +#define PIPE_CONTROL_QW_WRITE (1<<14) +#define PIPE_CONTROL_DEPTH_STALL (1<<13) +#define PIPE_CONTROL_WRITE_FLUSH (1<<12) +#define PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH (1<<12) /* gen6+ */ +#define PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE (1<<11) /* MBZ on Ironlake */ +#define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */ +#define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9) +#define PIPE_CONTROL_NOTIFY (1<<8) +#define PIPE_CONTROL_VF_CACHE_INVALIDATE (1<<4) +#define PIPE_CONTROL_CONST_CACHE_INVALIDATE (1<<3) +#define PIPE_CONTROL_STATE_CACHE_INVALIDATE (1<<2) +#define PIPE_CONTROL_STALL_AT_SCOREBOARD (1<<1) +#define PIPE_CONTROL_DEPTH_CACHE_FLUSH (1<<0) #define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */ -#define PIPE_CONTROL_STALL_EN (1<<1) /* in addr word, Ironlake+ only */ /* @@ -290,6 +320,12 @@ #define RING_CTL(base) ((base)+0x3c) #define RING_SYNC_0(base) ((base)+0x40) #define RING_SYNC_1(base) ((base)+0x44) +#define GEN6_RVSYNC (RING_SYNC_0(RENDER_RING_BASE)) +#define GEN6_RBSYNC (RING_SYNC_1(RENDER_RING_BASE)) +#define GEN6_VRSYNC (RING_SYNC_1(GEN6_BSD_RING_BASE)) +#define GEN6_VBSYNC (RING_SYNC_0(GEN6_BSD_RING_BASE)) +#define GEN6_BRSYNC (RING_SYNC_0(BLT_RING_BASE)) +#define GEN6_BVSYNC (RING_SYNC_1(BLT_RING_BASE)) #define RING_MAX_IDLE(base) ((base)+0x54) #define RING_HWS_PGA(base) ((base)+0x80) #define RING_HWS_PGA_GEN6(base) ((base)+0x2080) @@ -327,6 +363,7 @@ #define IPEIR_I965 0x02064 #define IPEHR_I965 0x02068 #define INSTDONE_I965 0x0206c +#define RING_INSTPM(base) ((base)+0xc0) #define INSTPS 0x02070 /* 965+ only */ #define INSTDONE1 0x0207c /* 965+ only */ #define ACTHD_I965 0x02074 @@ -367,9 +404,14 @@ #define MI_MODE 0x0209c # define VS_TIMER_DISPATCH (1 << 6) -# define MI_FLUSH_ENABLE (1 << 11) +# define MI_FLUSH_ENABLE (1 << 12) +# define ASYNC_FLIP_PERF_DISABLE (1 << 14) + +#define GEN6_GT_MODE 0x20d0 +#define GEN6_GT_MODE_HI (1 << 9) #define GFX_MODE 0x02520 +#define GFX_MODE_GEN7 0x0229c #define GFX_RUN_LIST_ENABLE (1<<15) #define GFX_TLB_INVALIDATE_ALWAYS (1<<13) #define GFX_SURFACE_FAULT_ENABLE (1<<12) @@ -377,6 +419,9 @@ #define GFX_PSMI_GRANULARITY (1<<10) #define GFX_PPGTT_ENABLE (1<<9) +#define GFX_MODE_ENABLE(bit) (((bit) << 16) | (bit)) +#define GFX_MODE_DISABLE(bit) (((bit) << 16) | (0)) + #define SCPD0 0x0209c /* 915+ only */ #define IER 0x020a0 #define IIR 0x020a4 @@ -414,6 +459,9 @@ #define INSTPM_AGPBUSY_DIS (1<<11) /* gen3: when disabled, pending interrupts will not assert AGPBUSY# and will only be delivered when out of C3. */ +#define INSTPM_FORCE_ORDERING (1<<7) /* GEN6+ */ +#define INSTPM_TLB_INVALIDATE (1<<9) +#define INSTPM_SYNC_FLUSH (1<<5) #define ACTHD 0x020c8 #define FW_BLC 0x020d8 #define FW_BLC2 0x020dc @@ -461,7 +509,7 @@ /* Enables non-sequential data reads through arbiter */ -#define MI_ARB_DUAL_DATA_PHASE_DISABLE (1 << 9) +#define MI_ARB_DUAL_DATA_PHASE_DISABLE (1 << 9) /* Disable FSB snooping of cacheable write cycles from binner/render * command stream @@ -494,6 +542,7 @@ #define CM0_MASK_SHIFT 16 #define CM0_IZ_OPT_DISABLE (1<<6) #define CM0_ZR_OPT_DISABLE (1<<5) +#define CM0_STC_EVICT_DISABLE_LRA_SNB (1<<5) #define CM0_DEPTH_EVICT_DISABLE (1<<4) #define CM0_COLOR_EVICT_DISABLE (1<<3) #define CM0_DEPTH_WRITE_DISABLE (1<<1) @@ -597,6 +646,7 @@ #define DPFC_CTL_PLANEA (0<<30) #define DPFC_CTL_PLANEB (1<<30) #define DPFC_CTL_FENCE_EN (1<<29) +#define DPFC_CTL_PERSISTENT_MODE (1<<25) #define DPFC_SR_EN (1<<10) #define DPFC_CTL_LIMIT_1X (0<<6) #define DPFC_CTL_LIMIT_2X (1<<6) @@ -631,7 +681,7 @@ #define ILK_DISPLAY_CHICKEN1 0x42000 #define ILK_FBCQ_DIS (1<<22) -#define ILK_PABSTRETCH_DIS (1<<21) +#define ILK_PABSTRETCH_DIS (1<<21) /* @@ -1327,6 +1377,7 @@ #define ADPA_PIPE_SELECT_MASK (1<<30) #define ADPA_PIPE_A_SELECT 0 #define ADPA_PIPE_B_SELECT (1<<30) +#define ADPA_PIPE_SELECT(pipe) ((pipe) << 30) #define ADPA_USE_VGA_HVPOLARITY (1<<15) #define ADPA_SETS_HVPOLARITY 0 #define ADPA_VSYNC_CNTL_DISABLE (1<<11) @@ -1379,14 +1430,20 @@ #define DPC_HOTPLUG_INT_STATUS (1 << 28) #define HDMID_HOTPLUG_INT_STATUS (1 << 27) #define DPD_HOTPLUG_INT_STATUS (1 << 27) +/* CRT/TV common between gen3+ */ #define CRT_HOTPLUG_INT_STATUS (1 << 11) #define TV_HOTPLUG_INT_STATUS (1 << 10) #define CRT_HOTPLUG_MONITOR_MASK (3 << 8) #define CRT_HOTPLUG_MONITOR_COLOR (3 << 8) #define CRT_HOTPLUG_MONITOR_MONO (2 << 8) #define CRT_HOTPLUG_MONITOR_NONE (0 << 8) -#define SDVOC_HOTPLUG_INT_STATUS (1 << 7) -#define SDVOB_HOTPLUG_INT_STATUS (1 << 6) +/* SDVO is different across gen3/4 */ +#define SDVOC_HOTPLUG_INT_STATUS_G4X (1 << 3) +#define SDVOB_HOTPLUG_INT_STATUS_G4X (1 << 2) +#define SDVOC_HOTPLUG_INT_STATUS_I965 (3 << 4) +#define SDVOB_HOTPLUG_INT_STATUS_I965 (3 << 2) +#define SDVOC_HOTPLUG_INT_STATUS_I915 (1 << 7) +#define SDVOB_HOTPLUG_INT_STATUS_I915 (1 << 6) /* SDVO port control */ #define SDVOB 0x61140 @@ -1469,6 +1526,7 @@ /* Selects pipe B for LVDS data. Must be set on pre-965. */ #define LVDS_PIPEB_SELECT (1 << 30) #define LVDS_PIPE_MASK (1 << 30) +#define LVDS_PIPE(pipe) ((pipe) << 30) /* LVDS dithering flag on 965/g4x platform */ #define LVDS_ENABLE_DITHER (1 << 25) /* LVDS sync polarity flags. Set to invert (i.e. negative) */ @@ -1508,11 +1566,12 @@ #define LVDS_B0B3_POWER_DOWN (0 << 2) #define LVDS_B0B3_POWER_UP (3 << 2) -#define LVDS_PIPE_ENABLED(V, P) \ - (((V) & (LVDS_PIPE_MASK | LVDS_PORT_EN)) == ((P) << 30 | LVDS_PORT_EN)) - /* Video Data Island Packet control */ #define VIDEO_DIP_DATA 0x61178 +/* Read the description of VIDEO_DIP_DATA (before Haswel) or VIDEO_DIP_ECC + * (Haswell and newer) to see which VIDEO_DIP_DATA byte corresponds to each byte + * of the infoframe structure specified by CEA-861. */ +#define VIDEO_DIP_DATA_SIZE 32 #define VIDEO_DIP_CTL 0x61170 #define VIDEO_DIP_ENABLE (1 << 31) #define VIDEO_DIP_PORT_B (1 << 29) @@ -1523,6 +1582,7 @@ #define VIDEO_DIP_SELECT_AVI (0 << 19) #define VIDEO_DIP_SELECT_VENDOR (1 << 19) #define VIDEO_DIP_SELECT_SPD (3 << 19) +#define VIDEO_DIP_SELECT_MASK (3 << 19) #define VIDEO_DIP_FREQ_ONCE (0 << 16) #define VIDEO_DIP_FREQ_VSYNC (1 << 16) #define VIDEO_DIP_FREQ_2VSYNC (2 << 16) @@ -1539,12 +1599,21 @@ */ #define PP_READY (1 << 30) #define PP_SEQUENCE_NONE (0 << 28) -#define PP_SEQUENCE_ON (1 << 28) -#define PP_SEQUENCE_OFF (2 << 28) -#define PP_SEQUENCE_MASK 0x30000000 +#define PP_SEQUENCE_POWER_UP (1 << 28) +#define PP_SEQUENCE_POWER_DOWN (2 << 28) +#define PP_SEQUENCE_MASK (3 << 28) +#define PP_SEQUENCE_SHIFT 28 #define PP_CYCLE_DELAY_ACTIVE (1 << 27) -#define PP_SEQUENCE_STATE_ON_IDLE (1 << 3) #define PP_SEQUENCE_STATE_MASK 0x0000000f +#define PP_SEQUENCE_STATE_OFF_IDLE (0x0 << 0) +#define PP_SEQUENCE_STATE_OFF_S0_1 (0x1 << 0) +#define PP_SEQUENCE_STATE_OFF_S0_2 (0x2 << 0) +#define PP_SEQUENCE_STATE_OFF_S0_3 (0x3 << 0) +#define PP_SEQUENCE_STATE_ON_IDLE (0x8 << 0) +#define PP_SEQUENCE_STATE_ON_S1_0 (0x9 << 0) +#define PP_SEQUENCE_STATE_ON_S1_2 (0xa << 0) +#define PP_SEQUENCE_STATE_ON_S1_3 (0xb << 0) +#define PP_SEQUENCE_STATE_RESET (0xf << 0) #define PP_CONTROL 0x61204 #define POWER_TARGET_ON (1 << 0) #define PP_ON_DELAYS 0x61208 @@ -2101,9 +2170,6 @@ #define DP_PIPEB_SELECT (1 << 30) #define DP_PIPE_MASK (1 << 30) -#define DP_PIPE_ENABLED(V, P) \ - (((V) & (DP_PIPE_MASK | DP_PORT_EN)) == ((P) << 30 | DP_PORT_EN)) - /* Link training mode - select a suitable mode for each stage */ #define DP_LINK_TRAIN_PAT_1 (0 << 28) #define DP_LINK_TRAIN_PAT_2 (1 << 28) @@ -2367,7 +2433,7 @@ #define DSPFW1 0x70034 #define DSPFW_SR_SHIFT 23 -#define DSPFW_SR_MASK (0x1ff<<23) +#define DSPFW_SR_MASK (0x1ff<<23) #define DSPFW_CURSORB_SHIFT 16 #define DSPFW_CURSORB_MASK (0x3f<<16) #define DSPFW_PLANEB_SHIFT 8 @@ -2425,6 +2491,7 @@ #define WM0_PIPE_CURSOR_MASK (0x1f) #define WM0_PIPEB_ILK 0x45104 +#define WM0_PIPEC_IVB 0x45200 #define WM1_LP_ILK 0x45108 #define WM1_LP_SR_EN (1<<31) #define WM1_LP_LATENCY_SHIFT 24 @@ -2936,12 +3003,13 @@ #define SDEIER 0xc400c /* digital port hotplug */ -#define PCH_PORT_HOTPLUG 0xc4030 +#define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */ #define PORTD_HOTPLUG_ENABLE (1 << 20) #define PORTD_PULSE_DURATION_2ms (0) #define PORTD_PULSE_DURATION_4_5ms (1 << 18) #define PORTD_PULSE_DURATION_6ms (2 << 18) #define PORTD_PULSE_DURATION_100ms (3 << 18) +#define PORTD_PULSE_DURATION_MASK (3 << 18) #define PORTD_HOTPLUG_NO_DETECT (0) #define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) #define PORTD_HOTPLUG_LONG_DETECT (1 << 17) @@ -2950,6 +3018,7 @@ #define PORTC_PULSE_DURATION_4_5ms (1 << 10) #define PORTC_PULSE_DURATION_6ms (2 << 10) #define PORTC_PULSE_DURATION_100ms (3 << 10) +#define PORTC_PULSE_DURATION_MASK (3 << 10) #define PORTC_HOTPLUG_NO_DETECT (0) #define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) #define PORTC_HOTPLUG_LONG_DETECT (1 << 9) @@ -2958,6 +3027,7 @@ #define PORTB_PULSE_DURATION_4_5ms (1 << 2) #define PORTB_PULSE_DURATION_6ms (2 << 2) #define PORTB_PULSE_DURATION_100ms (3 << 2) +#define PORTB_PULSE_DURATION_MASK (3 << 2) #define PORTB_HOTPLUG_NO_DETECT (0) #define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) #define PORTB_HOTPLUG_LONG_DETECT (1 << 1) @@ -2978,15 +3048,15 @@ #define _PCH_DPLL_A 0xc6014 #define _PCH_DPLL_B 0xc6018 -#define PCH_DPLL(pipe) _PIPE(pipe, _PCH_DPLL_A, _PCH_DPLL_B) +#define PCH_DPLL(pipe) (pipe == 0 ? _PCH_DPLL_A : _PCH_DPLL_B) #define _PCH_FPA0 0xc6040 #define FP_CB_TUNE (0x3<<22) #define _PCH_FPA1 0xc6044 #define _PCH_FPB0 0xc6048 #define _PCH_FPB1 0xc604c -#define PCH_FP0(pipe) _PIPE(pipe, _PCH_FPA0, _PCH_FPB0) -#define PCH_FP1(pipe) _PIPE(pipe, _PCH_FPA1, _PCH_FPB1) +#define PCH_FP0(pipe) (pipe == 0 ? _PCH_FPA0 : _PCH_FPB0) +#define PCH_FP1(pipe) (pipe == 0 ? _PCH_FPA1 : _PCH_FPB1) #define PCH_DPLL_TEST 0xc606c @@ -3066,6 +3136,20 @@ #define _TRANSA_DP_LINK_M2 0xe0048 #define _TRANSA_DP_LINK_N2 0xe004c +/* Per-transcoder DIP controls */ + +#define _VIDEO_DIP_CTL_A 0xe0200 +#define _VIDEO_DIP_DATA_A 0xe0208 +#define _VIDEO_DIP_GCP_A 0xe0210 + +#define _VIDEO_DIP_CTL_B 0xe1200 +#define _VIDEO_DIP_DATA_B 0xe1208 +#define _VIDEO_DIP_GCP_B 0xe1210 + +#define TVIDEO_DIP_CTL(pipe) _PIPE(pipe, _VIDEO_DIP_CTL_A, _VIDEO_DIP_CTL_B) +#define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B) +#define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B) + #define _TRANS_HTOTAL_B 0xe1000 #define _TRANS_HBLANK_B 0xe1004 #define _TRANS_HSYNC_B 0xe1008 @@ -3123,6 +3207,11 @@ #define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2) #define TRANS_AUTOTRAIN_GEN_STALL_DIS (1<<31) +#define SOUTH_CHICKEN1 0xc2000 +#define FDIA_PHASE_SYNC_SHIFT_OVR 19 +#define FDIA_PHASE_SYNC_SHIFT_EN 18 +#define FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2))) +#define FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2))) #define SOUTH_CHICKEN2 0xc2004 #define DPLS_EDP_PPS_FIX_DIS (1<<0) @@ -3274,15 +3363,13 @@ #define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17) #define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16) -#define ADPA_PIPE_ENABLED(V, P) \ - (((V) & (ADPA_TRANS_SELECT_MASK | ADPA_DAC_ENABLE)) == ((P) << 30 | ADPA_DAC_ENABLE)) - /* or SDVOB */ #define HDMIB 0xe1140 #define PORT_ENABLE (1 << 31) -#define TRANSCODER_A (0) -#define TRANSCODER_B (1 << 30) -#define TRANSCODER_MASK (1 << 30) +#define TRANSCODER(pipe) ((pipe) << 30) +#define TRANSCODER_CPT(pipe) ((pipe) << 29) +#define TRANSCODER_MASK (1 << 30) +#define TRANSCODER_MASK_CPT (3 << 29) #define COLOR_FORMAT_8bpc (0) #define COLOR_FORMAT_12bpc (3 << 26) #define SDVOB_HOTPLUG_ENABLE (1 << 23) @@ -3298,9 +3385,6 @@ #define HSYNC_ACTIVE_HIGH (1 << 3) #define PORT_DETECTED (1 << 2) -#define HDMI_PIPE_ENABLED(V, P) \ - (((V) & (TRANSCODER_MASK | PORT_ENABLE)) == ((P) << 30 | PORT_ENABLE)) - /* PCH SDVOB multiplex with HDMIB */ #define PCH_SDVOB HDMIB @@ -3328,15 +3412,35 @@ #define PCH_PP_STATUS 0xc7200 #define PCH_PP_CONTROL 0xc7204 #define PANEL_UNLOCK_REGS (0xabcd << 16) +#define PANEL_UNLOCK_MASK (0xffff << 16) #define EDP_FORCE_VDD (1 << 3) #define EDP_BLC_ENABLE (1 << 2) #define PANEL_POWER_RESET (1 << 1) #define PANEL_POWER_OFF (0 << 0) #define PANEL_POWER_ON (1 << 0) #define PCH_PP_ON_DELAYS 0xc7208 +#define PANEL_PORT_SELECT_MASK (3 << 30) +#define PANEL_PORT_SELECT_LVDS (0 << 30) +#define PANEL_PORT_SELECT_DPA (1 << 30) #define EDP_PANEL (1 << 30) +#define PANEL_PORT_SELECT_DPC (2 << 30) +#define PANEL_PORT_SELECT_DPD (3 << 30) +#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_UP_DELAY_SHIFT 16 +#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_ON_DELAY_SHIFT 0 + #define PCH_PP_OFF_DELAYS 0xc720c +#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_DOWN_DELAY_SHIFT 16 +#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_OFF_DELAY_SHIFT 0 + #define PCH_PP_DIVISOR 0xc7210 +#define PP_REFERENCE_DIVIDER_MASK (0xffffff00) +#define PP_REFERENCE_DIVIDER_SHIFT 8 +#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f) +#define PANEL_POWER_CYCLE_DELAY_SHIFT 0 #define PCH_DP_B 0xe4100 #define PCH_DPB_AUX_CH_CTL 0xe4110 @@ -3367,6 +3471,7 @@ #define PORT_TRANS_B_SEL_CPT (1<<29) #define PORT_TRANS_C_SEL_CPT (2<<29) #define PORT_TRANS_SEL_MASK (3<<29) +#define PORT_TRANS_SEL_CPT(pipe) ((pipe) << 29) #define TRANS_DP_CTL_A 0xe0300 #define TRANS_DP_CTL_B 0xe1300 @@ -3405,10 +3510,33 @@ #define EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B (0x38<<22) #define EDP_LINK_TRAIN_VOL_EMP_MASK_SNB (0x3f<<22) +/* IVB */ +#define EDP_LINK_TRAIN_400MV_0DB_IVB (0x24 <<22) +#define EDP_LINK_TRAIN_400MV_3_5DB_IVB (0x2a <<22) +#define EDP_LINK_TRAIN_400MV_6DB_IVB (0x2f <<22) +#define EDP_LINK_TRAIN_600MV_0DB_IVB (0x30 <<22) +#define EDP_LINK_TRAIN_600MV_3_5DB_IVB (0x36 <<22) +#define EDP_LINK_TRAIN_800MV_0DB_IVB (0x38 <<22) +#define EDP_LINK_TRAIN_800MV_3_5DB_IVB (0x3e <<22) + +/* legacy values */ +#define EDP_LINK_TRAIN_500MV_0DB_IVB (0x00 <<22) +#define EDP_LINK_TRAIN_1000MV_0DB_IVB (0x20 <<22) +#define EDP_LINK_TRAIN_500MV_3_5DB_IVB (0x02 <<22) +#define EDP_LINK_TRAIN_1000MV_3_5DB_IVB (0x22 <<22) +#define EDP_LINK_TRAIN_1000MV_6DB_IVB (0x23 <<22) + +#define EDP_LINK_TRAIN_VOL_EMP_MASK_IVB (0x3f<<22) + #define FORCEWAKE 0xA18C #define FORCEWAKE_ACK 0x130090 +#define FORCEWAKE_MT 0xa188 /* multi-threaded */ +#define FORCEWAKE_MT_ACK 0x130040 +#define ECOBUS 0xa180 +#define FORCEWAKE_MT_ENABLE (1<<5) #define GT_FIFO_FREE_ENTRIES 0x120008 +#define GT_FIFO_NUM_RESERVED_ENTRIES 20 #define GEN6_UCGCTL2 0x9404 # define GEN6_RCZUNIT_CLOCK_GATE_DISABLE (1 << 13) @@ -3436,7 +3564,11 @@ #define GEN6_CAGF_MASK (0x7f << GEN6_CAGF_SHIFT) #define GEN6_RP_CONTROL 0xA024 #define GEN6_RP_MEDIA_TURBO (1<<11) -#define GEN6_RP_USE_NORMAL_FREQ (1<<9) +#define GEN6_RP_MEDIA_MODE_MASK (3<<9) +#define GEN6_RP_MEDIA_HW_TURBO_MODE (3<<9) +#define GEN6_RP_MEDIA_HW_NORMAL_MODE (2<<9) +#define GEN6_RP_MEDIA_HW_MODE (1<<9) +#define GEN6_RP_MEDIA_SW_MODE (0<<9) #define GEN6_RP_MEDIA_IS_GFX (1<<8) #define GEN6_RP_ENABLE (1<<7) #define GEN6_RP_UP_IDLE_MIN (0x1<<3) @@ -3488,7 +3620,34 @@ #define GEN6_PCODE_MAILBOX 0x138124 #define GEN6_PCODE_READY (1<<31) #define GEN6_READ_OC_PARAMS 0xc -#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x9 +#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x8 +#define GEN6_PCODE_READ_MIN_FREQ_TABLE 0x9 #define GEN6_PCODE_DATA 0x138128 +#define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 + +#define G4X_AUD_VID_DID 0x62020 +#define INTEL_AUDIO_DEVCL 0x808629FB +#define INTEL_AUDIO_DEVBLC 0x80862801 +#define INTEL_AUDIO_DEVCTG 0x80862802 + +#define G4X_AUD_CNTL_ST 0x620B4 +#define G4X_ELDV_DEVCL_DEVBLC (1 << 13) +#define G4X_ELDV_DEVCTG (1 << 14) +#define G4X_ELD_ADDR (0xf << 5) +#define G4X_ELD_ACK (1 << 4) +#define G4X_HDMIW_HDMIEDID 0x6210C + +#define GEN5_HDMIW_HDMIEDID_A 0xE2050 +#define GEN5_AUD_CNTL_ST_A 0xE20B4 +#define GEN5_ELD_BUFFER_SIZE (0x1f << 10) +#define GEN5_ELD_ADDRESS (0x1f << 5) +#define GEN5_ELD_ACK (1 << 4) +#define GEN5_AUD_CNTL_ST2 0xE20C0 +#define GEN5_ELD_VALIDB (1 << 0) +#define GEN5_CP_READYB (1 << 1) + +#define GEN7_HDMIW_HDMIEDID_A 0xE5050 +#define GEN7_AUD_CNTRL_ST_A 0xE50B4 +#define GEN7_AUD_CNTRL_ST2 0xE50C0 #endif /* _I915_REG_H_ */ diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 5ad0b51..f38d196 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -64,7 +64,7 @@ static void i915_save_palette(struct drm_device *dev, enum pipe pipe) else array = dev_priv->save_palette_b; - for(i = 0; i < 256; i++) + for (i = 0; i < 256; i++) array[i] = I915_READ(reg + (i << 2)); } @@ -86,7 +86,7 @@ static void i915_restore_palette(struct drm_device *dev, enum pipe pipe) else array = dev_priv->save_palette_b; - for(i = 0; i < 256; i++) + for (i = 0; i < 256; i++) I915_WRITE(reg + (i << 2), array[i]); } @@ -769,15 +769,13 @@ static void i915_restore_display(struct drm_device *dev) /* FIXME: restore TV & SDVO state */ /* only restore FBC info on the platform that supports FBC*/ + intel_disable_fbc(dev); if (I915_HAS_FBC(dev)) { if (HAS_PCH_SPLIT(dev)) { - ironlake_disable_fbc(dev); I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE); } else if (IS_GM45(dev)) { - g4x_disable_fbc(dev); I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE); } else { - i8xx_disable_fbc(dev); I915_WRITE(FBC_CFB_BASE, dev_priv->saveFBC_CFB_BASE); I915_WRITE(FBC_LL_BASE, dev_priv->saveFBC_LL_BASE); I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2); @@ -831,7 +829,7 @@ int i915_save_state(struct drm_device *dev) if (IS_IRONLAKE_M(dev)) ironlake_disable_drps(dev); - if (IS_GEN6(dev)) + if (INTEL_INFO(dev)->gen >= 6) gen6_disable_rps(dev); /* Cache mode state */ @@ -882,23 +880,26 @@ int i915_restore_state(struct drm_device *dev) } mutex_unlock(&dev->struct_mutex); - intel_init_clock_gating(dev); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + intel_init_clock_gating(dev); if (IS_IRONLAKE_M(dev)) { ironlake_enable_drps(dev); intel_init_emon(dev); } - if (IS_GEN6(dev)) + if (INTEL_INFO(dev)->gen >= 6) { gen6_enable_rps(dev_priv); + gen6_update_ring_freq(dev_priv); + } mutex_lock(&dev->struct_mutex); /* Cache mode state */ - I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); + I915_WRITE(CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); /* Memory arbitration state */ - I915_WRITE (MI_ARB_STATE, dev_priv->saveMI_ARB_STATE | 0xffff0000); + I915_WRITE(MI_ARB_STATE, dev_priv->saveMI_ARB_STATE | 0xffff0000); for (i = 0; i < 16; i++) { I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]); diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index d623fef..dac7bba 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -385,29 +385,29 @@ TRACE_EVENT(i915_flip_complete, ); TRACE_EVENT(i915_reg_rw, - TP_PROTO(bool write, u32 reg, u64 val, int len), - - TP_ARGS(write, reg, val, len), - - TP_STRUCT__entry( - __field(u64, val) - __field(u32, reg) - __field(u16, write) - __field(u16, len) - ), - - TP_fast_assign( - __entry->val = (u64)val; - __entry->reg = reg; - __entry->write = write; - __entry->len = len; - ), - - TP_printk("%s reg=0x%x, len=%d, val=(0x%x, 0x%x)", - __entry->write ? "write" : "read", - __entry->reg, __entry->len, - (u32)(__entry->val & 0xffffffff), - (u32)(__entry->val >> 32)) + TP_PROTO(bool write, u32 reg, u64 val, int len), + + TP_ARGS(write, reg, val, len), + + TP_STRUCT__entry( + __field(u64, val) + __field(u32, reg) + __field(u16, write) + __field(u16, len) + ), + + TP_fast_assign( + __entry->val = (u64)val; + __entry->reg = reg; + __entry->write = write; + __entry->len = len; + ), + + TP_printk("%s reg=0x%x, len=%d, val=(0x%x, 0x%x)", + __entry->write ? "write" : "read", + __entry->reg, __entry->len, + (u32)(__entry->val & 0xffffffff), + (u32)(__entry->val >> 32)) ); #endif /* _I915_TRACE_H_ */ diff --git a/drivers/gpu/drm/i915/intel_acpi.c b/drivers/gpu/drm/i915/intel_acpi.c index 2cb8e0b..cb91210 100644 --- a/drivers/gpu/drm/i915/intel_acpi.c +++ b/drivers/gpu/drm/i915/intel_acpi.c @@ -64,7 +64,7 @@ static int intel_dsm(acpi_handle handle, int func, int arg) case ACPI_TYPE_BUFFER: if (obj->buffer.length == 4) { - result =(obj->buffer.pointer[0] | + result = (obj->buffer.pointer[0] | (obj->buffer.pointer[1] << 8) | (obj->buffer.pointer[2] << 16) | (obj->buffer.pointer[3] << 24)); diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index e5fa074..96d5cfc 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -1,5 +1,5 @@ /* - * Copyright © 2006 Intel Corporation + * Copyright © 2006 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -75,7 +75,7 @@ get_blocksize(void *p) static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, - struct lvds_dvo_timing *dvo_timing) + const struct lvds_dvo_timing *dvo_timing) { panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | dvo_timing->hactive_lo; @@ -116,20 +116,98 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, drm_mode_set_name(panel_fixed_mode); } +static bool +lvds_dvo_timing_equal_size(const struct lvds_dvo_timing *a, + const struct lvds_dvo_timing *b) +{ + if (a->hactive_hi != b->hactive_hi || + a->hactive_lo != b->hactive_lo) + return false; + + if (a->hsync_off_hi != b->hsync_off_hi || + a->hsync_off_lo != b->hsync_off_lo) + return false; + + if (a->hsync_pulse_width != b->hsync_pulse_width) + return false; + + if (a->hblank_hi != b->hblank_hi || + a->hblank_lo != b->hblank_lo) + return false; + + if (a->vactive_hi != b->vactive_hi || + a->vactive_lo != b->vactive_lo) + return false; + + if (a->vsync_off != b->vsync_off) + return false; + + if (a->vsync_pulse_width != b->vsync_pulse_width) + return false; + + if (a->vblank_hi != b->vblank_hi || + a->vblank_lo != b->vblank_lo) + return false; + + return true; +} + +static const struct lvds_dvo_timing * +get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data, + const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs, + int index) +{ + /* + * the size of fp_timing varies on the different platform. + * So calculate the DVO timing relative offset in LVDS data + * entry to get the DVO timing entry + */ + + int lfp_data_size = + lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset - + lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset; + int dvo_timing_offset = + lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset - + lvds_lfp_data_ptrs->ptr[0].fp_timing_offset; + char *entry = (char *)lvds_lfp_data->data + lfp_data_size * index; + + return (struct lvds_dvo_timing *)(entry + dvo_timing_offset); +} + +/* get lvds_fp_timing entry + * this function may return NULL if the corresponding entry is invalid + */ +static const struct lvds_fp_timing * +get_lvds_fp_timing(const struct bdb_header *bdb, + const struct bdb_lvds_lfp_data *data, + const struct bdb_lvds_lfp_data_ptrs *ptrs, + int index) +{ + size_t data_ofs = (const u8 *)data - (const u8 *)bdb; + u16 data_size = ((const u16 *)data)[-1]; /* stored in header */ + size_t ofs; + + if (index >= ARRAY_SIZE(ptrs->ptr)) + return NULL; + ofs = ptrs->ptr[index].fp_timing_offset; + if (ofs < data_ofs || + ofs + sizeof(struct lvds_fp_timing) > data_ofs + data_size) + return NULL; + return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs); +} + /* Try to find integrated panel data */ static void parse_lfp_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb) { - struct bdb_lvds_options *lvds_options; - struct bdb_lvds_lfp_data *lvds_lfp_data; - struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; - struct bdb_lvds_lfp_data_entry *entry; - struct lvds_dvo_timing *dvo_timing; + const struct bdb_lvds_options *lvds_options; + const struct bdb_lvds_lfp_data *lvds_lfp_data; + const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; + const struct lvds_dvo_timing *panel_dvo_timing; + const struct lvds_fp_timing *fp_timing; struct drm_display_mode *panel_fixed_mode; - int lfp_data_size, dvo_timing_offset; - int i, temp_downclock; - struct drm_display_mode *temp_mode; + int i, downclock; lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); if (!lvds_options) @@ -151,75 +229,57 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, dev_priv->lvds_vbt = 1; - lfp_data_size = lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset - - lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset; - entry = (struct bdb_lvds_lfp_data_entry *) - ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * - lvds_options->panel_type)); - dvo_timing_offset = lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset - - lvds_lfp_data_ptrs->ptr[0].fp_timing_offset; - - /* - * the size of fp_timing varies on the different platform. - * So calculate the DVO timing relative offset in LVDS data - * entry to get the DVO timing entry - */ - dvo_timing = (struct lvds_dvo_timing *) - ((unsigned char *)entry + dvo_timing_offset); + panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data, + lvds_lfp_data_ptrs, + lvds_options->panel_type); panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); if (!panel_fixed_mode) return; - fill_detail_timing_data(panel_fixed_mode, dvo_timing); + fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing); dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n"); drm_mode_debug_printmodeline(panel_fixed_mode); - temp_mode = kzalloc(sizeof(*temp_mode), GFP_KERNEL); - temp_downclock = panel_fixed_mode->clock; /* - * enumerate the LVDS panel timing info entry in VBT to check whether - * the LVDS downclock is found. + * Iterate over the LVDS panel timing info to find the lowest clock + * for the native resolution. */ + downclock = panel_dvo_timing->clock; for (i = 0; i < 16; i++) { - entry = (struct bdb_lvds_lfp_data_entry *) - ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i)); - dvo_timing = (struct lvds_dvo_timing *) - ((unsigned char *)entry + dvo_timing_offset); - - fill_detail_timing_data(temp_mode, dvo_timing); - - if (temp_mode->hdisplay == panel_fixed_mode->hdisplay && - temp_mode->hsync_start == panel_fixed_mode->hsync_start && - temp_mode->hsync_end == panel_fixed_mode->hsync_end && - temp_mode->htotal == panel_fixed_mode->htotal && - temp_mode->vdisplay == panel_fixed_mode->vdisplay && - temp_mode->vsync_start == panel_fixed_mode->vsync_start && - temp_mode->vsync_end == panel_fixed_mode->vsync_end && - temp_mode->vtotal == panel_fixed_mode->vtotal && - temp_mode->clock < temp_downclock) { - /* - * downclock is already found. But we expect - * to find the lower downclock. - */ - temp_downclock = temp_mode->clock; - } - /* clear it to zero */ - memset(temp_mode, 0, sizeof(*temp_mode)); + const struct lvds_dvo_timing *dvo_timing; + + dvo_timing = get_lvds_dvo_timing(lvds_lfp_data, + lvds_lfp_data_ptrs, + i); + if (lvds_dvo_timing_equal_size(dvo_timing, panel_dvo_timing) && + dvo_timing->clock < downclock) + downclock = dvo_timing->clock; } - kfree(temp_mode); - if (temp_downclock < panel_fixed_mode->clock && - i915_lvds_downclock) { + + if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) { dev_priv->lvds_downclock_avail = 1; - dev_priv->lvds_downclock = temp_downclock; + dev_priv->lvds_downclock = downclock * 10; DRM_DEBUG_KMS("LVDS downclock is found in VBT. " "Normal Clock %dKHz, downclock %dKHz\n", - temp_downclock, panel_fixed_mode->clock); + panel_fixed_mode->clock, 10*downclock); + } + + fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data, + lvds_lfp_data_ptrs, + lvds_options->panel_type); + if (fp_timing) { + /* check the resolution, just to be sure */ + if (fp_timing->x_res == panel_fixed_mode->hdisplay && + fp_timing->y_res == panel_fixed_mode->vdisplay) { + dev_priv->bios_lvds_val = fp_timing->lvds_reg_val; + DRM_DEBUG_KMS("VBT initial LVDS value %x\n", + dev_priv->bios_lvds_val); + } } - return; } /* Try to find sdvo panel data */ @@ -286,6 +346,13 @@ parse_general_features(struct drm_i915_private *dev_priv, dev_priv->lvds_use_ssc = general->enable_ssc; dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, general->ssc_freq); + dev_priv->display_clock_mode = general->display_clock_mode; + DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d\n", + dev_priv->int_tv_support, + dev_priv->int_crt_support, + dev_priv->lvds_use_ssc, + dev_priv->lvds_ssc_freq, + dev_priv->display_clock_mode); } } @@ -358,7 +425,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, if (p_child->dvo_port != DEVICE_PORT_DVOB && p_child->dvo_port != DEVICE_PORT_DVOC) { /* skip the incorrect SDVO port */ - DRM_DEBUG_KMS("Incorrect SDVO port. Skip it \n"); + DRM_DEBUG_KMS("Incorrect SDVO port. Skip it\n"); continue; } DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on" @@ -373,15 +440,13 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, p_mapping->dvo_wiring = p_child->dvo_wiring; p_mapping->ddc_pin = p_child->ddc_pin; p_mapping->i2c_pin = p_child->i2c_pin; - p_mapping->i2c_speed = p_child->i2c_speed; p_mapping->initialized = 1; - DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d, i2c_speed=%d\n", + DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n", p_mapping->dvo_port, p_mapping->slave_addr, p_mapping->dvo_wiring, p_mapping->ddc_pin, - p_mapping->i2c_pin, - p_mapping->i2c_speed); + p_mapping->i2c_pin); } else { DRM_DEBUG_KMS("Maybe one SDVO port is shared by " "two SDVO device.\n"); @@ -430,12 +495,8 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) edp = find_section(bdb, BDB_EDP); if (!edp) { - if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp.support) { - DRM_DEBUG_KMS("No eDP BDB found but eDP panel " - "supported, assume %dbpp panel color " - "depth.\n", - dev_priv->edp.bpp); - } + if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp.support) + DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported.\n"); return; } @@ -541,7 +602,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv, count++; } if (!count) { - DRM_DEBUG_KMS("no child dev is parsed from VBT \n"); + DRM_DEBUG_KMS("no child dev is parsed from VBT\n"); return; } dev_priv->child_dev = kzalloc(sizeof(*p_child) * count, GFP_KERNEL); @@ -587,13 +648,10 @@ init_vbt_defaults(struct drm_i915_private *dev_priv) /* Default to using SSC */ dev_priv->lvds_use_ssc = 1; dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1); - DRM_DEBUG("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq); - - /* eDP data */ - dev_priv->edp.bpp = 18; + DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq); } -static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id) +static int intel_no_opregion_vbt_callback(const struct dmi_system_id *id) { DRM_DEBUG_KMS("Falling back to manually reading VBT from " "VBIOS ROM for %s\n", @@ -636,7 +694,7 @@ intel_parse_bios(struct drm_device *dev) if (!dmi_check_system(intel_no_opregion_vbt) && dev_priv->opregion.vbt) { struct vbt_header *vbt = dev_priv->opregion.vbt; if (memcmp(vbt->signature, "$VBT", 4) == 0) { - DRM_DEBUG_DRIVER("Using VBT from OpRegion: %20s\n", + DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n", vbt->signature); bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset); } else diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 5f8e4ed..8af3735 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -1,5 +1,5 @@ /* - * Copyright © 2006 Intel Corporation + * Copyright © 2006 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -120,7 +120,9 @@ struct bdb_general_features { u8 ssc_freq:1; u8 enable_lfp_on_override:1; u8 disable_ssc_ddt:1; - u8 rsvd8:3; /* finish byte */ + u8 rsvd7:1; + u8 display_clock_mode:1; + u8 rsvd8:1; /* finish byte */ /* bits 3 */ u8 disable_smooth_vision:1; @@ -133,7 +135,10 @@ struct bdb_general_features { /* bits 5 */ u8 int_crt_support:1; u8 int_tv_support:1; - u8 rsvd11:6; /* finish byte */ + u8 int_efp_support:1; + u8 dp_ssc_enb:1; /* PCH attached eDP supports SSC */ + u8 dp_ssc_freq:1; /* SSC freq for PCH attached eDP */ + u8 rsvd11:3; /* finish byte */ } __attribute__((packed)); /* pre-915 */ @@ -197,8 +202,7 @@ struct bdb_general_features { struct child_device_config { u16 handle; u16 device_type; - u8 i2c_speed; - u8 rsvd[9]; + u8 device_id[10]; /* ascii string */ u16 addin_offset; u8 dvo_port; /* See Device_PORT_* above */ u8 i2c_pin; @@ -240,7 +244,7 @@ struct bdb_general_definitions { * And the device num is related with the size of general definition * block. It is obtained by using the following formula: * number = (block_size - sizeof(bdb_general_definitions))/ - * sizeof(child_device_config); + * sizeof(child_device_config); */ struct child_device_config devices[0]; } __attribute__((packed)); @@ -446,11 +450,11 @@ struct bdb_driver_features { #define EDP_VSWING_1_2V 3 struct edp_power_seq { - u16 t3; - u16 t7; + u16 t1_t3; + u16 t8; u16 t9; u16 t10; - u16 t12; + u16 t11_t12; } __attribute__ ((packed)); struct edp_link_params { diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 0979d88..fee0ad0 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -69,7 +69,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode) temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); temp &= ~ADPA_DAC_ENABLE; - switch(mode) { + switch (mode) { case DRM_MODE_DPMS_ON: temp |= ADPA_DAC_ENABLE; break; @@ -152,17 +152,13 @@ static void intel_crt_mode_set(struct drm_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) adpa |= ADPA_VSYNC_ACTIVE_HIGH; - if (intel_crtc->pipe == 0) { - if (HAS_PCH_CPT(dev)) - adpa |= PORT_TRANS_A_SEL_CPT; - else - adpa |= ADPA_PIPE_A_SELECT; - } else { - if (HAS_PCH_CPT(dev)) - adpa |= PORT_TRANS_B_SEL_CPT; - else - adpa |= ADPA_PIPE_B_SELECT; - } + /* For CPT allow 3 pipe config, for others just use A or B */ + if (HAS_PCH_CPT(dev)) + adpa |= PORT_TRANS_SEL_CPT(intel_crtc->pipe); + else if (intel_crtc->pipe == 0) + adpa |= ADPA_PIPE_A_SELECT; + else + adpa |= ADPA_PIPE_B_SELECT; if (!HAS_PCH_SPLIT(dev)) I915_WRITE(BCLRPAT(intel_crtc->pipe), 0); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2e0c24d..27999d9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -24,56 +24,59 @@ * Eric Anholt <eric@anholt.net> */ +#include <linux/cpufreq.h> +#include <linux/dmi.h> #include <linux/module.h> #include <linux/input.h> #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/vgaarb.h> +#include <drm/drm_edid.h> #include "drmP.h" #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" #include "i915_trace.h" #include "drm_dp_helper.h" - #include "drm_crtc_helper.h" +#include <linux/dma_remapping.h> #define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) -bool intel_pipe_has_type (struct drm_crtc *crtc, int type); +bool intel_pipe_has_type(struct drm_crtc *crtc, int type); static void intel_update_watermarks(struct drm_device *dev); static void intel_increase_pllclock(struct drm_crtc *crtc); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); typedef struct { - /* given values */ - int n; - int m1, m2; - int p1, p2; - /* derived values */ - int dot; - int vco; - int m; - int p; + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; } intel_clock_t; typedef struct { - int min, max; + int min, max; } intel_range_t; typedef struct { - int dot_limit; - int p2_slow, p2_fast; + int dot_limit; + int p2_slow, p2_fast; } intel_p2_t; #define INTEL_P2_NUM 2 typedef struct intel_limit intel_limit_t; struct intel_limit { - intel_range_t dot, vco, n, m, m1, m2, p, p1; - intel_p2_t p2; - bool (* find_pll)(const intel_limit_t *, struct drm_crtc *, - int, int, intel_clock_t *); + intel_range_t dot, vco, n, m, m1, m2, p, p1; + intel_p2_t p2; + bool (* find_pll)(const intel_limit_t *, struct drm_crtc *, + int, int, intel_clock_t *); }; /* FDI */ @@ -104,56 +107,56 @@ intel_fdi_link_freq(struct drm_device *dev) } static const intel_limit_t intel_limits_i8xx_dvo = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 930000, .max = 1400000 }, - .n = { .min = 3, .max = 16 }, - .m = { .min = 96, .max = 140 }, - .m1 = { .min = 18, .max = 26 }, - .m2 = { .min = 6, .max = 16 }, - .p = { .min = 4, .max = 128 }, - .p1 = { .min = 2, .max = 33 }, + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 930000, .max = 1400000 }, + .n = { .min = 3, .max = 16 }, + .m = { .min = 96, .max = 140 }, + .m1 = { .min = 18, .max = 26 }, + .m2 = { .min = 6, .max = 16 }, + .p = { .min = 4, .max = 128 }, + .p1 = { .min = 2, .max = 33 }, .p2 = { .dot_limit = 165000, .p2_slow = 4, .p2_fast = 2 }, .find_pll = intel_find_best_PLL, }; static const intel_limit_t intel_limits_i8xx_lvds = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 930000, .max = 1400000 }, - .n = { .min = 3, .max = 16 }, - .m = { .min = 96, .max = 140 }, - .m1 = { .min = 18, .max = 26 }, - .m2 = { .min = 6, .max = 16 }, - .p = { .min = 4, .max = 128 }, - .p1 = { .min = 1, .max = 6 }, + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 930000, .max = 1400000 }, + .n = { .min = 3, .max = 16 }, + .m = { .min = 96, .max = 140 }, + .m1 = { .min = 18, .max = 26 }, + .m2 = { .min = 6, .max = 16 }, + .p = { .min = 4, .max = 128 }, + .p1 = { .min = 1, .max = 6 }, .p2 = { .dot_limit = 165000, .p2_slow = 14, .p2_fast = 7 }, .find_pll = intel_find_best_PLL, }; static const intel_limit_t intel_limits_i9xx_sdvo = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1400000, .max = 2800000 }, - .n = { .min = 1, .max = 6 }, - .m = { .min = 70, .max = 120 }, - .m1 = { .min = 10, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8 }, + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1400000, .max = 2800000 }, + .n = { .min = 1, .max = 6 }, + .m = { .min = 70, .max = 120 }, + .m1 = { .min = 8, .max = 18 }, + .m2 = { .min = 3, .max = 7 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8 }, .p2 = { .dot_limit = 200000, .p2_slow = 10, .p2_fast = 5 }, .find_pll = intel_find_best_PLL, }; static const intel_limit_t intel_limits_i9xx_lvds = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1400000, .max = 2800000 }, - .n = { .min = 1, .max = 6 }, - .m = { .min = 70, .max = 120 }, - .m1 = { .min = 10, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 7, .max = 98 }, - .p1 = { .min = 1, .max = 8 }, + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1400000, .max = 2800000 }, + .n = { .min = 1, .max = 6 }, + .m = { .min = 70, .max = 120 }, + .m1 = { .min = 10, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 7, .max = 98 }, + .p1 = { .min = 1, .max = 8 }, .p2 = { .dot_limit = 112000, .p2_slow = 14, .p2_fast = 7 }, .find_pll = intel_find_best_PLL, @@ -221,44 +224,44 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = { }; static const intel_limit_t intel_limits_g4x_display_port = { - .dot = { .min = 161670, .max = 227000 }, - .vco = { .min = 1750000, .max = 3500000}, - .n = { .min = 1, .max = 2 }, - .m = { .min = 97, .max = 108 }, - .m1 = { .min = 0x10, .max = 0x12 }, - .m2 = { .min = 0x05, .max = 0x06 }, - .p = { .min = 10, .max = 20 }, - .p1 = { .min = 1, .max = 2}, - .p2 = { .dot_limit = 0, + .dot = { .min = 161670, .max = 227000 }, + .vco = { .min = 1750000, .max = 3500000}, + .n = { .min = 1, .max = 2 }, + .m = { .min = 97, .max = 108 }, + .m1 = { .min = 0x10, .max = 0x12 }, + .m2 = { .min = 0x05, .max = 0x06 }, + .p = { .min = 10, .max = 20 }, + .p1 = { .min = 1, .max = 2}, + .p2 = { .dot_limit = 0, .p2_slow = 10, .p2_fast = 10 }, - .find_pll = intel_find_pll_g4x_dp, + .find_pll = intel_find_pll_g4x_dp, }; static const intel_limit_t intel_limits_pineview_sdvo = { - .dot = { .min = 20000, .max = 400000}, - .vco = { .min = 1700000, .max = 3500000 }, + .dot = { .min = 20000, .max = 400000}, + .vco = { .min = 1700000, .max = 3500000 }, /* Pineview's Ncounter is a ring counter */ - .n = { .min = 3, .max = 6 }, - .m = { .min = 2, .max = 256 }, + .n = { .min = 3, .max = 6 }, + .m = { .min = 2, .max = 256 }, /* Pineview only has one combined m divider, which we treat as m2. */ - .m1 = { .min = 0, .max = 0 }, - .m2 = { .min = 0, .max = 254 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8 }, + .m1 = { .min = 0, .max = 0 }, + .m2 = { .min = 0, .max = 254 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8 }, .p2 = { .dot_limit = 200000, .p2_slow = 10, .p2_fast = 5 }, .find_pll = intel_find_best_PLL, }; static const intel_limit_t intel_limits_pineview_lvds = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1700000, .max = 3500000 }, - .n = { .min = 3, .max = 6 }, - .m = { .min = 2, .max = 256 }, - .m1 = { .min = 0, .max = 0 }, - .m2 = { .min = 0, .max = 254 }, - .p = { .min = 7, .max = 112 }, - .p1 = { .min = 1, .max = 8 }, + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1700000, .max = 3500000 }, + .n = { .min = 3, .max = 6 }, + .m = { .min = 2, .max = 256 }, + .m1 = { .min = 0, .max = 0 }, + .m2 = { .min = 0, .max = 254 }, + .p = { .min = 7, .max = 112 }, + .p1 = { .min = 1, .max = 8 }, .p2 = { .dot_limit = 112000, .p2_slow = 14, .p2_fast = 14 }, .find_pll = intel_find_best_PLL, @@ -320,7 +323,7 @@ static const intel_limit_t intel_limits_ironlake_single_lvds_100m = { .m1 = { .min = 12, .max = 22 }, .m2 = { .min = 5, .max = 9 }, .p = { .min = 28, .max = 112 }, - .p1 = { .min = 2,.max = 8 }, + .p1 = { .min = 2, .max = 8 }, .p2 = { .dot_limit = 225000, .p2_slow = 14, .p2_fast = 14 }, .find_pll = intel_g4x_find_best_PLL, @@ -334,26 +337,47 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = { .m1 = { .min = 12, .max = 22 }, .m2 = { .min = 5, .max = 9 }, .p = { .min = 14, .max = 42 }, - .p1 = { .min = 2,.max = 6 }, + .p1 = { .min = 2, .max = 6 }, .p2 = { .dot_limit = 225000, .p2_slow = 7, .p2_fast = 7 }, .find_pll = intel_g4x_find_best_PLL, }; static const intel_limit_t intel_limits_ironlake_display_port = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000}, - .n = { .min = 1, .max = 2 }, - .m = { .min = 81, .max = 90 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 10, .max = 20 }, - .p1 = { .min = 1, .max = 2}, - .p2 = { .dot_limit = 0, + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000}, + .n = { .min = 1, .max = 2 }, + .m = { .min = 81, .max = 90 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 10, .max = 20 }, + .p1 = { .min = 1, .max = 2}, + .p2 = { .dot_limit = 0, .p2_slow = 10, .p2_fast = 10 }, - .find_pll = intel_find_pll_ironlake_dp, + .find_pll = intel_find_pll_ironlake_dp, }; +static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, + unsigned int reg) +{ + unsigned int val; + + if (dev_priv->lvds_val) + val = dev_priv->lvds_val; + else { + /* BIOS should set the proper LVDS register value at boot, but + * in reality, it doesn't set the value when the lid is closed; + * we need to check "the value to be set" in VBT when LVDS + * register is uninitialized. + */ + val = I915_READ(reg); + if (!(val & ~LVDS_DETECTED)) + val = dev_priv->bios_lvds_val; + dev_priv->lvds_val = val; + } + return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; +} + static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, int refclk) { @@ -362,8 +386,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) { + if (is_dual_link_lvds(dev_priv, PCH_LVDS)) { /* LVDS dual channel */ if (refclk == 100000) limit = &intel_limits_ironlake_dual_lvds_100m; @@ -391,8 +414,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) + if (is_dual_link_lvds(dev_priv, LVDS)) /* LVDS with dual channel */ limit = &intel_limits_g4x_dual_channel_lvds; else @@ -403,7 +425,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) limit = &intel_limits_g4x_hdmi; } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { limit = &intel_limits_g4x_sdvo; - } else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) { + } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { limit = &intel_limits_g4x_display_port; } else /* The option is for other outputs */ limit = &intel_limits_i9xx_sdvo; @@ -487,26 +509,26 @@ static bool intel_PLL_is_valid(struct drm_device *dev, const intel_clock_t *clock) { if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) - INTELPllInvalid ("p1 out of range\n"); + INTELPllInvalid("p1 out of range\n"); if (clock->p < limit->p.min || limit->p.max < clock->p) - INTELPllInvalid ("p out of range\n"); + INTELPllInvalid("p out of range\n"); if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) - INTELPllInvalid ("m2 out of range\n"); + INTELPllInvalid("m2 out of range\n"); if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) - INTELPllInvalid ("m1 out of range\n"); + INTELPllInvalid("m1 out of range\n"); if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev)) - INTELPllInvalid ("m1 <= m2\n"); + INTELPllInvalid("m1 <= m2\n"); if (clock->m < limit->m.min || limit->m.max < clock->m) - INTELPllInvalid ("m out of range\n"); + INTELPllInvalid("m out of range\n"); if (clock->n < limit->n.min || limit->n.max < clock->n) - INTELPllInvalid ("n out of range\n"); + INTELPllInvalid("n out of range\n"); if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) - INTELPllInvalid ("vco out of range\n"); + INTELPllInvalid("vco out of range\n"); /* XXX: We may need to be checking "Dot clock" depending on the multiplier, * connector, etc., rather than just a single range. */ if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) - INTELPllInvalid ("dot out of range\n"); + INTELPllInvalid("dot out of range\n"); return true; } @@ -529,8 +551,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, * reliably set up different single/dual channel state, if we * even can. */ - if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) + if (is_dual_link_lvds(dev_priv, LVDS)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -541,7 +562,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, clock.p2 = limit->p2.p2_fast; } - memset (best_clock, 0, sizeof (*best_clock)); + memset(best_clock, 0, sizeof(*best_clock)); for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { @@ -801,6 +822,19 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv, u32 val; bool cur_state; + if (HAS_PCH_CPT(dev_priv->dev)) { + u32 pch_dpll; + + pch_dpll = I915_READ(PCH_DPLL_SEL); + + /* Make sure the selected PLL is enabled to the transcoder */ + WARN(!((pch_dpll >> (4 * pipe)) & 8), + "transcoder %d PLL not enabled\n", pipe); + + /* Convert the transcoder pipe number to a pll pipe number */ + pipe = (pch_dpll >> (4 * pipe)) & 1; + } + reg = PCH_DPLL(pipe); val = I915_READ(reg); cur_state = !!(val & DPLL_VCO_ENABLE); @@ -877,7 +911,7 @@ static void assert_panel_unlocked(struct drm_i915_private *dev_priv, int pp_reg, lvds_reg; u32 val; enum pipe panel_pipe = PIPE_A; - bool locked = locked; + bool locked = true; if (HAS_PCH_SPLIT(dev_priv->dev)) { pp_reg = PCH_PP_CONTROL; @@ -979,11 +1013,76 @@ static void assert_transcoder_disabled(struct drm_i915_private *dev_priv, pipe_name(pipe)); } +static bool dp_pipe_enabled(struct drm_i915_private *dev_priv, + enum pipe pipe, u32 port_sel, u32 val) +{ + if ((val & DP_PORT_EN) == 0) + return false; + + if (HAS_PCH_CPT(dev_priv->dev)) { + u32 trans_dp_ctl_reg = TRANS_DP_CTL(pipe); + u32 trans_dp_ctl = I915_READ(trans_dp_ctl_reg); + if ((trans_dp_ctl & TRANS_DP_PORT_SEL_MASK) != port_sel) + return false; + } else { + if ((val & DP_PIPE_MASK) != (pipe << 30)) + return false; + } + return true; +} + +static bool hdmi_pipe_enabled(struct drm_i915_private *dev_priv, + enum pipe pipe, u32 val) +{ + if ((val & PORT_ENABLE) == 0) + return false; + + if (HAS_PCH_CPT(dev_priv->dev)) { + if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe)) + return false; + } else { + if ((val & TRANSCODER_MASK) != TRANSCODER(pipe)) + return false; + } + return true; +} + +static bool lvds_pipe_enabled(struct drm_i915_private *dev_priv, + enum pipe pipe, u32 val) +{ + if ((val & LVDS_PORT_EN) == 0) + return false; + + if (HAS_PCH_CPT(dev_priv->dev)) { + if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe)) + return false; + } else { + if ((val & LVDS_PIPE_MASK) != LVDS_PIPE(pipe)) + return false; + } + return true; +} + +static bool adpa_pipe_enabled(struct drm_i915_private *dev_priv, + enum pipe pipe, u32 val) +{ + if ((val & ADPA_DAC_ENABLE) == 0) + return false; + if (HAS_PCH_CPT(dev_priv->dev)) { + if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe)) + return false; + } else { + if ((val & ADPA_PIPE_SELECT_MASK) != ADPA_PIPE_SELECT(pipe)) + return false; + } + return true; +} + static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv, - enum pipe pipe, int reg) + enum pipe pipe, int reg, u32 port_sel) { u32 val = I915_READ(reg); - WARN(DP_PIPE_ENABLED(val, pipe), + WARN(dp_pipe_enabled(dev_priv, pipe, port_sel, val), "PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n", reg, pipe_name(pipe)); } @@ -992,8 +1091,8 @@ static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv, enum pipe pipe, int reg) { u32 val = I915_READ(reg); - WARN(HDMI_PIPE_ENABLED(val, pipe), - "PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n", + WARN(hdmi_pipe_enabled(dev_priv, pipe, val), + "PCH HDMI (0x%08x) enabled on transcoder %c, should be disabled\n", reg, pipe_name(pipe)); } @@ -1003,19 +1102,19 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, int reg; u32 val; - assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B); - assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C); - assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D); + assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B); + assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C); + assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D); reg = PCH_ADPA; val = I915_READ(reg); - WARN(ADPA_PIPE_ENABLED(val, pipe), + WARN(adpa_pipe_enabled(dev_priv, pipe, val), "PCH VGA enabled on transcoder %c, should be disabled\n", pipe_name(pipe)); reg = PCH_LVDS; val = I915_READ(reg); - WARN(LVDS_PIPE_ENABLED(val, pipe), + WARN(lvds_pipe_enabled(dev_priv, pipe, val), "PCH LVDS enabled on transcoder %c, should be disabled\n", pipe_name(pipe)); @@ -1105,6 +1204,9 @@ static void intel_enable_pch_pll(struct drm_i915_private *dev_priv, int reg; u32 val; + if (pipe > 1) + return; + /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); @@ -1125,6 +1227,9 @@ static void intel_disable_pch_pll(struct drm_i915_private *dev_priv, int reg; u32 val; + if (pipe > 1) + return; + /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); @@ -1157,12 +1262,15 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv, reg = TRANSCONF(pipe); val = I915_READ(reg); - /* - * make the BPC in transcoder be consistent with - * that in pipeconf reg. - */ - val &= ~PIPE_BPC_MASK; - val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK; + + if (HAS_PCH_IBX(dev_priv->dev)) { + /* + * make the BPC in transcoder be consistent with + * that in pipeconf reg. + */ + val &= ~PIPE_BPC_MASK; + val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK; + } I915_WRITE(reg, val | TRANS_ENABLE); if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100)) DRM_ERROR("failed to enable transcoder %d\n", pipe); @@ -1187,7 +1295,7 @@ static void intel_disable_transcoder(struct drm_i915_private *dev_priv, I915_WRITE(reg, val); /* wait for PCH transcoder off, transcoder state */ if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50)) - DRM_ERROR("failed to disable transcoder\n"); + DRM_ERROR("failed to disable transcoder %d\n", pipe); } /** @@ -1272,6 +1380,17 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv, intel_wait_for_pipe_off(dev_priv->dev, pipe); } +/* + * Plane regs are double buffered, going from enabled->disabled needs a + * trigger in order to latch. The display address reg provides this. + */ +static void intel_flush_display_plane(struct drm_i915_private *dev_priv, + enum plane plane) +{ + I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane))); + I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane))); +} + /** * intel_enable_plane - enable a display plane on a given pipe * @dev_priv: i915 private structure @@ -1295,20 +1414,10 @@ static void intel_enable_plane(struct drm_i915_private *dev_priv, return; I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE); + intel_flush_display_plane(dev_priv, plane); intel_wait_for_vblank(dev_priv->dev, pipe); } -/* - * Plane regs are double buffered, going from enabled->disabled needs a - * trigger in order to latch. The display address reg provides this. - */ -static void intel_flush_display_plane(struct drm_i915_private *dev_priv, - enum plane plane) -{ - u32 reg = DSPADDR(plane); - I915_WRITE(reg, I915_READ(reg)); -} - /** * intel_disable_plane - disable a display plane * @dev_priv: i915 private structure @@ -1334,19 +1443,24 @@ static void intel_disable_plane(struct drm_i915_private *dev_priv, } static void disable_pch_dp(struct drm_i915_private *dev_priv, - enum pipe pipe, int reg) + enum pipe pipe, int reg, u32 port_sel) { u32 val = I915_READ(reg); - if (DP_PIPE_ENABLED(val, pipe)) + if (dp_pipe_enabled(dev_priv, pipe, port_sel, val)) { + DRM_DEBUG_KMS("Disabling pch dp %x on pipe %d\n", reg, pipe); I915_WRITE(reg, val & ~DP_PORT_EN); + } } static void disable_pch_hdmi(struct drm_i915_private *dev_priv, enum pipe pipe, int reg) { u32 val = I915_READ(reg); - if (HDMI_PIPE_ENABLED(val, pipe)) + if (hdmi_pipe_enabled(dev_priv, pipe, val)) { + DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n", + reg, pipe); I915_WRITE(reg, val & ~PORT_ENABLE); + } } /* Disable any ports connected to this transcoder */ @@ -1358,18 +1472,19 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv, val = I915_READ(PCH_PP_CONTROL); I915_WRITE(PCH_PP_CONTROL, val | PANEL_UNLOCK_REGS); - disable_pch_dp(dev_priv, pipe, PCH_DP_B); - disable_pch_dp(dev_priv, pipe, PCH_DP_C); - disable_pch_dp(dev_priv, pipe, PCH_DP_D); + disable_pch_dp(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B); + disable_pch_dp(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C); + disable_pch_dp(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D); reg = PCH_ADPA; val = I915_READ(reg); - if (ADPA_PIPE_ENABLED(val, pipe)) + if (adpa_pipe_enabled(dev_priv, pipe, val)) I915_WRITE(reg, val & ~ADPA_DAC_ENABLE); reg = PCH_LVDS; val = I915_READ(reg); - if (LVDS_PIPE_ENABLED(val, pipe)) { + if (lvds_pipe_enabled(dev_priv, pipe, val)) { + DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val); I915_WRITE(reg, val & ~LVDS_PORT_EN); POSTING_READ(reg); udelay(100); @@ -1380,6 +1495,28 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv, disable_pch_hdmi(dev_priv, pipe, HDMID); } +static void i8xx_disable_fbc(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 fbc_ctl; + + /* Disable compression */ + fbc_ctl = I915_READ(FBC_CONTROL); + if ((fbc_ctl & FBC_CTL_EN) == 0) + return; + + fbc_ctl &= ~FBC_CTL_EN; + I915_WRITE(FBC_CONTROL, fbc_ctl); + + /* Wait for compressing bit to clear */ + if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) { + DRM_DEBUG_KMS("FBC idle timed out\n"); + return; + } + + DRM_DEBUG_KMS("disabled FBC\n"); +} + static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { struct drm_device *dev = crtc->dev; @@ -1388,36 +1525,25 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct drm_i915_gem_object *obj = intel_fb->obj; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int cfb_pitch; int plane, i; u32 fbc_ctl, fbc_ctl2; - if (fb->pitch == dev_priv->cfb_pitch && - obj->fence_reg == dev_priv->cfb_fence && - intel_crtc->plane == dev_priv->cfb_plane && - I915_READ(FBC_CONTROL) & FBC_CTL_EN) - return; - - i8xx_disable_fbc(dev); - - dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; - - if (fb->pitch < dev_priv->cfb_pitch) - dev_priv->cfb_pitch = fb->pitch; + cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; + if (fb->pitch < cfb_pitch) + cfb_pitch = fb->pitch; /* FBC_CTL wants 64B units */ - dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; - dev_priv->cfb_fence = obj->fence_reg; - dev_priv->cfb_plane = intel_crtc->plane; - plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB; + cfb_pitch = (cfb_pitch / 64) - 1; + plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB; /* Clear old tags */ for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++) I915_WRITE(FBC_TAG + (i * 4), 0); /* Set it up... */ - fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane; - if (obj->tiling_mode != I915_TILING_NONE) - fbc_ctl2 |= FBC_CTL_CPU_FENCE; + fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE; + fbc_ctl2 |= plane; I915_WRITE(FBC_CONTROL2, fbc_ctl2); I915_WRITE(FBC_FENCE_OFF, crtc->y); @@ -1425,36 +1551,13 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC; if (IS_I945GM(dev)) fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */ - fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; + fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT; - if (obj->tiling_mode != I915_TILING_NONE) - fbc_ctl |= dev_priv->cfb_fence; - I915_WRITE(FBC_CONTROL, fbc_ctl); - - DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ", - dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane); -} - -void i8xx_disable_fbc(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 fbc_ctl; - - /* Disable compression */ - fbc_ctl = I915_READ(FBC_CONTROL); - if ((fbc_ctl & FBC_CTL_EN) == 0) - return; - - fbc_ctl &= ~FBC_CTL_EN; + fbc_ctl |= obj->fence_reg; I915_WRITE(FBC_CONTROL, fbc_ctl); - /* Wait for compressing bit to clear */ - if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) { - DRM_DEBUG_KMS("FBC idle timed out\n"); - return; - } - - DRM_DEBUG_KMS("disabled FBC\n"); + DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ", + cfb_pitch, crtc->y, intel_crtc->plane); } static bool i8xx_fbc_enabled(struct drm_device *dev) @@ -1476,30 +1579,9 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) unsigned long stall_watermark = 200; u32 dpfc_ctl; - dpfc_ctl = I915_READ(DPFC_CONTROL); - if (dpfc_ctl & DPFC_CTL_EN) { - if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 && - dev_priv->cfb_fence == obj->fence_reg && - dev_priv->cfb_plane == intel_crtc->plane && - dev_priv->cfb_y == crtc->y) - return; - - I915_WRITE(DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN); - intel_wait_for_vblank(dev, intel_crtc->pipe); - } - - dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; - dev_priv->cfb_fence = obj->fence_reg; - dev_priv->cfb_plane = intel_crtc->plane; - dev_priv->cfb_y = crtc->y; - dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X; - if (obj->tiling_mode != I915_TILING_NONE) { - dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence; - I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); - } else { - I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY); - } + dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg; + I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | @@ -1512,7 +1594,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); } -void g4x_disable_fbc(struct drm_device *dev) +static void g4x_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 dpfc_ctl; @@ -1567,32 +1649,12 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) u32 dpfc_ctl; dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); - if (dpfc_ctl & DPFC_CTL_EN) { - if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 && - dev_priv->cfb_fence == obj->fence_reg && - dev_priv->cfb_plane == intel_crtc->plane && - dev_priv->cfb_offset == obj->gtt_offset && - dev_priv->cfb_y == crtc->y) - return; - - I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN); - intel_wait_for_vblank(dev, intel_crtc->pipe); - } - - dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; - dev_priv->cfb_fence = obj->fence_reg; - dev_priv->cfb_plane = intel_crtc->plane; - dev_priv->cfb_offset = obj->gtt_offset; - dev_priv->cfb_y = crtc->y; - dpfc_ctl &= DPFC_RESERVED; dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X); - if (obj->tiling_mode != I915_TILING_NONE) { - dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence); - I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY); - } else { - I915_WRITE(ILK_DPFC_CHICKEN, ~DPFC_HT_MODIFY); - } + /* Set persistent mode for front-buffer rendering, ala X. */ + dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE; + dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg); + I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY); I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | @@ -1604,7 +1666,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) if (IS_GEN6(dev)) { I915_WRITE(SNB_DPFC_CTL_SA, - SNB_CPU_FENCE_ENABLE | dev_priv->cfb_fence); + SNB_CPU_FENCE_ENABLE | obj->fence_reg); I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y); sandybridge_blit_fbc_update(dev); } @@ -1612,7 +1674,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); } -void ironlake_disable_fbc(struct drm_device *dev) +static void ironlake_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 dpfc_ctl; @@ -1644,24 +1706,109 @@ bool intel_fbc_enabled(struct drm_device *dev) return dev_priv->display.fbc_enabled(dev); } -void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) +static void intel_fbc_work_fn(struct work_struct *__work) +{ + struct intel_fbc_work *work = + container_of(to_delayed_work(__work), + struct intel_fbc_work, work); + struct drm_device *dev = work->crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + mutex_lock(&dev->struct_mutex); + if (work == dev_priv->fbc_work) { + /* Double check that we haven't switched fb without cancelling + * the prior work. + */ + if (work->crtc->fb == work->fb) { + dev_priv->display.enable_fbc(work->crtc, + work->interval); + + dev_priv->cfb_plane = to_intel_crtc(work->crtc)->plane; + dev_priv->cfb_fb = work->crtc->fb->base.id; + dev_priv->cfb_y = work->crtc->y; + } + + dev_priv->fbc_work = NULL; + } + mutex_unlock(&dev->struct_mutex); + + kfree(work); +} + +static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv) +{ + if (dev_priv->fbc_work == NULL) + return; + + DRM_DEBUG_KMS("cancelling pending FBC enable\n"); + + /* Synchronisation is provided by struct_mutex and checking of + * dev_priv->fbc_work, so we can perform the cancellation + * entirely asynchronously. + */ + if (cancel_delayed_work(&dev_priv->fbc_work->work)) + /* tasklet was killed before being run, clean up */ + kfree(dev_priv->fbc_work); + + /* Mark the work as no longer wanted so that if it does + * wake-up (because the work was already running and waiting + * for our mutex), it will discover that is no longer + * necessary to run. + */ + dev_priv->fbc_work = NULL; +} + +static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { - struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_fbc_work *work; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; if (!dev_priv->display.enable_fbc) return; - dev_priv->display.enable_fbc(crtc, interval); + intel_cancel_fbc_work(dev_priv); + + work = kzalloc(sizeof *work, GFP_KERNEL); + if (work == NULL) { + dev_priv->display.enable_fbc(crtc, interval); + return; + } + + work->crtc = crtc; + work->fb = crtc->fb; + work->interval = interval; + INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn); + + dev_priv->fbc_work = work; + + DRM_DEBUG_KMS("scheduling delayed FBC enable\n"); + + /* Delay the actual enabling to let pageflipping cease and the + * display to settle before starting the compression. Note that + * this delay also serves a second purpose: it allows for a + * vblank to pass after disabling the FBC before we attempt + * to modify the control registers. + * + * A more complicated solution would involve tracking vblanks + * following the termination of the page-flipping sequence + * and indeed performing the enable as a co-routine and not + * waiting synchronously upon the vblank. + */ + schedule_delayed_work(&work->work, msecs_to_jiffies(50)); } void intel_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + intel_cancel_fbc_work(dev_priv); + if (!dev_priv->display.disable_fbc) return; dev_priv->display.disable_fbc(dev); + dev_priv->cfb_plane = -1; } /** @@ -1691,6 +1838,7 @@ static void intel_update_fbc(struct drm_device *dev) struct drm_framebuffer *fb; struct intel_framebuffer *intel_fb; struct drm_i915_gem_object *obj; + int enable_fbc; DRM_DEBUG_KMS("\n"); @@ -1731,8 +1879,15 @@ static void intel_update_fbc(struct drm_device *dev) intel_fb = to_intel_framebuffer(fb); obj = intel_fb->obj; - if (!i915_enable_fbc) { - DRM_DEBUG_KMS("fbc disabled per module param (default off)\n"); + enable_fbc = i915_enable_fbc; + if (enable_fbc < 0) { + DRM_DEBUG_KMS("fbc set to per-chip default\n"); + enable_fbc = 1; + if (INTEL_INFO(dev)->gen <= 6) + enable_fbc = 0; + } + if (!enable_fbc) { + DRM_DEBUG_KMS("fbc disabled per module param\n"); dev_priv->no_fbc_reason = FBC_MODULE_PARAM; goto out_disable; } @@ -1760,8 +1915,13 @@ static void intel_update_fbc(struct drm_device *dev) dev_priv->no_fbc_reason = FBC_BAD_PLANE; goto out_disable; } - if (obj->tiling_mode != I915_TILING_X) { - DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n"); + + /* The use of a CPU fence is mandatory in order to detect writes + * by the CPU to the scanout and trigger updates to the FBC. + */ + if (obj->tiling_mode != I915_TILING_X || + obj->fence_reg == I915_FENCE_REG_NONE) { + DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n"); dev_priv->no_fbc_reason = FBC_NOT_TILED; goto out_disable; } @@ -1770,6 +1930,44 @@ static void intel_update_fbc(struct drm_device *dev) if (in_dbg_master()) goto out_disable; + /* If the scanout has not changed, don't modify the FBC settings. + * Note that we make the fundamental assumption that the fb->obj + * cannot be unpinned (and have its GTT offset and fence revoked) + * without first being decoupled from the scanout and FBC disabled. + */ + if (dev_priv->cfb_plane == intel_crtc->plane && + dev_priv->cfb_fb == fb->base.id && + dev_priv->cfb_y == crtc->y) + return; + + if (intel_fbc_enabled(dev)) { + /* We update FBC along two paths, after changing fb/crtc + * configuration (modeswitching) and after page-flipping + * finishes. For the latter, we know that not only did + * we disable the FBC at the start of the page-flip + * sequence, but also more than one vblank has passed. + * + * For the former case of modeswitching, it is possible + * to switch between two FBC valid configurations + * instantaneously so we do need to disable the FBC + * before we can modify its control registers. We also + * have to wait for the next vblank for that to take + * effect. However, since we delay enabling FBC we can + * assume that a vblank has passed since disabling and + * that we can safely alter the registers in the deferred + * callback. + * + * In the scenario that we go from a valid to invalid + * and then back to valid FBC configuration we have + * no strict enforcement that a vblank occurred since + * disabling the FBC. However, along all current pipe + * disabling paths we do need to wait for a vblank at + * some point. And we wait before enabling FBC anyway. + */ + DRM_DEBUG_KMS("disabling active FBC for update\n"); + intel_disable_fbc(dev); + } + intel_enable_fbc(crtc, 500); return; @@ -1812,14 +2010,10 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, } dev_priv->mm.interruptible = false; - ret = i915_gem_object_pin(obj, alignment, true); + ret = i915_gem_object_pin_to_display_plane(obj, alignment, pipelined); if (ret) goto err_interruptible; - ret = i915_gem_object_set_to_display_plane(obj, pipelined); - if (ret) - goto err_unpin; - /* Install a fence for tiled scan-out. Pre-i965 always needs a * fence, whereas 965+ only requires a fence if using * framebuffer compression. For simplicity, we always install @@ -1841,10 +2035,8 @@ err_interruptible: return ret; } -/* Assume fb object is pinned & idle & fenced and just update base pointers */ -static int -intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, - int x, int y, enum mode_set_atomic state) +static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1887,7 +2079,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, dspcntr |= DISPPLANE_32BPP_NO_ALPHA; break; default: - DRM_ERROR("Unknown color depth\n"); + DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel); return -EINVAL; } if (INTEL_INFO(dev)->gen >= 4) { @@ -1897,10 +2089,6 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, dspcntr &= ~DISPPLANE_TILED; } - if (HAS_PCH_SPLIT(dev)) - /* must disable */ - dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - I915_WRITE(reg, dspcntr); Start = obj->gtt_offset; @@ -1917,6 +2105,100 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, I915_WRITE(DSPADDR(plane), Start + Offset); POSTING_READ(reg); + return 0; +} + +static int ironlake_update_plane(struct drm_crtc *crtc, + struct drm_framebuffer *fb, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_framebuffer *intel_fb; + struct drm_i915_gem_object *obj; + int plane = intel_crtc->plane; + unsigned long Start, Offset; + u32 dspcntr; + u32 reg; + + switch (plane) { + case 0: + case 1: + case 2: + break; + default: + DRM_ERROR("Can't update plane %d in SAREA\n", plane); + return -EINVAL; + } + + intel_fb = to_intel_framebuffer(fb); + obj = intel_fb->obj; + + reg = DSPCNTR(plane); + dspcntr = I915_READ(reg); + /* Mask out pixel format bits in case we change it */ + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + switch (fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (fb->depth != 16) + return -EINVAL; + + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + if (fb->depth == 24) + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + else if (fb->depth == 30) + dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA; + else + return -EINVAL; + break; + default: + DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel); + return -EINVAL; + } + + if (obj->tiling_mode != I915_TILING_NONE) + dspcntr |= DISPPLANE_TILED; + else + dspcntr &= ~DISPPLANE_TILED; + + /* must disable */ + dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; + + I915_WRITE(reg, dspcntr); + + Start = obj->gtt_offset; + Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8); + + DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", + Start, Offset, x, y, fb->pitch); + I915_WRITE(DSPSTRIDE(plane), fb->pitch); + I915_WRITE(DSPSURF(plane), Start); + I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); + I915_WRITE(DSPADDR(plane), Offset); + POSTING_READ(reg); + + return 0; +} + +/* Assume fb object is pinned & idle & fenced and just update base pointers */ +static int +intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y, enum mode_set_atomic state) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = dev_priv->display.update_plane(crtc, fb, x, y); + if (ret) + return ret; + intel_update_fbc(dev); intel_increase_pllclock(crtc); @@ -1924,6 +2206,33 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, } static int +intel_finish_fb(struct drm_framebuffer *old_fb) +{ + struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj; + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + bool was_interruptible = dev_priv->mm.interruptible; + int ret; + + wait_event(dev_priv->pending_flip_queue, + atomic_read(&dev_priv->mm.wedged) || + atomic_read(&obj->pending_flip) == 0); + + /* Big Hammer, we also need to ensure that any pending + * MI_WAIT_FOR_EVENT inside a user batch buffer on the + * current scanout is retired before unpinning the old + * framebuffer. + * + * This should only fail upon a hung GPU, in which case we + * can safely continue. + */ + dev_priv->mm.interruptible = false; + ret = i915_gem_object_finish_gpu(obj); + dev_priv->mm.interruptible = was_interruptible; + + return ret; +} + +static int intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { @@ -1934,7 +2243,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, /* no fb bound */ if (!crtc->fb) { - DRM_DEBUG_KMS("No FB bound\n"); + DRM_ERROR("No FB bound\n"); return 0; } @@ -1942,7 +2251,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, case 0: case 1: break; + case 2: + if (IS_IVYBRIDGE(dev)) + break; + /* fall through otherwise */ default: + DRM_ERROR("no plane for crtc\n"); return -EINVAL; } @@ -1952,34 +2266,19 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, NULL); if (ret != 0) { mutex_unlock(&dev->struct_mutex); + DRM_ERROR("pin & fence failed\n"); return ret; } - if (old_fb) { - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj; - - wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->mm.wedged) || - atomic_read(&obj->pending_flip) == 0); - - /* Big Hammer, we also need to ensure that any pending - * MI_WAIT_FOR_EVENT inside a user batch buffer on the - * current scanout is retired before unpinning the old - * framebuffer. - * - * This should only fail upon a hung GPU, in which case we - * can safely continue. - */ - ret = i915_gem_object_flush_gpu(obj); - (void) ret; - } + if (old_fb) + intel_finish_fb(old_fb); ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y, LEAVE_ATOMIC_MODE_SET); if (ret) { i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj); mutex_unlock(&dev->struct_mutex); + DRM_ERROR("failed to update base address\n"); return ret; } @@ -2129,11 +2428,9 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) udelay(150); /* Ironlake workaround, enable clock pointer after FDI enable*/ - if (HAS_PCH_IBX(dev)) { - I915_WRITE(FDI_RX_CHICKEN(pipe), FDI_RX_PHASE_SYNC_POINTER_OVR); - I915_WRITE(FDI_RX_CHICKEN(pipe), FDI_RX_PHASE_SYNC_POINTER_OVR | - FDI_RX_PHASE_SYNC_POINTER_EN); - } + I915_WRITE(FDI_RX_CHICKEN(pipe), FDI_RX_PHASE_SYNC_POINTER_OVR); + I915_WRITE(FDI_RX_CHICKEN(pipe), FDI_RX_PHASE_SYNC_POINTER_OVR | + FDI_RX_PHASE_SYNC_POINTER_EN); reg = FDI_RX_IIR(pipe); for (tries = 0; tries < 5; tries++) { @@ -2183,7 +2480,7 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) } -static const int snb_b_fdi_train_param [] = { +static const int snb_b_fdi_train_param[] = { FDI_LINK_TRAIN_400MV_0DB_SNB_B, FDI_LINK_TRAIN_400MV_6DB_SNB_B, FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, @@ -2236,7 +2533,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) POSTING_READ(reg); udelay(150); - for (i = 0; i < 4; i++ ) { + for (i = 0; i < 4; i++) { reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2285,7 +2582,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) POSTING_READ(reg); udelay(150); - for (i = 0; i < 4; i++ ) { + for (i = 0; i < 4; i++) { reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2354,7 +2651,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) POSTING_READ(reg); udelay(150); - for (i = 0; i < 4; i++ ) { + for (i = 0; i < 4; i++) { reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2396,7 +2693,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) POSTING_READ(reg); udelay(150); - for (i = 0; i < 4; i++ ) { + for (i = 0; i < 4; i++) { reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2539,18 +2836,37 @@ static void intel_clear_scanline_wait(struct drm_device *dev) I915_WRITE_CTL(ring, tmp); } +static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long flags; + bool pending; + + if (atomic_read(&dev_priv->mm.wedged)) + return false; + + spin_lock_irqsave(&dev->event_lock, flags); + pending = to_intel_crtc(crtc)->unpin_work != NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + + return pending; +} + static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) { - struct drm_i915_gem_object *obj; - struct drm_i915_private *dev_priv; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; if (crtc->fb == NULL) return; - obj = to_intel_framebuffer(crtc->fb)->obj; - dev_priv = crtc->dev->dev_private; wait_event(dev_priv->pending_flip_queue, - atomic_read(&obj->pending_flip) == 0); + !intel_crtc_has_pending_flip(crtc)); + + mutex_lock(&dev->struct_mutex); + intel_finish_fb(crtc->fb); + mutex_unlock(&dev->struct_mutex); } static bool intel_crtc_driving_pch(struct drm_crtc *crtc) @@ -2592,7 +2908,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - u32 reg, temp; + u32 reg, temp, transc_sel; /* For PCH output, training FDI link */ dev_priv->display.fdi_link_train(crtc); @@ -2600,12 +2916,21 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) intel_enable_pch_pll(dev_priv, pipe); if (HAS_PCH_CPT(dev)) { + transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL : + TRANSC_DPLLB_SEL; + /* Be sure PCH DPLL SEL is set */ temp = I915_READ(PCH_DPLL_SEL); - if (pipe == 0 && (temp & TRANSA_DPLL_ENABLE) == 0) + if (pipe == 0) { + temp &= ~(TRANSA_DPLLB_SEL); temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); - else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0) + } else if (pipe == 1) { + temp &= ~(TRANSB_DPLLB_SEL); temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); + } else if (pipe == 2) { + temp &= ~(TRANSC_DPLLB_SEL); + temp |= (TRANSC_DPLL_ENABLE | transc_sel); + } I915_WRITE(PCH_DPLL_SEL, temp); } @@ -2623,7 +2948,9 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) /* For PCH DP, enable TRANS_DP_CTL */ if (HAS_PCH_CPT(dev) && - intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || + intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) { + u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5; reg = TRANS_DP_CTL(pipe); temp = I915_READ(reg); temp &= ~(TRANS_DP_PORT_SEL_MASK | @@ -2631,7 +2958,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) TRANS_DP_BPC_MASK); temp |= (TRANS_DP_OUTPUT_ENABLE | TRANS_DP_ENH_FRAMING); - temp |= TRANS_DP_8BPC; + temp |= bpc << 9; /* same format but at 11:9 */ if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC) temp |= TRANS_DP_HSYNC_ACTIVE_HIGH; @@ -2660,6 +2987,24 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) intel_enable_transcoder(dev_priv, pipe); } +void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int dslreg = PIPEDSL(pipe), tc2reg = TRANS_CHICKEN2(pipe); + u32 temp; + + temp = I915_READ(dslreg); + udelay(500); + if (wait_for(I915_READ(dslreg) != temp, 5)) { + /* Without this, mode sets may fail silently on FDI */ + I915_WRITE(tc2reg, TRANS_AUTOTRAIN_GEN_STALL_DIS); + udelay(250); + I915_WRITE(tc2reg, 0); + if (wait_for(I915_READ(dslreg) != temp, 5)) + DRM_ERROR("mode set failed: pipe %d stuck\n", pipe); + } +} + static void ironlake_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -2742,9 +3087,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_disable_plane(dev_priv, plane, pipe); - if (dev_priv->cfb_plane == plane && - dev_priv->display.disable_fbc) - dev_priv->display.disable_fbc(dev); + if (dev_priv->cfb_plane == plane) + intel_disable_fbc(dev); intel_disable_pipe(dev_priv, pipe); @@ -2775,13 +3119,13 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) temp = I915_READ(PCH_DPLL_SEL); switch (pipe) { case 0: - temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); + temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); break; case 1: temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); break; case 2: - /* FIXME: manage transcoder PLLs? */ + /* C shares PLL A or B */ temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); break; default: @@ -2791,7 +3135,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) } /* disable PCH DPLL */ - intel_disable_pch_pll(dev_priv, pipe); + if (!intel_crtc->no_pll) + intel_disable_pch_pll(dev_priv, pipe); /* Switch from PCDclk to Rawclk */ reg = FDI_RX_CTL(pipe); @@ -2909,9 +3254,8 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) intel_crtc_dpms_overlay(intel_crtc, false); intel_crtc_update_cursor(crtc, false); - if (dev_priv->cfb_plane == plane && - dev_priv->display.disable_fbc) - dev_priv->display.disable_fbc(dev); + if (dev_priv->cfb_plane == plane) + intel_disable_fbc(dev); intel_disable_plane(dev_priv, plane, pipe); intel_disable_pipe(dev_priv, pipe); @@ -3032,18 +3376,25 @@ static void ironlake_crtc_commit(struct drm_crtc *crtc) ironlake_crtc_enable(crtc); } -void intel_encoder_prepare (struct drm_encoder *encoder) +void intel_encoder_prepare(struct drm_encoder *encoder) { struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; /* lvds has its own version of prepare see intel_lvds_prepare */ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); } -void intel_encoder_commit (struct drm_encoder *encoder) +void intel_encoder_commit(struct drm_encoder *encoder) { struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + struct drm_device *dev = encoder->dev; + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + struct intel_crtc *intel_crtc = to_intel_crtc(intel_encoder->base.crtc); + /* lvds has its own version of commit see intel_lvds_commit */ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); + + if (HAS_PCH_CPT(dev)) + intel_cpt_verify_modeset(dev, intel_crtc->pipe); } void intel_encoder_destroy(struct drm_encoder *encoder) @@ -4217,6 +4568,20 @@ static void sandybridge_update_wm(struct drm_device *dev) enabled |= 2; } + /* IVB has 3 pipes */ + if (IS_IVYBRIDGE(dev) && + g4x_compute_wm0(dev, 2, + &sandybridge_display_wm_info, latency, + &sandybridge_cursor_wm_info, latency, + &plane_wm, &cursor_wm)) { + I915_WRITE(WM0_PIPEC_IVB, + (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); + DRM_DEBUG_KMS("FIFO watermarks For pipe C -" + " plane %d, cursor: %d\n", + plane_wm, cursor_wm); + enabled |= 3; + } + /* * Calculate and update the self-refresh watermark only when one * display plane is used. @@ -4323,10 +4688,151 @@ static void intel_update_watermarks(struct drm_device *dev) static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) { - return dev_priv->lvds_use_ssc && i915_panel_use_ssc + if (i915_panel_use_ssc >= 0) + return i915_panel_use_ssc != 0; + return dev_priv->lvds_use_ssc && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); } +/** + * intel_choose_pipe_bpp_dither - figure out what color depth the pipe should send + * @crtc: CRTC structure + * @mode: requested mode + * + * A pipe may be connected to one or more outputs. Based on the depth of the + * attached framebuffer, choose a good color depth to use on the pipe. + * + * If possible, match the pipe depth to the fb depth. In some cases, this + * isn't ideal, because the connected output supports a lesser or restricted + * set of depths. Resolve that here: + * LVDS typically supports only 6bpc, so clamp down in that case + * HDMI supports only 8bpc or 12bpc, so clamp to 8bpc with dither for 10bpc + * Displays may support a restricted set as well, check EDID and clamp as + * appropriate. + * DP may want to dither down to 6bpc to fit larger modes + * + * RETURNS: + * Dithering requirement (i.e. false if display bpc and pipe bpc match, + * true if they don't match). + */ +static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, + unsigned int *pipe_bpp, + struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_encoder *encoder; + struct drm_connector *connector; + unsigned int display_bpc = UINT_MAX, bpc; + + /* Walk the encoders & connectors on this crtc, get min bpc */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + + if (encoder->crtc != crtc) + continue; + + if (intel_encoder->type == INTEL_OUTPUT_LVDS) { + unsigned int lvds_bpc; + + if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == + LVDS_A3_POWER_UP) + lvds_bpc = 8; + else + lvds_bpc = 6; + + if (lvds_bpc < display_bpc) { + DRM_DEBUG_KMS("clamping display bpc (was %d) to LVDS (%d)\n", display_bpc, lvds_bpc); + display_bpc = lvds_bpc; + } + continue; + } + + /* Not one of the known troublemakers, check the EDID */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + if (connector->encoder != encoder) + continue; + + /* Don't use an invalid EDID bpc value */ + if (connector->display_info.bpc && + connector->display_info.bpc < display_bpc) { + DRM_DEBUG_KMS("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc); + display_bpc = connector->display_info.bpc; + } + } + + if (intel_encoder->type == INTEL_OUTPUT_EDP) { + /* Use VBT settings if we have an eDP panel */ + unsigned int edp_bpc = dev_priv->edp.bpp / 3; + + if (edp_bpc && edp_bpc < display_bpc) { + DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc); + display_bpc = edp_bpc; + } + continue; + } + + /* + * HDMI is either 12 or 8, so if the display lets 10bpc sneak + * through, clamp it down. (Note: >12bpc will be caught below.) + */ + if (intel_encoder->type == INTEL_OUTPUT_HDMI) { + if (display_bpc > 8 && display_bpc < 12) { + DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n"); + display_bpc = 12; + } else { + DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n"); + display_bpc = 8; + } + } + } + + if (mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { + DRM_DEBUG_KMS("Dithering DP to 6bpc\n"); + display_bpc = 6; + } + + /* + * We could just drive the pipe at the highest bpc all the time and + * enable dithering as needed, but that costs bandwidth. So choose + * the minimum value that expresses the full color range of the fb but + * also stays within the max display bpc discovered above. + */ + + switch (crtc->fb->depth) { + case 8: + bpc = 8; /* since we go through a colormap */ + break; + case 15: + case 16: + bpc = 6; /* min is 18bpp */ + break; + case 24: + bpc = 8; + break; + case 30: + bpc = 10; + break; + case 48: + bpc = 12; + break; + default: + DRM_DEBUG("unsupported depth, assuming 24 bits\n"); + bpc = min((unsigned int)8, display_bpc); + break; + } + + display_bpc = min(display_bpc, bpc); + + DRM_DEBUG_KMS("setting pipe bpc to %d (max display bpc %d)\n", + bpc, display_bpc); + + *pipe_bpp = display_bpc * 3; + + return display_bpc != bpc; +} + static int i9xx_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -4548,6 +5054,16 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, pipeconf &= ~PIPECONF_DOUBLE_WIDE; } + /* default to 8bpc */ + pipeconf &= ~(PIPECONF_BPP_MASK | PIPECONF_DITHER_EN); + if (is_dp) { + if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { + pipeconf |= PIPECONF_BPP_6 | + PIPECONF_DITHER_EN | + PIPECONF_DITHER_TYPE_SP; + } + } + dpll |= DPLL_VCO_ENABLE; DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); @@ -4715,6 +5231,156 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, return ret; } +/* + * Initialize reference clocks when the driver loads + */ +void ironlake_init_pch_refclk(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_mode_config *mode_config = &dev->mode_config; + struct intel_encoder *encoder; + u32 temp; + bool has_lvds = false; + bool has_cpu_edp = false; + bool has_pch_edp = false; + bool has_panel = false; + bool has_ck505 = false; + bool can_ssc = false; + + /* We need to take the global config into account */ + list_for_each_entry(encoder, &mode_config->encoder_list, + base.head) { + switch (encoder->type) { + case INTEL_OUTPUT_LVDS: + has_panel = true; + has_lvds = true; + break; + case INTEL_OUTPUT_EDP: + has_panel = true; + if (intel_encoder_is_pch_edp(&encoder->base)) + has_pch_edp = true; + else + has_cpu_edp = true; + break; + } + } + + if (HAS_PCH_IBX(dev)) { + has_ck505 = dev_priv->display_clock_mode; + can_ssc = has_ck505; + } else { + has_ck505 = false; + can_ssc = true; + } + + DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n", + has_panel, has_lvds, has_pch_edp, has_cpu_edp, + has_ck505); + + /* Ironlake: try to setup display ref clock before DPLL + * enabling. This is only under driver's control after + * PCH B stepping, previous chipset stepping should be + * ignoring this setting. + */ + temp = I915_READ(PCH_DREF_CONTROL); + /* Always enable nonspread source */ + temp &= ~DREF_NONSPREAD_SOURCE_MASK; + + if (has_ck505) + temp |= DREF_NONSPREAD_CK505_ENABLE; + else + temp |= DREF_NONSPREAD_SOURCE_ENABLE; + + if (has_panel) { + temp &= ~DREF_SSC_SOURCE_MASK; + temp |= DREF_SSC_SOURCE_ENABLE; + + /* SSC must be turned on before enabling the CPU output */ + if (intel_panel_use_ssc(dev_priv) && can_ssc) { + DRM_DEBUG_KMS("Using SSC on panel\n"); + temp |= DREF_SSC1_ENABLE; + } + + /* Get SSC going before enabling the outputs */ + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + udelay(200); + + temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; + + /* Enable CPU source on CPU attached eDP */ + if (has_cpu_edp) { + if (intel_panel_use_ssc(dev_priv) && can_ssc) { + DRM_DEBUG_KMS("Using SSC on eDP\n"); + temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; + } + else + temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; + } else + temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + udelay(200); + } else { + DRM_DEBUG_KMS("Disabling SSC entirely\n"); + + temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; + + /* Turn off CPU output */ + temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + udelay(200); + + /* Turn off the SSC source */ + temp &= ~DREF_SSC_SOURCE_MASK; + temp |= DREF_SSC_SOURCE_DISABLE; + + /* Turn off SSC1 */ + temp &= ~ DREF_SSC1_ENABLE; + + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + udelay(200); + } +} + +static int ironlake_get_refclk(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *encoder; + struct drm_mode_config *mode_config = &dev->mode_config; + struct intel_encoder *edp_encoder = NULL; + int num_connectors = 0; + bool is_lvds = false; + + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { + if (encoder->base.crtc != crtc) + continue; + + switch (encoder->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_EDP: + edp_encoder = encoder; + break; + } + num_connectors++; + } + + if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { + DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", + dev_priv->lvds_ssc_freq); + return dev_priv->lvds_ssc_freq * 1000; + } + + return 120000; +} + static int ironlake_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -4739,7 +5405,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, struct fdi_m_n m_n = {0}; u32 temp; u32 lvds_sync = 0; - int target_clock, pixel_multiplier, lane, link_bw, bpp, factor; + int target_clock, pixel_multiplier, lane, link_bw, factor; + unsigned int pipe_bpp; + bool dither; list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { if (encoder->base.crtc != crtc) @@ -4772,16 +5440,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, num_connectors++; } - if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { - refclk = dev_priv->lvds_ssc_freq * 1000; - DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", - refclk / 1000); - } else { - refclk = 96000; - if (!has_edp_encoder || - intel_encoder_is_pch_edp(&has_edp_encoder->base)) - refclk = 120000; /* 120Mhz refclk */ - } + refclk = ironlake_get_refclk(crtc); /* * Returns a set of divisors for the desired target clock with the given @@ -4866,56 +5525,38 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* determine panel color depth */ temp = I915_READ(PIPECONF(pipe)); temp &= ~PIPE_BPC_MASK; - if (is_lvds) { - /* the BPC will be 6 if it is 18-bit LVDS panel */ - if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP) - temp |= PIPE_8BPC; - else - temp |= PIPE_6BPC; - } else if (has_edp_encoder) { - switch (dev_priv->edp.bpp/3) { - case 8: - temp |= PIPE_8BPC; - break; - case 10: - temp |= PIPE_10BPC; - break; - case 6: - temp |= PIPE_6BPC; - break; - case 12: - temp |= PIPE_12BPC; - break; - } - } else - temp |= PIPE_8BPC; - I915_WRITE(PIPECONF(pipe), temp); - - switch (temp & PIPE_BPC_MASK) { - case PIPE_8BPC: - bpp = 24; + dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp, adjusted_mode); + switch (pipe_bpp) { + case 18: + temp |= PIPE_6BPC; break; - case PIPE_10BPC: - bpp = 30; + case 24: + temp |= PIPE_8BPC; break; - case PIPE_6BPC: - bpp = 18; + case 30: + temp |= PIPE_10BPC; break; - case PIPE_12BPC: - bpp = 36; + case 36: + temp |= PIPE_12BPC; break; default: - DRM_ERROR("unknown pipe bpc value\n"); - bpp = 24; + WARN(1, "intel_choose_pipe_bpp returned invalid value %d\n", + pipe_bpp); + temp |= PIPE_8BPC; + pipe_bpp = 24; + break; } + intel_crtc->bpp = pipe_bpp; + I915_WRITE(PIPECONF(pipe), temp); + if (!lane) { /* * Account for spread spectrum to avoid * oversubscribing the link. Max center spread * is 2.5%; use 5% for safety's sake. */ - u32 bps = target_clock * bpp * 21 / 20; + u32 bps = target_clock * intel_crtc->bpp * 21 / 20; lane = bps / (link_bw * 8) + 1; } @@ -4923,51 +5564,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (pixel_multiplier > 1) link_bw *= pixel_multiplier; - ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n); - - /* Ironlake: try to setup display ref clock before DPLL - * enabling. This is only under driver's control after - * PCH B stepping, previous chipset stepping should be - * ignoring this setting. - */ - temp = I915_READ(PCH_DREF_CONTROL); - /* Always enable nonspread source */ - temp &= ~DREF_NONSPREAD_SOURCE_MASK; - temp |= DREF_NONSPREAD_SOURCE_ENABLE; - temp &= ~DREF_SSC_SOURCE_MASK; - temp |= DREF_SSC_SOURCE_ENABLE; - I915_WRITE(PCH_DREF_CONTROL, temp); - - POSTING_READ(PCH_DREF_CONTROL); - udelay(200); - - if (has_edp_encoder) { - if (intel_panel_use_ssc(dev_priv)) { - temp |= DREF_SSC1_ENABLE; - I915_WRITE(PCH_DREF_CONTROL, temp); - - POSTING_READ(PCH_DREF_CONTROL); - udelay(200); - } - temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; - - /* Enable CPU source on CPU attached eDP */ - if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) { - if (intel_panel_use_ssc(dev_priv)) - temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; - else - temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; - } else { - /* Enable SSC on PCH eDP if needed */ - if (intel_panel_use_ssc(dev_priv)) { - DRM_ERROR("enabling SSC on PCH\n"); - temp |= DREF_SUPERSPREAD_SOURCE_ENABLE; - } - } - I915_WRITE(PCH_DREF_CONTROL, temp); - POSTING_READ(PCH_DREF_CONTROL); - udelay(200); - } + ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, + &m_n); fp = clock.n << 16 | clock.m1 << 8 | clock.m2; if (has_reduced_clock) @@ -5040,39 +5638,32 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); /* PCH eDP needs FDI, but CPU eDP does not */ - if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) { - I915_WRITE(PCH_FP0(pipe), fp); - I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); - - POSTING_READ(PCH_DPLL(pipe)); - udelay(150); - } + if (!intel_crtc->no_pll) { + if (!has_edp_encoder || + intel_encoder_is_pch_edp(&has_edp_encoder->base)) { + I915_WRITE(PCH_FP0(pipe), fp); + I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); - /* enable transcoder DPLL */ - if (HAS_PCH_CPT(dev)) { - temp = I915_READ(PCH_DPLL_SEL); - switch (pipe) { - case 0: - temp |= TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL; - break; - case 1: - temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL; - break; - case 2: - /* FIXME: manage transcoder PLLs? */ - temp |= TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL; - break; - default: - BUG(); + POSTING_READ(PCH_DPLL(pipe)); + udelay(150); + } + } else { + if (dpll == (I915_READ(PCH_DPLL(0)) & 0x7fffffff) && + fp == I915_READ(PCH_FP0(0))) { + intel_crtc->use_pll_a = true; + DRM_DEBUG_KMS("using pipe a dpll\n"); + } else if (dpll == (I915_READ(PCH_DPLL(1)) & 0x7fffffff) && + fp == I915_READ(PCH_FP0(1))) { + intel_crtc->use_pll_a = false; + DRM_DEBUG_KMS("using pipe b dpll\n"); + } else { + DRM_DEBUG_KMS("no matching PLL configuration for pipe 2\n"); + return -EINVAL; } - I915_WRITE(PCH_DPLL_SEL, temp); - - POSTING_READ(PCH_DPLL_SEL); - udelay(150); } /* The LVDS pin pair needs to be on before the DPLLs are enabled. @@ -5082,17 +5673,16 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (is_lvds) { temp = I915_READ(PCH_LVDS); temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (pipe == 1) { - if (HAS_PCH_CPT(dev)) - temp |= PORT_TRANS_B_SEL_CPT; - else - temp |= LVDS_PIPEB_SELECT; + if (HAS_PCH_CPT(dev)) { + temp &= ~PORT_TRANS_SEL_MASK; + temp |= PORT_TRANS_SEL_CPT(pipe); } else { - if (HAS_PCH_CPT(dev)) - temp &= ~PORT_TRANS_SEL_MASK; + if (pipe == 1) + temp |= LVDS_PIPEB_SELECT; else temp &= ~LVDS_PIPEB_SELECT; } + /* set the corresponsding LVDS_BORDER bit */ temp |= dev_priv->lvds_border_bits; /* Set the B0-B3 data pairs corresponding to whether we're going to @@ -5126,14 +5716,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(PCH_LVDS, temp); } - /* set the dithering flag and clear for anything other than a panel. */ pipeconf &= ~PIPECONF_DITHER_EN; pipeconf &= ~PIPECONF_DITHER_TYPE_MASK; - if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) { + if ((is_lvds && dev_priv->lvds_dither) || dither) { pipeconf |= PIPECONF_DITHER_EN; - pipeconf |= PIPECONF_DITHER_TYPE_ST1; + pipeconf |= PIPECONF_DITHER_TYPE_SP; } - if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) { intel_dp_set_m_n(crtc, mode, adjusted_mode); } else { @@ -5144,8 +5732,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(TRANSDPLINK_N1(pipe), 0); } - if (!has_edp_encoder || - intel_encoder_is_pch_edp(&has_edp_encoder->base)) { + if (!intel_crtc->no_pll && + (!has_edp_encoder || + intel_encoder_is_pch_edp(&has_edp_encoder->base))) { I915_WRITE(PCH_DPLL(pipe), dpll); /* Wait for the clocks to stabilize. */ @@ -5161,18 +5750,20 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } intel_crtc->lowfreq_avail = false; - if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(PCH_FP1(pipe), fp2); - intel_crtc->lowfreq_avail = true; - if (HAS_PIPE_CXSR(dev)) { - DRM_DEBUG_KMS("enabling CxSR downclocking\n"); - pipeconf |= PIPECONF_CXSR_DOWNCLOCK; - } - } else { - I915_WRITE(PCH_FP1(pipe), fp); - if (HAS_PIPE_CXSR(dev)) { - DRM_DEBUG_KMS("disabling CxSR downclocking\n"); - pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; + if (!intel_crtc->no_pll) { + if (is_lvds && has_reduced_clock && i915_powersave) { + I915_WRITE(PCH_FP1(pipe), fp2); + intel_crtc->lowfreq_avail = true; + if (HAS_PIPE_CXSR(dev)) { + DRM_DEBUG_KMS("enabling CxSR downclocking\n"); + pipeconf |= PIPECONF_CXSR_DOWNCLOCK; + } + } else { + I915_WRITE(PCH_FP1(pipe), fp); + if (HAS_PIPE_CXSR(dev)) { + DRM_DEBUG_KMS("disabling CxSR downclocking\n"); + pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; + } } } @@ -5264,9 +5855,136 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, drm_vblank_post_modeset(dev, pipe); + intel_crtc->dpms_mode = DRM_MODE_DPMS_ON; + return ret; } +static void g4x_write_eld(struct drm_connector *connector, + struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = connector->dev->dev_private; + uint8_t *eld = connector->eld; + uint32_t eldv; + uint32_t len; + uint32_t i; + + i = I915_READ(G4X_AUD_VID_DID); + + if (i == INTEL_AUDIO_DEVBLC || i == INTEL_AUDIO_DEVCL) + eldv = G4X_ELDV_DEVCL_DEVBLC; + else + eldv = G4X_ELDV_DEVCTG; + + i = I915_READ(G4X_AUD_CNTL_ST); + i &= ~(eldv | G4X_ELD_ADDR); + len = (i >> 9) & 0x1f; /* ELD buffer size */ + I915_WRITE(G4X_AUD_CNTL_ST, i); + + if (!eld[0]) + return; + + len = min_t(uint8_t, eld[2], len); + DRM_DEBUG_DRIVER("ELD size %d\n", len); + for (i = 0; i < len; i++) + I915_WRITE(G4X_HDMIW_HDMIEDID, *((uint32_t *)eld + i)); + + i = I915_READ(G4X_AUD_CNTL_ST); + i |= eldv; + I915_WRITE(G4X_AUD_CNTL_ST, i); +} + +static void ironlake_write_eld(struct drm_connector *connector, + struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = connector->dev->dev_private; + uint8_t *eld = connector->eld; + uint32_t eldv; + uint32_t i; + int len; + int hdmiw_hdmiedid; + int aud_cntl_st; + int aud_cntrl_st2; + + if (HAS_PCH_IBX(connector->dev)) { + hdmiw_hdmiedid = GEN5_HDMIW_HDMIEDID_A; + aud_cntl_st = GEN5_AUD_CNTL_ST_A; + aud_cntrl_st2 = GEN5_AUD_CNTL_ST2; + } else { + hdmiw_hdmiedid = GEN7_HDMIW_HDMIEDID_A; + aud_cntl_st = GEN7_AUD_CNTRL_ST_A; + aud_cntrl_st2 = GEN7_AUD_CNTRL_ST2; + } + + i = to_intel_crtc(crtc)->pipe; + hdmiw_hdmiedid += i * 0x100; + aud_cntl_st += i * 0x100; + + DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i)); + + i = I915_READ(aud_cntl_st); + i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */ + if (!i) { + DRM_DEBUG_DRIVER("Audio directed to unknown port\n"); + /* operate blindly on all ports */ + eldv = GEN5_ELD_VALIDB; + eldv |= GEN5_ELD_VALIDB << 4; + eldv |= GEN5_ELD_VALIDB << 8; + } else { + DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i); + eldv = GEN5_ELD_VALIDB << ((i - 1) * 4); + } + + i = I915_READ(aud_cntrl_st2); + i &= ~eldv; + I915_WRITE(aud_cntrl_st2, i); + + if (!eld[0]) + return; + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n"); + eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */ + } + + i = I915_READ(aud_cntl_st); + i &= ~GEN5_ELD_ADDRESS; + I915_WRITE(aud_cntl_st, i); + + len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */ + DRM_DEBUG_DRIVER("ELD size %d\n", len); + for (i = 0; i < len; i++) + I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i)); + + i = I915_READ(aud_cntrl_st2); + i |= eldv; + I915_WRITE(aud_cntrl_st2, i); +} + +void intel_write_eld(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct drm_crtc *crtc = encoder->crtc; + struct drm_connector *connector; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + connector = drm_select_eld(encoder, mode); + if (!connector) + return; + + DRM_DEBUG_DRIVER("ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", + connector->base.id, + drm_get_connector_name(connector), + connector->encoder->base.id, + drm_get_encoder_name(connector->encoder)); + + connector->eld[6] = drm_av_sync_delay(connector, mode) / 2; + + if (dev_priv->display.write_eld) + dev_priv->display.write_eld(connector, crtc); +} + /** Loads the palette/gamma unit for the CRTC with the prepared values */ void intel_crtc_load_lut(struct drm_crtc *crtc) { @@ -5345,7 +6063,9 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) intel_crtc->cursor_visible = visible; } /* and commit changes on next vblank */ + POSTING_READ(CURCNTR(pipe)); I915_WRITE(CURBASE(pipe), base); + POSTING_READ(CURBASE(pipe)); } static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) @@ -5370,7 +6090,9 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) intel_crtc->cursor_visible = visible; } /* and commit changes on next vblank */ + POSTING_READ(CURCNTR_IVB(pipe)); I915_WRITE(CURBASE_IVB(pipe), base); + POSTING_READ(CURBASE_IVB(pipe)); } /* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */ @@ -5483,21 +6205,15 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, goto fail_locked; } - ret = i915_gem_object_pin(obj, PAGE_SIZE, true); - if (ret) { - DRM_ERROR("failed to pin cursor bo\n"); - goto fail_locked; - } - - ret = i915_gem_object_set_to_gtt_domain(obj, 0); + ret = i915_gem_object_pin_to_display_plane(obj, 0, NULL); if (ret) { DRM_ERROR("failed to move cursor bo into the GTT\n"); - goto fail_unpin; + goto fail_locked; } ret = i915_gem_object_put_fence(obj); if (ret) { - DRM_ERROR("failed to move cursor bo into the GTT\n"); + DRM_ERROR("failed to release fence for cursor"); goto fail_unpin; } @@ -6200,6 +6916,7 @@ static void intel_unpin_work_fn(struct work_struct *__work) drm_gem_object_unreference(&work->pending_flip_obj->base); drm_gem_object_unreference(&work->old_fb_obj->base); + intel_update_fbc(work->dev); mutex_unlock(&work->dev->struct_mutex); kfree(work); } @@ -6223,11 +6940,18 @@ static void do_intel_finish_page_flip(struct drm_device *dev, spin_lock_irqsave(&dev->event_lock, flags); work = intel_crtc->unpin_work; - if (work == NULL || !work->pending) { + + /* Ensure we don't miss a work->pending update ... */ + smp_rmb(); + + if (work == NULL || atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) { spin_unlock_irqrestore(&dev->event_lock, flags); return; } + /* and that the unpin work is consistent wrt ->pending. */ + smp_rmb(); + intel_crtc->unpin_work = NULL; if (work->event) { @@ -6269,9 +6993,8 @@ static void do_intel_finish_page_flip(struct drm_device *dev, atomic_clear_mask(1 << intel_crtc->plane, &obj->pending_flip.counter); - if (atomic_read(&obj->pending_flip) == 0) - wake_up(&dev_priv->pending_flip_queue); + wake_up(&dev_priv->pending_flip_queue); schedule_work(&work->work); trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj); @@ -6300,16 +7023,25 @@ void intel_prepare_page_flip(struct drm_device *dev, int plane) to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]); unsigned long flags; + /* NB: An MMIO update of the plane base pointer will also + * generate a page-flip completion irq, i.e. every modeset + * is also accompanied by a spurious intel_prepare_page_flip(). + */ spin_lock_irqsave(&dev->event_lock, flags); - if (intel_crtc->unpin_work) { - if ((++intel_crtc->unpin_work->pending) > 1) - DRM_ERROR("Prepared flip multiple times\n"); - } else { - DRM_DEBUG_DRIVER("preparing flip with no unpin work?\n"); - } + if (intel_crtc->unpin_work) + atomic_inc_not_zero(&intel_crtc->unpin_work->pending); spin_unlock_irqrestore(&dev->event_lock, flags); } +inline static void intel_mark_page_flip_active(struct intel_crtc *intel_crtc) +{ + /* Ensure that the work item is consistent when activating it ... */ + smp_wmb(); + atomic_set(&intel_crtc->unpin_work->pending, INTEL_FLIP_PENDING); + /* and that it is marked active as soon as the irq could fire. */ + smp_wmb(); +} + static int intel_gen2_queue_flip(struct drm_device *dev, struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -6323,14 +7055,14 @@ static int intel_gen2_queue_flip(struct drm_device *dev, ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); if (ret) - goto out; + goto err; /* Offset into the new buffer for cases of shared fbs between CRTCs */ offset = crtc->y * fb->pitch + crtc->x * fb->bits_per_pixel/8; ret = BEGIN_LP_RING(6); if (ret) - goto out; + goto err_unpin; /* Can't queue multiple flips, so wait for the previous * one to finish before executing the next. @@ -6346,8 +7078,14 @@ static int intel_gen2_queue_flip(struct drm_device *dev, OUT_RING(fb->pitch); OUT_RING(obj->gtt_offset + offset); OUT_RING(MI_NOOP); + + intel_mark_page_flip_active(intel_crtc); ADVANCE_LP_RING(); -out: + return 0; + +err_unpin: + i915_gem_object_unpin(obj); +err: return ret; } @@ -6364,14 +7102,14 @@ static int intel_gen3_queue_flip(struct drm_device *dev, ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); if (ret) - goto out; + goto err; /* Offset into the new buffer for cases of shared fbs between CRTCs */ offset = crtc->y * fb->pitch + crtc->x * fb->bits_per_pixel/8; ret = BEGIN_LP_RING(6); if (ret) - goto out; + goto err_unpin; if (intel_crtc->plane) flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; @@ -6385,8 +7123,13 @@ static int intel_gen3_queue_flip(struct drm_device *dev, OUT_RING(obj->gtt_offset + offset); OUT_RING(MI_NOOP); + intel_mark_page_flip_active(intel_crtc); ADVANCE_LP_RING(); -out: + return 0; + +err_unpin: + i915_gem_object_unpin(obj); +err: return ret; } @@ -6402,11 +7145,11 @@ static int intel_gen4_queue_flip(struct drm_device *dev, ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); if (ret) - goto out; + goto err; ret = BEGIN_LP_RING(4); if (ret) - goto out; + goto err_unpin; /* i965+ uses the linear or tiled offsets from the * Display Registers (which do not change across a page-flip) @@ -6424,8 +7167,14 @@ static int intel_gen4_queue_flip(struct drm_device *dev, pf = 0; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; OUT_RING(pf | pipesrc); + + intel_mark_page_flip_active(intel_crtc); ADVANCE_LP_RING(); -out: + return 0; + +err_unpin: + i915_gem_object_unpin(obj); +err: return ret; } @@ -6441,11 +7190,11 @@ static int intel_gen6_queue_flip(struct drm_device *dev, ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); if (ret) - goto out; + goto err; ret = BEGIN_LP_RING(4); if (ret) - goto out; + goto err_unpin; OUT_RING(MI_DISPLAY_FLIP | MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); @@ -6455,8 +7204,14 @@ static int intel_gen6_queue_flip(struct drm_device *dev, pf = I915_READ(PF_CTL(intel_crtc->pipe)) & PF_ENABLE; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; OUT_RING(pf | pipesrc); + + intel_mark_page_flip_active(intel_crtc); ADVANCE_LP_RING(); -out: + return 0; + +err_unpin: + i915_gem_object_unpin(obj); +err: return ret; } @@ -6474,22 +7229,59 @@ static int intel_gen7_queue_flip(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_ring_buffer *ring = &dev_priv->ring[BCS]; + uint32_t plane_bit = 0; int ret; ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) - goto out; + goto err; + + switch(intel_crtc->plane) { + case PLANE_A: + plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_A; + break; + case PLANE_B: + plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_B; + break; + case PLANE_C: + plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_C; + break; + default: + WARN_ONCE(1, "unknown plane in flip command\n"); + ret = -ENODEV; + goto err_unpin; + } + + /* + * BSpec MI_DISPLAY_FLIP for IVB: + * "The full packet must be contained within the same cache line." + * + * Currently the LRI+SRM+MI_DISPLAY_FLIP all fit within the same + * cacheline, if we ever start emitting more commands before + * the MI_DISPLAY_FLIP we may need to first emit everything else, + * then do the cacheline alignment, and finally emit the + * MI_DISPLAY_FLIP. + */ + ret = intel_ring_cacheline_align(ring); + if (ret) + goto err_unpin; ret = intel_ring_begin(ring, 4); if (ret) - goto out; + goto err_unpin; - intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | (intel_crtc->plane << 19)); + intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | plane_bit); intel_ring_emit(ring, (fb->pitch | obj->tiling_mode)); intel_ring_emit(ring, (obj->gtt_offset)); intel_ring_emit(ring, (MI_NOOP)); + + intel_mark_page_flip_active(intel_crtc); intel_ring_advance(ring); -out: + return 0; + +err_unpin: + i915_gem_object_unpin(obj); +err: return ret; } @@ -6561,6 +7353,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (ret) goto cleanup_pending; + intel_disable_fbc(dev); mutex_unlock(&dev->struct_mutex); trace_i915_flip_request(intel_crtc->plane, obj); @@ -6698,8 +7491,11 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc_reset(&intel_crtc->base); intel_crtc->active = true; /* force the pipe off on setup_init_config */ + intel_crtc->bpp = 24; /* default for pre-Ironlake */ if (HAS_PCH_SPLIT(dev)) { + if (pipe == 2 && IS_IVYBRIDGE(dev)) + intel_crtc->no_pll = true; intel_helper_funcs.prepare = ironlake_crtc_prepare; intel_helper_funcs.commit = ironlake_crtc_commit; } else { @@ -6877,10 +7673,11 @@ static void intel_setup_outputs(struct drm_device *dev) intel_encoder_clones(dev, encoder->clone_mask); } - intel_panel_setup_backlight(dev); - /* disable all the possible outputs/crtcs before entering KMS mode */ drm_helper_disable_unused_functions(dev); + + if (HAS_PCH_SPLIT(dev)) + ironlake_init_pch_refclk(dev); } static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) @@ -6924,6 +7721,11 @@ int intel_framebuffer_init(struct drm_device *dev, switch (mode_cmd->bpp) { case 8: case 16: + /* Only pre-ILK can handle 5:5:5 */ + if (mode_cmd->depth == 15 && !HAS_PCH_SPLIT(dev)) + return -EINVAL; + break; + case 24: case 32: break; @@ -7120,6 +7922,10 @@ void gen6_disable_rps(struct drm_device *dev) I915_WRITE(GEN6_RPNSWREQ, 1 << 31); I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); I915_WRITE(GEN6_PMIER, 0); + /* Complete PM interrupt masking here doesn't race with the rps work + * item again unmasking PM interrupts because that is using a different + * register (PMIMR) to mask PM interrupts. The only risk is in leaving + * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */ spin_lock_irq(&dev_priv->rps_lock); dev_priv->pm_iir = 0; @@ -7214,6 +8020,31 @@ void intel_init_emon(struct drm_device *dev) dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK); } +static bool intel_enable_rc6(struct drm_device *dev) +{ + /* + * Respect the kernel parameter if it is set + */ + if (i915_enable_rc6 >= 0) + return i915_enable_rc6; + + /* + * Disable RC6 on Ironlake + */ + if (INTEL_INFO(dev)->gen == 5) + return 0; + + /* + * Disable rc6 on Sandybridge + */ + if (INTEL_INFO(dev)->gen == 6) { + DRM_DEBUG_DRIVER("Sandybridge: RC6 disabled\n"); + return 0; + } + DRM_DEBUG_DRIVER("RC6 enabled\n"); + return 1; +} + void gen6_enable_rps(struct drm_i915_private *dev_priv) { u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); @@ -7247,12 +8078,12 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RC_SLEEP, 0); I915_WRITE(GEN6_RC1e_THRESHOLD, 1000); I915_WRITE(GEN6_RC6_THRESHOLD, 50000); - I915_WRITE(GEN6_RC6p_THRESHOLD, 100000); + I915_WRITE(GEN6_RC6p_THRESHOLD, 150000); I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */ - if (i915_enable_rc6) - rc6_mask = GEN6_RC_CTL_RC6p_ENABLE | - GEN6_RC_CTL_RC6_ENABLE; + if (intel_enable_rc6(dev_priv->dev)) + rc6_mask = GEN6_RC_CTL_RC6_ENABLE | + ((IS_GEN7(dev_priv->dev)) ? GEN6_RC_CTL_RC6p_ENABLE : 0); I915_WRITE(GEN6_RC_CONTROL, rc6_mask | @@ -7277,7 +8108,7 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); I915_WRITE(GEN6_RP_CONTROL, GEN6_RP_MEDIA_TURBO | - GEN6_RP_USE_NORMAL_FREQ | + GEN6_RP_MEDIA_HW_NORMAL_MODE | GEN6_RP_MEDIA_IS_GFX | GEN6_RP_ENABLE | GEN6_RP_UP_BUSY_AVG | @@ -7338,6 +8169,59 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) mutex_unlock(&dev_priv->dev->struct_mutex); } +void gen6_update_ring_freq(struct drm_i915_private *dev_priv) +{ + int min_freq = 15; + int gpu_freq, ia_freq, max_ia_freq; + int scaling_factor = 180; + + max_ia_freq = cpufreq_quick_get_max(0); + /* + * Default to measured freq if none found, PCU will ensure we don't go + * over + */ + if (!max_ia_freq) + max_ia_freq = tsc_khz; + + /* Convert from kHz to MHz */ + max_ia_freq /= 1000; + + mutex_lock(&dev_priv->dev->struct_mutex); + + /* + * For each potential GPU frequency, load a ring frequency we'd like + * to use for memory access. We do this by specifying the IA frequency + * the PCU should use as a reference to determine the ring frequency. + */ + for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay; + gpu_freq--) { + int diff = dev_priv->max_delay - gpu_freq; + + /* + * For GPU frequencies less than 750MHz, just use the lowest + * ring freq. + */ + if (gpu_freq < min_freq) + ia_freq = 800; + else + ia_freq = max_ia_freq - ((diff * scaling_factor) / 2); + ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100); + + I915_WRITE(GEN6_PCODE_DATA, + (ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) | + gpu_freq); + I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | + GEN6_PCODE_WRITE_MIN_FREQ_TABLE); + if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & + GEN6_PCODE_READY) == 0, 10)) { + DRM_ERROR("pcode write of freq table timed out\n"); + continue; + } + } + + mutex_unlock(&dev_priv->dev->struct_mutex); +} + static void ironlake_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -7461,10 +8345,17 @@ static void gen6_init_clock_gating(struct drm_device *dev) ILK_DPARB_CLK_GATE | ILK_DPFD_CLK_GATE); - for_each_pipe(pipe) + for_each_pipe(pipe) { I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | DISPPLANE_TRICKLE_FEED_DISABLE); + intel_flush_display_plane(dev_priv, pipe); + } + + /* The default value should be 0x200 according to docs, but the two + * platforms I checked have a 0 for this. (Maybe BIOS overrides?) */ + I915_WRITE(GEN6_GT_MODE, 0xffff << 16); + I915_WRITE(GEN6_GT_MODE, GEN6_GT_MODE_HI << 16 | GEN6_GT_MODE_HI); } static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv) @@ -7513,10 +8404,14 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - for_each_pipe(pipe) + for_each_pipe(pipe) { I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | DISPPLANE_TRICKLE_FEED_DISABLE); + intel_flush_display_plane(dev_priv, pipe); + } + + gen7_setup_fixed_func_scheduler(dev_priv); } static void g4x_init_clock_gating(struct drm_device *dev) @@ -7629,8 +8524,6 @@ static void ironlake_teardown_rc6(struct drm_device *dev) drm_gem_object_unreference(&dev_priv->pwrctx->base); dev_priv->pwrctx = NULL; } - - gen7_setup_fixed_func_scheduler(dev_priv); } static void ironlake_disable_rc6(struct drm_device *dev) @@ -7680,7 +8573,7 @@ void ironlake_enable_rc6(struct drm_device *dev) /* rc6 disabled by default due to repeated reports of hanging during * boot and resume. */ - if (!i915_enable_rc6) + if (!intel_enable_rc6(dev)) return; mutex_lock(&dev->struct_mutex); @@ -7750,9 +8643,11 @@ static void intel_init_display(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { dev_priv->display.dpms = ironlake_crtc_dpms; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; + dev_priv->display.update_plane = ironlake_update_plane; } else { dev_priv->display.dpms = i9xx_crtc_dpms; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; + dev_priv->display.update_plane = i9xx_update_plane; } if (I915_HAS_FBC(dev)) { @@ -7773,7 +8668,7 @@ static void intel_init_display(struct drm_device *dev) } /* Returns the core display clock speed */ - if (IS_I945G(dev) || (IS_G33(dev) && ! IS_PINEVIEW_M(dev))) + if (IS_I945G(dev) || (IS_G33(dev) && !IS_PINEVIEW_M(dev))) dev_priv->display.get_display_clock_speed = i945_get_display_clock_speed; else if (IS_I915G(dev)) @@ -7797,6 +8692,28 @@ static void intel_init_display(struct drm_device *dev) /* For FIFO watermark updates */ if (HAS_PCH_SPLIT(dev)) { + dev_priv->display.force_wake_get = __gen6_gt_force_wake_get; + dev_priv->display.force_wake_put = __gen6_gt_force_wake_put; + + /* IVB configs may use multi-threaded forcewake */ + if (IS_IVYBRIDGE(dev)) { + u32 ecobus; + + mutex_lock(&dev->struct_mutex); + __gen6_gt_force_wake_mt_get(dev_priv); + ecobus = I915_READ(ECOBUS); + __gen6_gt_force_wake_mt_put(dev_priv); + mutex_unlock(&dev->struct_mutex); + + if (ecobus & FORCEWAKE_MT_ENABLE) { + DRM_DEBUG_KMS("Using MT version of forcewake\n"); + dev_priv->display.force_wake_get = + __gen6_gt_force_wake_mt_get; + dev_priv->display.force_wake_put = + __gen6_gt_force_wake_mt_put; + } + } + if (HAS_PCH_IBX(dev)) dev_priv->display.init_pch_clock_gating = ibx_init_clock_gating; else if (HAS_PCH_CPT(dev)) @@ -7812,6 +8729,7 @@ static void intel_init_display(struct drm_device *dev) } dev_priv->display.fdi_link_train = ironlake_fdi_link_train; dev_priv->display.init_clock_gating = ironlake_init_clock_gating; + dev_priv->display.write_eld = ironlake_write_eld; } else if (IS_GEN6(dev)) { if (SNB_READ_WM0_LATENCY()) { dev_priv->display.update_wm = sandybridge_update_wm; @@ -7822,6 +8740,7 @@ static void intel_init_display(struct drm_device *dev) } dev_priv->display.fdi_link_train = gen6_fdi_link_train; dev_priv->display.init_clock_gating = gen6_init_clock_gating; + dev_priv->display.write_eld = ironlake_write_eld; } else if (IS_IVYBRIDGE(dev)) { /* FIXME: detect B0+ stepping and use auto training */ dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; @@ -7833,7 +8752,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_wm = NULL; } dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; - + dev_priv->display.write_eld = ironlake_write_eld; } else dev_priv->display.update_wm = NULL; } else if (IS_PINEVIEW(dev)) { @@ -7844,7 +8763,7 @@ static void intel_init_display(struct drm_device *dev) DRM_INFO("failed to find known CxSR latency " "(found ddr%s fsb freq %d, mem freq %d), " "disabling CxSR\n", - (dev_priv->is_ddr3 == 1) ? "3": "2", + (dev_priv->is_ddr3 == 1) ? "3" : "2", dev_priv->fsb_freq, dev_priv->mem_freq); /* Disable CxSR and never update its watermark again */ pineview_disable_cxsr(dev); @@ -7853,6 +8772,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_wm = pineview_update_wm; dev_priv->display.init_clock_gating = gen3_init_clock_gating; } else if (IS_G4X(dev)) { + dev_priv->display.write_eld = g4x_write_eld; dev_priv->display.update_wm = g4x_update_wm; dev_priv->display.init_clock_gating = g4x_init_clock_gating; } else if (IS_GEN4(dev)) { @@ -7913,7 +8833,7 @@ static void intel_init_display(struct drm_device *dev) * resume, or other times. This quirk makes sure that's the case for * affected systems. */ -static void quirk_pipea_force (struct drm_device *dev) +static void quirk_pipea_force(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -7930,6 +8850,27 @@ static void quirk_ssc_force_disable(struct drm_device *dev) dev_priv->quirks |= QUIRK_LVDS_SSC_DISABLE; } +/* + * A machine (e.g. Acer Aspire 5734Z) may need to invert the panel backlight + * brightness value + */ +static void quirk_invert_brightness(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + dev_priv->quirks |= QUIRK_INVERT_BRIGHTNESS; +} + +/* + * Some machines (Dell XPS13) suffer broken backlight controls if + * BLM_PCH_PWM_ENABLE is set. + */ +static void quirk_no_pcm_pwm_enable(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + dev_priv->quirks |= QUIRK_NO_PCH_PWM_ENABLE; + DRM_INFO("applying no-PCH_PWM_ENABLE quirk\n"); +} + struct intel_quirk { int device; int subsystem_vendor; @@ -7937,11 +8878,39 @@ struct intel_quirk { void (*hook)(struct drm_device *dev); }; +/* For systems that don't have a meaningful PCI subdevice/subvendor ID */ +struct intel_dmi_quirk { + void (*hook)(struct drm_device *dev); + const struct dmi_system_id (*dmi_id_list)[]; +}; + +static int intel_dmi_reverse_brightness(const struct dmi_system_id *id) +{ + DRM_INFO("Backlight polarity reversed on %s\n", id->ident); + return 1; +} + +static const struct intel_dmi_quirk intel_dmi_quirks[] = { + { + .dmi_id_list = &(const struct dmi_system_id[]) { + { + .callback = intel_dmi_reverse_brightness, + .ident = "NCR Corporation", + .matches = {DMI_MATCH(DMI_SYS_VENDOR, "NCR Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, ""), + }, + }, + { } /* terminating entry */ + }, + .hook = quirk_invert_brightness, + }, +}; + struct intel_quirk intel_quirks[] = { /* HP Compaq 2730p needs pipe A force quirk (LP: #291555) */ { 0x2a42, 0x103c, 0x30eb, quirk_pipea_force }, /* HP Mini needs pipe A force quirk (LP: #322104) */ - { 0x27ae,0x103c, 0x361a, quirk_pipea_force }, + { 0x27ae, 0x103c, 0x361a, quirk_pipea_force }, /* Thinkpad R31 needs pipe A force quirk */ { 0x3577, 0x1014, 0x0505, quirk_pipea_force }, @@ -7961,6 +8930,32 @@ struct intel_quirk intel_quirks[] = { /* Lenovo U160 cannot use SSC on LVDS */ { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, + + /* Sony Vaio Y cannot use SSC on LVDS */ + { 0x0046, 0x104d, 0x9076, quirk_ssc_force_disable }, + + /* Acer Aspire 5734Z must invert backlight brightness */ + { 0x2a42, 0x1025, 0x0459, quirk_invert_brightness }, + + /* Acer/eMachines G725 */ + { 0x2a42, 0x1025, 0x0210, quirk_invert_brightness }, + + /* Acer/eMachines e725 */ + { 0x2a42, 0x1025, 0x0212, quirk_invert_brightness }, + + /* Acer/Packard Bell NCL20 */ + { 0x2a42, 0x1025, 0x034b, quirk_invert_brightness }, + + /* Acer Aspire 4736Z */ + { 0x2a42, 0x1025, 0x0260, quirk_invert_brightness }, + + /* Acer Aspire 5336 */ + { 0x2a42, 0x1025, 0x048a, quirk_invert_brightness }, + + /* Dell XPS13 HD Sandy Bridge */ + { 0x0116, 0x1028, 0x052e, quirk_no_pcm_pwm_enable }, + /* Dell XPS13 HD and XPS13 FHD Ivy Bridge */ + { 0x0166, 0x1028, 0x058b, quirk_no_pcm_pwm_enable }, }; static void intel_init_quirks(struct drm_device *dev) @@ -7978,6 +8973,10 @@ static void intel_init_quirks(struct drm_device *dev) q->subsystem_device == PCI_ANY_ID)) q->hook(dev); } + for (i = 0; i < ARRAY_SIZE(intel_dmi_quirks); i++) { + if (dmi_check_system(*intel_dmi_quirks[i].dmi_id_list) != 0) + intel_dmi_quirks[i].hook(dev); + } } /* Disable the VGA plane that we never use */ @@ -8003,6 +9002,23 @@ static void i915_disable_vga(struct drm_device *dev) POSTING_READ(vga_reg); } +void i915_redisable_vga(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 vga_reg; + + if (HAS_PCH_SPLIT(dev)) + vga_reg = CPU_VGACNTRL; + else + vga_reg = VGACNTRL; + + if (I915_READ(vga_reg) != VGA_DISP_DISABLE) { + DRM_DEBUG_KMS("Something enabled VGA plane, disabling it\n"); + I915_WRITE(vga_reg, VGA_DISP_DISABLE); + POSTING_READ(vga_reg); + } +} + void intel_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -8049,8 +9065,10 @@ void intel_modeset_init(struct drm_device *dev) intel_init_emon(dev); } - if (IS_GEN6(dev) || IS_GEN7(dev)) + if (IS_GEN6(dev) || IS_GEN7(dev)) { gen6_enable_rps(dev_priv); + gen6_update_ring_freq(dev_priv); + } INIT_WORK(&dev_priv->idle_work, intel_idle_update); setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, @@ -8086,8 +9104,7 @@ void intel_modeset_cleanup(struct drm_device *dev) intel_increase_pllclock(crtc); } - if (dev_priv->display.disable_fbc) - dev_priv->display.disable_fbc(dev); + intel_disable_fbc(dev); if (IS_IRONLAKE_M(dev)) ironlake_disable_drps(dev); @@ -8103,6 +9120,10 @@ void intel_modeset_cleanup(struct drm_device *dev) * enqueue unpin/hotplug work. */ drm_irq_uninstall(dev); cancel_work_sync(&dev_priv->hotplug_work); + cancel_work_sync(&dev_priv->rps_work); + + /* flush any delayed tasks or pending work */ + flush_scheduled_work(); /* Shut off idle work before the crtcs get freed. */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -8112,6 +9133,9 @@ void intel_modeset_cleanup(struct drm_device *dev) del_timer_sync(&dev_priv->idle_timer); cancel_work_sync(&dev_priv->idle_work); + /* destroy backlight, if any, before the connectors */ + intel_panel_destroy_backlight(dev); + drm_mode_config_cleanup(dev); } @@ -8137,14 +9161,15 @@ void intel_connector_attach_encoder(struct intel_connector *connector, int intel_modeset_vga_set_state(struct drm_device *dev, bool state) { struct drm_i915_private *dev_priv = dev->dev_private; + unsigned reg = INTEL_INFO(dev)->gen >= 6 ? SNB_GMCH_CTRL : INTEL_GMCH_CTRL; u16 gmch_ctrl; - pci_read_config_word(dev_priv->bridge_dev, INTEL_GMCH_CTRL, &gmch_ctrl); + pci_read_config_word(dev_priv->bridge_dev, reg, &gmch_ctrl); if (state) gmch_ctrl &= ~INTEL_GMCH_VGA_DISABLE; else gmch_ctrl |= INTEL_GMCH_VGA_DISABLE; - pci_write_config_word(dev_priv->bridge_dev, INTEL_GMCH_CTRL, gmch_ctrl); + pci_write_config_word(dev_priv->bridge_dev, reg, gmch_ctrl); return 0; } @@ -8185,7 +9210,7 @@ struct intel_display_error_state { struct intel_display_error_state * intel_display_capture_error_state(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_private_t *dev_priv = dev->dev_private; struct intel_display_error_state *error; int i; @@ -8201,7 +9226,7 @@ intel_display_capture_error_state(struct drm_device *dev) error->plane[i].control = I915_READ(DSPCNTR(i)); error->plane[i].stride = I915_READ(DSPSTRIDE(i)); error->plane[i].size = I915_READ(DSPSIZE(i)); - error->plane[i].pos= I915_READ(DSPPOS(i)); + error->plane[i].pos = I915_READ(DSPPOS(i)); error->plane[i].addr = I915_READ(DSPADDR(i)); if (INTEL_INFO(dev)->gen >= 4) { error->plane[i].surface = I915_READ(DSPSURF(i)); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 57b3728..ca75076 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -27,6 +27,7 @@ #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/export.h> #include "drmP.h" #include "drm.h" #include "drm_crtc.h" @@ -36,7 +37,7 @@ #include "i915_drv.h" #include "drm_dp_helper.h" - +#define DP_RECEIVER_CAP_SIZE 0xf #define DP_LINK_STATUS_SIZE 6 #define DP_LINK_CHECK_TIMEOUT (10 * 1000) @@ -50,14 +51,22 @@ struct intel_dp { bool has_audio; int force_audio; uint32_t color_range; + int dpms_mode; uint8_t link_bw; uint8_t lane_count; - uint8_t dpcd[4]; + uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; bool is_pch_edp; uint8_t train_set[4]; - uint8_t link_status[DP_LINK_STATUS_SIZE]; + int panel_power_up_delay; + int panel_power_down_delay; + int panel_power_cycle_delay; + int backlight_on_delay; + int backlight_off_delay; + struct drm_display_mode *panel_fixed_mode; /* for eDP */ + struct delayed_work panel_vdd_work; + bool want_panel_vdd; }; /** @@ -85,6 +94,17 @@ static bool is_pch_edp(struct intel_dp *intel_dp) return intel_dp->is_pch_edp; } +/** + * is_cpu_edp - is the port on the CPU and attached to an eDP panel? + * @intel_dp: DP struct + * + * Returns true if the given DP struct corresponds to a CPU eDP port. + */ +static bool is_cpu_edp(struct intel_dp *intel_dp) +{ + return is_edp(intel_dp) && !is_pch_edp(intel_dp); +} + static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) { return container_of(encoder, struct intel_dp, base.base); @@ -120,7 +140,7 @@ static void intel_dp_complete_link_train(struct intel_dp *intel_dp); static void intel_dp_link_down(struct intel_dp *intel_dp); void -intel_edp_link_config (struct intel_encoder *intel_encoder, +intel_edp_link_config(struct intel_encoder *intel_encoder, int *lane_num, int *link_bw) { struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base); @@ -135,16 +155,12 @@ intel_edp_link_config (struct intel_encoder *intel_encoder, static int intel_dp_max_lane_count(struct intel_dp *intel_dp) { - int max_lane_count = 4; - - if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) { - max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; - switch (max_lane_count) { - case 1: case 2: case 4: - break; - default: - max_lane_count = 4; - } + int max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; + switch (max_lane_count) { + case 1: case 2: case 4: + break; + default: + max_lane_count = 4; } return max_lane_count; } @@ -174,16 +190,27 @@ intel_dp_link_clock(uint8_t link_bw) return 162000; } -/* I think this is a fiction */ +/* + * The units on the numbers in the next two are... bizarre. Examples will + * make it clearer; this one parallels an example in the eDP spec. + * + * intel_dp_max_data_rate for one lane of 2.7GHz evaluates as: + * + * 270000 * 1 * 8 / 10 == 216000 + * + * The actual data capacity of that configuration is 2.16Gbit/s, so the + * units are decakilobits. ->clock in a drm_display_mode is in kilohertz - + * or equivalently, kilopixels per second - so for 1680x1050R it'd be + * 119000. At 18bpp that's 2142000 kilobits per second. + * + * Thus the strange-looking division by 10 in intel_dp_link_required, to + * get the result in decakilobits instead of kilobits. + */ + static int -intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pixel_clock) +intel_dp_link_required(int pixel_clock, int bpp) { - struct drm_i915_private *dev_priv = dev->dev_private; - - if (is_edp(intel_dp)) - return (pixel_clock * dev_priv->edp.bpp + 7) / 8; - else - return pixel_clock * 3; + return (pixel_clock * bpp + 9) / 10; } static int @@ -192,29 +219,48 @@ intel_dp_max_data_rate(int max_link_clock, int max_lanes) return (max_link_clock * max_lanes * 8) / 10; } +static bool +intel_dp_adjust_dithering(struct intel_dp *intel_dp, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); + int max_lanes = intel_dp_max_lane_count(intel_dp); + int max_rate, mode_rate; + + mode_rate = intel_dp_link_required(mode->clock, 24); + max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); + + if (mode_rate > max_rate) { + mode_rate = intel_dp_link_required(mode->clock, 18); + if (mode_rate > max_rate) + return false; + + if (adjusted_mode) + adjusted_mode->private_flags + |= INTEL_MODE_DP_FORCE_6BPC; + + return true; + } + + return true; +} + static int intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct intel_dp *intel_dp = intel_attached_dp(connector); - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); - int max_lanes = intel_dp_max_lane_count(intel_dp); - if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) { - if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay) + if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { + if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) return MODE_PANEL; - if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay) + if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay) return MODE_PANEL; } - /* only refuse the mode on non eDP since we have seen some weird eDP panels - which are outside spec tolerances but somehow work by magic */ - if (!is_edp(intel_dp) && - (intel_dp_link_required(connector->dev, intel_dp, mode->clock) - > intel_dp_max_data_rate(max_link_clock, max_lanes))) + if (!intel_dp_adjust_dithering(intel_dp, mode, NULL)) return MODE_CLOCK_HIGH; if (mode->clock < 10000) @@ -276,6 +322,38 @@ intel_hrawclk(struct drm_device *dev) } } +static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + return (I915_READ(PCH_PP_STATUS) & PP_ON) != 0; +} + +static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + return (I915_READ(PCH_PP_CONTROL) & EDP_FORCE_VDD) != 0; +} + +static void +intel_dp_check_edp(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + if (!is_edp(intel_dp)) + return; + if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) { + WARN(1, "eDP powered off while attempting aux channel communication.\n"); + DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n", + I915_READ(PCH_PP_STATUS), + I915_READ(PCH_PP_CONTROL)); + } +} + static int intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *send, int send_bytes, @@ -292,6 +370,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, uint32_t aux_clock_divider; int try, precharge; + intel_dp_check_edp(intel_dp); /* The clock divider is based off the hrawclk, * and would like to run at 2MHz. So, take the * hrawclk value and divide by 2 and use that @@ -299,9 +378,9 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, * Note that PCH attached eDP panels should use a 125MHz input * clock divider. */ - if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) { - if (IS_GEN6(dev)) - aux_clock_divider = 200; /* SNB eDP input clock at 400Mhz */ + if (is_cpu_edp(intel_dp)) { + if (IS_GEN6(dev) || IS_GEN7(dev)) + aux_clock_divider = 200; /* SNB & IVB eDP input clock at 400Mhz */ else aux_clock_divider = 225; /* eDP input clock at 450Mhz */ } else if (HAS_PCH_SPLIT(dev)) @@ -314,9 +393,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, else precharge = 5; - if (I915_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) { - DRM_ERROR("dp_aux_ch not started status 0x%08x\n", - I915_READ(ch_ctl)); + /* Try to wait for any previous AUX channel activity */ + for (try = 0; try < 3; try++) { + status = I915_READ(ch_ctl); + if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) + break; + msleep(1); + } + + if (try == 3) { + WARN(1, "dp_aux_ch not started status 0x%08x\n", + I915_READ(ch_ctl)); return -EBUSY; } @@ -326,7 +413,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, for (i = 0; i < send_bytes; i += 4) I915_WRITE(ch_data + i, pack_aux(send + i, send_bytes - i)); - + /* Send the command and wait for it to complete */ I915_WRITE(ch_ctl, DP_AUX_CH_CTL_SEND_BUSY | @@ -343,7 +430,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, break; udelay(100); } - + /* Clear done status and any errors */ I915_WRITE(ch_ctl, status | @@ -379,7 +466,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT); if (recv_bytes > recv_size) recv_bytes = recv_size; - + for (i = 0; i < recv_bytes; i += 4) unpack_aux(I915_READ(ch_data + i), recv + i, recv_bytes - i); @@ -396,7 +483,9 @@ intel_dp_aux_native_write(struct intel_dp *intel_dp, uint8_t msg[20]; int msg_bytes; uint8_t ack; + int retry; + intel_dp_check_edp(intel_dp); if (send_bytes > 16) return -1; msg[0] = AUX_NATIVE_WRITE << 4; @@ -405,18 +494,20 @@ intel_dp_aux_native_write(struct intel_dp *intel_dp, msg[3] = send_bytes - 1; memcpy(&msg[4], send, send_bytes); msg_bytes = send_bytes + 4; - for (;;) { + for (retry = 0; retry < 7; retry++) { ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, &ack, 1); if (ret < 0) return ret; if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) - break; + return send_bytes; else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) - udelay(100); + usleep_range(400, 500); else return -EIO; } - return send_bytes; + + DRM_ERROR("too many retries, giving up\n"); + return -EIO; } /* Write a single byte to the aux channel in native mode */ @@ -438,7 +529,9 @@ intel_dp_aux_native_read(struct intel_dp *intel_dp, int reply_bytes; uint8_t ack; int ret; + int retry; + intel_dp_check_edp(intel_dp); msg[0] = AUX_NATIVE_READ << 4; msg[1] = address >> 8; msg[2] = address & 0xff; @@ -447,7 +540,7 @@ intel_dp_aux_native_read(struct intel_dp *intel_dp, msg_bytes = 4; reply_bytes = recv_bytes + 1; - for (;;) { + for (retry = 0; retry < 7; retry++) { ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, reply, reply_bytes); if (ret == 0) @@ -460,10 +553,13 @@ intel_dp_aux_native_read(struct intel_dp *intel_dp, return ret - 1; } else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) - udelay(100); + usleep_range(400, 500); else return -EIO; } + + DRM_ERROR("too many retries, giving up\n"); + return -EIO; } static int @@ -482,6 +578,7 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, int reply_bytes; int ret; + intel_dp_check_edp(intel_dp); /* Set up the command byte */ if (mode & MODE_I2C_READ) msg[0] = AUX_I2C_READ << 4; @@ -573,24 +670,32 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, return -EREMOTEIO; } +static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp); +static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); + static int intel_dp_i2c_init(struct intel_dp *intel_dp, struct intel_connector *intel_connector, const char *name) { + int ret; + DRM_DEBUG_KMS("i2c_init %s\n", name); intel_dp->algo.running = false; intel_dp->algo.address = 0; intel_dp->algo.aux_ch = intel_dp_i2c_aux_ch; - memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter)); + memset(&intel_dp->adapter, '\0', sizeof(intel_dp->adapter)); intel_dp->adapter.owner = THIS_MODULE; intel_dp->adapter.class = I2C_CLASS_DDC; - strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); + strncpy(intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; intel_dp->adapter.algo_data = &intel_dp->algo; intel_dp->adapter.dev.parent = &intel_connector->base.kdev; - return i2c_dp_aux_add_bus(&intel_dp->adapter); + ironlake_edp_panel_vdd_on(intel_dp); + ret = i2c_dp_aux_add_bus(&intel_dp->adapter); + ironlake_edp_panel_vdd_off(intel_dp, false); + return ret; } static bool @@ -598,29 +703,34 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); int lane_count, clock; int max_lane_count = intel_dp_max_lane_count(intel_dp); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; + int bpp; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; - if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) { - intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode); + if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { + intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode); intel_pch_panel_fitting(dev, DRM_MODE_SCALE_FULLSCREEN, mode, adjusted_mode); /* * the mode->clock is used to calculate the Data&Link M/N * of the pipe. For the eDP the fixed clock should be used. */ - mode->clock = dev_priv->panel_fixed_mode->clock; + mode->clock = intel_dp->panel_fixed_mode->clock; } - for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { - for (clock = 0; clock <= max_clock; clock++) { + if (!intel_dp_adjust_dithering(intel_dp, mode, adjusted_mode)) + return false; + + bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + + for (clock = 0; clock <= max_clock; clock++) { + for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); - if (intel_dp_link_required(encoder->dev, intel_dp, mode->clock) + if (intel_dp_link_required(mode->clock, bpp) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; @@ -634,19 +744,6 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, } } - if (is_edp(intel_dp)) { - /* okay we failed just pick the highest */ - intel_dp->lane_count = max_lane_count; - intel_dp->link_bw = bws[max_clock]; - adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw); - DRM_DEBUG_KMS("Force picking display port link bw %02x lane " - "count %d clock %d\n", - intel_dp->link_bw, intel_dp->lane_count, - adjusted_mode->clock); - - return true; - } - return false; } @@ -692,7 +789,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_encoder *encoder; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int lane_count = 4, bpp = 24; + int lane_count = 4; struct intel_dp_m_n m_n; int pipe = intel_crtc->pipe; @@ -706,13 +803,11 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, continue; intel_dp = enc_to_intel_dp(encoder); - if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT) { + if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT || + intel_dp->base.type == INTEL_OUTPUT_EDP) + { lane_count = intel_dp->lane_count; break; - } else if (is_edp(intel_dp)) { - lane_count = dev_priv->edp.lanes; - bpp = dev_priv->edp.bpp; - break; } } @@ -721,7 +816,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, * the number of bytes_per_pixel post-LUT, which we always * set up for 8-bits of R/G/B, or 3 bytes total. */ - intel_dp_compute_m_n(bpp, lane_count, + intel_dp_compute_m_n(intel_crtc->bpp, lane_count, mode->clock, adjusted_mode->clock, &m_n); if (HAS_PCH_SPLIT(dev)) { @@ -741,27 +836,53 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, } } +static void ironlake_edp_pll_on(struct drm_encoder *encoder); +static void ironlake_edp_pll_off(struct drm_encoder *encoder); + static void intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct drm_crtc *crtc = intel_dp->base.base.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; - intel_dp->DP |= intel_dp->color_range; + /* Turn on the eDP PLL if needed */ + if (is_edp(intel_dp)) { + if (!is_pch_edp(intel_dp)) + ironlake_edp_pll_on(encoder); + else + ironlake_edp_pll_off(encoder); + } + + /* + * There are four kinds of DP registers: + * + * IBX PCH + * SNB CPU + * IVB CPU + * CPT PCH + * + * IBX PCH and CPU are the same for almost everything, + * except that the CPU DP PLL is configured in this + * register + * + * CPT PCH is quite different, having many bits moved + * to the TRANS_DP_CTL register instead. That + * configuration happens (oddly) in ironlake_pch_enable + */ + + /* Preserve the BIOS-computed detected bit. This is + * supposed to be read-only. + */ + intel_dp->DP = I915_READ(intel_dp->output_reg) & DP_DETECTED; + intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; - if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) - intel_dp->DP |= DP_SYNC_HS_HIGH; - if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) - intel_dp->DP |= DP_SYNC_VS_HIGH; + /* Handle DP bits in common between all three register formats */ - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) - intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; - else - intel_dp->DP |= DP_LINK_TRAIN_OFF; + intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; switch (intel_dp->lane_count) { case 1: @@ -774,134 +895,301 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, intel_dp->DP |= DP_PORT_WIDTH_4; break; } - if (intel_dp->has_audio) + if (intel_dp->has_audio) { + DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n", + pipe_name(intel_crtc->pipe)); intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; - + intel_write_eld(encoder, adjusted_mode); + } memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); intel_dp->link_configuration[0] = intel_dp->link_bw; intel_dp->link_configuration[1] = intel_dp->lane_count; - + intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B; /* * Check for DPCD version > 1.1 and enhanced framing support */ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) { intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; - intel_dp->DP |= DP_ENHANCED_FRAMING; } - /* CPT DP's pipe select is decided in TRANS_DP_CTL */ - if (intel_crtc->pipe == 1 && !HAS_PCH_CPT(dev)) - intel_dp->DP |= DP_PIPEB_SELECT; + /* Split out the IBX/CPU vs CPT settings */ + + if (is_cpu_edp(intel_dp) && IS_GEN7(dev)) { + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + intel_dp->DP |= DP_SYNC_HS_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + intel_dp->DP |= DP_SYNC_VS_HIGH; + intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; + + if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN) + intel_dp->DP |= DP_ENHANCED_FRAMING; + + intel_dp->DP |= intel_crtc->pipe << 29; - if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) { /* don't miss out required setting for eDP */ intel_dp->DP |= DP_PLL_ENABLE; if (adjusted_mode->clock < 200000) intel_dp->DP |= DP_PLL_FREQ_160MHZ; else intel_dp->DP |= DP_PLL_FREQ_270MHZ; + } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) { + intel_dp->DP |= intel_dp->color_range; + + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + intel_dp->DP |= DP_SYNC_HS_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + intel_dp->DP |= DP_SYNC_VS_HIGH; + intel_dp->DP |= DP_LINK_TRAIN_OFF; + + if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN) + intel_dp->DP |= DP_ENHANCED_FRAMING; + + if (intel_crtc->pipe == 1) + intel_dp->DP |= DP_PIPEB_SELECT; + + if (is_cpu_edp(intel_dp)) { + /* don't miss out required setting for eDP */ + intel_dp->DP |= DP_PLL_ENABLE; + if (adjusted_mode->clock < 200000) + intel_dp->DP |= DP_PLL_FREQ_160MHZ; + else + intel_dp->DP |= DP_PLL_FREQ_270MHZ; + } + } else { + intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; + } +} + +#define IDLE_ON_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) +#define IDLE_ON_VALUE (PP_ON | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_ON_IDLE) + +#define IDLE_OFF_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) +#define IDLE_OFF_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE) + +#define IDLE_CYCLE_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK) +#define IDLE_CYCLE_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE) + +static void ironlake_wait_panel_status(struct intel_dp *intel_dp, + u32 mask, + u32 value) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + DRM_DEBUG_KMS("mask %08x value %08x status %08x control %08x\n", + mask, value, + I915_READ(PCH_PP_STATUS), + I915_READ(PCH_PP_CONTROL)); + + if (_wait_for((I915_READ(PCH_PP_STATUS) & mask) == value, 5000, 10)) { + DRM_ERROR("Panel status timeout: status %08x control %08x\n", + I915_READ(PCH_PP_STATUS), + I915_READ(PCH_PP_CONTROL)); } } +static void ironlake_wait_panel_on(struct intel_dp *intel_dp) +{ + DRM_DEBUG_KMS("Wait for panel power on\n"); + ironlake_wait_panel_status(intel_dp, IDLE_ON_MASK, IDLE_ON_VALUE); +} + +static void ironlake_wait_panel_off(struct intel_dp *intel_dp) +{ + DRM_DEBUG_KMS("Wait for panel power off time\n"); + ironlake_wait_panel_status(intel_dp, IDLE_OFF_MASK, IDLE_OFF_VALUE); +} + +static void ironlake_wait_panel_power_cycle(struct intel_dp *intel_dp) +{ + DRM_DEBUG_KMS("Wait for panel power cycle\n"); + ironlake_wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE); +} + + +/* Read the current pp_control value, unlocking the register if it + * is locked + */ + +static u32 ironlake_get_pp_control(struct drm_i915_private *dev_priv) +{ + u32 control = I915_READ(PCH_PP_CONTROL); + + control &= ~PANEL_UNLOCK_MASK; + control |= PANEL_UNLOCK_REGS; + return control; +} + static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; - /* - * If the panel wasn't on, make sure there's not a currently - * active PP sequence before enabling AUX VDD. - */ - if (!(I915_READ(PCH_PP_STATUS) & PP_ON)) - msleep(dev_priv->panel_t3); + if (!is_edp(intel_dp)) + return; + DRM_DEBUG_KMS("Turn eDP VDD on\n"); + + WARN(intel_dp->want_panel_vdd, + "eDP VDD already requested on\n"); + + intel_dp->want_panel_vdd = true; - pp = I915_READ(PCH_PP_CONTROL); + if (ironlake_edp_have_panel_vdd(intel_dp)) { + DRM_DEBUG_KMS("eDP VDD already on\n"); + return; + } + + if (!ironlake_edp_have_panel_power(intel_dp)) + ironlake_wait_panel_power_cycle(intel_dp); + + pp = ironlake_get_pp_control(dev_priv); pp |= EDP_FORCE_VDD; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); + DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", + I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); + + /* + * If the panel wasn't on, delay before accessing aux channel + */ + if (!ironlake_edp_have_panel_power(intel_dp)) { + DRM_DEBUG_KMS("eDP was not running\n"); + msleep(intel_dp->panel_power_up_delay); + } } -static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp) +static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; - pp = I915_READ(PCH_PP_CONTROL); - pp &= ~EDP_FORCE_VDD; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) { + pp = ironlake_get_pp_control(dev_priv); + pp &= ~EDP_FORCE_VDD; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + + /* Make sure sequencer is idle before allowing subsequent activity */ + DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", + I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); - /* Make sure sequencer is idle before allowing subsequent activity */ - msleep(dev_priv->panel_t12); + msleep(intel_dp->panel_power_down_delay); + } } -/* Returns true if the panel was already on when called */ -static bool ironlake_edp_panel_on (struct intel_dp *intel_dp) +static void ironlake_panel_vdd_work(struct work_struct *__work) +{ + struct intel_dp *intel_dp = container_of(to_delayed_work(__work), + struct intel_dp, panel_vdd_work); + struct drm_device *dev = intel_dp->base.base.dev; + + mutex_lock(&dev->mode_config.mutex); + ironlake_panel_vdd_off_sync(intel_dp); + mutex_unlock(&dev->mode_config.mutex); +} + +static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) +{ + if (!is_edp(intel_dp)) + return; + + DRM_DEBUG_KMS("Turn eDP VDD off %d\n", intel_dp->want_panel_vdd); + WARN(!intel_dp->want_panel_vdd, "eDP VDD not forced on"); + + intel_dp->want_panel_vdd = false; + + if (sync) { + ironlake_panel_vdd_off_sync(intel_dp); + } else { + /* + * Queue the timer to fire a long + * time from now (relative to the power down delay) + * to keep the panel power up across a sequence of operations + */ + schedule_delayed_work(&intel_dp->panel_vdd_work, + msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5)); + } +} + +static void ironlake_edp_panel_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_STATE_ON_IDLE; + u32 pp; - if (I915_READ(PCH_PP_STATUS) & PP_ON) - return true; + if (!is_edp(intel_dp)) + return; - pp = I915_READ(PCH_PP_CONTROL); + DRM_DEBUG_KMS("Turn eDP power on\n"); - /* ILK workaround: disable reset around power sequence */ - pp &= ~PANEL_POWER_RESET; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + if (ironlake_edp_have_panel_power(intel_dp)) { + DRM_DEBUG_KMS("eDP power already on\n"); + return; + } - pp |= PANEL_UNLOCK_REGS | POWER_TARGET_ON; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + ironlake_wait_panel_power_cycle(intel_dp); + + pp = ironlake_get_pp_control(dev_priv); + if (IS_GEN5(dev)) { + /* ILK workaround: disable reset around power sequence */ + pp &= ~PANEL_POWER_RESET; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + } - if (wait_for((I915_READ(PCH_PP_STATUS) & idle_on_mask) == idle_on_mask, - 5000)) - DRM_ERROR("panel on wait timed out: 0x%08x\n", - I915_READ(PCH_PP_STATUS)); + pp |= POWER_TARGET_ON; + if (!IS_GEN5(dev)) + pp |= PANEL_POWER_RESET; - pp |= PANEL_POWER_RESET; /* restore panel reset bit */ I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); - return false; + ironlake_wait_panel_on(intel_dp); + + if (IS_GEN5(dev)) { + pp |= PANEL_POWER_RESET; /* restore panel reset bit */ + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + } } -static void ironlake_edp_panel_off (struct drm_device *dev) +static void ironlake_edp_panel_off(struct intel_dp *intel_dp) { + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp, idle_off_mask = PP_ON | PP_SEQUENCE_MASK | - PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK; + u32 pp; - pp = I915_READ(PCH_PP_CONTROL); + if (!is_edp(intel_dp)) + return; - /* ILK workaround: disable reset around power sequence */ - pp &= ~PANEL_POWER_RESET; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + DRM_DEBUG_KMS("Turn eDP power off\n"); - pp &= ~POWER_TARGET_ON; + WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n"); + + pp = ironlake_get_pp_control(dev_priv); + /* We need to switch off panel power _and_ force vdd, for otherwise some + * panels get very unhappy and cease to work. */ + pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE); I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); - if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000)) - DRM_ERROR("panel off wait timed out: 0x%08x\n", - I915_READ(PCH_PP_STATUS)); + intel_dp->want_panel_vdd = false; - pp |= PANEL_POWER_RESET; /* restore panel reset bit */ - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + ironlake_wait_panel_off(intel_dp); } -static void ironlake_edp_backlight_on (struct drm_device *dev) +static void ironlake_edp_backlight_on(struct intel_dp *intel_dp) { + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + if (!is_edp(intel_dp)) + return; + DRM_DEBUG_KMS("\n"); /* * If we enable the backlight right away following a panel power @@ -909,21 +1197,28 @@ static void ironlake_edp_backlight_on (struct drm_device *dev) * link. So delay a bit to make sure the image is solid before * allowing it to appear. */ - msleep(300); - pp = I915_READ(PCH_PP_CONTROL); + msleep(intel_dp->backlight_on_delay); + pp = ironlake_get_pp_control(dev_priv); pp |= EDP_BLC_ENABLE; I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); } -static void ironlake_edp_backlight_off (struct drm_device *dev) +static void ironlake_edp_backlight_off(struct intel_dp *intel_dp) { + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + if (!is_edp(intel_dp)) + return; + DRM_DEBUG_KMS("\n"); - pp = I915_READ(PCH_PP_CONTROL); + pp = ironlake_get_pp_control(dev_priv); pp &= ~EDP_BLC_ENABLE; I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + msleep(intel_dp->backlight_off_delay); } static void ironlake_edp_pll_on(struct drm_encoder *encoder) @@ -986,19 +1281,14 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) static void intel_dp_prepare(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_device *dev = encoder->dev; - /* Wake up the sink first */ - intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); - if (is_edp(intel_dp)) { - ironlake_edp_backlight_off(dev); - ironlake_edp_panel_off(dev); - if (!is_pch_edp(intel_dp)) - ironlake_edp_pll_on(encoder); - else - ironlake_edp_pll_off(encoder); - } + /* Make sure the panel is off before trying to change the mode. But also + * ensure that we have vdd while we switch off the panel. */ + ironlake_edp_panel_vdd_on(intel_dp); + ironlake_edp_backlight_off(intel_dp); + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); + ironlake_edp_panel_off(intel_dp); intel_dp_link_down(intel_dp); } @@ -1006,21 +1296,20 @@ static void intel_dp_commit(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct drm_device *dev = encoder->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc); - if (is_edp(intel_dp)) - ironlake_edp_panel_vdd_on(intel_dp); - + ironlake_edp_panel_vdd_on(intel_dp); + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); - - if (is_edp(intel_dp)) { - ironlake_edp_panel_on(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp); - } - + ironlake_edp_panel_on(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, true); intel_dp_complete_link_train(intel_dp); + ironlake_edp_backlight_on(intel_dp); - if (is_edp(intel_dp)) - ironlake_edp_backlight_on(dev); + intel_dp->dpms_mode = DRM_MODE_DPMS_ON; + + if (HAS_PCH_CPT(dev)) + intel_cpt_verify_modeset(dev, intel_crtc->pipe); } static void @@ -1032,29 +1321,31 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) uint32_t dp_reg = I915_READ(intel_dp->output_reg); if (mode != DRM_MODE_DPMS_ON) { - if (is_edp(intel_dp)) - ironlake_edp_backlight_off(dev); + /* Switching the panel off requires vdd. */ + ironlake_edp_panel_vdd_on(intel_dp); + ironlake_edp_backlight_off(intel_dp); intel_dp_sink_dpms(intel_dp, mode); + ironlake_edp_panel_off(intel_dp); intel_dp_link_down(intel_dp); - if (is_edp(intel_dp)) - ironlake_edp_panel_off(dev); - if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) + + if (is_cpu_edp(intel_dp)) ironlake_edp_pll_off(encoder); } else { - if (is_edp(intel_dp)) - ironlake_edp_panel_vdd_on(intel_dp); + if (is_cpu_edp(intel_dp)) + ironlake_edp_pll_on(encoder); + + ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, mode); if (!(dp_reg & DP_PORT_EN)) { intel_dp_start_link_train(intel_dp); - if (is_edp(intel_dp)) { - ironlake_edp_panel_on(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp); - } + ironlake_edp_panel_on(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, true); intel_dp_complete_link_train(intel_dp); - } - if (is_edp(intel_dp)) - ironlake_edp_backlight_on(dev); + } else + ironlake_edp_panel_vdd_off(intel_dp, false); + ironlake_edp_backlight_on(intel_dp); } + intel_dp->dpms_mode = mode; } /* @@ -1087,11 +1378,11 @@ intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address, * link status information */ static bool -intel_dp_get_link_status(struct intel_dp *intel_dp) +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { return intel_dp_aux_native_read_retry(intel_dp, DP_LANE0_1_STATUS, - intel_dp->link_status, + link_status, DP_LINK_STATUS_SIZE); } @@ -1103,27 +1394,25 @@ intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], } static uint8_t -intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], +intel_get_adjust_request_voltage(uint8_t adjust_request[2], int lane) { - int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); int s = ((lane & 1) ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); - uint8_t l = intel_dp_link_status(link_status, i); + uint8_t l = adjust_request[lane>>1]; return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; } static uint8_t -intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], +intel_get_adjust_request_pre_emphasis(uint8_t adjust_request[2], int lane) { - int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); int s = ((lane & 1) ? DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); - uint8_t l = intel_dp_link_status(link_status, i); + uint8_t l = adjust_request[lane>>1]; return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; } @@ -1145,34 +1434,63 @@ static char *link_train_names[] = { * These are source-specific values; current Intel hardware supports * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB */ -#define I830_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_800 static uint8_t -intel_dp_pre_emphasis_max(uint8_t voltage_swing) +intel_dp_voltage_max(struct intel_dp *intel_dp) { - switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_600: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; - case DP_TRAIN_VOLTAGE_SWING_1200: - default: - return DP_TRAIN_PRE_EMPHASIS_0; + struct drm_device *dev = intel_dp->base.base.dev; + + if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) + return DP_TRAIN_VOLTAGE_SWING_800; + else if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) + return DP_TRAIN_VOLTAGE_SWING_1200; + else + return DP_TRAIN_VOLTAGE_SWING_800; +} + +static uint8_t +intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) +{ + struct drm_device *dev = intel_dp->base.base.dev; + + if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { + switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_800: + return DP_TRAIN_PRE_EMPHASIS_3_5; + default: + return DP_TRAIN_PRE_EMPHASIS_0; + } + } else { + switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_600: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_800: + return DP_TRAIN_PRE_EMPHASIS_3_5; + case DP_TRAIN_VOLTAGE_SWING_1200: + default: + return DP_TRAIN_PRE_EMPHASIS_0; + } } } static void -intel_get_adjust_train(struct intel_dp *intel_dp) +intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { uint8_t v = 0; uint8_t p = 0; int lane; + uint8_t *adjust_request = link_status + (DP_ADJUST_REQUEST_LANE0_1 - DP_LANE0_1_STATUS); + uint8_t voltage_max; + uint8_t preemph_max; for (lane = 0; lane < intel_dp->lane_count; lane++) { - uint8_t this_v = intel_get_adjust_request_voltage(intel_dp->link_status, lane); - uint8_t this_p = intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); + uint8_t this_v = intel_get_adjust_request_voltage(adjust_request, lane); + uint8_t this_p = intel_get_adjust_request_pre_emphasis(adjust_request, lane); if (this_v > v) v = this_v; @@ -1180,18 +1498,20 @@ intel_get_adjust_train(struct intel_dp *intel_dp) p = this_p; } - if (v >= I830_DP_VOLTAGE_MAX) - v = I830_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED; + voltage_max = intel_dp_voltage_max(intel_dp); + if (v >= voltage_max) + v = voltage_max | DP_TRAIN_MAX_SWING_REACHED; - if (p >= intel_dp_pre_emphasis_max(v)) - p = intel_dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + preemph_max = intel_dp_pre_emphasis_max(intel_dp, v); + if (p >= preemph_max) + p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; for (lane = 0; lane < 4; lane++) intel_dp->train_set[lane] = v | p; } static uint32_t -intel_dp_signal_levels(uint8_t train_set, int lane_count) +intel_dp_signal_levels(uint8_t train_set) { uint32_t signal_levels = 0; @@ -1256,13 +1576,43 @@ intel_gen6_edp_signal_levels(uint8_t train_set) } } +/* Gen7's DP voltage swing and pre-emphasis control */ +static uint32_t +intel_gen7_edp_signal_levels(uint8_t train_set) +{ + int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | + DP_TRAIN_PRE_EMPHASIS_MASK); + switch (signal_levels) { + case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: + return EDP_LINK_TRAIN_400MV_0DB_IVB; + case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: + return EDP_LINK_TRAIN_400MV_3_5DB_IVB; + case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: + return EDP_LINK_TRAIN_400MV_6DB_IVB; + + case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: + return EDP_LINK_TRAIN_600MV_0DB_IVB; + case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: + return EDP_LINK_TRAIN_600MV_3_5DB_IVB; + + case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: + return EDP_LINK_TRAIN_800MV_0DB_IVB; + case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: + return EDP_LINK_TRAIN_800MV_3_5DB_IVB; + + default: + DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" + "0x%x\n", signal_levels); + return EDP_LINK_TRAIN_500MV_0DB_IVB; + } +} + static uint8_t intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) { - int i = DP_LANE0_1_STATUS + (lane >> 1); int s = (lane & 1) * 4; - uint8_t l = intel_dp_link_status(link_status, i); + uint8_t l = link_status[lane>>1]; return (l >> s) & 0xf; } @@ -1287,18 +1637,18 @@ intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count DP_LANE_CHANNEL_EQ_DONE|\ DP_LANE_SYMBOL_LOCKED) static bool -intel_channel_eq_ok(struct intel_dp *intel_dp) +intel_channel_eq_ok(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { uint8_t lane_align; uint8_t lane_status; int lane; - lane_align = intel_dp_link_status(intel_dp->link_status, + lane_align = intel_dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED); if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) return false; for (lane = 0; lane < intel_dp->lane_count; lane++) { - lane_status = intel_get_lane_status(intel_dp->link_status, lane); + lane_status = intel_get_lane_status(link_status, lane); if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) return false; } @@ -1323,8 +1673,9 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_LANE0_SET, - intel_dp->train_set, 4); - if (ret != 4) + intel_dp->train_set, + intel_dp->lane_count); + if (ret != intel_dp->lane_count) return false; return true; @@ -1340,14 +1691,20 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) int i; uint8_t voltage; bool clock_recovery = false; - int tries; + int voltage_tries, loop_tries; u32 reg; uint32_t DP = intel_dp->DP; - /* Enable output, wait for it to become active */ - I915_WRITE(intel_dp->output_reg, intel_dp->DP); - POSTING_READ(intel_dp->output_reg); - intel_wait_for_vblank(dev, intel_crtc->pipe); + /* + * On CPT we have to enable the port in training pattern 1, which + * will happen below in intel_dp_set_link_train. Otherwise, enable + * the port and wait for it to become active. + */ + if (!HAS_PCH_CPT(dev)) { + I915_WRITE(intel_dp->output_reg, intel_dp->DP); + POSTING_READ(intel_dp->output_reg); + intel_wait_for_vblank(dev, intel_crtc->pipe); + } /* Write the link configuration data */ intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET, @@ -1355,40 +1712,53 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) DP_LINK_CONFIGURATION_SIZE); DP |= DP_PORT_EN; - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + + if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) DP &= ~DP_LINK_TRAIN_MASK_CPT; else DP &= ~DP_LINK_TRAIN_MASK; memset(intel_dp->train_set, 0, 4); voltage = 0xff; - tries = 0; + voltage_tries = 0; + loop_tries = 0; clock_recovery = false; for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + uint8_t link_status[DP_LINK_STATUS_SIZE]; uint32_t signal_levels; - if (IS_GEN6(dev) && is_edp(intel_dp)) { + + + if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { + signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); + DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; + } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; } else { - signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count); + signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); + DRM_DEBUG_KMS("training pattern 1 signal levels %08x\n", signal_levels); DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_PAT_1_CPT; else reg = DP | DP_LINK_TRAIN_PAT_1; if (!intel_dp_set_link_train(intel_dp, reg, - DP_TRAINING_PATTERN_1)) + DP_TRAINING_PATTERN_1 | + DP_LINK_SCRAMBLING_DISABLE)) break; /* Set training pattern 1 */ udelay(100); - if (!intel_dp_get_link_status(intel_dp)) + if (!intel_dp_get_link_status(intel_dp, link_status)) { + DRM_ERROR("failed to get link status\n"); break; + } - if (intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + if (intel_clock_recovery_ok(link_status, intel_dp->lane_count)) { + DRM_DEBUG_KMS("clock recovery OK\n"); clock_recovery = true; break; } @@ -1397,20 +1767,30 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) for (i = 0; i < intel_dp->lane_count; i++) if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; - if (i == intel_dp->lane_count) - break; + if (i == intel_dp->lane_count) { + ++loop_tries; + if (loop_tries == 5) { + DRM_DEBUG_KMS("too many full retries, give up\n"); + break; + } + memset(intel_dp->train_set, 0, 4); + voltage_tries = 0; + continue; + } /* Check to see if we've tried the same voltage 5 times */ if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { - ++tries; - if (tries == 5) + ++voltage_tries; + if (voltage_tries == 5) { + DRM_DEBUG_KMS("too many voltage retries, give up\n"); break; + } } else - tries = 0; + voltage_tries = 0; voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; /* Compute new intel_dp->train_set as requested by target */ - intel_get_adjust_train(intel_dp); + intel_get_adjust_train(intel_dp, link_status); } intel_dp->DP = DP; @@ -1433,6 +1813,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint32_t signal_levels; + uint8_t link_status[DP_LINK_STATUS_SIZE]; if (cr_tries > 5) { DRM_ERROR("failed to train DP, aborting\n"); @@ -1440,36 +1821,40 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) break; } - if (IS_GEN6(dev) && is_edp(intel_dp)) { + if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { + signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); + DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; + } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; } else { - signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count); + signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_PAT_2_CPT; else reg = DP | DP_LINK_TRAIN_PAT_2; /* channel eq pattern */ if (!intel_dp_set_link_train(intel_dp, reg, - DP_TRAINING_PATTERN_2)) + DP_TRAINING_PATTERN_2 | + DP_LINK_SCRAMBLING_DISABLE)) break; udelay(400); - if (!intel_dp_get_link_status(intel_dp)) + if (!intel_dp_get_link_status(intel_dp, link_status)) break; /* Make sure clock is still ok */ - if (!intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + if (!intel_clock_recovery_ok(link_status, intel_dp->lane_count)) { intel_dp_start_link_train(intel_dp); cr_tries++; continue; } - if (intel_channel_eq_ok(intel_dp)) { + if (intel_channel_eq_ok(intel_dp, link_status)) { channel_eq = true; break; } @@ -1484,11 +1869,11 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) } /* Compute new intel_dp->train_set as requested by target */ - intel_get_adjust_train(intel_dp); + intel_get_adjust_train(intel_dp, link_status); ++tries; } - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_OFF_CPT; else reg = DP | DP_LINK_TRAIN_OFF; @@ -1518,7 +1903,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) udelay(100); } - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) { + if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) { DP &= ~DP_LINK_TRAIN_MASK_CPT; I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT); } else { @@ -1529,8 +1914,12 @@ intel_dp_link_down(struct intel_dp *intel_dp) msleep(17); - if (is_edp(intel_dp)) - DP |= DP_LINK_TRAIN_OFF; + if (is_edp(intel_dp)) { + if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) + DP |= DP_LINK_TRAIN_OFF_CPT; + else + DP |= DP_LINK_TRAIN_OFF; + } if (!HAS_PCH_CPT(dev) && I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) { @@ -1568,6 +1957,40 @@ intel_dp_link_down(struct intel_dp *intel_dp) DP &= ~DP_AUDIO_OUTPUT_ENABLE; I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); POSTING_READ(intel_dp->output_reg); + msleep(intel_dp->panel_power_down_delay); +} + +static bool +intel_dp_get_dpcd(struct intel_dp *intel_dp) +{ + if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, + sizeof(intel_dp->dpcd)) && + (intel_dp->dpcd[DP_DPCD_REV] != 0)) { + return true; + } + + return false; +} + +static bool +intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector) +{ + int ret; + + ret = intel_dp_aux_native_read_retry(intel_dp, + DP_DEVICE_SERVICE_IRQ_VECTOR, + sink_irq_vector, 1); + if (!ret) + return false; + + return true; +} + +static void +intel_dp_handle_test_request(struct intel_dp *intel_dp) +{ + /* NAK by default */ + intel_dp_aux_native_write_1(intel_dp, DP_TEST_RESPONSE, DP_TEST_ACK); } /* @@ -1582,36 +2005,61 @@ intel_dp_link_down(struct intel_dp *intel_dp) static void intel_dp_check_link_status(struct intel_dp *intel_dp) { - int ret; + u8 sink_irq_vector; + u8 link_status[DP_LINK_STATUS_SIZE]; + + if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON) + return; if (!intel_dp->base.base.crtc) return; - if (!intel_dp_get_link_status(intel_dp)) { + /* Try to read receiver status if the link appears to be up */ + if (!intel_dp_get_link_status(intel_dp, link_status)) { intel_dp_link_down(intel_dp); return; } - /* Try to read receiver status if the link appears to be up */ - ret = intel_dp_aux_native_read(intel_dp, - 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)); - if (ret != sizeof(intel_dp->dpcd)) { + /* Now read the DPCD to see if it's actually running */ + if (!intel_dp_get_dpcd(intel_dp)) { intel_dp_link_down(intel_dp); return; } - if (!intel_channel_eq_ok(intel_dp)) { + /* Try to read the source of the interrupt */ + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && + intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) { + /* Clear interrupt source */ + intel_dp_aux_native_write_1(intel_dp, + DP_DEVICE_SERVICE_IRQ_VECTOR, + sink_irq_vector); + + if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST) + intel_dp_handle_test_request(intel_dp); + if (sink_irq_vector & (DP_CP_IRQ | DP_SINK_SPECIFIC_IRQ)) + DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); + } + + if (!intel_channel_eq_ok(intel_dp, link_status)) { + DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n", + drm_get_encoder_name(&intel_dp->base.base)); intel_dp_start_link_train(intel_dp); intel_dp_complete_link_train(intel_dp); } } static enum drm_connector_status +intel_dp_detect_dpcd(struct intel_dp *intel_dp) +{ + if (intel_dp_get_dpcd(intel_dp)) + return connector_status_connected; + return connector_status_disconnected; +} + +static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { enum drm_connector_status status; - bool ret; /* Can't disconnect eDP, but you can close the lid... */ if (is_edp(intel_dp)) { @@ -1621,15 +2069,7 @@ ironlake_dp_detect(struct intel_dp *intel_dp) return status; } - status = connector_status_disconnected; - ret = intel_dp_aux_native_read_retry(intel_dp, - 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)); - if (ret && intel_dp->dpcd[DP_DPCD_REV] != 0) - status = connector_status_connected; - DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0], - intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]); - return status; + return intel_dp_detect_dpcd(intel_dp); } static enum drm_connector_status @@ -1637,7 +2077,6 @@ g4x_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum drm_connector_status status; uint32_t temp, bit; switch (intel_dp->output_reg) { @@ -1659,15 +2098,7 @@ g4x_dp_detect(struct intel_dp *intel_dp) if ((temp & bit) == 0) return connector_status_disconnected; - status = connector_status_disconnected; - if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) - { - if (intel_dp->dpcd[DP_DPCD_REV] != 0) - status = connector_status_connected; - } - - return status; + return intel_dp_detect_dpcd(intel_dp); } static struct edid * @@ -1678,7 +2109,7 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) ironlake_edp_panel_vdd_on(intel_dp); edid = drm_get_edid(connector, adapter); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); return edid; } @@ -1690,7 +2121,7 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada ironlake_edp_panel_vdd_on(intel_dp); ret = intel_ddc_get_modes(connector, adapter); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); return ret; } @@ -1715,6 +2146,12 @@ intel_dp_detect(struct drm_connector *connector, bool force) status = ironlake_dp_detect(intel_dp); else status = g4x_dp_detect(intel_dp); + + DRM_DEBUG_KMS("DPCD: %02hx%02hx%02hx%02hx%02hx%02hx%02hx%02hx\n", + intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2], + intel_dp->dpcd[3], intel_dp->dpcd[4], intel_dp->dpcd[5], + intel_dp->dpcd[6], intel_dp->dpcd[7]); + if (status != connector_status_connected) return status; @@ -1744,26 +2181,34 @@ static int intel_dp_get_modes(struct drm_connector *connector) ret = intel_dp_get_edid_modes(connector, &intel_dp->adapter); if (ret) { - if (is_edp(intel_dp) && !dev_priv->panel_fixed_mode) { + if (is_edp(intel_dp) && !intel_dp->panel_fixed_mode) { struct drm_display_mode *newmode; list_for_each_entry(newmode, &connector->probed_modes, head) { - if (newmode->type & DRM_MODE_TYPE_PREFERRED) { - dev_priv->panel_fixed_mode = + if ((newmode->type & DRM_MODE_TYPE_PREFERRED)) { + intel_dp->panel_fixed_mode = drm_mode_duplicate(dev, newmode); break; } } } - return ret; } /* if eDP has no EDID, try to use fixed panel mode from VBT */ if (is_edp(intel_dp)) { - if (dev_priv->panel_fixed_mode != NULL) { + /* initialize panel mode from VBT if available for eDP */ + if (intel_dp->panel_fixed_mode == NULL && dev_priv->lfp_lvds_vbt_mode != NULL) { + intel_dp->panel_fixed_mode = + drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + if (intel_dp->panel_fixed_mode) { + intel_dp->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + } + } + if (intel_dp->panel_fixed_mode) { struct drm_display_mode *mode; - mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); + mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode); drm_mode_probed_add(connector, mode); return 1; } @@ -1845,7 +2290,7 @@ done: } static void -intel_dp_destroy (struct drm_connector *connector) +intel_dp_destroy(struct drm_connector *connector) { drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -1858,6 +2303,10 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder) i2c_del_adapter(&intel_dp->adapter); drm_encoder_cleanup(encoder); + if (is_edp(intel_dp)) { + cancel_delayed_work_sync(&intel_dp->panel_vdd_work); + ironlake_panel_vdd_off_sync(intel_dp); + } kfree(intel_dp); } @@ -1897,7 +2346,7 @@ intel_dp_hot_plug(struct intel_encoder *intel_encoder) /* Return which DP Port should be selected for Transcoder DP control */ int -intel_trans_dp_port_sel (struct drm_crtc *crtc) +intel_trans_dp_port_sel(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_mode_config *mode_config = &dev->mode_config; @@ -1910,7 +2359,8 @@ intel_trans_dp_port_sel (struct drm_crtc *crtc) continue; intel_dp = enc_to_intel_dp(encoder); - if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT) + if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT || + intel_dp->base.type == INTEL_OUTPUT_EDP) return intel_dp->output_reg; } @@ -1960,6 +2410,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) return; intel_dp->output_reg = output_reg; + intel_dp->dpms_mode = -1; intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL); if (!intel_connector) { @@ -1993,10 +2444,13 @@ intel_dp_init(struct drm_device *dev, int output_reg) else if (output_reg == DP_D || output_reg == PCH_DP_D) intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT); - if (is_edp(intel_dp)) + if (is_edp(intel_dp)) { intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT); + INIT_DELAYED_WORK(&intel_dp->panel_vdd_work, + ironlake_panel_vdd_work); + } - intel_encoder->crtc_mask = (1 << 0) | (1 << 1); + intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); connector->interlace_allowed = true; connector->doublescan_allowed = 0; @@ -2032,28 +2486,60 @@ intel_dp_init(struct drm_device *dev, int output_reg) break; } - intel_dp_i2c_init(intel_dp, intel_connector, name); - /* Cache some DPCD data in the eDP case */ if (is_edp(intel_dp)) { - int ret; - u32 pp_on, pp_div; + bool ret; + struct edp_power_seq cur, vbt; + u32 pp_on, pp_off, pp_div; pp_on = I915_READ(PCH_PP_ON_DELAYS); + pp_off = I915_READ(PCH_PP_OFF_DELAYS); pp_div = I915_READ(PCH_PP_DIVISOR); - /* Get T3 & T12 values (note: VESA not bspec terminology) */ - dev_priv->panel_t3 = (pp_on & 0x1fff0000) >> 16; - dev_priv->panel_t3 /= 10; /* t3 in 100us units */ - dev_priv->panel_t12 = pp_div & 0xf; - dev_priv->panel_t12 *= 100; /* t12 in 100ms units */ + /* Pull timing values out of registers */ + cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> + PANEL_POWER_UP_DELAY_SHIFT; + + cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> + PANEL_LIGHT_ON_DELAY_SHIFT; + + cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> + PANEL_LIGHT_OFF_DELAY_SHIFT; + + cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> + PANEL_POWER_DOWN_DELAY_SHIFT; + + cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> + PANEL_POWER_CYCLE_DELAY_SHIFT) * 1000; + + DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); + + vbt = dev_priv->edp.pps; + + DRM_DEBUG_KMS("vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + vbt.t1_t3, vbt.t8, vbt.t9, vbt.t10, vbt.t11_t12); + +#define get_delay(field) ((max(cur.field, vbt.field) + 9) / 10) + + intel_dp->panel_power_up_delay = get_delay(t1_t3); + intel_dp->backlight_on_delay = get_delay(t8); + intel_dp->backlight_off_delay = get_delay(t9); + intel_dp->panel_power_down_delay = get_delay(t10); + intel_dp->panel_power_cycle_delay = get_delay(t11_t12); + + DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", + intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, + intel_dp->panel_power_cycle_delay); + + DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", + intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); ironlake_edp_panel_vdd_on(intel_dp); - ret = intel_dp_aux_native_read(intel_dp, DP_DPCD_REV, - intel_dp->dpcd, - sizeof(intel_dp->dpcd)); - ironlake_edp_panel_vdd_off(intel_dp); - if (ret == sizeof(intel_dp->dpcd)) { + ret = intel_dp_get_dpcd(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); + + if (ret) { if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) dev_priv->no_aux_handshake = intel_dp->dpcd[DP_MAX_DOWNSPREAD] & @@ -2067,18 +2553,13 @@ intel_dp_init(struct drm_device *dev, int output_reg) } } + intel_dp_i2c_init(intel_dp, intel_connector, name); + intel_encoder->hot_plug = intel_dp_hot_plug; if (is_edp(intel_dp)) { - /* initialize panel mode from VBT if available for eDP */ - if (dev_priv->lfp_lvds_vbt_mode) { - dev_priv->panel_fixed_mode = - drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - if (dev_priv->panel_fixed_mode) { - dev_priv->panel_fixed_mode->type |= - DRM_MODE_TYPE_PREFERRED; - } - } + dev_priv->int_edp_connector = connector; + intel_panel_setup_backlight(dev); } intel_dp_add_properties(intel_dp, connector); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 58b54ff..5212284 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -34,7 +34,7 @@ #define _wait_for(COND, MS, W) ({ \ unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ int ret__ = 0; \ - while (! (COND)) { \ + while (!(COND)) { \ if (time_after(jiffies, timeout__)) { \ ret__ = -ETIMEDOUT; \ break; \ @@ -49,10 +49,10 @@ #define MSLEEP(x) do { \ if (in_dbg_master()) \ - mdelay(x); \ + mdelay(x); \ else \ msleep(x); \ -} while(0) +} while (0) #define KHz(x) (1000*x) #define MHz(x) KHz(1000*x) @@ -110,6 +110,7 @@ /* drm_display_mode->private_flags */ #define INTEL_MODE_PIXEL_MULTIPLIER_SHIFT (0x0) #define INTEL_MODE_PIXEL_MULTIPLIER_MASK (0xf << INTEL_MODE_PIXEL_MULTIPLIER_SHIFT) +#define INTEL_MODE_DP_FORCE_6BPC (0x10) static inline void intel_mode_set_pixel_multiplier(struct drm_display_mode *mode, @@ -170,6 +171,10 @@ struct intel_crtc { int16_t cursor_x, cursor_y; int16_t cursor_width, cursor_height; bool cursor_visible; + unsigned int bpp; + + bool no_pll; /* tertiary pipe for IVB */ + bool use_pll_a; }; #define to_intel_crtc(x) container_of(x, struct intel_crtc, base) @@ -177,10 +182,28 @@ struct intel_crtc { #define to_intel_encoder(x) container_of(x, struct intel_encoder, base) #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base) +#define DIP_HEADER_SIZE 5 + #define DIP_TYPE_AVI 0x82 #define DIP_VERSION_AVI 0x2 #define DIP_LEN_AVI 13 +#define DIP_TYPE_SPD 0x83 +#define DIP_VERSION_SPD 0x1 +#define DIP_LEN_SPD 25 +#define DIP_SPD_UNKNOWN 0 +#define DIP_SPD_DSTB 0x1 +#define DIP_SPD_DVDP 0x2 +#define DIP_SPD_DVHS 0x3 +#define DIP_SPD_HDDVR 0x4 +#define DIP_SPD_DVC 0x5 +#define DIP_SPD_DSC 0x6 +#define DIP_SPD_VCD 0x7 +#define DIP_SPD_GAME 0x8 +#define DIP_SPD_PC 0x9 +#define DIP_SPD_BD 0xa +#define DIP_SPD_SCD 0xb + struct dip_infoframe { uint8_t type; /* HB0 */ uint8_t ver; /* HB1 */ @@ -205,6 +228,11 @@ struct dip_infoframe { uint16_t left_bar_end; uint16_t right_bar_start; } __attribute__ ((packed)) avi; + struct { + uint8_t vn[8]; + uint8_t pd[16]; + uint8_t sdi; + } __attribute__ ((packed)) spd; uint8_t payload[27]; } __attribute__ ((packed)) body; } __attribute__((packed)); @@ -229,10 +257,20 @@ struct intel_unpin_work { struct drm_i915_gem_object *old_fb_obj; struct drm_i915_gem_object *pending_flip_obj; struct drm_pending_vblank_event *event; - int pending; + atomic_t pending; +#define INTEL_FLIP_INACTIVE 0 +#define INTEL_FLIP_PENDING 1 +#define INTEL_FLIP_COMPLETE 2 bool enable_stall_check; }; +struct intel_fbc_work { + struct delayed_work work; + struct drm_crtc *crtc; + struct drm_framebuffer *fb; + int interval; +}; + int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter); extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus); @@ -253,7 +291,7 @@ void intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); extern bool intel_dpd_is_edp(struct drm_device *dev); -extern void intel_edp_link_config (struct intel_encoder *, int *, int *); +extern void intel_edp_link_config(struct intel_encoder *, int *, int *); extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder); /* intel_panel.c */ @@ -266,14 +304,15 @@ extern void intel_pch_panel_fitting(struct drm_device *dev, extern u32 intel_panel_get_max_backlight(struct drm_device *dev); extern u32 intel_panel_get_backlight(struct drm_device *dev); extern void intel_panel_set_backlight(struct drm_device *dev, u32 level); -extern void intel_panel_setup_backlight(struct drm_device *dev); +extern int intel_panel_setup_backlight(struct drm_device *dev); extern void intel_panel_enable_backlight(struct drm_device *dev); extern void intel_panel_disable_backlight(struct drm_device *dev); +extern void intel_panel_destroy_backlight(struct drm_device *dev); extern enum drm_connector_status intel_panel_detect(struct drm_device *dev); extern void intel_crtc_load_lut(struct drm_crtc *crtc); -extern void intel_encoder_prepare (struct drm_encoder *encoder); -extern void intel_encoder_commit (struct drm_encoder *encoder); +extern void intel_encoder_prepare(struct drm_encoder *encoder); +extern void intel_encoder_commit(struct drm_encoder *encoder); extern void intel_encoder_destroy(struct drm_encoder *encoder); static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) @@ -305,9 +344,6 @@ extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, struct drm_connector *connector, struct intel_load_detect_pipe *old); -extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB); -extern int intel_sdvo_supports_hotplug(struct drm_connector *connector); -extern void intel_sdvo_set_hotplug(struct drm_connector *connector, int enable); extern void intelfb_restore(void); extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno); @@ -317,6 +353,7 @@ extern void intel_enable_clock_gating(struct drm_device *dev); extern void ironlake_enable_drps(struct drm_device *dev); extern void ironlake_disable_drps(struct drm_device *dev); extern void gen6_enable_rps(struct drm_i915_private *dev_priv); +extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv); extern void gen6_disable_rps(struct drm_device *dev); extern void intel_init_emon(struct drm_device *dev); @@ -347,4 +384,8 @@ extern void intel_fb_output_poll_changed(struct drm_device *dev); extern void intel_fb_restore_mode(struct drm_device *dev); extern void intel_init_clock_gating(struct drm_device *dev); +extern void intel_write_eld(struct drm_encoder *encoder, + struct drm_display_mode *mode); +extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); + #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 918bac8..497da2a 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -45,6 +45,8 @@ struct intel_hdmi { bool has_hdmi_sink; bool has_audio; int force_audio; + void (*write_infoframe)(struct drm_encoder *encoder, + struct dip_infoframe *frame); }; static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) @@ -58,37 +60,69 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) struct intel_hdmi, base); } -void intel_dip_infoframe_csum(struct dip_infoframe *avi_if) +void intel_dip_infoframe_csum(struct dip_infoframe *frame) { - uint8_t *data = (uint8_t *)avi_if; + uint8_t *data = (uint8_t *)frame; uint8_t sum = 0; unsigned i; - avi_if->checksum = 0; - avi_if->ecc = 0; + frame->checksum = 0; + frame->ecc = 0; - for (i = 0; i < sizeof(*avi_if); i++) + for (i = 0; i < frame->len + DIP_HEADER_SIZE; i++) sum += data[i]; - avi_if->checksum = 0x100 - sum; + frame->checksum = 0x100 - sum; } -static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +static u32 intel_infoframe_index(struct dip_infoframe *frame) { - struct dip_infoframe avi_if = { - .type = DIP_TYPE_AVI, - .ver = DIP_VERSION_AVI, - .len = DIP_LEN_AVI, - }; - uint32_t *data = (uint32_t *)&avi_if; + u32 flags = 0; + + switch (frame->type) { + case DIP_TYPE_AVI: + flags |= VIDEO_DIP_SELECT_AVI; + break; + case DIP_TYPE_SPD: + flags |= VIDEO_DIP_SELECT_SPD; + break; + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + break; + } + + return flags; +} + +static u32 intel_infoframe_flags(struct dip_infoframe *frame) +{ + u32 flags = 0; + + switch (frame->type) { + case DIP_TYPE_AVI: + flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC; + break; + case DIP_TYPE_SPD: + flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC; + break; + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + break; + } + + return flags; +} + +static void i9xx_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - u32 port; - unsigned i; + u32 port, flags, val = I915_READ(VIDEO_DIP_CTL); + unsigned i, len = DIP_HEADER_SIZE + frame->len; - if (!intel_hdmi->has_hdmi_sink) - return; /* XXX first guess at handling video port, is this corrent? */ if (intel_hdmi->sdvox_reg == SDVOB) @@ -98,18 +132,99 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) else return; - I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | - VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC); + flags = intel_infoframe_index(frame); + + val &= ~VIDEO_DIP_SELECT_MASK; - intel_dip_infoframe_csum(&avi_if); - for (i = 0; i < sizeof(avi_if); i += 4) { + I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); + + mmiowb(); + for (i = 0; i < len; i += 4) { I915_WRITE(VIDEO_DIP_DATA, *data); data++; } + /* Write every possible data byte to force correct ECC calculation. */ + for (; i < VIDEO_DIP_DATA_SIZE; i += 4) + I915_WRITE(VIDEO_DIP_DATA, 0); + mmiowb(); + + flags |= intel_infoframe_flags(frame); + + I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); + POSTING_READ(VIDEO_DIP_CTL); +} + +static void ironlake_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + uint32_t *data = (uint32_t *)frame; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); + unsigned i, len = DIP_HEADER_SIZE + frame->len; + u32 flags, val = I915_READ(reg); + + intel_wait_for_vblank(dev, intel_crtc->pipe); + + flags = intel_infoframe_index(frame); + + val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ + + I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + + mmiowb(); + for (i = 0; i < len; i += 4) { + I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); + data++; + } + /* Write every possible data byte to force correct ECC calculation. */ + for (; i < VIDEO_DIP_DATA_SIZE; i += 4) + I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0); + mmiowb(); + + flags |= intel_infoframe_flags(frame); + + I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + POSTING_READ(reg); +} +static void intel_set_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + + if (!intel_hdmi->has_hdmi_sink) + return; + + intel_dip_infoframe_csum(frame); + intel_hdmi->write_infoframe(encoder, frame); +} + +static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +{ + struct dip_infoframe avi_if = { + .type = DIP_TYPE_AVI, + .ver = DIP_VERSION_AVI, + .len = DIP_LEN_AVI, + }; + + intel_set_infoframe(encoder, &avi_if); +} + +static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) +{ + struct dip_infoframe spd_if; + + memset(&spd_if, 0, sizeof(spd_if)); + spd_if.type = DIP_TYPE_SPD; + spd_if.ver = DIP_VERSION_SPD; + spd_if.len = DIP_LEN_SPD; + strcpy(spd_if.body.spd.vn, "Intel"); + strcpy(spd_if.body.spd.pd, "Integrated gfx"); + spd_if.body.spd.sdi = DIP_SPD_PC; - I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | - VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC | - VIDEO_DIP_ENABLE_AVI); + intel_set_infoframe(encoder, &spd_if); } static void intel_hdmi_mode_set(struct drm_encoder *encoder, @@ -124,32 +239,40 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, u32 sdvox; sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE; - sdvox |= intel_hdmi->color_range; + if (!HAS_PCH_SPLIT(dev)) + sdvox |= intel_hdmi->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) sdvox |= SDVO_VSYNC_ACTIVE_HIGH; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) sdvox |= SDVO_HSYNC_ACTIVE_HIGH; + if (intel_crtc->bpp > 24) + sdvox |= COLOR_FORMAT_12bpc; + else + sdvox |= COLOR_FORMAT_8bpc; + /* Required on CPT */ if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev)) sdvox |= HDMI_MODE_SELECT; if (intel_hdmi->has_audio) { + DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n", + pipe_name(intel_crtc->pipe)); sdvox |= SDVO_AUDIO_ENABLE; sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC; + intel_write_eld(encoder, adjusted_mode); } - if (intel_crtc->pipe == 1) { - if (HAS_PCH_CPT(dev)) - sdvox |= PORT_TRANS_B_SEL_CPT; - else - sdvox |= SDVO_PIPE_B_SELECT; - } + if (HAS_PCH_CPT(dev)) + sdvox |= PORT_TRANS_SEL_CPT(intel_crtc->pipe); + else if (intel_crtc->pipe == 1) + sdvox |= SDVO_PIPE_B_SELECT; I915_WRITE(intel_hdmi->sdvox_reg, sdvox); POSTING_READ(intel_hdmi->sdvox_reg); intel_hdmi_set_avi_infoframe(encoder); + intel_hdmi_set_spd_infoframe(encoder); } static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) @@ -379,6 +502,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) struct intel_encoder *intel_encoder; struct intel_connector *intel_connector; struct intel_hdmi *intel_hdmi; + int i; intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL); if (!intel_hdmi) @@ -404,7 +528,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) connector->polled = DRM_CONNECTOR_POLL_HPD; connector->interlace_allowed = 0; connector->doublescan_allowed = 0; - intel_encoder->crtc_mask = (1 << 0) | (1 << 1); + intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); /* Set up the DDC bus. */ if (sdvox_reg == SDVOB) { @@ -431,6 +555,18 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_hdmi->sdvox_reg = sdvox_reg; + if (!HAS_PCH_SPLIT(dev)) { + intel_hdmi->write_infoframe = i9xx_write_infoframe; + I915_WRITE(VIDEO_DIP_CTL, 0); + POSTING_READ(VIDEO_DIP_CTL); + } else { + intel_hdmi->write_infoframe = ironlake_write_infoframe; + for_each_pipe(i) { + I915_WRITE(TVIDEO_DIP_CTL(i), 0); + POSTING_READ(TVIDEO_DIP_CTL(i)); + } + } + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); intel_hdmi_add_properties(intel_hdmi, connector); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index d98cee6..d30cccc 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -28,6 +28,7 @@ */ #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> +#include <linux/export.h> #include "drmP.h" #include "drm.h" #include "intel_drv.h" @@ -422,13 +423,7 @@ void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed) { struct intel_gmbus *bus = to_intel_gmbus(adapter); - /* speed: - * 0x0 = 100 KHz - * 0x1 = 50 KHz - * 0x2 = 400 KHz - * 0x3 = 1000 Khz - */ - bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | (speed << 8); + bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | speed; } void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 09881ac..4da8182 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -72,14 +72,16 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds) { struct drm_device *dev = intel_lvds->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, lvds_reg; + u32 ctl_reg, lvds_reg, stat_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; lvds_reg = PCH_LVDS; + stat_reg = PCH_PP_STATUS; } else { ctl_reg = PP_CONTROL; lvds_reg = LVDS; + stat_reg = PP_STATUS; } I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); @@ -94,17 +96,16 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds) DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", intel_lvds->pfit_control, intel_lvds->pfit_pgm_ratios); - if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) { - DRM_ERROR("timed out waiting for panel to power off\n"); - } else { - I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios); - I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control); - intel_lvds->pfit_dirty = false; - } + + I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios); + I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control); + intel_lvds->pfit_dirty = false; } I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); POSTING_READ(lvds_reg); + if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000)) + DRM_ERROR("timed out waiting for panel to power on\n"); intel_panel_enable_backlight(dev); } @@ -113,24 +114,25 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds) { struct drm_device *dev = intel_lvds->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, lvds_reg; + u32 ctl_reg, lvds_reg, stat_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; lvds_reg = PCH_LVDS; + stat_reg = PCH_PP_STATUS; } else { ctl_reg = PP_CONTROL; lvds_reg = LVDS; + stat_reg = PP_STATUS; } intel_panel_disable_backlight(dev); I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); + if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000)) + DRM_ERROR("timed out waiting for panel to power off\n"); if (intel_lvds->pfit_control) { - if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) - DRM_ERROR("timed out waiting for panel to power off\n"); - I915_WRITE(PFIT_CONTROL, 0); intel_lvds->pfit_dirty = true; } @@ -398,53 +400,15 @@ out: static void intel_lvds_prepare(struct drm_encoder *encoder) { - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - /* We try to do the minimum that is necessary in order to unlock - * the registers for mode setting. - * - * On Ironlake, this is quite simple as we just set the unlock key - * and ignore all subtleties. (This may cause some issues...) - * - * Prior to Ironlake, we must disable the pipe if we want to adjust - * the panel fitter. However at all other times we can just reset - * the registers regardless. - */ - - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_PP_CONTROL, - I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); - } else if (intel_lvds->pfit_dirty) { - I915_WRITE(PP_CONTROL, - (I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS) - & ~POWER_TARGET_ON); - } else { - I915_WRITE(PP_CONTROL, - I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); - } + intel_lvds_disable(intel_lvds); } static void intel_lvds_commit(struct drm_encoder *encoder) { - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - /* Undo any unlocking done in prepare to prevent accidental - * adjustment of the registers. - */ - if (HAS_PCH_SPLIT(dev)) { - u32 val = I915_READ(PCH_PP_CONTROL); - if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) - I915_WRITE(PCH_PP_CONTROL, val & 0x3); - } else { - u32 val = I915_READ(PP_CONTROL); - if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) - I915_WRITE(PP_CONTROL, val & 0x3); - } - /* Always do a full power on as we do not know what state * we were left in. */ @@ -565,6 +529,7 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, mutex_lock(&dev->mode_config.mutex); drm_helper_resume_force_mode(dev); + i915_redisable_vga(dev); mutex_unlock(&dev->mode_config.mutex); return NOTIFY_OK; @@ -648,7 +613,7 @@ static const struct drm_encoder_funcs intel_lvds_enc_funcs = { .destroy = intel_encoder_destroy, }; -static int __init intel_no_lvds_dmi_callback(const struct dmi_system_id *id) +static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id) { DRM_DEBUG_KMS("Skipping LVDS initialization for %s\n", id->ident); return 1; @@ -690,6 +655,14 @@ static const struct dmi_system_id intel_no_lvds[] = { }, { .callback = intel_no_lvds_dmi_callback, + .ident = "Dell OptiPlex FX170", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, .ident = "AOpen Mini PC", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "AOpen"), @@ -736,6 +709,14 @@ static const struct dmi_system_id intel_no_lvds[] = { }, }, { + .callback = intel_no_lvds_dmi_callback, + .ident = "Clientron E830", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Clientron"), + DMI_MATCH(DMI_PRODUCT_NAME, "E830"), + }, + }, + { .callback = intel_no_lvds_dmi_callback, .ident = "Asus EeeBox PC EB1007", .matches = { @@ -745,6 +726,38 @@ static const struct dmi_system_id intel_no_lvds[] = { }, { .callback = intel_no_lvds_dmi_callback, + .ident = "Asus AT5NM10T-I", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Hewlett-Packard HP t5740", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, " t5740"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Hewlett-Packard t5745", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Hewlett-Packard st5747", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, .ident = "MSI Wind Box DC500", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), @@ -901,6 +914,18 @@ bool intel_lvds_init(struct drm_device *dev) int pipe; u8 pin; + /* + * Unlock registers and just leave them unlocked. Do this before + * checking quirk lists to avoid bogus WARNINGs. + */ + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(PCH_PP_CONTROL, + I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); + } else { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); + } + /* Skip init on machines we know falsely report LVDS */ if (dmi_check_system(intel_no_lvds)) return false; @@ -948,9 +973,11 @@ bool intel_lvds_init(struct drm_device *dev) intel_encoder->type = INTEL_OUTPUT_LVDS; intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT); - intel_encoder->crtc_mask = (1 << 1); - if (INTEL_INFO(dev)->gen >= 5) - intel_encoder->crtc_mask |= (1 << 0); + if (HAS_PCH_SPLIT(dev)) + intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); + else + intel_encoder->crtc_mask = (1 << 1); + drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs); drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs); connector->display_info.subpixel_order = SubPixelHorizontalRGB; @@ -1054,7 +1081,8 @@ bool intel_lvds_init(struct drm_device *dev) goto failed; out: - if (HAS_PCH_SPLIT(dev)) { + if (HAS_PCH_SPLIT(dev) && + !(dev_priv->quirks & QUIRK_NO_PCH_PWM_ENABLE)) { u32 pwm; pipe = (I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT) ? 1 : 0; @@ -1081,6 +1109,9 @@ out: /* keep the LVDS connector */ dev_priv->int_lvds_connector = connector; drm_sysfs_connector_add(connector); + + intel_panel_setup_backlight(dev); + return true; failed: diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index 3b26a3b..be2c6fe 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/fb.h> +#include <drm/drm_edid.h> #include "drmP.h" #include "intel_drv.h" #include "i915_drv.h" @@ -74,6 +75,7 @@ int intel_ddc_get_modes(struct drm_connector *connector, if (edid) { drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); connector->display_info.raw_edid = NULL; kfree(edid); } diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index e7a97b5..356a252 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -51,61 +51,61 @@ #define MBOX_ASLE (1<<2) struct opregion_header { - u8 signature[16]; - u32 size; - u32 opregion_ver; - u8 bios_ver[32]; - u8 vbios_ver[16]; - u8 driver_ver[16]; - u32 mboxes; - u8 reserved[164]; + u8 signature[16]; + u32 size; + u32 opregion_ver; + u8 bios_ver[32]; + u8 vbios_ver[16]; + u8 driver_ver[16]; + u32 mboxes; + u8 reserved[164]; } __attribute__((packed)); /* OpRegion mailbox #1: public ACPI methods */ struct opregion_acpi { - u32 drdy; /* driver readiness */ - u32 csts; /* notification status */ - u32 cevt; /* current event */ - u8 rsvd1[20]; - u32 didl[8]; /* supported display devices ID list */ - u32 cpdl[8]; /* currently presented display list */ - u32 cadl[8]; /* currently active display list */ - u32 nadl[8]; /* next active devices list */ - u32 aslp; /* ASL sleep time-out */ - u32 tidx; /* toggle table index */ - u32 chpd; /* current hotplug enable indicator */ - u32 clid; /* current lid state*/ - u32 cdck; /* current docking state */ - u32 sxsw; /* Sx state resume */ - u32 evts; /* ASL supported events */ - u32 cnot; /* current OS notification */ - u32 nrdy; /* driver status */ - u8 rsvd2[60]; + u32 drdy; /* driver readiness */ + u32 csts; /* notification status */ + u32 cevt; /* current event */ + u8 rsvd1[20]; + u32 didl[8]; /* supported display devices ID list */ + u32 cpdl[8]; /* currently presented display list */ + u32 cadl[8]; /* currently active display list */ + u32 nadl[8]; /* next active devices list */ + u32 aslp; /* ASL sleep time-out */ + u32 tidx; /* toggle table index */ + u32 chpd; /* current hotplug enable indicator */ + u32 clid; /* current lid state*/ + u32 cdck; /* current docking state */ + u32 sxsw; /* Sx state resume */ + u32 evts; /* ASL supported events */ + u32 cnot; /* current OS notification */ + u32 nrdy; /* driver status */ + u8 rsvd2[60]; } __attribute__((packed)); /* OpRegion mailbox #2: SWSCI */ struct opregion_swsci { - u32 scic; /* SWSCI command|status|data */ - u32 parm; /* command parameters */ - u32 dslp; /* driver sleep time-out */ - u8 rsvd[244]; + u32 scic; /* SWSCI command|status|data */ + u32 parm; /* command parameters */ + u32 dslp; /* driver sleep time-out */ + u8 rsvd[244]; } __attribute__((packed)); /* OpRegion mailbox #3: ASLE */ struct opregion_asle { - u32 ardy; /* driver readiness */ - u32 aslc; /* ASLE interrupt command */ - u32 tche; /* technology enabled indicator */ - u32 alsi; /* current ALS illuminance reading */ - u32 bclp; /* backlight brightness to set */ - u32 pfit; /* panel fitting state */ - u32 cblv; /* current brightness level */ - u16 bclm[20]; /* backlight level duty cycle mapping table */ - u32 cpfm; /* current panel fitting mode */ - u32 epfm; /* enabled panel fitting modes */ - u8 plut[74]; /* panel LUT and identifier */ - u32 pfmb; /* PWM freq and min brightness */ - u8 rsvd[102]; + u32 ardy; /* driver readiness */ + u32 aslc; /* ASLE interrupt command */ + u32 tche; /* technology enabled indicator */ + u32 alsi; /* current ALS illuminance reading */ + u32 bclp; /* backlight brightness to set */ + u32 pfit; /* panel fitting state */ + u32 cblv; /* current brightness level */ + u16 bclm[20]; /* backlight level duty cycle mapping table */ + u32 cpfm; /* current panel fitting mode */ + u32 epfm; /* enabled panel fitting modes */ + u8 plut[74]; /* panel LUT and identifier */ + u32 pfmb; /* PWM freq and min brightness */ + u8 rsvd[102]; } __attribute__((packed)); /* ASLE irq request bits */ @@ -161,7 +161,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) max = intel_panel_get_max_backlight(dev); intel_panel_set_backlight(dev, bclp * max / 255); - asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; + asle->cblv = DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID; return 0; } @@ -227,7 +227,6 @@ void intel_opregion_asle_intr(struct drm_device *dev) asle->aslc = asle_stat; } -/* Only present on Ironlake+ */ void intel_opregion_gse_intr(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -297,19 +296,26 @@ static int intel_opregion_video_event(struct notifier_block *nb, /* The only video events relevant to opregion are 0x80. These indicate either a docking event, lid switch or display switch request. In Linux, these are handled by the dock, button and video drivers. - We might want to fix the video driver to be opregion-aware in - future, but right now we just indicate to the firmware that the - request has been handled */ + */ struct opregion_acpi *acpi; + struct acpi_bus_event *event = data; + int ret = NOTIFY_OK; + + if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0) + return NOTIFY_DONE; if (!system_opregion) return NOTIFY_DONE; acpi = system_opregion->acpi; + + if (event->type == 0x80 && !(acpi->cevt & 0x1)) + ret = NOTIFY_BAD; + acpi->csts = 0; - return NOTIFY_OK; + return ret; } static struct notifier_block intel_opregion_notifier = { @@ -355,7 +361,7 @@ static void intel_didl_outputs(struct drm_device *dev) list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) { if (i >= 8) { - dev_printk (KERN_ERR, &dev->pdev->dev, + dev_printk(KERN_ERR, &dev->pdev->dev, "More than 8 outputs detected\n"); return; } @@ -381,7 +387,7 @@ blind_set: list_for_each_entry(connector, &dev->mode_config.connector_list, head) { int output_type = ACPI_OTHER_OUTPUT; if (i >= 8) { - dev_printk (KERN_ERR, &dev->pdev->dev, + dev_printk(KERN_ERR, &dev->pdev->dev, "More than 8 outputs detected\n"); return; } diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 1fe7c07..478b51f 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -117,57 +117,57 @@ /* memory bufferd overlay registers */ struct overlay_registers { - u32 OBUF_0Y; - u32 OBUF_1Y; - u32 OBUF_0U; - u32 OBUF_0V; - u32 OBUF_1U; - u32 OBUF_1V; - u32 OSTRIDE; - u32 YRGB_VPH; - u32 UV_VPH; - u32 HORZ_PH; - u32 INIT_PHS; - u32 DWINPOS; - u32 DWINSZ; - u32 SWIDTH; - u32 SWIDTHSW; - u32 SHEIGHT; - u32 YRGBSCALE; - u32 UVSCALE; - u32 OCLRC0; - u32 OCLRC1; - u32 DCLRKV; - u32 DCLRKM; - u32 SCLRKVH; - u32 SCLRKVL; - u32 SCLRKEN; - u32 OCONFIG; - u32 OCMD; - u32 RESERVED1; /* 0x6C */ - u32 OSTART_0Y; - u32 OSTART_1Y; - u32 OSTART_0U; - u32 OSTART_0V; - u32 OSTART_1U; - u32 OSTART_1V; - u32 OTILEOFF_0Y; - u32 OTILEOFF_1Y; - u32 OTILEOFF_0U; - u32 OTILEOFF_0V; - u32 OTILEOFF_1U; - u32 OTILEOFF_1V; - u32 FASTHSCALE; /* 0xA0 */ - u32 UVSCALEV; /* 0xA4 */ - u32 RESERVEDC[(0x200 - 0xA8) / 4]; /* 0xA8 - 0x1FC */ - u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; /* 0x200 */ - u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES]; - u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; /* 0x300 */ - u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES]; - u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; /* 0x500 */ - u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES]; - u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; /* 0x600 */ - u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES]; + u32 OBUF_0Y; + u32 OBUF_1Y; + u32 OBUF_0U; + u32 OBUF_0V; + u32 OBUF_1U; + u32 OBUF_1V; + u32 OSTRIDE; + u32 YRGB_VPH; + u32 UV_VPH; + u32 HORZ_PH; + u32 INIT_PHS; + u32 DWINPOS; + u32 DWINSZ; + u32 SWIDTH; + u32 SWIDTHSW; + u32 SHEIGHT; + u32 YRGBSCALE; + u32 UVSCALE; + u32 OCLRC0; + u32 OCLRC1; + u32 DCLRKV; + u32 DCLRKM; + u32 SCLRKVH; + u32 SCLRKVL; + u32 SCLRKEN; + u32 OCONFIG; + u32 OCMD; + u32 RESERVED1; /* 0x6C */ + u32 OSTART_0Y; + u32 OSTART_1Y; + u32 OSTART_0U; + u32 OSTART_0V; + u32 OSTART_1U; + u32 OSTART_1V; + u32 OTILEOFF_0Y; + u32 OTILEOFF_1Y; + u32 OTILEOFF_0U; + u32 OTILEOFF_0V; + u32 OTILEOFF_1U; + u32 OTILEOFF_1V; + u32 FASTHSCALE; /* 0xA0 */ + u32 UVSCALEV; /* 0xA4 */ + u32 RESERVEDC[(0x200 - 0xA8) / 4]; /* 0xA8 - 0x1FC */ + u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; /* 0x200 */ + u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES]; + u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; /* 0x300 */ + u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES]; + u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; /* 0x500 */ + u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES]; + u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; /* 0x600 */ + u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES]; }; struct intel_overlay { @@ -192,7 +192,7 @@ struct intel_overlay { static struct overlay_registers * intel_overlay_map_regs(struct intel_overlay *overlay) { - drm_i915_private_t *dev_priv = overlay->dev->dev_private; + drm_i915_private_t *dev_priv = overlay->dev->dev_private; struct overlay_registers *regs; if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) @@ -264,7 +264,7 @@ i830_activate_pipe_a(struct drm_device *dev) mode = drm_mode_duplicate(dev, &vesa_640x480); drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); - if(!drm_crtc_helper_set_mode(&crtc->base, mode, + if (!drm_crtc_helper_set_mode(&crtc->base, mode, crtc->base.x, crtc->base.y, crtc->base.fb)) return 0; @@ -332,7 +332,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay, bool load_polyphase_filter) { struct drm_device *dev = overlay->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_request *request; u32 flip_addr = overlay->flip_addr; u32 tmp; @@ -359,7 +359,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay, } OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); OUT_RING(flip_addr); - ADVANCE_LP_RING(); + ADVANCE_LP_RING(); ret = i915_add_request(LP_RING(dev_priv), NULL, request); if (ret) { @@ -591,7 +591,7 @@ static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width) ret = ((offset + width + mask) >> shift) - (offset >> shift); if (!IS_GEN2(dev)) ret <<= 1; - ret -=1; + ret -= 1; return ret << 2; } @@ -781,14 +781,10 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, if (ret != 0) return ret; - ret = i915_gem_object_pin(new_bo, PAGE_SIZE, true); + ret = i915_gem_object_pin_to_display_plane(new_bo, 0, NULL); if (ret != 0) return ret; - ret = i915_gem_object_set_to_gtt_domain(new_bo, 0); - if (ret != 0) - goto out_unpin; - ret = i915_gem_object_put_fence(new_bo); if (ret) goto out_unpin; @@ -829,7 +825,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, regs->SWIDTHSW = calc_swidthsw(overlay->dev, params->offset_Y, tmp_width); regs->SHEIGHT = params->src_h; - regs->OBUF_0Y = new_bo->gtt_offset + params-> offset_Y; + regs->OBUF_0Y = new_bo->gtt_offset + params->offset_Y; regs->OSTRIDE = params->stride_Y; if (params->format & I915_OVERLAY_YUV_PLANAR) { @@ -929,7 +925,7 @@ static void update_pfit_vscale_ratio(struct intel_overlay *overlay) * line with the intel documentation for the i965 */ if (INTEL_INFO(dev)->gen >= 4) { - /* on i965 use the PGM reg to read out the autoscaler values */ + /* on i965 use the PGM reg to read out the autoscaler values */ ratio = I915_READ(PFIT_PGM_RATIOS) >> PFIT_VERT_SCALE_SHIFT_965; } else { if (pfit_control & VERT_AUTO_SCALE) @@ -1110,7 +1106,7 @@ static int intel_panel_fitter_pipe(struct drm_device *dev) } int intel_overlay_put_image(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file_priv) { struct drm_intel_overlay_put_image *put_image_rec = data; drm_i915_private_t *dev_priv = dev->dev_private; @@ -1313,10 +1309,10 @@ static int check_gamma(struct drm_intel_overlay_attrs *attrs) } int intel_overlay_attrs(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file_priv) { struct drm_intel_overlay_attrs *attrs = data; - drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_private_t *dev_priv = dev->dev_private; struct intel_overlay *overlay; struct overlay_registers *regs; int ret; @@ -1405,7 +1401,7 @@ out_unlock: void intel_setup_overlay(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_private_t *dev_priv = dev->dev_private; struct intel_overlay *overlay; struct drm_i915_gem_object *reg_bo; struct overlay_registers *regs; @@ -1433,24 +1429,24 @@ void intel_setup_overlay(struct drm_device *dev) ret = i915_gem_attach_phys_object(dev, reg_bo, I915_GEM_PHYS_OVERLAY_REGS, PAGE_SIZE); - if (ret) { - DRM_ERROR("failed to attach phys overlay regs\n"); - goto out_free_bo; - } + if (ret) { + DRM_ERROR("failed to attach phys overlay regs\n"); + goto out_free_bo; + } overlay->flip_addr = reg_bo->phys_obj->handle->busaddr; } else { ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true); if (ret) { - DRM_ERROR("failed to pin overlay register bo\n"); - goto out_free_bo; - } + DRM_ERROR("failed to pin overlay register bo\n"); + goto out_free_bo; + } overlay->flip_addr = reg_bo->gtt_offset; ret = i915_gem_object_set_to_gtt_domain(reg_bo, true); if (ret) { - DRM_ERROR("failed to move overlay register bo into the GTT\n"); - goto out_unpin_bo; - } + DRM_ERROR("failed to move overlay register bo into the GTT\n"); + goto out_unpin_bo; + } } /* init all values */ @@ -1537,7 +1533,7 @@ static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay, struct intel_overlay_error_state * intel_overlay_capture_error_state(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_private_t *dev_priv = dev->dev_private; struct intel_overlay *overlay = dev_priv->overlay; struct intel_overlay_error_state *error; struct overlay_registers __iomem *regs; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index f8aa821..04cb34a 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -28,6 +28,7 @@ * Chris Wilson <chris@chris-wilson.co.uk> */ +#include <linux/moduleparam.h> #include "intel_drv.h" #define PCI_LBPC 0xf4 /* legacy/combination backlight modes */ @@ -84,7 +85,7 @@ intel_pch_panel_fitting(struct drm_device *dev, if (scaled_width > scaled_height) { /* pillar */ width = scaled_height / mode->vdisplay; if (width & 1) - width++; + width++; x = (adjusted_mode->hdisplay - width + 1) / 2; y = 0; height = adjusted_mode->vdisplay; @@ -178,13 +179,10 @@ u32 intel_panel_get_max_backlight(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { max >>= 16; } else { - if (IS_PINEVIEW(dev)) { + if (INTEL_INFO(dev)->gen < 4) max >>= 17; - } else { + else max >>= 16; - if (INTEL_INFO(dev)->gen < 4) - max &= ~1; - } if (is_backlight_combination_mode(dev)) max *= 0xff; @@ -194,6 +192,27 @@ u32 intel_panel_get_max_backlight(struct drm_device *dev) return max; } +static int i915_panel_invert_brightness; +MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness " + "(-1 force normal, 0 machine defaults, 1 force inversion), please " + "report PCI device ID, subsystem vendor and subsystem device ID " + "to dri-devel@lists.freedesktop.org, if your machine needs it. " + "It will then be included in an upcoming module version."); +module_param_named(invert_brightness, i915_panel_invert_brightness, int, 0600); +static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (i915_panel_invert_brightness < 0) + return val; + + if (i915_panel_invert_brightness > 0 || + dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) + return intel_panel_get_max_backlight(dev) - val; + + return val; +} + u32 intel_panel_get_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -203,18 +222,18 @@ u32 intel_panel_get_backlight(struct drm_device *dev) val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; } else { val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; - if (IS_PINEVIEW(dev)) + if (INTEL_INFO(dev)->gen < 4) val >>= 1; - if (is_backlight_combination_mode(dev)){ + if (is_backlight_combination_mode(dev)) { u8 lbpc; - val &= ~1; pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc); val *= lbpc; } } + val = intel_panel_compute_brightness(dev, val); DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); return val; } @@ -232,11 +251,12 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level u32 tmp; DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level); + level = intel_panel_compute_brightness(dev, level); if (HAS_PCH_SPLIT(dev)) return intel_pch_panel_set_backlight(dev, level); - if (is_backlight_combination_mode(dev)){ + if (is_backlight_combination_mode(dev)) { u32 max = intel_panel_get_max_backlight(dev); u8 lbpc; @@ -246,11 +266,9 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level } tmp = I915_READ(BLC_PWM_CTL); - if (IS_PINEVIEW(dev)) { - tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1); + if (INTEL_INFO(dev)->gen < 4) level <<= 1; - } else - tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; + tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; I915_WRITE(BLC_PWM_CTL, tmp | level); } @@ -282,7 +300,7 @@ void intel_panel_enable_backlight(struct drm_device *dev) intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); } -void intel_panel_setup_backlight(struct drm_device *dev) +static void intel_panel_init_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -314,3 +332,79 @@ intel_panel_detect(struct drm_device *dev) return connector_status_unknown; } + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE +static int intel_panel_update_status(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(bd); + intel_panel_set_backlight(dev, bd->props.brightness); + return 0; +} + +static int intel_panel_get_brightness(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(bd); + struct drm_i915_private *dev_priv = dev->dev_private; + return dev_priv->backlight_level; +} + +static const struct backlight_ops intel_panel_bl_ops = { + .update_status = intel_panel_update_status, + .get_brightness = intel_panel_get_brightness, +}; + +int intel_panel_setup_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct backlight_properties props; + struct drm_connector *connector; + + intel_panel_init_backlight(dev); + + if (WARN_ON(dev_priv->backlight)) + return -ENODEV; + + if (dev_priv->int_lvds_connector) + connector = dev_priv->int_lvds_connector; + else if (dev_priv->int_edp_connector) + connector = dev_priv->int_edp_connector; + else + return -ENODEV; + + props.type = BACKLIGHT_RAW; + props.max_brightness = intel_panel_get_max_backlight(dev); + dev_priv->backlight = + backlight_device_register("intel_backlight", + &connector->kdev, dev, + &intel_panel_bl_ops, &props); + + if (IS_ERR(dev_priv->backlight)) { + DRM_ERROR("Failed to register backlight: %ld\n", + PTR_ERR(dev_priv->backlight)); + dev_priv->backlight = NULL; + return -ENODEV; + } + dev_priv->backlight->props.brightness = intel_panel_get_backlight(dev); + return 0; +} + +void intel_panel_destroy_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + if (dev_priv->backlight) { + backlight_device_unregister(dev_priv->backlight); + dev_priv->backlight = NULL; + } +} +#else +int intel_panel_setup_backlight(struct drm_device *dev) +{ + intel_panel_init_backlight(dev); + return 0; +} + +void intel_panel_destroy_backlight(struct drm_device *dev) +{ + return; +} +#endif diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 3bd85f7..8ee068e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -34,6 +34,16 @@ #include "i915_trace.h" #include "intel_drv.h" +/* + * 965+ support PIPE_CONTROL commands, which provide finer grained control + * over cache flushing. + */ +struct pipe_control { + struct drm_i915_gem_object *obj; + volatile u32 *cpu_page; + u32 gtt_offset; +}; + static inline int ring_space(struct intel_ring_buffer *ring) { int space = (ring->head & HEAD_ADDR) - (ring->tail + 8); @@ -42,20 +52,6 @@ static inline int ring_space(struct intel_ring_buffer *ring) return space; } -static u32 i915_gem_get_seqno(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - u32 seqno; - - seqno = dev_priv->next_seqno; - - /* reserve 0 for non-seqno */ - if (++dev_priv->next_seqno == 0) - dev_priv->next_seqno = 1; - - return seqno; -} - static int render_ring_flush(struct intel_ring_buffer *ring, u32 invalidate_domains, @@ -123,6 +119,118 @@ render_ring_flush(struct intel_ring_buffer *ring, return 0; } +/** + * Emits a PIPE_CONTROL with a non-zero post-sync operation, for + * implementing two workarounds on gen6. From section 1.4.7.1 + * "PIPE_CONTROL" of the Sandy Bridge PRM volume 2 part 1: + * + * [DevSNB-C+{W/A}] Before any depth stall flush (including those + * produced by non-pipelined state commands), software needs to first + * send a PIPE_CONTROL with no bits set except Post-Sync Operation != + * 0. + * + * [Dev-SNB{W/A}]: Before a PIPE_CONTROL with Write Cache Flush Enable + * =1, a PIPE_CONTROL with any non-zero post-sync-op is required. + * + * And the workaround for these two requires this workaround first: + * + * [Dev-SNB{W/A}]: Pipe-control with CS-stall bit set must be sent + * BEFORE the pipe-control with a post-sync op and no write-cache + * flushes. + * + * And this last workaround is tricky because of the requirements on + * that bit. From section 1.4.7.2.3 "Stall" of the Sandy Bridge PRM + * volume 2 part 1: + * + * "1 of the following must also be set: + * - Render Target Cache Flush Enable ([12] of DW1) + * - Depth Cache Flush Enable ([0] of DW1) + * - Stall at Pixel Scoreboard ([1] of DW1) + * - Depth Stall ([13] of DW1) + * - Post-Sync Operation ([13] of DW1) + * - Notify Enable ([8] of DW1)" + * + * The cache flushes require the workaround flush that triggered this + * one, so we can't use it. Depth stall would trigger the same. + * Post-sync nonzero is what triggered this second workaround, so we + * can't use that one either. Notify enable is IRQs, which aren't + * really our business. That leaves only stall at scoreboard. + */ +static int +intel_emit_post_sync_nonzero_flush(struct intel_ring_buffer *ring) +{ + struct pipe_control *pc = ring->private; + u32 scratch_addr = pc->gtt_offset + 128; + int ret; + + + ret = intel_ring_begin(ring, 6); + if (ret) + return ret; + + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, PIPE_CONTROL_CS_STALL | + PIPE_CONTROL_STALL_AT_SCOREBOARD); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */ + intel_ring_emit(ring, 0); /* low dword */ + intel_ring_emit(ring, 0); /* high dword */ + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + + ret = intel_ring_begin(ring, 6); + if (ret) + return ret; + + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, PIPE_CONTROL_QW_WRITE); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */ + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + + return 0; +} + +static int +gen6_render_ring_flush(struct intel_ring_buffer *ring, + u32 invalidate_domains, u32 flush_domains) +{ + u32 flags = 0; + struct pipe_control *pc = ring->private; + u32 scratch_addr = pc->gtt_offset + 128; + int ret; + + /* Force SNB workarounds for PIPE_CONTROL flushes */ + intel_emit_post_sync_nonzero_flush(ring); + + /* Just flush everything. Experiments have shown that reducing the + * number of bits based on the write domains has little performance + * impact. + */ + flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; + + ret = intel_ring_begin(ring, 6); + if (ret) + return ret; + + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, flags); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); + intel_ring_emit(ring, 0); /* lower dword */ + intel_ring_emit(ring, 0); /* uppwer dword */ + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + + return 0; +} + static void ring_write_tail(struct intel_ring_buffer *ring, u32 value) { @@ -141,10 +249,15 @@ u32 intel_ring_get_active_head(struct intel_ring_buffer *ring) static int init_ring_common(struct intel_ring_buffer *ring) { - drm_i915_private_t *dev_priv = ring->dev->dev_private; + struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj = ring->obj; + int ret = 0; u32 head; + if (HAS_FORCE_WAKE(dev)) + gen6_gt_force_wake_get(dev_priv); + /* Stop the ring if it's running. */ I915_WRITE_CTL(ring, 0); I915_WRITE_HEAD(ring, 0); @@ -182,12 +295,12 @@ static int init_ring_common(struct intel_ring_buffer *ring) I915_WRITE_START(ring, obj->gtt_offset); I915_WRITE_CTL(ring, ((ring->size - PAGE_SIZE) & RING_NR_PAGES) - | RING_REPORT_64K | RING_VALID); + | RING_VALID); /* If the head is still not zero, the ring is dead */ - if ((I915_READ_CTL(ring) & RING_VALID) == 0 || - I915_READ_START(ring) != obj->gtt_offset || - (I915_READ_HEAD(ring) & HEAD_ADDR) != 0) { + if (wait_for((I915_READ_CTL(ring) & RING_VALID) != 0 && + I915_READ_START(ring) == obj->gtt_offset && + (I915_READ_HEAD(ring) & HEAD_ADDR) == 0, 50)) { DRM_ERROR("%s initialization failed " "ctl %08x head %08x tail %08x start %08x\n", ring->name, @@ -195,7 +308,8 @@ static int init_ring_common(struct intel_ring_buffer *ring) I915_READ_HEAD(ring), I915_READ_TAIL(ring), I915_READ_START(ring)); - return -EIO; + ret = -EIO; + goto out; } if (!drm_core_check_feature(ring->dev, DRIVER_MODESET)) @@ -206,18 +320,12 @@ static int init_ring_common(struct intel_ring_buffer *ring) ring->space = ring_space(ring); } - return 0; -} +out: + if (HAS_FORCE_WAKE(dev)) + gen6_gt_force_wake_put(dev_priv); -/* - * 965+ support PIPE_CONTROL commands, which provide finer grained control - * over cache flushing. - */ -struct pipe_control { - struct drm_i915_gem_object *obj; - volatile u32 *cpu_page; - u32 gtt_offset; -}; + return ret; +} static int init_pipe_control(struct intel_ring_buffer *ring) @@ -239,7 +347,8 @@ init_pipe_control(struct intel_ring_buffer *ring) ret = -ENOMEM; goto err; } - obj->cache_level = I915_CACHE_LLC; + + i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); ret = i915_gem_object_pin(obj, 4096, true); if (ret) @@ -289,18 +398,43 @@ static int init_render_ring(struct intel_ring_buffer *ring) if (INTEL_INFO(dev)->gen > 3) { int mode = VS_TIMER_DISPATCH << 16 | VS_TIMER_DISPATCH; - if (IS_GEN6(dev) || IS_GEN7(dev)) - mode |= MI_FLUSH_ENABLE << 16 | MI_FLUSH_ENABLE; I915_WRITE(MI_MODE, mode); } - if (INTEL_INFO(dev)->gen >= 6) { - } else if (IS_GEN5(dev)) { + /* We need to disable the AsyncFlip performance optimisations in order + * to use MI_WAIT_FOR_EVENT within the CS. It should already be + * programmed to '1' on all products. + */ + if (INTEL_INFO(dev)->gen >= 6) + I915_WRITE(MI_MODE, GFX_MODE_ENABLE(ASYNC_FLIP_PERF_DISABLE)); + + if (IS_GEN7(dev)) + I915_WRITE(GFX_MODE_GEN7, + GFX_MODE_DISABLE(GFX_TLB_INVALIDATE_ALWAYS) | + GFX_MODE_ENABLE(GFX_REPLAY_MODE)); + + if (INTEL_INFO(dev)->gen >= 5) { ret = init_pipe_control(ring); if (ret) return ret; } + + if (IS_GEN6(dev)) { + /* From the Sandybridge PRM, volume 1 part 3, page 24: + * "If this bit is set, STCunit will have LRA as replacement + * policy. [...] This bit must be reset. LRA replacement + * policy is not supported." + */ + I915_WRITE(CACHE_MODE_0, + CM0_STC_EVICT_DISABLE_LRA_SNB << CM0_MASK_SHIFT); + } + + if (INTEL_INFO(dev)->gen >= 6) { + I915_WRITE(INSTPM, + INSTPM_FORCE_ORDERING << 16 | INSTPM_FORCE_ORDERING); + } + return ret; } @@ -313,83 +447,131 @@ static void render_ring_cleanup(struct intel_ring_buffer *ring) } static void -update_semaphore(struct intel_ring_buffer *ring, int i, u32 seqno) +update_mboxes(struct intel_ring_buffer *ring, + u32 seqno, + u32 mmio_offset) { - struct drm_device *dev = ring->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int id; - - /* - * cs -> 1 = vcs, 0 = bcs - * vcs -> 1 = bcs, 0 = cs, - * bcs -> 1 = cs, 0 = vcs. - */ - id = ring - dev_priv->ring; - id += 2 - i; - id %= 3; - - intel_ring_emit(ring, - MI_SEMAPHORE_MBOX | - MI_SEMAPHORE_REGISTER | - MI_SEMAPHORE_UPDATE); + intel_ring_emit(ring, MI_SEMAPHORE_MBOX | + MI_SEMAPHORE_GLOBAL_GTT | + MI_SEMAPHORE_REGISTER | + MI_SEMAPHORE_UPDATE); intel_ring_emit(ring, seqno); - intel_ring_emit(ring, - RING_SYNC_0(dev_priv->ring[id].mmio_base) + 4*i); + intel_ring_emit(ring, mmio_offset); } +/** + * gen6_add_request - Update the semaphore mailbox registers + * + * @ring - ring that is adding a request + * @seqno - return seqno stuck into the ring + * + * Update the mailbox registers in the *other* rings with the current seqno. + * This acts like a signal in the canonical semaphore. + */ static int gen6_add_request(struct intel_ring_buffer *ring, - u32 *result) + u32 *seqno) { - u32 seqno; + u32 mbox1_reg; + u32 mbox2_reg; int ret; ret = intel_ring_begin(ring, 10); if (ret) return ret; - seqno = i915_gem_get_seqno(ring->dev); - update_semaphore(ring, 0, seqno); - update_semaphore(ring, 1, seqno); + mbox1_reg = ring->signal_mbox[0]; + mbox2_reg = ring->signal_mbox[1]; + *seqno = i915_gem_next_request_seqno(ring); + + update_mboxes(ring, *seqno, mbox1_reg); + update_mboxes(ring, *seqno, mbox2_reg); intel_ring_emit(ring, MI_STORE_DWORD_INDEX); intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); - intel_ring_emit(ring, seqno); + intel_ring_emit(ring, *seqno); intel_ring_emit(ring, MI_USER_INTERRUPT); intel_ring_advance(ring); - *result = seqno; return 0; } -int -intel_ring_sync(struct intel_ring_buffer *ring, - struct intel_ring_buffer *to, +/** + * intel_ring_sync - sync the waiter to the signaller on seqno + * + * @waiter - ring that is waiting + * @signaller - ring which has, or will signal + * @seqno - seqno which the waiter will block on + */ +static int +intel_ring_sync(struct intel_ring_buffer *waiter, + struct intel_ring_buffer *signaller, + int ring, u32 seqno) { int ret; + u32 dw1 = MI_SEMAPHORE_MBOX | + MI_SEMAPHORE_COMPARE | + MI_SEMAPHORE_REGISTER; - ret = intel_ring_begin(ring, 4); + ret = intel_ring_begin(waiter, 4); if (ret) return ret; - intel_ring_emit(ring, - MI_SEMAPHORE_MBOX | - MI_SEMAPHORE_REGISTER | - intel_ring_sync_index(ring, to) << 17 | - MI_SEMAPHORE_COMPARE); - intel_ring_emit(ring, seqno); - intel_ring_emit(ring, 0); - intel_ring_emit(ring, MI_NOOP); - intel_ring_advance(ring); + intel_ring_emit(waiter, dw1 | signaller->semaphore_register[ring]); + intel_ring_emit(waiter, seqno); + intel_ring_emit(waiter, 0); + intel_ring_emit(waiter, MI_NOOP); + intel_ring_advance(waiter); return 0; } +/* VCS->RCS (RVSYNC) or BCS->RCS (RBSYNC) */ +int +render_ring_sync_to(struct intel_ring_buffer *waiter, + struct intel_ring_buffer *signaller, + u32 seqno) +{ + WARN_ON(signaller->semaphore_register[RCS] == MI_SEMAPHORE_SYNC_INVALID); + return intel_ring_sync(waiter, + signaller, + RCS, + seqno); +} + +/* RCS->VCS (VRSYNC) or BCS->VCS (VBSYNC) */ +int +gen6_bsd_ring_sync_to(struct intel_ring_buffer *waiter, + struct intel_ring_buffer *signaller, + u32 seqno) +{ + WARN_ON(signaller->semaphore_register[VCS] == MI_SEMAPHORE_SYNC_INVALID); + return intel_ring_sync(waiter, + signaller, + VCS, + seqno); +} + +/* RCS->BCS (BRSYNC) or VCS->BCS (BVSYNC) */ +int +gen6_blt_ring_sync_to(struct intel_ring_buffer *waiter, + struct intel_ring_buffer *signaller, + u32 seqno) +{ + WARN_ON(signaller->semaphore_register[BCS] == MI_SEMAPHORE_SYNC_INVALID); + return intel_ring_sync(waiter, + signaller, + BCS, + seqno); +} + + + #define PIPE_CONTROL_FLUSH(ring__, addr__) \ do { \ - intel_ring_emit(ring__, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | \ - PIPE_CONTROL_DEPTH_STALL | 2); \ + intel_ring_emit(ring__, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | \ + PIPE_CONTROL_DEPTH_STALL); \ intel_ring_emit(ring__, (addr__) | PIPE_CONTROL_GLOBAL_GTT); \ intel_ring_emit(ring__, 0); \ intel_ring_emit(ring__, 0); \ @@ -399,8 +581,7 @@ static int pc_render_add_request(struct intel_ring_buffer *ring, u32 *result) { - struct drm_device *dev = ring->dev; - u32 seqno = i915_gem_get_seqno(dev); + u32 seqno = i915_gem_next_request_seqno(ring); struct pipe_control *pc = ring->private; u32 scratch_addr = pc->gtt_offset + 128; int ret; @@ -417,8 +598,9 @@ pc_render_add_request(struct intel_ring_buffer *ring, if (ret) return ret; - intel_ring_emit(ring, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | - PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | + PIPE_CONTROL_WRITE_FLUSH | + PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE); intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT); intel_ring_emit(ring, seqno); intel_ring_emit(ring, 0); @@ -433,8 +615,9 @@ pc_render_add_request(struct intel_ring_buffer *ring, PIPE_CONTROL_FLUSH(ring, scratch_addr); scratch_addr += 128; PIPE_CONTROL_FLUSH(ring, scratch_addr); - intel_ring_emit(ring, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | - PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH | + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | + PIPE_CONTROL_WRITE_FLUSH | + PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE | PIPE_CONTROL_NOTIFY); intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT); intel_ring_emit(ring, seqno); @@ -449,8 +632,7 @@ static int render_ring_add_request(struct intel_ring_buffer *ring, u32 *result) { - struct drm_device *dev = ring->dev; - u32 seqno = i915_gem_get_seqno(dev); + u32 seqno = i915_gem_next_request_seqno(ring); int ret; ret = intel_ring_begin(ring, 4); @@ -468,6 +650,19 @@ render_ring_add_request(struct intel_ring_buffer *ring, } static u32 +gen6_ring_get_seqno(struct intel_ring_buffer *ring) +{ + struct drm_device *dev = ring->dev; + + /* Workaround to force correct ordering between irq and seqno writes on + * ivb (and maybe also on snb) by reading from a CS register (like + * ACTHD) before reading the status page. */ + if (IS_GEN7(dev)) + intel_ring_get_active_head(ring); + return intel_read_status_page(ring, I915_GEM_HWS_INDEX); +} + +static u32 ring_get_seqno(struct intel_ring_buffer *ring) { return intel_read_status_page(ring, I915_GEM_HWS_INDEX); @@ -581,6 +776,18 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring) I915_WRITE(mmio, (u32)ring->status_page.gfx_addr); POSTING_READ(mmio); + + /* Flush the TLB for this page */ + if (INTEL_INFO(dev)->gen >= 6) { + u32 reg = RING_INSTPM(ring->mmio_base); + I915_WRITE(reg, + _MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE | + INSTPM_SYNC_FLUSH)); + if (wait_for((I915_READ(reg) & INSTPM_SYNC_FLUSH) == 0, + 1000)) + DRM_ERROR("%s: wait for SyncFlush to complete for TLB invalidation timed out\n", + ring->name); + } } static int @@ -611,7 +818,7 @@ ring_add_request(struct intel_ring_buffer *ring, if (ret) return ret; - seqno = i915_gem_get_seqno(ring->dev); + seqno = i915_gem_next_request_seqno(ring); intel_ring_emit(ring, MI_STORE_DWORD_INDEX); intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); @@ -632,6 +839,12 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) if (!dev->irq_enabled) return false; + /* It looks like we need to prevent the gt from suspending while waiting + * for an notifiy irq, otherwise irqs seem to get lost on at least the + * blt/bsd rings on ivb. */ + if (IS_GEN7(dev)) + gen6_gt_force_wake_get(dev_priv); + spin_lock(&ring->irq_lock); if (ring->irq_refcount++ == 0) { ring->irq_mask &= ~rflag; @@ -656,6 +869,9 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) ironlake_disable_irq(dev_priv, gflag); } spin_unlock(&ring->irq_lock); + + if (IS_GEN7(dev)) + gen6_gt_force_wake_put(dev_priv); } static bool @@ -779,7 +995,8 @@ static int init_status_page(struct intel_ring_buffer *ring) ret = -ENOMEM; goto err; } - obj->cache_level = I915_CACHE_LLC; + + i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); ret = i915_gem_object_pin(obj, 4096, true); if (ret != 0) { @@ -843,6 +1060,10 @@ int intel_init_ring_buffer(struct drm_device *dev, if (ret) goto err_unref; + ret = i915_gem_object_set_to_gtt_domain(obj, true); + if (ret) + goto err_unpin; + ring->map.size = ring->size; ring->map.offset = dev->agp->base + obj->gtt_offset; ring->map.type = 0; @@ -941,18 +1162,6 @@ int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n) struct drm_device *dev = ring->dev; struct drm_i915_private *dev_priv = dev->dev_private; unsigned long end; - u32 head; - - /* If the reported head position has wrapped or hasn't advanced, - * fallback to the slow and accurate path. - */ - head = intel_read_status_page(ring, 4); - if (head > ring->head) { - ring->head = head; - ring->space = ring_space(ring); - if (ring->space >= n) - return 0; - } trace_i915_ring_wait_begin(ring); end = jiffies + 3 * HZ; @@ -1004,6 +1213,27 @@ int intel_ring_begin(struct intel_ring_buffer *ring, return 0; } +/* Align the ring tail to a cacheline boundary */ +int intel_ring_cacheline_align(struct intel_ring_buffer *ring) +{ + int num_dwords = (64 - (ring->tail & 63)) / sizeof(uint32_t); + int ret; + + if (num_dwords == 0) + return 0; + + ret = intel_ring_begin(ring, num_dwords); + if (ret) + return ret; + + while (num_dwords--) + intel_ring_emit(ring, MI_NOOP); + + intel_ring_advance(ring); + + return 0; +} + void intel_ring_advance(struct intel_ring_buffer *ring) { ring->tail &= ring->size - 1; @@ -1023,7 +1253,12 @@ static const struct intel_ring_buffer render_ring = { .irq_get = render_ring_get_irq, .irq_put = render_ring_put_irq, .dispatch_execbuffer = render_ring_dispatch_execbuffer, - .cleanup = render_ring_cleanup, + .cleanup = render_ring_cleanup, + .sync_to = render_ring_sync_to, + .semaphore_register = {MI_SEMAPHORE_SYNC_INVALID, + MI_SEMAPHORE_SYNC_RV, + MI_SEMAPHORE_SYNC_RB}, + .signal_mbox = {GEN6_VRSYNC, GEN6_BRSYNC}, }; /* ring buffer for bit-stream decoder */ @@ -1047,23 +1282,23 @@ static const struct intel_ring_buffer bsd_ring = { static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring, u32 value) { - drm_i915_private_t *dev_priv = ring->dev->dev_private; + drm_i915_private_t *dev_priv = ring->dev->dev_private; /* Every tail move must follow the sequence below */ - I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL, - GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK | - GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE); - I915_WRITE(GEN6_BSD_RNCID, 0x0); + I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL, + GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK | + GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE); + I915_WRITE(GEN6_BSD_RNCID, 0x0); - if (wait_for((I915_READ(GEN6_BSD_SLEEP_PSMI_CONTROL) & - GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR) == 0, - 50)) - DRM_ERROR("timed out waiting for IDLE Indicator\n"); + if (wait_for((I915_READ(GEN6_BSD_SLEEP_PSMI_CONTROL) & + GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR) == 0, + 50)) + DRM_ERROR("timed out waiting for IDLE Indicator\n"); - I915_WRITE_TAIL(ring, value); - I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL, - GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK | - GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE); + I915_WRITE_TAIL(ring, value); + I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL, + GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK | + GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE); } static int gen6_ring_flush(struct intel_ring_buffer *ring, @@ -1091,18 +1326,18 @@ static int gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring, u32 offset, u32 len) { - int ret; + int ret; - ret = intel_ring_begin(ring, 2); - if (ret) - return ret; + ret = intel_ring_begin(ring, 2); + if (ret) + return ret; - intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965); - /* bit0-7 is the length on GEN6+ */ - intel_ring_emit(ring, offset); - intel_ring_advance(ring); + intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965); + /* bit0-7 is the length on GEN6+ */ + intel_ring_emit(ring, offset); + intel_ring_advance(ring); - return 0; + return 0; } static bool @@ -1147,10 +1382,15 @@ static const struct intel_ring_buffer gen6_bsd_ring = { .write_tail = gen6_bsd_ring_write_tail, .flush = gen6_ring_flush, .add_request = gen6_add_request, - .get_seqno = ring_get_seqno, + .get_seqno = gen6_ring_get_seqno, .irq_get = gen6_bsd_ring_get_irq, .irq_put = gen6_bsd_ring_put_irq, .dispatch_execbuffer = gen6_ring_dispatch_execbuffer, + .sync_to = gen6_bsd_ring_sync_to, + .semaphore_register = {MI_SEMAPHORE_SYNC_VR, + MI_SEMAPHORE_SYNC_INVALID, + MI_SEMAPHORE_SYNC_VB}, + .signal_mbox = {GEN6_RVSYNC, GEN6_BVSYNC}, }; /* Blitter support (SandyBridge+) */ @@ -1269,19 +1509,24 @@ static void blt_ring_cleanup(struct intel_ring_buffer *ring) } static const struct intel_ring_buffer gen6_blt_ring = { - .name = "blt ring", - .id = RING_BLT, - .mmio_base = BLT_RING_BASE, - .size = 32 * PAGE_SIZE, - .init = blt_ring_init, - .write_tail = ring_write_tail, - .flush = blt_ring_flush, - .add_request = gen6_add_request, - .get_seqno = ring_get_seqno, - .irq_get = blt_ring_get_irq, - .irq_put = blt_ring_put_irq, - .dispatch_execbuffer = gen6_ring_dispatch_execbuffer, - .cleanup = blt_ring_cleanup, + .name = "blt ring", + .id = RING_BLT, + .mmio_base = BLT_RING_BASE, + .size = 32 * PAGE_SIZE, + .init = blt_ring_init, + .write_tail = ring_write_tail, + .flush = blt_ring_flush, + .add_request = gen6_add_request, + .get_seqno = gen6_ring_get_seqno, + .irq_get = blt_ring_get_irq, + .irq_put = blt_ring_put_irq, + .dispatch_execbuffer = gen6_ring_dispatch_execbuffer, + .cleanup = blt_ring_cleanup, + .sync_to = gen6_blt_ring_sync_to, + .semaphore_register = {MI_SEMAPHORE_SYNC_BR, + MI_SEMAPHORE_SYNC_BV, + MI_SEMAPHORE_SYNC_INVALID}, + .signal_mbox = {GEN6_RBSYNC, GEN6_VBSYNC}, }; int intel_init_render_ring_buffer(struct drm_device *dev) @@ -1292,8 +1537,10 @@ int intel_init_render_ring_buffer(struct drm_device *dev) *ring = render_ring; if (INTEL_INFO(dev)->gen >= 6) { ring->add_request = gen6_add_request; + ring->flush = gen6_render_ring_flush; ring->irq_get = gen6_render_ring_get_irq; ring->irq_put = gen6_render_ring_put_irq; + ring->get_seqno = gen6_ring_get_seqno; } else if (IS_GEN5(dev)) { ring->add_request = pc_render_add_request; ring->get_seqno = pc_render_get_seqno; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 39ac2b6..d83cc97 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -2,10 +2,10 @@ #define _INTEL_RINGBUFFER_H_ enum { - RCS = 0x0, - VCS, - BCS, - I915_NUM_RINGS, + RCS = 0x0, + VCS, + BCS, + I915_NUM_RINGS, }; struct intel_hw_status_page { @@ -75,7 +75,12 @@ struct intel_ring_buffer { int (*dispatch_execbuffer)(struct intel_ring_buffer *ring, u32 offset, u32 length); void (*cleanup)(struct intel_ring_buffer *ring); + int (*sync_to)(struct intel_ring_buffer *ring, + struct intel_ring_buffer *to, + u32 seqno); + u32 semaphore_register[3]; /*our mbox written by others */ + u32 signal_mbox[2]; /* mboxes this ring signals to */ /** * List of objects currently involved in rendering from the * ringbuffer. @@ -169,6 +174,7 @@ static inline int intel_wait_ring_idle(struct intel_ring_buffer *ring) } int __must_check intel_ring_begin(struct intel_ring_buffer *ring, int n); +int __must_check intel_ring_cacheline_align(struct intel_ring_buffer *ring); static inline void intel_ring_emit(struct intel_ring_buffer *ring, u32 data) @@ -180,9 +186,6 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring, void intel_ring_advance(struct intel_ring_buffer *ring); u32 intel_ring_get_seqno(struct intel_ring_buffer *ring); -int intel_ring_sync(struct intel_ring_buffer *ring, - struct intel_ring_buffer *to, - u32 seqno); int intel_init_render_ring_buffer(struct drm_device *dev); int intel_init_bsd_ring_buffer(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 06b51ff..00ec0dd 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -28,6 +28,7 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/export.h> #include "drmP.h" #include "drm.h" #include "drm_crtc.h" @@ -43,12 +44,13 @@ #define SDVO_TV_MASK (SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_SVID0) #define SDVO_OUTPUT_MASK (SDVO_TMDS_MASK | SDVO_RGB_MASK | SDVO_LVDS_MASK |\ - SDVO_TV_MASK) + SDVO_TV_MASK) #define IS_TV(c) (c->output_flag & SDVO_TV_MASK) #define IS_TMDS(c) (c->output_flag & SDVO_TMDS_MASK) #define IS_LVDS(c) (c->output_flag & SDVO_LVDS_MASK) #define IS_TV_OR_LVDS(c) (c->output_flag & (SDVO_TV_MASK | SDVO_LVDS_MASK)) +#define IS_DIGITAL(c) (c->output_flag & (SDVO_TMDS_MASK | SDVO_LVDS_MASK)) static const char *tv_format_names[] = { @@ -92,6 +94,11 @@ struct intel_sdvo { */ uint16_t attached_output; + /* + * Hotplug activation bits for this device + */ + uint8_t hotplug_active[2]; + /** * This is used to select the color range of RBG outputs in HDMI mode. * It is only valid when using TMDS encoding and 8 bit per color mode. @@ -283,117 +290,117 @@ static const struct _sdvo_cmd_name { u8 cmd; const char *name; } sdvo_cmd_names[] = { - SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS), - - /* Add the op code for SDVO enhancements */ - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HPOS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HPOS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HPOS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_VPOS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_VPOS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_VPOS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_2D), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_2D), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_2D), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SHARPNESS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SHARPNESS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SHARPNESS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DOT_CRAWL), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DOT_CRAWL), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_CHROMA_FILTER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_CHROMA_FILTER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_CHROMA_FILTER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_LUMA_FILTER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_LUMA_FILTER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_LUMA_FILTER), - - /* HDMI op code */ - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS), + + /* Add the op code for SDVO enhancements */ + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_VPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_VPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_VPOS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_2D), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_2D), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_2D), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SHARPNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SHARPNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SHARPNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DOT_CRAWL), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DOT_CRAWL), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_CHROMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_CHROMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_CHROMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_LUMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_LUMA_FILTER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_LUMA_FILTER), + + /* HDMI op code */ + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA), }; #define IS_SDVOB(reg) (reg == SDVOB || reg == PCH_SDVOB) @@ -1113,8 +1120,12 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, } sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; } - if (intel_crtc->pipe == 1) - sdvox |= SDVO_PIPE_B_SELECT; + + if (INTEL_PCH_TYPE(dev) >= PCH_CPT) + sdvox |= TRANSCODER_CPT(intel_crtc->pipe); + else + sdvox |= TRANSCODER(intel_crtc->pipe); + if (intel_sdvo->has_hdmi_audio) sdvox |= SDVO_AUDIO_ENABLE; @@ -1241,81 +1252,32 @@ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct in return true; } -/* No use! */ -#if 0 -struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB) -{ - struct drm_connector *connector = NULL; - struct intel_sdvo *iout = NULL; - struct intel_sdvo *sdvo; - - /* find the sdvo connector */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - iout = to_intel_sdvo(connector); - - if (iout->type != INTEL_OUTPUT_SDVO) - continue; - - sdvo = iout->dev_priv; - - if (sdvo->sdvo_reg == SDVOB && sdvoB) - return connector; - - if (sdvo->sdvo_reg == SDVOC && !sdvoB) - return connector; - - } - - return NULL; -} - -int intel_sdvo_supports_hotplug(struct drm_connector *connector) +static int intel_sdvo_supports_hotplug(struct intel_sdvo *intel_sdvo) { + struct drm_device *dev = intel_sdvo->base.base.dev; u8 response[2]; - u8 status; - struct intel_sdvo *intel_sdvo; - DRM_DEBUG_KMS("\n"); - - if (!connector) - return 0; - intel_sdvo = to_intel_sdvo(connector); + /* HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise + * on the line. */ + if (IS_I945G(dev) || IS_I945GM(dev)) + return false; return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, &response, 2) && response[0]; } -void intel_sdvo_set_hotplug(struct drm_connector *connector, int on) +static void intel_sdvo_enable_hotplug(struct intel_encoder *encoder) { - u8 response[2]; - u8 status; - struct intel_sdvo *intel_sdvo = to_intel_sdvo(connector); - - intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0); - intel_sdvo_read_response(intel_sdvo, &response, 2); + struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); - if (on) { - intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0); - status = intel_sdvo_read_response(intel_sdvo, &response, 2); - - intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2); - } else { - response[0] = 0; - response[1] = 0; - intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2); - } - - intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0); - intel_sdvo_read_response(intel_sdvo, &response, 2); + intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &intel_sdvo->hotplug_active, 2); } -#endif static bool intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo) { /* Is there more than one type of output? */ - int caps = intel_sdvo->caps.output_flags & 0xf; - return caps & -caps; + return hweight16(intel_sdvo->caps.output_flags) > 1; } static struct edid * @@ -1336,7 +1298,7 @@ intel_sdvo_get_analog_edid(struct drm_connector *connector) } enum drm_connector_status -intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) +intel_sdvo_tmds_sink_detect(struct drm_connector *connector) { struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); enum drm_connector_status status; @@ -1396,6 +1358,18 @@ intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) return status; } +static bool +intel_sdvo_connector_matches_edid(struct intel_sdvo_connector *sdvo, + struct edid *edid) +{ + bool monitor_is_digital = !!(edid->input & DRM_EDID_INPUT_DIGITAL); + bool connector_is_digital = !!IS_DIGITAL(sdvo); + + DRM_DEBUG_KMS("connector_is_digital? %d, monitor_is_digital? %d\n", + connector_is_digital, monitor_is_digital); + return connector_is_digital == monitor_is_digital; +} + static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector, bool force) { @@ -1431,7 +1405,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) if ((intel_sdvo_connector->output_flag & response) == 0) ret = connector_status_disconnected; else if (IS_TMDS(intel_sdvo_connector)) - ret = intel_sdvo_hdmi_sink_detect(connector); + ret = intel_sdvo_tmds_sink_detect(connector); else { struct edid *edid; @@ -1440,10 +1414,12 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) if (edid == NULL) edid = intel_sdvo_get_analog_edid(connector); if (edid != NULL) { - if (edid->input & DRM_EDID_INPUT_DIGITAL) - ret = connector_status_disconnected; - else + if (intel_sdvo_connector_matches_edid(intel_sdvo_connector, + edid)) ret = connector_status_connected; + else + ret = connector_status_disconnected; + connector->display_info.raw_edid = NULL; kfree(edid); } else @@ -1484,11 +1460,8 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) edid = intel_sdvo_get_analog_edid(connector); if (edid != NULL) { - struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector); - bool monitor_is_digital = !!(edid->input & DRM_EDID_INPUT_DIGITAL); - bool connector_is_digital = !!IS_TMDS(intel_sdvo_connector); - - if (connector_is_digital == monitor_is_digital) { + if (intel_sdvo_connector_matches_edid(to_intel_sdvo_connector(connector), + edid)) { drm_mode_connector_update_edid_property(connector, edid); drm_add_edid_modes(connector, edid); } @@ -1980,7 +1953,7 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv, struct intel_sdvo *sdvo, u32 reg) { struct sdvo_device_mapping *mapping; - u8 pin, speed; + u8 pin; if (IS_SDVOB(reg)) mapping = &dev_priv->sdvo_mappings[0]; @@ -1988,18 +1961,16 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv, mapping = &dev_priv->sdvo_mappings[1]; pin = GMBUS_PORT_DPB; - speed = GMBUS_RATE_1MHZ >> 8; - if (mapping->initialized) { + if (mapping->initialized) pin = mapping->i2c_pin; - speed = mapping->i2c_speed; - } if (pin < GMBUS_NUM_PORTS) { sdvo->i2c = &dev_priv->gmbus[pin].adapter; - intel_gmbus_set_speed(sdvo->i2c, speed); + intel_gmbus_set_speed(sdvo->i2c, GMBUS_RATE_1MHZ); intel_gmbus_force_bit(sdvo->i2c, true); - } else + } else { sdvo->i2c = &dev_priv->gmbus[GMBUS_PORT_DPB].adapter; + } } static bool @@ -2080,6 +2051,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) { struct drm_encoder *encoder = &intel_sdvo->base.base; struct drm_connector *connector; + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); struct intel_connector *intel_connector; struct intel_sdvo_connector *intel_sdvo_connector; @@ -2097,7 +2069,17 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) intel_connector = &intel_sdvo_connector->base; connector = &intel_connector->base; - connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + if (intel_sdvo_supports_hotplug(intel_sdvo) & (1 << device)) { + connector->polled = DRM_CONNECTOR_POLL_HPD; + intel_sdvo->hotplug_active[0] |= 1 << device; + /* Some SDVO devices have one-shot hotplug interrupts. + * Ensure that they get re-enabled when an interrupt happens. + */ + intel_encoder->hot_plug = intel_sdvo_enable_hotplug; + intel_sdvo_enable_hotplug(intel_encoder); + } + else + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; encoder->encoder_type = DRM_MODE_ENCODER_TMDS; connector->connector_type = DRM_MODE_CONNECTOR_DVID; @@ -2279,11 +2261,23 @@ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags) bytes[0], bytes[1]); return false; } - intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1); + intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); return true; } +static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo) +{ + struct drm_device *dev = intel_sdvo->base.base.dev; + struct drm_connector *connector, *tmp; + + list_for_each_entry_safe(connector, tmp, + &dev->mode_config.connector_list, head) { + if (intel_attached_encoder(connector) == &intel_sdvo->base) + intel_sdvo_destroy(connector); + } +} + static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo, struct intel_sdvo_connector *intel_sdvo_connector, int type) @@ -2348,7 +2342,7 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo, DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \ data_value[0], data_value[1], response); \ } \ -} while(0) +} while (0) static bool intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, @@ -2515,7 +2509,7 @@ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo, if (IS_TV(intel_sdvo_connector)) return intel_sdvo_create_enhance_property_tv(intel_sdvo, intel_sdvo_connector, enhancements.reply); - else if(IS_LVDS(intel_sdvo_connector)) + else if (IS_LVDS(intel_sdvo_connector)) return intel_sdvo_create_enhance_property_lvds(intel_sdvo, intel_sdvo_connector, enhancements.reply); else return true; @@ -2563,6 +2557,7 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *intel_encoder; struct intel_sdvo *intel_sdvo; + u32 hotplug_mask; int i; intel_sdvo = kzalloc(sizeof(struct intel_sdvo), GFP_KERNEL); @@ -2593,10 +2588,17 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg) } } - if (IS_SDVOB(sdvo_reg)) - dev_priv->hotplug_supported_mask |= SDVOB_HOTPLUG_INT_STATUS; - else - dev_priv->hotplug_supported_mask |= SDVOC_HOTPLUG_INT_STATUS; + hotplug_mask = 0; + if (IS_G4X(dev)) { + hotplug_mask = IS_SDVOB(sdvo_reg) ? + SDVOB_HOTPLUG_INT_STATUS_G4X : SDVOC_HOTPLUG_INT_STATUS_G4X; + } else if (IS_GEN4(dev)) { + hotplug_mask = IS_SDVOB(sdvo_reg) ? + SDVOB_HOTPLUG_INT_STATUS_I965 : SDVOC_HOTPLUG_INT_STATUS_I965; + } else { + hotplug_mask = IS_SDVOB(sdvo_reg) ? + SDVOB_HOTPLUG_INT_STATUS_I915 : SDVOC_HOTPLUG_INT_STATUS_I915; + } drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs); @@ -2608,19 +2610,26 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg) intel_sdvo->caps.output_flags) != true) { DRM_DEBUG_KMS("SDVO output failed to setup on SDVO%c\n", IS_SDVOB(sdvo_reg) ? 'B' : 'C'); - goto err; + /* Output_setup can leave behind connectors! */ + goto err_output; } + /* Only enable the hotplug irq if we need it, to work around noisy + * hotplug lines. + */ + if (intel_sdvo->hotplug_active[0]) + dev_priv->hotplug_supported_mask |= hotplug_mask; + intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg); /* Set the input timing to the screen. Assume always input 0. */ if (!intel_sdvo_set_target_input(intel_sdvo)) - goto err; + goto err_output; if (!intel_sdvo_get_input_pixel_clock_range(intel_sdvo, &intel_sdvo->pixel_clock_min, &intel_sdvo->pixel_clock_max)) - goto err; + goto err_output; DRM_DEBUG_KMS("%s device VID/DID: %02X:%02X.%02X, " "clock range %dMHz - %dMHz, " @@ -2640,6 +2649,9 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg) (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N'); return true; +err_output: + intel_sdvo_output_cleanup(intel_sdvo); + err: drm_encoder_cleanup(&intel_encoder->base); i2c_del_adapter(&intel_sdvo->ddc); diff --git a/drivers/gpu/drm/i915/intel_sdvo_regs.h b/drivers/gpu/drm/i915/intel_sdvo_regs.h index 50bebc3..4193c54 100644 --- a/drivers/gpu/drm/i915/intel_sdvo_regs.h +++ b/drivers/gpu/drm/i915/intel_sdvo_regs.h @@ -46,19 +46,19 @@ #define SDVO_OUTPUT_LAST (14) struct intel_sdvo_caps { - u8 vendor_id; - u8 device_id; - u8 device_rev_id; - u8 sdvo_version_major; - u8 sdvo_version_minor; - unsigned int sdvo_inputs_mask:2; - unsigned int smooth_scaling:1; - unsigned int sharp_scaling:1; - unsigned int up_scaling:1; - unsigned int down_scaling:1; - unsigned int stall_support:1; - unsigned int pad:1; - u16 output_flags; + u8 vendor_id; + u8 device_id; + u8 device_rev_id; + u8 sdvo_version_major; + u8 sdvo_version_minor; + unsigned int sdvo_inputs_mask:2; + unsigned int smooth_scaling:1; + unsigned int sharp_scaling:1; + unsigned int up_scaling:1; + unsigned int down_scaling:1; + unsigned int stall_support:1; + unsigned int pad:1; + u16 output_flags; } __attribute__((packed)); /* Note: SDVO detailed timing flags match EDID misc flags. */ @@ -68,46 +68,46 @@ struct intel_sdvo_caps { /** This matches the EDID DTD structure, more or less */ struct intel_sdvo_dtd { - struct { - u16 clock; /**< pixel clock, in 10kHz units */ - u8 h_active; /**< lower 8 bits (pixels) */ - u8 h_blank; /**< lower 8 bits (pixels) */ - u8 h_high; /**< upper 4 bits each h_active, h_blank */ - u8 v_active; /**< lower 8 bits (lines) */ - u8 v_blank; /**< lower 8 bits (lines) */ - u8 v_high; /**< upper 4 bits each v_active, v_blank */ - } part1; - - struct { - u8 h_sync_off; /**< lower 8 bits, from hblank start */ - u8 h_sync_width; /**< lower 8 bits (pixels) */ - /** lower 4 bits each vsync offset, vsync width */ - u8 v_sync_off_width; - /** - * 2 high bits of hsync offset, 2 high bits of hsync width, - * bits 4-5 of vsync offset, and 2 high bits of vsync width. - */ - u8 sync_off_width_high; - u8 dtd_flags; - u8 sdvo_flags; - /** bits 6-7 of vsync offset at bits 6-7 */ - u8 v_sync_off_high; - u8 reserved; - } part2; + struct { + u16 clock; /**< pixel clock, in 10kHz units */ + u8 h_active; /**< lower 8 bits (pixels) */ + u8 h_blank; /**< lower 8 bits (pixels) */ + u8 h_high; /**< upper 4 bits each h_active, h_blank */ + u8 v_active; /**< lower 8 bits (lines) */ + u8 v_blank; /**< lower 8 bits (lines) */ + u8 v_high; /**< upper 4 bits each v_active, v_blank */ + } part1; + + struct { + u8 h_sync_off; /**< lower 8 bits, from hblank start */ + u8 h_sync_width; /**< lower 8 bits (pixels) */ + /** lower 4 bits each vsync offset, vsync width */ + u8 v_sync_off_width; + /** + * 2 high bits of hsync offset, 2 high bits of hsync width, + * bits 4-5 of vsync offset, and 2 high bits of vsync width. + */ + u8 sync_off_width_high; + u8 dtd_flags; + u8 sdvo_flags; + /** bits 6-7 of vsync offset at bits 6-7 */ + u8 v_sync_off_high; + u8 reserved; + } part2; } __attribute__((packed)); struct intel_sdvo_pixel_clock_range { - u16 min; /**< pixel clock, in 10kHz units */ - u16 max; /**< pixel clock, in 10kHz units */ + u16 min; /**< pixel clock, in 10kHz units */ + u16 max; /**< pixel clock, in 10kHz units */ } __attribute__((packed)); struct intel_sdvo_preferred_input_timing_args { - u16 clock; - u16 width; - u16 height; - u8 interlace:1; - u8 scaled:1; - u8 pad:6; + u16 clock; + u16 width; + u16 height; + u8 interlace:1; + u8 scaled:1; + u8 pad:6; } __attribute__((packed)); /* I2C registers for SDVO */ @@ -159,9 +159,9 @@ struct intel_sdvo_preferred_input_timing_args { */ #define SDVO_CMD_GET_TRAINED_INPUTS 0x03 struct intel_sdvo_get_trained_inputs_response { - unsigned int input0_trained:1; - unsigned int input1_trained:1; - unsigned int pad:6; + unsigned int input0_trained:1; + unsigned int input1_trained:1; + unsigned int pad:6; } __attribute__((packed)); /** Returns a struct intel_sdvo_output_flags of active outputs. */ @@ -182,7 +182,7 @@ struct intel_sdvo_get_trained_inputs_response { */ #define SDVO_CMD_GET_IN_OUT_MAP 0x06 struct intel_sdvo_in_out_map { - u16 in0, in1; + u16 in0, in1; }; /** @@ -215,10 +215,10 @@ struct intel_sdvo_in_out_map { #define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f struct intel_sdvo_get_interrupt_event_source_response { - u16 interrupt_status; - unsigned int ambient_light_interrupt:1; - unsigned int hdmi_audio_encrypt_change:1; - unsigned int pad:6; + u16 interrupt_status; + unsigned int ambient_light_interrupt:1; + unsigned int hdmi_audio_encrypt_change:1; + unsigned int pad:6; } __attribute__((packed)); /** @@ -230,8 +230,8 @@ struct intel_sdvo_get_interrupt_event_source_response { */ #define SDVO_CMD_SET_TARGET_INPUT 0x10 struct intel_sdvo_set_target_input_args { - unsigned int target_1:1; - unsigned int pad:7; + unsigned int target_1:1; + unsigned int pad:7; } __attribute__((packed)); /** @@ -319,57 +319,57 @@ struct intel_sdvo_set_target_input_args { #define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27 /** 6 bytes of bit flags for TV formats shared by all TV format functions */ struct intel_sdvo_tv_format { - unsigned int ntsc_m:1; - unsigned int ntsc_j:1; - unsigned int ntsc_443:1; - unsigned int pal_b:1; - unsigned int pal_d:1; - unsigned int pal_g:1; - unsigned int pal_h:1; - unsigned int pal_i:1; - - unsigned int pal_m:1; - unsigned int pal_n:1; - unsigned int pal_nc:1; - unsigned int pal_60:1; - unsigned int secam_b:1; - unsigned int secam_d:1; - unsigned int secam_g:1; - unsigned int secam_k:1; - - unsigned int secam_k1:1; - unsigned int secam_l:1; - unsigned int secam_60:1; - unsigned int hdtv_std_smpte_240m_1080i_59:1; - unsigned int hdtv_std_smpte_240m_1080i_60:1; - unsigned int hdtv_std_smpte_260m_1080i_59:1; - unsigned int hdtv_std_smpte_260m_1080i_60:1; - unsigned int hdtv_std_smpte_274m_1080i_50:1; - - unsigned int hdtv_std_smpte_274m_1080i_59:1; - unsigned int hdtv_std_smpte_274m_1080i_60:1; - unsigned int hdtv_std_smpte_274m_1080p_23:1; - unsigned int hdtv_std_smpte_274m_1080p_24:1; - unsigned int hdtv_std_smpte_274m_1080p_25:1; - unsigned int hdtv_std_smpte_274m_1080p_29:1; - unsigned int hdtv_std_smpte_274m_1080p_30:1; - unsigned int hdtv_std_smpte_274m_1080p_50:1; - - unsigned int hdtv_std_smpte_274m_1080p_59:1; - unsigned int hdtv_std_smpte_274m_1080p_60:1; - unsigned int hdtv_std_smpte_295m_1080i_50:1; - unsigned int hdtv_std_smpte_295m_1080p_50:1; - unsigned int hdtv_std_smpte_296m_720p_59:1; - unsigned int hdtv_std_smpte_296m_720p_60:1; - unsigned int hdtv_std_smpte_296m_720p_50:1; - unsigned int hdtv_std_smpte_293m_480p_59:1; - - unsigned int hdtv_std_smpte_170m_480i_59:1; - unsigned int hdtv_std_iturbt601_576i_50:1; - unsigned int hdtv_std_iturbt601_576p_50:1; - unsigned int hdtv_std_eia_7702a_480i_60:1; - unsigned int hdtv_std_eia_7702a_480p_60:1; - unsigned int pad:3; + unsigned int ntsc_m:1; + unsigned int ntsc_j:1; + unsigned int ntsc_443:1; + unsigned int pal_b:1; + unsigned int pal_d:1; + unsigned int pal_g:1; + unsigned int pal_h:1; + unsigned int pal_i:1; + + unsigned int pal_m:1; + unsigned int pal_n:1; + unsigned int pal_nc:1; + unsigned int pal_60:1; + unsigned int secam_b:1; + unsigned int secam_d:1; + unsigned int secam_g:1; + unsigned int secam_k:1; + + unsigned int secam_k1:1; + unsigned int secam_l:1; + unsigned int secam_60:1; + unsigned int hdtv_std_smpte_240m_1080i_59:1; + unsigned int hdtv_std_smpte_240m_1080i_60:1; + unsigned int hdtv_std_smpte_260m_1080i_59:1; + unsigned int hdtv_std_smpte_260m_1080i_60:1; + unsigned int hdtv_std_smpte_274m_1080i_50:1; + + unsigned int hdtv_std_smpte_274m_1080i_59:1; + unsigned int hdtv_std_smpte_274m_1080i_60:1; + unsigned int hdtv_std_smpte_274m_1080p_23:1; + unsigned int hdtv_std_smpte_274m_1080p_24:1; + unsigned int hdtv_std_smpte_274m_1080p_25:1; + unsigned int hdtv_std_smpte_274m_1080p_29:1; + unsigned int hdtv_std_smpte_274m_1080p_30:1; + unsigned int hdtv_std_smpte_274m_1080p_50:1; + + unsigned int hdtv_std_smpte_274m_1080p_59:1; + unsigned int hdtv_std_smpte_274m_1080p_60:1; + unsigned int hdtv_std_smpte_295m_1080i_50:1; + unsigned int hdtv_std_smpte_295m_1080p_50:1; + unsigned int hdtv_std_smpte_296m_720p_59:1; + unsigned int hdtv_std_smpte_296m_720p_60:1; + unsigned int hdtv_std_smpte_296m_720p_50:1; + unsigned int hdtv_std_smpte_293m_480p_59:1; + + unsigned int hdtv_std_smpte_170m_480i_59:1; + unsigned int hdtv_std_iturbt601_576i_50:1; + unsigned int hdtv_std_iturbt601_576p_50:1; + unsigned int hdtv_std_eia_7702a_480i_60:1; + unsigned int hdtv_std_eia_7702a_480p_60:1; + unsigned int pad:3; } __attribute__((packed)); #define SDVO_CMD_GET_TV_FORMAT 0x28 @@ -379,53 +379,53 @@ struct intel_sdvo_tv_format { /** Returns the resolutiosn that can be used with the given TV format */ #define SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT 0x83 struct intel_sdvo_sdtv_resolution_request { - unsigned int ntsc_m:1; - unsigned int ntsc_j:1; - unsigned int ntsc_443:1; - unsigned int pal_b:1; - unsigned int pal_d:1; - unsigned int pal_g:1; - unsigned int pal_h:1; - unsigned int pal_i:1; - - unsigned int pal_m:1; - unsigned int pal_n:1; - unsigned int pal_nc:1; - unsigned int pal_60:1; - unsigned int secam_b:1; - unsigned int secam_d:1; - unsigned int secam_g:1; - unsigned int secam_k:1; - - unsigned int secam_k1:1; - unsigned int secam_l:1; - unsigned int secam_60:1; - unsigned int pad:5; + unsigned int ntsc_m:1; + unsigned int ntsc_j:1; + unsigned int ntsc_443:1; + unsigned int pal_b:1; + unsigned int pal_d:1; + unsigned int pal_g:1; + unsigned int pal_h:1; + unsigned int pal_i:1; + + unsigned int pal_m:1; + unsigned int pal_n:1; + unsigned int pal_nc:1; + unsigned int pal_60:1; + unsigned int secam_b:1; + unsigned int secam_d:1; + unsigned int secam_g:1; + unsigned int secam_k:1; + + unsigned int secam_k1:1; + unsigned int secam_l:1; + unsigned int secam_60:1; + unsigned int pad:5; } __attribute__((packed)); struct intel_sdvo_sdtv_resolution_reply { - unsigned int res_320x200:1; - unsigned int res_320x240:1; - unsigned int res_400x300:1; - unsigned int res_640x350:1; - unsigned int res_640x400:1; - unsigned int res_640x480:1; - unsigned int res_704x480:1; - unsigned int res_704x576:1; - - unsigned int res_720x350:1; - unsigned int res_720x400:1; - unsigned int res_720x480:1; - unsigned int res_720x540:1; - unsigned int res_720x576:1; - unsigned int res_768x576:1; - unsigned int res_800x600:1; - unsigned int res_832x624:1; - - unsigned int res_920x766:1; - unsigned int res_1024x768:1; - unsigned int res_1280x1024:1; - unsigned int pad:5; + unsigned int res_320x200:1; + unsigned int res_320x240:1; + unsigned int res_400x300:1; + unsigned int res_640x350:1; + unsigned int res_640x400:1; + unsigned int res_640x480:1; + unsigned int res_704x480:1; + unsigned int res_704x576:1; + + unsigned int res_720x350:1; + unsigned int res_720x400:1; + unsigned int res_720x480:1; + unsigned int res_720x540:1; + unsigned int res_720x576:1; + unsigned int res_768x576:1; + unsigned int res_800x600:1; + unsigned int res_832x624:1; + + unsigned int res_920x766:1; + unsigned int res_1024x768:1; + unsigned int res_1280x1024:1; + unsigned int pad:5; } __attribute__((packed)); /* Get supported resolution with squire pixel aspect ratio that can be @@ -433,90 +433,90 @@ struct intel_sdvo_sdtv_resolution_reply { #define SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT 0x85 struct intel_sdvo_hdtv_resolution_request { - unsigned int hdtv_std_smpte_240m_1080i_59:1; - unsigned int hdtv_std_smpte_240m_1080i_60:1; - unsigned int hdtv_std_smpte_260m_1080i_59:1; - unsigned int hdtv_std_smpte_260m_1080i_60:1; - unsigned int hdtv_std_smpte_274m_1080i_50:1; - unsigned int hdtv_std_smpte_274m_1080i_59:1; - unsigned int hdtv_std_smpte_274m_1080i_60:1; - unsigned int hdtv_std_smpte_274m_1080p_23:1; - - unsigned int hdtv_std_smpte_274m_1080p_24:1; - unsigned int hdtv_std_smpte_274m_1080p_25:1; - unsigned int hdtv_std_smpte_274m_1080p_29:1; - unsigned int hdtv_std_smpte_274m_1080p_30:1; - unsigned int hdtv_std_smpte_274m_1080p_50:1; - unsigned int hdtv_std_smpte_274m_1080p_59:1; - unsigned int hdtv_std_smpte_274m_1080p_60:1; - unsigned int hdtv_std_smpte_295m_1080i_50:1; - - unsigned int hdtv_std_smpte_295m_1080p_50:1; - unsigned int hdtv_std_smpte_296m_720p_59:1; - unsigned int hdtv_std_smpte_296m_720p_60:1; - unsigned int hdtv_std_smpte_296m_720p_50:1; - unsigned int hdtv_std_smpte_293m_480p_59:1; - unsigned int hdtv_std_smpte_170m_480i_59:1; - unsigned int hdtv_std_iturbt601_576i_50:1; - unsigned int hdtv_std_iturbt601_576p_50:1; - - unsigned int hdtv_std_eia_7702a_480i_60:1; - unsigned int hdtv_std_eia_7702a_480p_60:1; - unsigned int pad:6; + unsigned int hdtv_std_smpte_240m_1080i_59:1; + unsigned int hdtv_std_smpte_240m_1080i_60:1; + unsigned int hdtv_std_smpte_260m_1080i_59:1; + unsigned int hdtv_std_smpte_260m_1080i_60:1; + unsigned int hdtv_std_smpte_274m_1080i_50:1; + unsigned int hdtv_std_smpte_274m_1080i_59:1; + unsigned int hdtv_std_smpte_274m_1080i_60:1; + unsigned int hdtv_std_smpte_274m_1080p_23:1; + + unsigned int hdtv_std_smpte_274m_1080p_24:1; + unsigned int hdtv_std_smpte_274m_1080p_25:1; + unsigned int hdtv_std_smpte_274m_1080p_29:1; + unsigned int hdtv_std_smpte_274m_1080p_30:1; + unsigned int hdtv_std_smpte_274m_1080p_50:1; + unsigned int hdtv_std_smpte_274m_1080p_59:1; + unsigned int hdtv_std_smpte_274m_1080p_60:1; + unsigned int hdtv_std_smpte_295m_1080i_50:1; + + unsigned int hdtv_std_smpte_295m_1080p_50:1; + unsigned int hdtv_std_smpte_296m_720p_59:1; + unsigned int hdtv_std_smpte_296m_720p_60:1; + unsigned int hdtv_std_smpte_296m_720p_50:1; + unsigned int hdtv_std_smpte_293m_480p_59:1; + unsigned int hdtv_std_smpte_170m_480i_59:1; + unsigned int hdtv_std_iturbt601_576i_50:1; + unsigned int hdtv_std_iturbt601_576p_50:1; + + unsigned int hdtv_std_eia_7702a_480i_60:1; + unsigned int hdtv_std_eia_7702a_480p_60:1; + unsigned int pad:6; } __attribute__((packed)); struct intel_sdvo_hdtv_resolution_reply { - unsigned int res_640x480:1; - unsigned int res_800x600:1; - unsigned int res_1024x768:1; - unsigned int res_1280x960:1; - unsigned int res_1400x1050:1; - unsigned int res_1600x1200:1; - unsigned int res_1920x1440:1; - unsigned int res_2048x1536:1; - - unsigned int res_2560x1920:1; - unsigned int res_3200x2400:1; - unsigned int res_3840x2880:1; - unsigned int pad1:5; - - unsigned int res_848x480:1; - unsigned int res_1064x600:1; - unsigned int res_1280x720:1; - unsigned int res_1360x768:1; - unsigned int res_1704x960:1; - unsigned int res_1864x1050:1; - unsigned int res_1920x1080:1; - unsigned int res_2128x1200:1; - - unsigned int res_2560x1400:1; - unsigned int res_2728x1536:1; - unsigned int res_3408x1920:1; - unsigned int res_4264x2400:1; - unsigned int res_5120x2880:1; - unsigned int pad2:3; - - unsigned int res_768x480:1; - unsigned int res_960x600:1; - unsigned int res_1152x720:1; - unsigned int res_1124x768:1; - unsigned int res_1536x960:1; - unsigned int res_1680x1050:1; - unsigned int res_1728x1080:1; - unsigned int res_1920x1200:1; - - unsigned int res_2304x1440:1; - unsigned int res_2456x1536:1; - unsigned int res_3072x1920:1; - unsigned int res_3840x2400:1; - unsigned int res_4608x2880:1; - unsigned int pad3:3; - - unsigned int res_1280x1024:1; - unsigned int pad4:7; - - unsigned int res_1280x768:1; - unsigned int pad5:7; + unsigned int res_640x480:1; + unsigned int res_800x600:1; + unsigned int res_1024x768:1; + unsigned int res_1280x960:1; + unsigned int res_1400x1050:1; + unsigned int res_1600x1200:1; + unsigned int res_1920x1440:1; + unsigned int res_2048x1536:1; + + unsigned int res_2560x1920:1; + unsigned int res_3200x2400:1; + unsigned int res_3840x2880:1; + unsigned int pad1:5; + + unsigned int res_848x480:1; + unsigned int res_1064x600:1; + unsigned int res_1280x720:1; + unsigned int res_1360x768:1; + unsigned int res_1704x960:1; + unsigned int res_1864x1050:1; + unsigned int res_1920x1080:1; + unsigned int res_2128x1200:1; + + unsigned int res_2560x1400:1; + unsigned int res_2728x1536:1; + unsigned int res_3408x1920:1; + unsigned int res_4264x2400:1; + unsigned int res_5120x2880:1; + unsigned int pad2:3; + + unsigned int res_768x480:1; + unsigned int res_960x600:1; + unsigned int res_1152x720:1; + unsigned int res_1124x768:1; + unsigned int res_1536x960:1; + unsigned int res_1680x1050:1; + unsigned int res_1728x1080:1; + unsigned int res_1920x1200:1; + + unsigned int res_2304x1440:1; + unsigned int res_2456x1536:1; + unsigned int res_3072x1920:1; + unsigned int res_3840x2400:1; + unsigned int res_4608x2880:1; + unsigned int pad3:3; + + unsigned int res_1280x1024:1; + unsigned int pad4:7; + + unsigned int res_1280x768:1; + unsigned int pad5:7; } __attribute__((packed)); /* Get supported power state returns info for encoder and monitor, rely on @@ -544,25 +544,25 @@ struct intel_sdvo_hdtv_resolution_reply { * The high fields are bits 8:9 of the 10-bit values. */ struct sdvo_panel_power_sequencing { - u8 t0; - u8 t1; - u8 t2; - u8 t3; - u8 t4; - - unsigned int t0_high:2; - unsigned int t1_high:2; - unsigned int t2_high:2; - unsigned int t3_high:2; - - unsigned int t4_high:2; - unsigned int pad:6; + u8 t0; + u8 t1; + u8 t2; + u8 t3; + u8 t4; + + unsigned int t0_high:2; + unsigned int t1_high:2; + unsigned int t2_high:2; + unsigned int t3_high:2; + + unsigned int t4_high:2; + unsigned int pad:6; } __attribute__((packed)); #define SDVO_CMD_GET_MAX_BACKLIGHT_LEVEL 0x30 struct sdvo_max_backlight_reply { - u8 max_value; - u8 default_value; + u8 max_value; + u8 default_value; } __attribute__((packed)); #define SDVO_CMD_GET_BACKLIGHT_LEVEL 0x31 @@ -570,16 +570,16 @@ struct sdvo_max_backlight_reply { #define SDVO_CMD_GET_AMBIENT_LIGHT 0x33 struct sdvo_get_ambient_light_reply { - u16 trip_low; - u16 trip_high; - u16 value; + u16 trip_low; + u16 trip_high; + u16 value; } __attribute__((packed)); #define SDVO_CMD_SET_AMBIENT_LIGHT 0x34 struct sdvo_set_ambient_light_reply { - u16 trip_low; - u16 trip_high; - unsigned int enable:1; - unsigned int pad:7; + u16 trip_low; + u16 trip_high; + unsigned int enable:1; + unsigned int pad:7; } __attribute__((packed)); /* Set display power state */ @@ -591,23 +591,23 @@ struct sdvo_set_ambient_light_reply { #define SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS 0x84 struct intel_sdvo_enhancements_reply { - unsigned int flicker_filter:1; - unsigned int flicker_filter_adaptive:1; - unsigned int flicker_filter_2d:1; - unsigned int saturation:1; - unsigned int hue:1; - unsigned int brightness:1; - unsigned int contrast:1; - unsigned int overscan_h:1; - - unsigned int overscan_v:1; - unsigned int hpos:1; - unsigned int vpos:1; - unsigned int sharpness:1; - unsigned int dot_crawl:1; - unsigned int dither:1; - unsigned int tv_chroma_filter:1; - unsigned int tv_luma_filter:1; + unsigned int flicker_filter:1; + unsigned int flicker_filter_adaptive:1; + unsigned int flicker_filter_2d:1; + unsigned int saturation:1; + unsigned int hue:1; + unsigned int brightness:1; + unsigned int contrast:1; + unsigned int overscan_h:1; + + unsigned int overscan_v:1; + unsigned int hpos:1; + unsigned int vpos:1; + unsigned int sharpness:1; + unsigned int dot_crawl:1; + unsigned int dither:1; + unsigned int tv_chroma_filter:1; + unsigned int tv_luma_filter:1; } __attribute__((packed)); /* Picture enhancement limits below are dependent on the current TV format, @@ -628,8 +628,8 @@ struct intel_sdvo_enhancements_reply { #define SDVO_CMD_GET_MAX_TV_CHROMA_FILTER 0x74 #define SDVO_CMD_GET_MAX_TV_LUMA_FILTER 0x77 struct intel_sdvo_enhancement_limits_reply { - u16 max_value; - u16 default_value; + u16 max_value; + u16 default_value; } __attribute__((packed)); #define SDVO_CMD_GET_LVDS_PANEL_INFORMATION 0x7f @@ -670,8 +670,8 @@ struct intel_sdvo_enhancement_limits_reply { #define SDVO_CMD_GET_TV_LUMA_FILTER 0x78 #define SDVO_CMD_SET_TV_LUMA_FILTER 0x79 struct intel_sdvo_enhancements_arg { - u16 value; -}__attribute__((packed)); + u16 value; +} __attribute__((packed)); #define SDVO_CMD_GET_DOT_CRAWL 0x70 #define SDVO_CMD_SET_DOT_CRAWL 0x71 @@ -724,7 +724,7 @@ struct intel_sdvo_enhancements_arg { #define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c #define SDVO_NEED_TO_STALL (1 << 7) -struct intel_sdvo_encode{ - u8 dvi_rev; - u8 hdmi_rev; +struct intel_sdvo_encode { + u8 dvi_rev; + u8 hdmi_rev; } __attribute__ ((packed)); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 2136e6b..b221f2b 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -194,10 +194,10 @@ static const u32 filter_table[] = { * * if (f >= 1) { * exp = 0x7; - * mant = 1 << 8; + * mant = 1 << 8; * } else { * for (exp = 0; exp < 3 && f < 0.5; exp++) - * f *= 2.0; + * f *= 2.0; * mant = (f * (1 << 9) + 0.5); * if (mant >= (1 << 9)) * mant = (1 << 9) - 1; @@ -430,7 +430,7 @@ static const struct tv_mode tv_modes[] = { .vsync_start_f1 = 6, .vsync_start_f2 = 7, .vsync_len = 6, - .veq_ena = true, .veq_start_f1 = 0, + .veq_ena = true, .veq_start_f1 = 0, .veq_start_f2 = 1, .veq_len = 18, .vi_end_f1 = 20, .vi_end_f2 = 21, @@ -472,7 +472,7 @@ static const struct tv_mode tv_modes[] = { .vsync_start_f1 = 6, .vsync_start_f2 = 7, .vsync_len = 6, - .veq_ena = true, .veq_start_f1 = 0, + .veq_ena = true, .veq_start_f1 = 0, .veq_start_f2 = 1, .veq_len = 18, .vi_end_f1 = 20, .vi_end_f2 = 21, @@ -515,7 +515,7 @@ static const struct tv_mode tv_modes[] = { .vsync_start_f1 = 6, .vsync_start_f2 = 7, .vsync_len = 6, - .veq_ena = true, .veq_start_f1 = 0, + .veq_ena = true, .veq_start_f1 = 0, .veq_start_f2 = 1, .veq_len = 18, .vi_end_f1 = 20, .vi_end_f2 = 21, @@ -558,7 +558,7 @@ static const struct tv_mode tv_modes[] = { .vsync_start_f1 = 6, .vsync_start_f2 = 7, .vsync_len = 6, - .veq_ena = true, .veq_start_f1 = 0, + .veq_ena = true, .veq_start_f1 = 0, .veq_start_f2 = 1, .veq_len = 18, .vi_end_f1 = 20, .vi_end_f2 = 21, @@ -602,14 +602,14 @@ static const struct tv_mode tv_modes[] = { .vsync_start_f1 = 6, .vsync_start_f2 = 7, .vsync_len = 6, - .veq_ena = true, .veq_start_f1 = 0, + .veq_ena = true, .veq_start_f1 = 0, .veq_start_f2 = 1, .veq_len = 18, .vi_end_f1 = 24, .vi_end_f2 = 25, .nbr_end = 286, .burst_ena = true, - .hburst_start = 73, .hburst_len = 34, + .hburst_start = 73, .hburst_len = 34, .vburst_start_f1 = 8, .vburst_end_f1 = 285, .vburst_start_f2 = 8, .vburst_end_f2 = 286, .vburst_start_f3 = 9, .vburst_end_f3 = 286, @@ -646,7 +646,7 @@ static const struct tv_mode tv_modes[] = { .vsync_start_f1 = 5, .vsync_start_f2 = 6, .vsync_len = 5, - .veq_ena = true, .veq_start_f1 = 0, + .veq_ena = true, .veq_start_f1 = 0, .veq_start_f2 = 1, .veq_len = 15, .vi_end_f1 = 24, .vi_end_f2 = 25, @@ -675,7 +675,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "480p@59.94Hz", - .clock = 107520, + .clock = 107520, .refresh = 59940, .oversample = TV_OVERSAMPLE_4X, .component_only = 1, @@ -683,7 +683,7 @@ static const struct tv_mode tv_modes[] = { .hsync_end = 64, .hblank_end = 122, .hblank_start = 842, .htotal = 857, - .progressive = true,.trilevel_sync = false, + .progressive = true, .trilevel_sync = false, .vsync_start_f1 = 12, .vsync_start_f2 = 12, .vsync_len = 12, @@ -699,7 +699,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "480p@60Hz", - .clock = 107520, + .clock = 107520, .refresh = 60000, .oversample = TV_OVERSAMPLE_4X, .component_only = 1, @@ -707,7 +707,7 @@ static const struct tv_mode tv_modes[] = { .hsync_end = 64, .hblank_end = 122, .hblank_start = 842, .htotal = 856, - .progressive = true,.trilevel_sync = false, + .progressive = true, .trilevel_sync = false, .vsync_start_f1 = 12, .vsync_start_f2 = 12, .vsync_len = 12, @@ -723,7 +723,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "576p", - .clock = 107520, + .clock = 107520, .refresh = 50000, .oversample = TV_OVERSAMPLE_4X, .component_only = 1, @@ -755,7 +755,7 @@ static const struct tv_mode tv_modes[] = { .hsync_end = 80, .hblank_end = 300, .hblank_start = 1580, .htotal = 1649, - .progressive = true, .trilevel_sync = true, + .progressive = true, .trilevel_sync = true, .vsync_start_f1 = 10, .vsync_start_f2 = 10, .vsync_len = 10, @@ -779,7 +779,7 @@ static const struct tv_mode tv_modes[] = { .hsync_end = 80, .hblank_end = 300, .hblank_start = 1580, .htotal = 1651, - .progressive = true, .trilevel_sync = true, + .progressive = true, .trilevel_sync = true, .vsync_start_f1 = 10, .vsync_start_f2 = 10, .vsync_len = 10, @@ -803,7 +803,7 @@ static const struct tv_mode tv_modes[] = { .hsync_end = 80, .hblank_end = 300, .hblank_start = 1580, .htotal = 1979, - .progressive = true, .trilevel_sync = true, + .progressive = true, .trilevel_sync = true, .vsync_start_f1 = 10, .vsync_start_f2 = 10, .vsync_len = 10, @@ -828,12 +828,12 @@ static const struct tv_mode tv_modes[] = { .hsync_end = 88, .hblank_end = 235, .hblank_start = 2155, .htotal = 2639, - .progressive = false, .trilevel_sync = true, + .progressive = false, .trilevel_sync = true, .vsync_start_f1 = 4, .vsync_start_f2 = 5, .vsync_len = 10, - .veq_ena = true, .veq_start_f1 = 4, + .veq_ena = true, .veq_start_f1 = 4, .veq_start_f2 = 4, .veq_len = 10, @@ -854,12 +854,12 @@ static const struct tv_mode tv_modes[] = { .hsync_end = 88, .hblank_end = 235, .hblank_start = 2155, .htotal = 2199, - .progressive = false, .trilevel_sync = true, + .progressive = false, .trilevel_sync = true, .vsync_start_f1 = 4, .vsync_start_f2 = 5, .vsync_len = 10, - .veq_ena = true, .veq_start_f1 = 4, + .veq_ena = true, .veq_start_f1 = 4, .veq_start_f2 = 4, .veq_len = 10, @@ -880,16 +880,16 @@ static const struct tv_mode tv_modes[] = { .hsync_end = 88, .hblank_end = 235, .hblank_start = 2155, .htotal = 2201, - .progressive = false, .trilevel_sync = true, + .progressive = false, .trilevel_sync = true, .vsync_start_f1 = 4, .vsync_start_f2 = 5, .vsync_len = 10, .veq_ena = true, .veq_start_f1 = 4, - .veq_start_f2 = 4, .veq_len = 10, + .veq_start_f2 = 4, .veq_len = 10, - .vi_end_f1 = 21, .vi_end_f2 = 22, + .vi_end_f1 = 21, .vi_end_f2 = 22, .nbr_end = 539, .burst_ena = false, @@ -916,7 +916,7 @@ intel_tv_dpms(struct drm_encoder *encoder, int mode) struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - switch(mode) { + switch (mode) { case DRM_MODE_DPMS_ON: I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); break; @@ -933,7 +933,7 @@ intel_tv_mode_lookup(const char *tv_format) { int i; - for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) { + for (i = 0; i < sizeof(tv_modes) / sizeof(tv_modes[0]); i++) { const struct tv_mode *tv_mode = &tv_modes[i]; if (!strcmp(tv_format, tv_mode->name)) @@ -1128,7 +1128,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, if (color_conversion) { I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) | color_conversion->gy); - I915_WRITE(TV_CSC_Y2,(color_conversion->by << 16) | + I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) | color_conversion->ay); I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) | color_conversion->gu); @@ -1232,10 +1232,12 @@ static const struct drm_display_mode reported_modes[] = { * \return false if TV is disconnected. */ static int -intel_tv_detect_type (struct intel_tv *intel_tv, +intel_tv_detect_type(struct intel_tv *intel_tv, struct drm_connector *connector) { struct drm_encoder *encoder = &intel_tv->base.base; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; unsigned long irqflags; @@ -1258,6 +1260,10 @@ intel_tv_detect_type (struct intel_tv *intel_tv, /* Poll for TV detection */ tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK); tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; + if (intel_crtc->pipe == 1) + tv_ctl |= TV_ENC_PIPEB_SELECT; + else + tv_ctl &= ~TV_ENC_PIPEB_SELECT; tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK); tv_dac |= (TVDAC_STATE_CHG_EN | @@ -1277,26 +1283,26 @@ intel_tv_detect_type (struct intel_tv *intel_tv, to_intel_crtc(intel_tv->base.base.crtc)->pipe); type = -1; - if (wait_for((tv_dac = I915_READ(TV_DAC)) & TVDAC_STATE_CHG, 20) == 0) { - DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac); - /* - * A B C - * 0 1 1 Composite - * 1 0 X svideo - * 0 0 0 Component - */ - if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { - DRM_DEBUG_KMS("Detected Composite TV connection\n"); - type = DRM_MODE_CONNECTOR_Composite; - } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { - DRM_DEBUG_KMS("Detected S-Video TV connection\n"); - type = DRM_MODE_CONNECTOR_SVIDEO; - } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { - DRM_DEBUG_KMS("Detected Component TV connection\n"); - type = DRM_MODE_CONNECTOR_Component; - } else { - DRM_DEBUG_KMS("Unrecognised TV connection\n"); - } + tv_dac = I915_READ(TV_DAC); + DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac); + /* + * A B C + * 0 1 1 Composite + * 1 0 X svideo + * 0 0 0 Component + */ + if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { + DRM_DEBUG_KMS("Detected Composite TV connection\n"); + type = DRM_MODE_CONNECTOR_Composite; + } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { + DRM_DEBUG_KMS("Detected S-Video TV connection\n"); + type = DRM_MODE_CONNECTOR_SVIDEO; + } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { + DRM_DEBUG_KMS("Detected Component TV connection\n"); + type = DRM_MODE_CONNECTOR_Component; + } else { + DRM_DEBUG_KMS("Unrecognised TV connection\n"); + type = -1; } I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); @@ -1485,7 +1491,7 @@ intel_tv_get_modes(struct drm_connector *connector) } static void -intel_tv_destroy (struct drm_connector *connector) +intel_tv_destroy(struct drm_connector *connector) { drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -1593,9 +1599,14 @@ static int tv_is_present_in_vbt(struct drm_device *dev) /* * If the device type is not TV, continue. */ - if (p_child->device_type != DEVICE_TYPE_INT_TV && - p_child->device_type != DEVICE_TYPE_TV) + switch (p_child->device_type) { + case DEVICE_TYPE_INT_TV: + case DEVICE_TYPE_TV: + case DEVICE_TYPE_TV_SVIDEO_COMPOSITE: + break; + default: continue; + } /* Only when the addin_offset is non-zero, it is regarded * as present. */ diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index 42d3187..33daa29 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c @@ -29,6 +29,8 @@ * Gareth Hughes <gareth@valinux.com> */ +#include <linux/module.h> + #include "drmP.h" #include "drm.h" #include "mga_drm.h" diff --git a/drivers/gpu/drm/mga/mga_warp.c b/drivers/gpu/drm/mga/mga_warp.c index f172bd5..722a91b 100644 --- a/drivers/gpu/drm/mga/mga_warp.c +++ b/drivers/gpu/drm/mga/mga_warp.c @@ -30,6 +30,7 @@ #include <linux/firmware.h> #include <linux/ihex.h> #include <linux/platform_device.h> +#include <linux/module.h> #include "drmP.h" #include "drm.h" diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 0583677..35ef5b1 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -21,16 +21,17 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv40_grctx.o nv50_grctx.o nvc0_grctx.o \ nv84_crypt.o \ nva3_copy.o nvc0_copy.o \ - nv40_mpeg.o nv50_mpeg.o \ + nv31_mpeg.o nv50_mpeg.o \ nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ - nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \ - nv50_cursor.o nv50_display.o \ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ nv04_crtc.o nv04_display.o nv04_cursor.o \ + nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \ + nv50_cursor.o nv50_display.o \ + nvd0_display.o \ nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ nv10_gpio.o nv50_gpio.o \ nv50_calc.o \ - nv04_pm.o nv50_pm.o nva3_pm.o \ + nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ nv50_vram.o nvc0_vram.o \ nv50_vm.o nvc0_vm.o diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 525744d..5ee8cca 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -245,7 +245,7 @@ static bool nouveau_dsm_detect(void) struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; struct pci_dev *pdev = NULL; int has_dsm = 0; - int has_optimus; + int has_optimus = 0; int vga_count = 0; bool guid_valid; int retval; @@ -332,9 +332,6 @@ bool nouveau_acpi_rom_supported(struct pci_dev *pdev) acpi_status status; acpi_handle dhandle, rom_handle; - if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected) - return false; - dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); if (!dhandle) return false; diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 00a55df..fa22b28 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -37,8 +37,10 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" #include "nouveau_reg.h" +#include "nouveau_encoder.h" -static int nv40_get_intensity(struct backlight_device *bd) +static int +nv40_get_intensity(struct backlight_device *bd) { struct drm_device *dev = bl_get_data(bd); int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK) @@ -47,7 +49,8 @@ static int nv40_get_intensity(struct backlight_device *bd) return val; } -static int nv40_set_intensity(struct backlight_device *bd) +static int +nv40_set_intensity(struct backlight_device *bd) { struct drm_device *dev = bl_get_data(bd); int val = bd->props.brightness; @@ -65,30 +68,8 @@ static const struct backlight_ops nv40_bl_ops = { .update_status = nv40_set_intensity, }; -static int nv50_get_intensity(struct backlight_device *bd) -{ - struct drm_device *dev = bl_get_data(bd); - - return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT); -} - -static int nv50_set_intensity(struct backlight_device *bd) -{ - struct drm_device *dev = bl_get_data(bd); - int val = bd->props.brightness; - - nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT, - val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); - return 0; -} - -static const struct backlight_ops nv50_bl_ops = { - .options = BL_CORE_SUSPENDRESUME, - .get_brightness = nv50_get_intensity, - .update_status = nv50_set_intensity, -}; - -static int nouveau_nv40_backlight_init(struct drm_connector *connector) +static int +nv40_backlight_init(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -113,34 +94,129 @@ static int nouveau_nv40_backlight_init(struct drm_connector *connector) return 0; } -static int nouveau_nv50_backlight_init(struct drm_connector *connector) +static int +nv50_get_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct drm_device *dev = nv_encoder->base.base.dev; + int or = nv_encoder->or; + u32 div = 1025; + u32 val; + + val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or)); + val &= NV50_PDISP_SOR_PWM_CTL_VAL; + return ((val * 100) + (div / 2)) / div; +} + +static int +nv50_set_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct drm_device *dev = nv_encoder->base.base.dev; + int or = nv_encoder->or; + u32 div = 1025; + u32 val = (bd->props.brightness * div) / 100; + + nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or), + NV50_PDISP_SOR_PWM_CTL_NEW | val); + return 0; +} + +static const struct backlight_ops nv50_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nv50_get_intensity, + .update_status = nv50_set_intensity, +}; + +static int +nva3_get_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct drm_device *dev = nv_encoder->base.base.dev; + int or = nv_encoder->or; + u32 div, val; + + div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or)); + val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or)); + val &= NVA3_PDISP_SOR_PWM_CTL_VAL; + if (div && div >= val) + return ((val * 100) + (div / 2)) / div; + + return 100; +} + +static int +nva3_set_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct drm_device *dev = nv_encoder->base.base.dev; + int or = nv_encoder->or; + u32 div, val; + + div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or)); + val = (bd->props.brightness * div) / 100; + if (div) { + nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or), val | + NV50_PDISP_SOR_PWM_CTL_NEW | + NVA3_PDISP_SOR_PWM_CTL_UNK); + return 0; + } + + return -EINVAL; +} + +static const struct backlight_ops nva3_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nva3_get_intensity, + .update_status = nva3_set_intensity, +}; + +static int +nv50_backlight_init(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_encoder *nv_encoder; struct backlight_properties props; struct backlight_device *bd; + const struct backlight_ops *ops; + + nv_encoder = find_encoder(connector, OUTPUT_LVDS); + if (!nv_encoder) { + nv_encoder = find_encoder(connector, OUTPUT_DP); + if (!nv_encoder) + return -ENODEV; + } - if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT)) + if (!nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or))) return 0; + if (dev_priv->chipset <= 0xa0 || + dev_priv->chipset == 0xaa || + dev_priv->chipset == 0xac) + ops = &nv50_bl_ops; + else + ops = &nva3_bl_ops; + memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; - props.max_brightness = 1025; - bd = backlight_device_register("nv_backlight", &connector->kdev, dev, - &nv50_bl_ops, &props); + props.max_brightness = 100; + bd = backlight_device_register("nv_backlight", &connector->kdev, + nv_encoder, ops, &props); if (IS_ERR(bd)) return PTR_ERR(bd); dev_priv->backlight = bd; - bd->props.brightness = nv50_get_intensity(bd); + bd->props.brightness = bd->ops->get_brightness(bd); backlight_update_status(bd); return 0; } -int nouveau_backlight_init(struct drm_connector *connector) +int +nouveau_backlight_init(struct drm_device *dev) { - struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; + struct drm_connector *connector; #ifdef CONFIG_ACPI if (acpi_video_backlight_support()) { @@ -150,21 +226,28 @@ int nouveau_backlight_init(struct drm_connector *connector) } #endif - switch (dev_priv->card_type) { - case NV_40: - return nouveau_nv40_backlight_init(connector); - case NV_50: - return nouveau_nv50_backlight_init(connector); - default: - break; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && + connector->connector_type != DRM_MODE_CONNECTOR_eDP) + continue; + + switch (dev_priv->card_type) { + case NV_40: + return nv40_backlight_init(connector); + case NV_50: + return nv50_backlight_init(connector); + default: + break; + } } + return 0; } -void nouveau_backlight_exit(struct drm_connector *connector) +void +nouveau_backlight_exit(struct drm_device *dev) { - struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; if (dev_priv->backlight) { diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 729d5fd..5fc201b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -135,13 +135,14 @@ static void load_vbios_pramin(struct drm_device *dev, uint8_t *data) int i; if (dev_priv->card_type >= NV_50) { - uint32_t vbios_vram = (nv_rd32(dev, 0x619f04) & ~0xff) << 8; - - if (!vbios_vram) - vbios_vram = (nv_rd32(dev, 0x1700) << 16) + 0xf0000; + u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8; + if (!addr) { + addr = (u64)nv_rd32(dev, 0x1700) << 16; + addr += 0xf0000; + } old_bar0_pramin = nv_rd32(dev, 0x1700); - nv_wr32(dev, 0x1700, vbios_vram >> 16); + nv_wr32(dev, 0x1700, addr >> 16); } /* bail if no rom signature */ @@ -295,6 +296,11 @@ munge_reg(struct nvbios *bios, uint32_t reg) if (dev_priv->card_type < NV_50) return reg; + if (reg & 0x80000000) { + BUG_ON(bios->display.crtc < 0); + reg += bios->display.crtc * 0x800; + } + if (reg & 0x40000000) { BUG_ON(!dcbent); @@ -303,7 +309,7 @@ munge_reg(struct nvbios *bios, uint32_t reg) reg += 0x00000080; } - reg &= ~0x60000000; + reg &= ~0xe0000000; return reg; } @@ -634,10 +640,9 @@ static int nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t reg0 = nv_rd32(dev, reg + 0); - uint32_t reg1 = nv_rd32(dev, reg + 4); struct nouveau_pll_vals pll; struct pll_lims pll_limits; + u32 ctrl, mask, coef; int ret; ret = get_pll_limits(dev, reg, &pll_limits); @@ -648,15 +653,20 @@ nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk) if (!clk) return -ERANGE; - reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16); - reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1; - - if (dev_priv->vbios.execute) { - still_alive(); - nv_wr32(dev, reg + 4, reg1); - nv_wr32(dev, reg + 0, reg0); + coef = pll.N1 << 8 | pll.M1; + ctrl = pll.log2P << 16; + mask = 0x00070000; + if (reg == 0x004008) { + mask |= 0x01f80000; + ctrl |= (pll_limits.log2p_bias << 19); + ctrl |= (pll.log2P << 22); } + if (!dev_priv->vbios.execute) + return 0; + + nv_mask(dev, reg + 0, mask, ctrl); + nv_wr32(dev, reg + 4, coef); return 0; } @@ -1173,22 +1183,19 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) * */ - struct bit_displayport_encoder_table *dpe = NULL; struct dcb_entry *dcb = bios->display.output; struct drm_device *dev = bios->dev; uint8_t cond = bios->data[offset + 1]; - int dummy; + uint8_t *table, *entry; BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond); if (!iexec->execute) return 3; - dpe = nouveau_bios_dp_table(dev, dcb, &dummy); - if (!dpe) { - NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset); + table = nouveau_dp_bios_data(dev, dcb, &entry); + if (!table) return 3; - } switch (cond) { case 0: @@ -1202,7 +1209,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) break; case 1: case 2: - if (!(dpe->unknown & cond)) + if (!(entry[5] & cond)) iexec->execute = false; break; case 5: @@ -3220,6 +3227,49 @@ init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) return 1; } +static void +init_gpio_unknv50(struct nvbios *bios, struct dcb_gpio_entry *gpio) +{ + const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; + u32 r, s, v; + + /* Not a clue, needs de-magicing */ + r = nv50_gpio_ctl[gpio->line >> 4]; + s = (gpio->line & 0x0f); + v = bios_rd32(bios, r) & ~(0x00010001 << s); + switch ((gpio->entry & 0x06000000) >> 25) { + case 1: + v |= (0x00000001 << s); + break; + case 2: + v |= (0x00010000 << s); + break; + default: + break; + } + + bios_wr32(bios, r, v); +} + +static void +init_gpio_unknvd0(struct nvbios *bios, struct dcb_gpio_entry *gpio) +{ + u32 v, i; + + v = bios_rd32(bios, 0x00d610 + (gpio->line * 4)); + v &= 0xffffff00; + v |= (gpio->entry & 0x00ff0000) >> 16; + bios_wr32(bios, 0x00d610 + (gpio->line * 4), v); + + i = (gpio->entry & 0x1f000000) >> 24; + if (i) { + v = bios_rd32(bios, 0x00d640 + ((i - 1) * 4)); + v &= 0xffffff00; + v |= gpio->line; + bios_wr32(bios, 0x00d640 + ((i - 1) * 4), v); + } +} + static int init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) { @@ -3234,7 +3284,6 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) struct drm_nouveau_private *dev_priv = bios->dev->dev_private; struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; int i; if (dev_priv->card_type < NV_50) { @@ -3247,33 +3296,20 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) for (i = 0; i < bios->dcb.gpio.entries; i++) { struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i]; - uint32_t r, s, v; BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry); BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n", offset, gpio->tag, gpio->state_default); - if (bios->execute) - pgpio->set(bios->dev, gpio->tag, gpio->state_default); - /* The NVIDIA binary driver doesn't appear to actually do - * any of this, my VBIOS does however. - */ - /* Not a clue, needs de-magicing */ - r = nv50_gpio_ctl[gpio->line >> 4]; - s = (gpio->line & 0x0f); - v = bios_rd32(bios, r) & ~(0x00010001 << s); - switch ((gpio->entry & 0x06000000) >> 25) { - case 1: - v |= (0x00000001 << s); - break; - case 2: - v |= (0x00010000 << s); - break; - default: - break; - } - bios_wr32(bios, r, v); + if (!bios->execute) + continue; + + pgpio->set(bios->dev, gpio->tag, gpio->state_default); + if (dev_priv->card_type < NV_D0) + init_gpio_unknv50(bios, gpio); + else + init_gpio_unknvd0(bios, gpio); } return 1; @@ -3736,6 +3772,10 @@ parse_init_table(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) int count = 0, i, ret; uint8_t id; + /* catch NULL script pointers */ + if (offset == 0) + return 0; + /* * Loop until INIT_DONE causes us to break out of the loop * (or until offset > bios length just in case... ) @@ -4388,86 +4428,37 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b return 0; } -static uint8_t * -bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent, - uint16_t record, int record_len, int record_nr, - bool match_link) +/* BIT 'U'/'d' table encoder subtables have hashes matching them to + * a particular set of encoders. + * + * This function returns true if a particular DCB entry matches. + */ +bool +bios_encoder_match(struct dcb_entry *dcb, u32 hash) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - uint32_t entry; - uint16_t table; - int i, v; + if ((hash & 0x000000f0) != (dcb->location << 4)) + return false; + if ((hash & 0x0000000f) != dcb->type) + return false; + if (!(hash & (dcb->or << 16))) + return false; - switch (dcbent->type) { + switch (dcb->type) { case OUTPUT_TMDS: case OUTPUT_LVDS: case OUTPUT_DP: - break; - default: - match_link = false; - break; - } - - for (i = 0; i < record_nr; i++, record += record_len) { - table = ROM16(bios->data[record]); - if (!table) - continue; - entry = ROM32(bios->data[table]); - - if (match_link) { - v = (entry & 0x00c00000) >> 22; - if (!(v & dcbent->sorconf.link)) - continue; + if (hash & 0x00c00000) { + if (!(hash & (dcb->sorconf.link << 22))) + return false; } - - v = (entry & 0x000f0000) >> 16; - if (!(v & dcbent->or)) - continue; - - v = (entry & 0x000000f0) >> 4; - if (v != dcbent->location) - continue; - - v = (entry & 0x0000000f); - if (v != dcbent->type) - continue; - - return &bios->data[table]; - } - - return NULL; -} - -void * -nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, - int *length) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - uint8_t *table; - - if (!bios->display.dp_table_ptr) { - NV_ERROR(dev, "No pointer to DisplayPort table\n"); - return NULL; - } - table = &bios->data[bios->display.dp_table_ptr]; - - if (table[0] != 0x20 && table[0] != 0x21) { - NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n", - table[0]); - return NULL; + default: + return true; } - - *length = table[4]; - return bios_output_config_match(dev, dcbent, - bios->display.dp_table_ptr + table[1], - table[2], table[3], table[0] >= 0x21); } int -nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, - uint32_t sub, int pxclk) +nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk, + struct dcb_entry *dcbent, int crtc) { /* * The display script table is located by the BIT 'U' table. @@ -4497,7 +4488,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, uint8_t *table = &bios->data[bios->display.script_table_ptr]; uint8_t *otable = NULL; uint16_t script; - int i = 0; + int i; if (!bios->display.script_table_ptr) { NV_ERROR(dev, "No pointer to output script table\n"); @@ -4549,30 +4540,33 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n", dcbent->type, dcbent->location, dcbent->or); - otable = bios_output_config_match(dev, dcbent, table[1] + - bios->display.script_table_ptr, - table[2], table[3], table[0] >= 0x21); + for (i = 0; i < table[3]; i++) { + otable = ROMPTR(bios, table[table[1] + (i * table[2])]); + if (otable && bios_encoder_match(dcbent, ROM32(otable[0]))) + break; + } + if (!otable) { NV_DEBUG_KMS(dev, "failed to match any output table\n"); return 1; } - if (pxclk < -2 || pxclk > 0) { + if (pclk < -2 || pclk > 0) { /* Try to find matching script table entry */ for (i = 0; i < otable[5]; i++) { - if (ROM16(otable[table[4] + i*6]) == sub) + if (ROM16(otable[table[4] + i*6]) == type) break; } if (i == otable[5]) { NV_ERROR(dev, "Table 0x%04x not found for %d/%d, " "using first\n", - sub, dcbent->type, dcbent->or); + type, dcbent->type, dcbent->or); i = 0; } } - if (pxclk == 0) { + if (pclk == 0) { script = ROM16(otable[6]); if (!script) { NV_DEBUG_KMS(dev, "output script 0 not found\n"); @@ -4580,9 +4574,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, } NV_DEBUG_KMS(dev, "0x%04X: parsing output script 0\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } else - if (pxclk == -1) { + if (pclk == -1) { script = ROM16(otable[8]); if (!script) { NV_DEBUG_KMS(dev, "output script 1 not found\n"); @@ -4590,9 +4584,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, } NV_DEBUG_KMS(dev, "0x%04X: parsing output script 1\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } else - if (pxclk == -2) { + if (pclk == -2) { if (table[4] >= 12) script = ROM16(otable[10]); else @@ -4603,31 +4597,31 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, } NV_DEBUG_KMS(dev, "0x%04X: parsing output script 2\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } else - if (pxclk > 0) { + if (pclk > 0) { script = ROM16(otable[table[4] + i*6 + 2]); if (script) - script = clkcmptable(bios, script, pxclk); + script = clkcmptable(bios, script, pclk); if (!script) { NV_DEBUG_KMS(dev, "clock script 0 not found\n"); return 1; } NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 0\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } else - if (pxclk < 0) { + if (pclk < 0) { script = ROM16(otable[table[4] + i*6 + 4]); if (script) - script = clkcmptable(bios, script, -pxclk); + script = clkcmptable(bios, script, -pclk); if (!script) { NV_DEBUG_KMS(dev, "clock script 1 not found\n"); return 1; } NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 1\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } return 0; @@ -5186,7 +5180,7 @@ static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, st load_table_ptr = ROM16(bios->data[bitentry->offset]); if (load_table_ptr == 0x0) { - NV_ERROR(dev, "Pointer to BIT loadval table invalid\n"); + NV_DEBUG(dev, "Pointer to BIT loadval table invalid\n"); return -EINVAL; } @@ -5477,14 +5471,6 @@ parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios, return 0; } -static int -parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios, - struct bit_entry *bitentry) -{ - bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]); - return 0; -} - struct bit_table { const char id; int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *); @@ -5558,7 +5544,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset) parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds)); parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds)); parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U)); - parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport)); return 0; } @@ -5883,9 +5868,15 @@ parse_dcb_gpio_table(struct nvbios *bios) } e->line = (e->entry & 0x0000001f) >> 0; - e->state_default = (e->entry & 0x01000000) >> 24; - e->state[0] = (e->entry & 0x18000000) >> 27; - e->state[1] = (e->entry & 0x60000000) >> 29; + if (gpio[0] == 0x40) { + e->state_default = (e->entry & 0x01000000) >> 24; + e->state[0] = (e->entry & 0x18000000) >> 27; + e->state[1] = (e->entry & 0x60000000) >> 29; + } else { + e->state_default = (e->entry & 0x00000080) >> 7; + e->state[0] = (entry[4] >> 4) & 3; + e->state[1] = (entry[4] >> 6) & 3; + } } } @@ -5965,6 +5956,12 @@ apply_dcb_connector_quirks(struct nvbios *bios, int idx) if (cte->type == DCB_CONNECTOR_HDMI_1) cte->type = DCB_CONNECTOR_DVI_I; } + + /* Gigabyte GV-NX86T512H */ + if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { + if (cte->type == DCB_CONNECTOR_HDMI_1) + cte->type = DCB_CONNECTOR_DVI_I; + } } static const u8 hpd_gpio[16] = { @@ -6149,7 +6146,14 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, } case OUTPUT_DP: entry->dpconf.sor.link = (conf & 0x00000030) >> 4; - entry->dpconf.link_bw = (conf & 0x00e00000) >> 21; + switch ((conf & 0x00e00000) >> 21) { + case 0: + entry->dpconf.link_bw = 162000; + break; + default: + entry->dpconf.link_bw = 270000; + break; + } switch ((conf & 0x0f000000) >> 24) { case 0xf: entry->dpconf.link_nr = 4; @@ -6377,6 +6381,37 @@ apply_dcb_encoder_quirks(struct drm_device *dev, int idx, u32 *conn, u32 *conf) } } + /* Some other twisted XFX board (rhbz#694914) + * + * The DVI/VGA encoder combo that's supposed to represent the + * DVI-I connector actually point at two different ones, and + * the HDMI connector ends up paired with the VGA instead. + * + * Connector table is missing anything for VGA at all, pointing it + * an invalid conntab entry 2 so we figure it out ourself. + */ + if (nv_match_device(dev, 0x0615, 0x1682, 0x2605)) { + if (idx == 0) { + *conn = 0x02002300; /* VGA, connector 2 */ + *conf = 0x00000028; + } else + if (idx == 1) { + *conn = 0x01010312; /* DVI, connector 0 */ + *conf = 0x00020030; + } else + if (idx == 2) { + *conn = 0x04020310; /* VGA, connector 0 */ + *conf = 0x00000028; + } else + if (idx == 3) { + *conn = 0x02021322; /* HDMI, connector 1 */ + *conf = 0x00020010; + } else { + *conn = 0x0000000e; /* EOL */ + *conf = 0x00000000; + } + } + return true; } @@ -6731,7 +6766,7 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev) void nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, - struct dcb_entry *dcbent) + struct dcb_entry *dcbent, int crtc) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvbios *bios = &dev_priv->vbios; @@ -6739,11 +6774,22 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, spin_lock_bh(&bios->lock); bios->display.output = dcbent; + bios->display.crtc = crtc; parse_init_table(bios, table, &iexec); bios->display.output = NULL; spin_unlock_bh(&bios->lock); } +void +nouveau_bios_init_exec(struct drm_device *dev, uint16_t table) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct init_exec iexec = { true, false }; + + parse_init_table(bios, table, &iexec); +} + static bool NVInitVBIOS(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -6825,9 +6871,8 @@ nouveau_run_vbios_init(struct drm_device *dev) if (dev_priv->card_type >= NV_50) { for (i = 0; i < bios->dcb.entries; i++) { - nouveau_bios_run_display_table(dev, - &bios->dcb.entry[i], - 0, 0); + nouveau_bios_run_display_table(dev, 0, 0, + &bios->dcb.entry[i], -1); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 050c314..8adb69e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -289,8 +289,8 @@ struct nvbios { struct { struct dcb_entry *output; + int crtc; uint16_t script_table_ptr; - uint16_t dp_table_ptr; } display; struct { diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 5fb98de..2bb29c9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -49,16 +49,12 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo) DRM_ERROR("bo %p still attached to GEM object\n", bo); nv10_mem_put_tile_region(dev, nvbo->tile, NULL); - if (nvbo->vma.node) { - nouveau_vm_unmap(&nvbo->vma); - nouveau_vm_put(&nvbo->vma); - } kfree(nvbo); } static void nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags, - int *align, int *size, int *page_shift) + int *align, int *size) { struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev); @@ -82,67 +78,51 @@ nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags, } } } else { - if (likely(dev_priv->chan_vm)) { - if (!(flags & TTM_PL_FLAG_TT) && *size > 256 * 1024) - *page_shift = dev_priv->chan_vm->lpg_shift; - else - *page_shift = dev_priv->chan_vm->spg_shift; - } else { - *page_shift = 12; - } - - *size = roundup(*size, (1 << *page_shift)); - *align = max((1 << *page_shift), *align); + *size = roundup(*size, (1 << nvbo->page_shift)); + *align = max((1 << nvbo->page_shift), *align); } *size = roundup(*size, PAGE_SIZE); } int -nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan, - int size, int align, uint32_t flags, uint32_t tile_mode, - uint32_t tile_flags, struct nouveau_bo **pnvbo) +nouveau_bo_new(struct drm_device *dev, int size, int align, + uint32_t flags, uint32_t tile_mode, uint32_t tile_flags, + struct nouveau_bo **pnvbo) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_bo *nvbo; - int ret = 0, page_shift = 0; + int ret; nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL); if (!nvbo) return -ENOMEM; INIT_LIST_HEAD(&nvbo->head); INIT_LIST_HEAD(&nvbo->entry); + INIT_LIST_HEAD(&nvbo->vma_list); nvbo->tile_mode = tile_mode; nvbo->tile_flags = tile_flags; nvbo->bo.bdev = &dev_priv->ttm.bdev; - nouveau_bo_fixup_align(nvbo, flags, &align, &size, &page_shift); - align >>= PAGE_SHIFT; - - if (dev_priv->chan_vm) { - ret = nouveau_vm_get(dev_priv->chan_vm, size, page_shift, - NV_MEM_ACCESS_RW, &nvbo->vma); - if (ret) { - kfree(nvbo); - return ret; - } + nvbo->page_shift = 12; + if (dev_priv->bar1_vm) { + if (!(flags & TTM_PL_FLAG_TT) && size > 256 * 1024) + nvbo->page_shift = dev_priv->bar1_vm->lpg_shift; } + nouveau_bo_fixup_align(nvbo, flags, &align, &size); nvbo->bo.mem.num_pages = size >> PAGE_SHIFT; nouveau_bo_placement_set(nvbo, flags, 0); - nvbo->channel = chan; ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size, - ttm_bo_type_device, &nvbo->placement, align, 0, - false, NULL, size, nouveau_bo_del_ttm); + ttm_bo_type_device, &nvbo->placement, + align >> PAGE_SHIFT, 0, false, NULL, size, + nouveau_bo_del_ttm); if (ret) { /* ttm will call nouveau_bo_del_ttm if it fails.. */ return ret; } - nvbo->channel = NULL; - if (nvbo->vma.node) - nvbo->bo.offset = nvbo->vma.offset; *pnvbo = nvbo; return 0; } @@ -168,7 +148,7 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type) if (dev_priv->card_type == NV_10 && nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) && - nvbo->bo.mem.num_pages < vram_pages / 2) { + nvbo->bo.mem.num_pages < vram_pages / 4) { /* * Make sure that the color and depth buffers are handled * by independent memory controller units. Up to a 9x @@ -312,8 +292,6 @@ nouveau_bo_validate(struct nouveau_bo *nvbo, bool interruptible, if (ret) return ret; - if (nvbo->vma.node) - nvbo->bo.offset = nvbo->vma.offset; return 0; } @@ -440,7 +418,6 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, TTM_MEMTYPE_FLAG_CMA; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; - man->gpu_offset = dev_priv->gart_info.aper_base; break; default: NV_ERROR(dev, "Unknown GART type: %d\n", @@ -501,19 +478,12 @@ static int nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) { - struct nouveau_mem *old_node = old_mem->mm_node; - struct nouveau_mem *new_node = new_mem->mm_node; - struct nouveau_bo *nvbo = nouveau_bo(bo); + struct nouveau_mem *node = old_mem->mm_node; + u64 src_offset = node->vma[0].offset; + u64 dst_offset = node->vma[1].offset; u32 page_count = new_mem->num_pages; - u64 src_offset, dst_offset; int ret; - src_offset = old_node->tmp_vma.offset; - if (new_node->tmp_vma.node) - dst_offset = new_node->tmp_vma.offset; - else - dst_offset = nvbo->vma.offset; - page_count = new_mem->num_pages; while (page_count) { int line_count = (page_count > 2047) ? 2047 : page_count; @@ -547,19 +517,13 @@ static int nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) { - struct nouveau_mem *old_node = old_mem->mm_node; - struct nouveau_mem *new_node = new_mem->mm_node; + struct nouveau_mem *node = old_mem->mm_node; struct nouveau_bo *nvbo = nouveau_bo(bo); u64 length = (new_mem->num_pages << PAGE_SHIFT); - u64 src_offset, dst_offset; + u64 src_offset = node->vma[0].offset; + u64 dst_offset = node->vma[1].offset; int ret; - src_offset = old_node->tmp_vma.offset; - if (new_node->tmp_vma.node) - dst_offset = new_node->tmp_vma.offset; - else - dst_offset = nvbo->vma.offset; - while (length) { u32 amount, stride, height; @@ -695,6 +659,27 @@ nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, } static int +nouveau_vma_getmap(struct nouveau_channel *chan, struct nouveau_bo *nvbo, + struct ttm_mem_reg *mem, struct nouveau_vma *vma) +{ + struct nouveau_mem *node = mem->mm_node; + int ret; + + ret = nouveau_vm_get(chan->vm, mem->num_pages << PAGE_SHIFT, + node->page_shift, NV_MEM_ACCESS_RO, vma); + if (ret) + return ret; + + if (mem->mem_type == TTM_PL_VRAM) + nouveau_vm_map(vma, node); + else + nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT, + node, node->pages); + + return 0; +} + +static int nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, bool no_wait_reserve, bool no_wait_gpu, struct ttm_mem_reg *new_mem) @@ -711,31 +696,20 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, mutex_lock_nested(&chan->mutex, NOUVEAU_KCHANNEL_MUTEX); } - /* create temporary vma for old memory, this will get cleaned - * up after ttm destroys the ttm_mem_reg + /* create temporary vmas for the transfer and attach them to the + * old nouveau_mem node, these will get cleaned up after ttm has + * destroyed the ttm_mem_reg */ if (dev_priv->card_type >= NV_50) { struct nouveau_mem *node = old_mem->mm_node; - if (!node->tmp_vma.node) { - u32 page_shift = nvbo->vma.node->type; - if (old_mem->mem_type == TTM_PL_TT) - page_shift = nvbo->vma.vm->spg_shift; - - ret = nouveau_vm_get(chan->vm, - old_mem->num_pages << PAGE_SHIFT, - page_shift, NV_MEM_ACCESS_RO, - &node->tmp_vma); - if (ret) - goto out; - } - if (old_mem->mem_type == TTM_PL_VRAM) - nouveau_vm_map(&node->tmp_vma, node); - else { - nouveau_vm_map_sg(&node->tmp_vma, 0, - old_mem->num_pages << PAGE_SHIFT, - node, node->pages); - } + ret = nouveau_vma_getmap(chan, nvbo, old_mem, &node->vma[0]); + if (ret) + goto out; + + ret = nouveau_vma_getmap(chan, nvbo, new_mem, &node->vma[1]); + if (ret) + goto out; } if (dev_priv->card_type < NV_50) @@ -762,7 +736,6 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, bool no_wait_reserve, bool no_wait_gpu, struct ttm_mem_reg *new_mem) { - struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; struct ttm_placement placement; struct ttm_mem_reg tmp_mem; @@ -782,23 +755,7 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, if (ret) goto out; - if (dev_priv->card_type >= NV_50) { - struct nouveau_bo *nvbo = nouveau_bo(bo); - struct nouveau_mem *node = tmp_mem.mm_node; - struct nouveau_vma *vma = &nvbo->vma; - if (vma->node->type != vma->vm->spg_shift) - vma = &node->tmp_vma; - nouveau_vm_map_sg(vma, 0, tmp_mem.num_pages << PAGE_SHIFT, - node, node->pages); - } - ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_reserve, no_wait_gpu, &tmp_mem); - - if (dev_priv->card_type >= NV_50) { - struct nouveau_bo *nvbo = nouveau_bo(bo); - nouveau_vm_unmap(&nvbo->vma); - } - if (ret) goto out; @@ -844,30 +801,22 @@ out: static void nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) { - struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); struct nouveau_mem *node = new_mem->mm_node; struct nouveau_bo *nvbo = nouveau_bo(bo); - struct nouveau_vma *vma = &nvbo->vma; - struct nouveau_vm *vm = vma->vm; - - if (dev_priv->card_type < NV_50) - return; - - switch (new_mem->mem_type) { - case TTM_PL_VRAM: - nouveau_vm_map(vma, node); - break; - case TTM_PL_TT: - if (vma->node->type != vm->spg_shift) { + struct nouveau_vma *vma; + + list_for_each_entry(vma, &nvbo->vma_list, head) { + if (new_mem->mem_type == TTM_PL_VRAM) { + nouveau_vm_map(vma, new_mem->mm_node); + } else + if (new_mem->mem_type == TTM_PL_TT && + nvbo->page_shift == vma->vm->spg_shift) { + nouveau_vm_map_sg(vma, 0, new_mem-> + num_pages << PAGE_SHIFT, + node, node->pages); + } else { nouveau_vm_unmap(vma); - vma = &node->tmp_vma; } - nouveau_vm_map_sg(vma, 0, new_mem->num_pages << PAGE_SHIFT, - node, node->pages); - break; - default: - nouveau_vm_unmap(&nvbo->vma); - break; } } @@ -991,7 +940,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) { mem->bus.offset = mem->start << PAGE_SHIFT; mem->bus.base = dev_priv->gart_info.aper_base; - mem->bus.is_iomem = true; + mem->bus.is_iomem = !dev->agp->cant_use_aperture; } #endif break; @@ -1007,7 +956,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) break; } - if (dev_priv->card_type == NV_C0) + if (dev_priv->card_type >= NV_C0) page_shift = node->page_shift; else page_shift = 12; @@ -1113,3 +1062,54 @@ struct ttm_bo_driver nouveau_bo_driver = { .io_mem_free = &nouveau_ttm_io_mem_free, }; +struct nouveau_vma * +nouveau_bo_vma_find(struct nouveau_bo *nvbo, struct nouveau_vm *vm) +{ + struct nouveau_vma *vma; + list_for_each_entry(vma, &nvbo->vma_list, head) { + if (vma->vm == vm) + return vma; + } + + return NULL; +} + +int +nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nouveau_vm *vm, + struct nouveau_vma *vma) +{ + const u32 size = nvbo->bo.mem.num_pages << PAGE_SHIFT; + struct nouveau_mem *node = nvbo->bo.mem.mm_node; + int ret; + + ret = nouveau_vm_get(vm, size, nvbo->page_shift, + NV_MEM_ACCESS_RW, vma); + if (ret) + return ret; + + if (nvbo->bo.mem.mem_type == TTM_PL_VRAM) + nouveau_vm_map(vma, nvbo->bo.mem.mm_node); + else + if (nvbo->bo.mem.mem_type == TTM_PL_TT) + nouveau_vm_map_sg(vma, 0, size, node, node->pages); + + list_add_tail(&vma->head, &nvbo->vma_list); + vma->refcount = 1; + return 0; +} + +void +nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nouveau_vma *vma) +{ + if (vma->node) { + if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) { + spin_lock(&nvbo->bo.bdev->fence_lock); + ttm_bo_wait(&nvbo->bo, false, false, false); + spin_unlock(&nvbo->bo.bdev->fence_lock); + nouveau_vm_unmap(vma); + } + + nouveau_vm_put(vma); + list_del(&vma->head); + } +} diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index d31d355..bb6ec9e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c @@ -27,40 +27,63 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" #include "nouveau_dma.h" +#include "nouveau_ramht.h" static int -nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan) +nouveau_channel_pushbuf_init(struct nouveau_channel *chan) { + u32 mem = nouveau_vram_pushbuf ? TTM_PL_FLAG_VRAM : TTM_PL_FLAG_TT; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_bo *pb = chan->pushbuf_bo; - struct nouveau_gpuobj *pushbuf = NULL; - int ret = 0; + int ret; + + /* allocate buffer object */ + ret = nouveau_bo_new(dev, 65536, 0, mem, 0, 0, &chan->pushbuf_bo); + if (ret) + goto out; + + ret = nouveau_bo_pin(chan->pushbuf_bo, mem); + if (ret) + goto out; + + ret = nouveau_bo_map(chan->pushbuf_bo); + if (ret) + goto out; + /* create DMA object covering the entire memtype where the push + * buffer resides, userspace can submit its own push buffers from + * anywhere within the same memtype. + */ + chan->pushbuf_base = chan->pushbuf_bo->bo.offset; if (dev_priv->card_type >= NV_50) { + ret = nouveau_bo_vma_add(chan->pushbuf_bo, chan->vm, + &chan->pushbuf_vma); + if (ret) + goto out; + if (dev_priv->card_type < NV_C0) { ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0, (1ULL << 40), NV_MEM_ACCESS_RO, NV_MEM_TARGET_VM, - &pushbuf); + &chan->pushbuf); } - chan->pushbuf_base = pb->bo.offset; + chan->pushbuf_base = chan->pushbuf_vma.offset; } else - if (pb->bo.mem.mem_type == TTM_PL_TT) { + if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_TT) { ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0, dev_priv->gart_info.aper_size, NV_MEM_ACCESS_RO, - NV_MEM_TARGET_GART, &pushbuf); - chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT; + NV_MEM_TARGET_GART, + &chan->pushbuf); } else if (dev_priv->card_type != NV_04) { ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0, dev_priv->fb_available_size, NV_MEM_ACCESS_RO, - NV_MEM_TARGET_VRAM, &pushbuf); - chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT; + NV_MEM_TARGET_VRAM, + &chan->pushbuf); } else { /* NV04 cmdbuf hack, from original ddx.. not sure of it's * exact reason for existing :) PCI access to cmdbuf in @@ -70,47 +93,22 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan) pci_resource_start(dev->pdev, 1), dev_priv->fb_available_size, NV_MEM_ACCESS_RO, - NV_MEM_TARGET_PCI, &pushbuf); - chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT; - } - - nouveau_gpuobj_ref(pushbuf, &chan->pushbuf); - nouveau_gpuobj_ref(NULL, &pushbuf); - return ret; -} - -static struct nouveau_bo * -nouveau_channel_user_pushbuf_alloc(struct drm_device *dev) -{ - struct nouveau_bo *pushbuf = NULL; - int location, ret; - - if (nouveau_vram_pushbuf) - location = TTM_PL_FLAG_VRAM; - else - location = TTM_PL_FLAG_TT; - - ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, &pushbuf); - if (ret) { - NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret); - return NULL; + NV_MEM_TARGET_PCI, + &chan->pushbuf); } - ret = nouveau_bo_pin(pushbuf, location); +out: if (ret) { - NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret); - nouveau_bo_ref(NULL, &pushbuf); - return NULL; - } - - ret = nouveau_bo_map(pushbuf); - if (ret) { - nouveau_bo_unpin(pushbuf); - nouveau_bo_ref(NULL, &pushbuf); - return NULL; + NV_ERROR(dev, "error initialising pushbuf: %d\n", ret); + nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma); + nouveau_gpuobj_ref(NULL, &chan->pushbuf); + if (chan->pushbuf_bo) { + nouveau_bo_unmap(chan->pushbuf_bo); + nouveau_bo_ref(NULL, &chan->pushbuf_bo); + } } - return pushbuf; + return 0; } /* allocates and initializes a fifo for user space consumption */ @@ -121,6 +119,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; + struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); struct nouveau_channel *chan; unsigned long flags; int ret; @@ -161,19 +160,14 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, INIT_LIST_HEAD(&chan->fence.pending); spin_lock_init(&chan->fence.lock); - /* Allocate DMA push buffer */ - chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev); - if (!chan->pushbuf_bo) { - ret = -ENOMEM; - NV_ERROR(dev, "pushbuf %d\n", ret); + /* setup channel's memory and vm */ + ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle); + if (ret) { + NV_ERROR(dev, "gpuobj %d\n", ret); nouveau_channel_put(&chan); return ret; } - nouveau_dma_pre_init(chan); - chan->user_put = 0x40; - chan->user_get = 0x44; - /* Allocate space for per-channel fixed notifier memory */ ret = nouveau_notifier_init_channel(chan); if (ret) { @@ -182,21 +176,17 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, return ret; } - /* Setup channel's default objects */ - ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle); + /* Allocate DMA push buffer */ + ret = nouveau_channel_pushbuf_init(chan); if (ret) { - NV_ERROR(dev, "gpuobj %d\n", ret); + NV_ERROR(dev, "pushbuf %d\n", ret); nouveau_channel_put(&chan); return ret; } - /* Create a dma object for the push buffer */ - ret = nouveau_channel_pushbuf_ctxdma_init(chan); - if (ret) { - NV_ERROR(dev, "pbctxdma %d\n", ret); - nouveau_channel_put(&chan); - return ret; - } + nouveau_dma_pre_init(chan); + chan->user_put = 0x40; + chan->user_get = 0x44; /* disable the fifo caches */ pfifo->reassign(dev, false); @@ -221,6 +211,11 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, nouveau_debugfs_channel_init(chan); NV_DEBUG(dev, "channel %d initialised\n", chan->id); + if (fpriv) { + spin_lock(&fpriv->lock); + list_add(&chan->list, &fpriv->channels); + spin_unlock(&fpriv->lock); + } *chan_ret = chan; return 0; } @@ -237,29 +232,23 @@ nouveau_channel_get_unlocked(struct nouveau_channel *ref) } struct nouveau_channel * -nouveau_channel_get(struct drm_device *dev, struct drm_file *file_priv, int id) +nouveau_channel_get(struct drm_file *file_priv, int id) { - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); struct nouveau_channel *chan; - unsigned long flags; - - if (unlikely(id < 0 || id >= NOUVEAU_MAX_CHANNEL_NR)) - return ERR_PTR(-EINVAL); - - spin_lock_irqsave(&dev_priv->channels.lock, flags); - chan = nouveau_channel_get_unlocked(dev_priv->channels.ptr[id]); - spin_unlock_irqrestore(&dev_priv->channels.lock, flags); - if (unlikely(!chan)) - return ERR_PTR(-EINVAL); - - if (unlikely(file_priv && chan->file_priv != file_priv)) { - nouveau_channel_put_unlocked(&chan); - return ERR_PTR(-EINVAL); + spin_lock(&fpriv->lock); + list_for_each_entry(chan, &fpriv->channels, list) { + if (chan->id == id) { + chan = nouveau_channel_get_unlocked(chan); + spin_unlock(&fpriv->lock); + mutex_lock(&chan->mutex); + return chan; + } } + spin_unlock(&fpriv->lock); - mutex_lock(&chan->mutex); - return chan; + return ERR_PTR(-EINVAL); } void @@ -313,12 +302,14 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan) /* destroy any resources the channel owned */ nouveau_gpuobj_ref(NULL, &chan->pushbuf); if (chan->pushbuf_bo) { + nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma); nouveau_bo_unmap(chan->pushbuf_bo); nouveau_bo_unpin(chan->pushbuf_bo); nouveau_bo_ref(NULL, &chan->pushbuf_bo); } - nouveau_gpuobj_channel_takedown(chan); + nouveau_ramht_ref(NULL, &chan->ramht, chan); nouveau_notifier_takedown_channel(chan); + nouveau_gpuobj_channel_takedown(chan); nouveau_channel_ref(NULL, pchan); } @@ -384,10 +375,11 @@ nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv) NV_DEBUG(dev, "clearing FIFO enables from file_priv\n"); for (i = 0; i < engine->fifo.channels; i++) { - chan = nouveau_channel_get(dev, file_priv, i); + chan = nouveau_channel_get(file_priv, i); if (IS_ERR(chan)) continue; + list_del(&chan->list); atomic_dec(&chan->users); nouveau_channel_put(&chan); } @@ -420,13 +412,17 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, return ret; init->channel = chan->id; - if (chan->dma.ib_max) - init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | - NOUVEAU_GEM_DOMAIN_GART; - else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM) + if (nouveau_vram_pushbuf == 0) { + if (chan->dma.ib_max) + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | + NOUVEAU_GEM_DOMAIN_GART; + else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM) + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM; + else + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART; + } else { init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM; - else - init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART; + } if (dev_priv->card_type < NV_C0) { init->subchan[0].handle = NvM2MF; @@ -460,10 +456,11 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data, struct drm_nouveau_channel_free *req = data; struct nouveau_channel *chan; - chan = nouveau_channel_get(dev, file_priv, req->channel); + chan = nouveau_channel_get(file_priv, req->channel); if (IS_ERR(chan)) return PTR_ERR(chan); + list_del(&chan->list); atomic_dec(&chan->users); nouveau_channel_put(&chan); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 1595d0b..1e72db5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -39,8 +39,8 @@ static void nouveau_connector_hotplug(void *, int); -static struct nouveau_encoder * -find_encoder_by_type(struct drm_connector *connector, int type) +struct nouveau_encoder * +find_encoder(struct drm_connector *connector, int type) { struct drm_device *dev = connector->dev; struct nouveau_encoder *nv_encoder; @@ -116,10 +116,6 @@ nouveau_connector_destroy(struct drm_connector *connector) nouveau_connector_hotplug, connector); } - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - connector->connector_type == DRM_MODE_CONNECTOR_eDP) - nouveau_backlight_exit(connector); - kfree(nv_connector->edid); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -170,8 +166,8 @@ nouveau_connector_of_detect(struct drm_connector *connector) struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev); if (!dn || - !((nv_encoder = find_encoder_by_type(connector, OUTPUT_TMDS)) || - (nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG)))) + !((nv_encoder = find_encoder(connector, OUTPUT_TMDS)) || + (nv_encoder = find_encoder(connector, OUTPUT_ANALOG)))) return NULL; for_each_child_of_node(dn, cn) { @@ -233,6 +229,7 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) struct drm_device *dev = connector->dev; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL; + struct nouveau_encoder *nv_partner; struct nouveau_i2c_chan *i2c; int type; @@ -266,19 +263,22 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) * same i2c channel so the value returned from ddc_detect * isn't necessarily correct. */ - if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { + nv_partner = NULL; + if (nv_encoder->dcb->type == OUTPUT_TMDS) + nv_partner = find_encoder(connector, OUTPUT_ANALOG); + if (nv_encoder->dcb->type == OUTPUT_ANALOG) + nv_partner = find_encoder(connector, OUTPUT_TMDS); + + if (nv_partner && ((nv_encoder->dcb->type == OUTPUT_ANALOG && + nv_partner->dcb->type == OUTPUT_TMDS) || + (nv_encoder->dcb->type == OUTPUT_TMDS && + nv_partner->dcb->type == OUTPUT_ANALOG))) { if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL) type = OUTPUT_TMDS; else type = OUTPUT_ANALOG; - nv_encoder = find_encoder_by_type(connector, type); - if (!nv_encoder) { - NV_ERROR(dev, "Detected %d encoder on %s, " - "but no object!\n", type, - drm_get_connector_name(connector)); - return connector_status_disconnected; - } + nv_encoder = find_encoder(connector, type); } nouveau_connector_set_encoder(connector, nv_encoder); @@ -292,9 +292,9 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) } detect_analog: - nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG); + nv_encoder = find_encoder(connector, OUTPUT_ANALOG); if (!nv_encoder && !nouveau_tv_disable) - nv_encoder = find_encoder_by_type(connector, OUTPUT_TV); + nv_encoder = find_encoder(connector, OUTPUT_TV); if (nv_encoder && force) { struct drm_encoder *encoder = to_drm_encoder(nv_encoder); struct drm_encoder_helper_funcs *helper = @@ -327,7 +327,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) nv_connector->edid = NULL; } - nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS); + nv_encoder = find_encoder(connector, OUTPUT_LVDS); if (!nv_encoder) return connector_status_disconnected; @@ -405,7 +405,7 @@ nouveau_connector_force(struct drm_connector *connector) } else type = OUTPUT_ANY; - nv_encoder = find_encoder_by_type(connector, type); + nv_encoder = find_encoder(connector, type); if (!nv_encoder) { NV_ERROR(connector->dev, "can't find encoder to force %s on!\n", drm_get_connector_name(connector)); @@ -602,6 +602,55 @@ nouveau_connector_scaler_modes_add(struct drm_connector *connector) return modes; } +static void +nouveau_connector_detect_depth(struct drm_connector *connector) +{ + struct drm_nouveau_private *dev_priv = connector->dev->dev_private; + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct nvbios *bios = &dev_priv->vbios; + struct drm_display_mode *mode = nv_connector->native_mode; + bool duallink; + + /* if the edid is feeling nice enough to provide this info, use it */ + if (nv_connector->edid && connector->display_info.bpc) + return; + + /* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */ + if (nv_connector->dcb->type == DCB_CONNECTOR_eDP) { + connector->display_info.bpc = 6; + return; + } + + /* we're out of options unless we're LVDS, default to 8bpc */ + if (nv_encoder->dcb->type != OUTPUT_LVDS) { + connector->display_info.bpc = 8; + return; + } + + connector->display_info.bpc = 6; + + /* LVDS: panel straps */ + if (bios->fp_no_ddc) { + if (bios->fp.if_is_24bit) + connector->display_info.bpc = 8; + return; + } + + /* LVDS: DDC panel, need to first determine the number of links to + * know which if_is_24bit flag to check... + */ + if (nv_connector->edid && + nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) + duallink = ((u8 *)nv_connector->edid)[121] == 2; + else + duallink = mode->clock >= bios->fp.duallink_transition_clk; + + if ((!duallink && (bios->fp.strapless_is_24bit & 1)) || + ( duallink && (bios->fp.strapless_is_24bit & 2))) + connector->display_info.bpc = 8; +} + static int nouveau_connector_get_modes(struct drm_connector *connector) { @@ -646,6 +695,12 @@ nouveau_connector_get_modes(struct drm_connector *connector) ret = 1; } + /* Attempt to determine display colour depth, this has to happen after + * we've determined the "native" mode for LVDS, as the VBIOS tables + * require us to compare against a pixel clock in some cases.. + */ + nouveau_connector_detect_depth(connector); + if (nv_encoder->dcb->type == OUTPUT_TV) ret = get_slave_funcs(encoder)->get_modes(encoder, connector); @@ -708,12 +763,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector, case OUTPUT_TV: return get_slave_funcs(encoder)->mode_valid(encoder, mode); case OUTPUT_DP: - if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7) - max_clock = nv_encoder->dp.link_nr * 270000; - else - max_clock = nv_encoder->dp.link_nr * 162000; - - clock = clock * nouveau_connector_bpp(connector) / 8; + max_clock = nv_encoder->dp.link_nr; + max_clock *= nv_encoder->dp.link_bw; + clock = clock * nouveau_connector_bpp(connector) / 10; break; default: BUG_ON(1); @@ -867,7 +919,6 @@ nouveau_connector_create(struct drm_device *dev, int index) dev->mode_config.scaling_mode_property, nv_connector->scaling_mode); } - connector->polled = DRM_CONNECTOR_POLL_CONNECT; /* fall-through */ case DCB_CONNECTOR_TV_0: case DCB_CONNECTOR_TV_1: @@ -884,27 +935,20 @@ nouveau_connector_create(struct drm_device *dev, int index) dev->mode_config.dithering_mode_property, nv_connector->use_dithering ? DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); - - if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) { - if (dev_priv->card_type >= NV_50) - connector->polled = DRM_CONNECTOR_POLL_HPD; - else - connector->polled = DRM_CONNECTOR_POLL_CONNECT; - } break; } - if (pgpio->irq_register) { + if (nv_connector->dcb->gpio_tag != 0xff && pgpio->irq_register) { pgpio->irq_register(dev, nv_connector->dcb->gpio_tag, nouveau_connector_hotplug, connector); + + connector->polled = DRM_CONNECTOR_POLL_HPD; + } else { + connector->polled = DRM_CONNECTOR_POLL_CONNECT; } drm_sysfs_connector_add(connector); - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - connector->connector_type == DRM_MODE_CONNECTOR_eDP) - nouveau_backlight_init(connector); - dcb->drm = connector; return dcb->drm; @@ -921,22 +965,13 @@ nouveau_connector_hotplug(void *data, int plugged) struct drm_connector *connector = data; struct drm_device *dev = connector->dev; - NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un", - drm_get_connector_name(connector)); - - if (connector->encoder && connector->encoder->crtc && - connector->encoder->crtc->enabled) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(connector->encoder); - struct drm_encoder_helper_funcs *helper = - connector->encoder->helper_private; + NV_DEBUG(dev, "%splugged %s\n", plugged ? "" : "un", + drm_get_connector_name(connector)); - if (nv_encoder->dcb->type == OUTPUT_DP) { - if (plugged) - helper->dpms(connector->encoder, DRM_MODE_DPMS_ON); - else - helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF); - } - } + if (plugged) + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + else + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); drm_helper_hpd_irq_event(dev); } diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index cb1ce2a..bf8e128 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -82,14 +82,13 @@ static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc) } int nv50_crtc_create(struct drm_device *dev, int index); -int nv50_cursor_init(struct nouveau_crtc *); -void nv50_cursor_fini(struct nouveau_crtc *); int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv, uint32_t buffer_handle, uint32_t width, uint32_t height); int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y); int nv04_cursor_init(struct nouveau_crtc *); +int nv50_cursor_init(struct nouveau_crtc *); struct nouveau_connector * nouveau_crtc_connector_get(struct nouveau_crtc *crtc); diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index eb514ea..6adef06 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -105,9 +105,12 @@ nouveau_framebuffer_init(struct drm_device *dev, if (dev_priv->chipset == 0x50) nv_fb->r_format |= (tile_flags << 8); - if (!tile_flags) - nv_fb->r_pitch = 0x00100000 | fb->pitch; - else { + if (!tile_flags) { + if (dev_priv->card_type < NV_D0) + nv_fb->r_pitch = 0x00100000 | fb->pitch; + else + nv_fb->r_pitch = 0x01000000 | fb->pitch; + } else { u32 mode = nvbo->tile_mode; if (dev_priv->card_type >= NV_C0) mode >>= 4; @@ -366,3 +369,48 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, spin_unlock_irqrestore(&dev->event_lock, flags); return 0; } + +int +nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct nouveau_bo *bo; + int ret; + + args->pitch = roundup(args->width * (args->bpp / 8), 256); + args->size = args->pitch * args->height; + args->size = roundup(args->size, PAGE_SIZE); + + ret = nouveau_gem_new(dev, args->size, 0, NOUVEAU_GEM_DOMAIN_VRAM, 0, 0, &bo); + if (ret) + return ret; + + ret = drm_gem_handle_create(file_priv, bo->gem, &args->handle); + drm_gem_object_unreference_unlocked(bo->gem); + return ret; +} + +int +nouveau_display_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file_priv, handle); +} + +int +nouveau_display_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, uint64_t *poffset) +{ + struct drm_gem_object *gem; + + gem = drm_gem_object_lookup(dev, file_priv, handle); + if (gem) { + struct nouveau_bo *bo = gem->driver_private; + *poffset = bo->bo.addr_space_offset; + drm_gem_object_unreference_unlocked(gem); + return 0; + } + + return -ENOENT; +} diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c index 568caed..00bc6ea 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.c +++ b/drivers/gpu/drm/nouveau/nouveau_dma.c @@ -167,8 +167,13 @@ nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo, int delta, int length) { struct nouveau_bo *pb = chan->pushbuf_bo; - uint64_t offset = bo->bo.offset + delta; + struct nouveau_vma *vma; int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base; + u64 offset; + + vma = nouveau_bo_vma_find(bo, chan->vm); + BUG_ON(!vma); + offset = vma->offset + delta; BUG_ON(chan->dma.ib_free < 1); nouveau_bo_wr32(pb, ip++, lower_32_bits(offset)); diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 7beb82a..de5efe7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -28,418 +28,619 @@ #include "nouveau_i2c.h" #include "nouveau_connector.h" #include "nouveau_encoder.h" +#include "nouveau_crtc.h" + +/****************************************************************************** + * aux channel util functions + *****************************************************************************/ +#define AUX_DBG(fmt, args...) do { \ + if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_AUXCH) { \ + NV_PRINTK(KERN_DEBUG, dev, "AUXCH(%d): " fmt, ch, ##args); \ + } \ +} while (0) +#define AUX_ERR(fmt, args...) NV_ERROR(dev, "AUXCH(%d): " fmt, ch, ##args) + +static void +auxch_fini(struct drm_device *dev, int ch) +{ + nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000); +} static int -auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size) +auxch_init(struct drm_device *dev, int ch) { - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_i2c_chan *auxch; - int ret; - - auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); - if (!auxch) - return -ENODEV; - - ret = nouveau_dp_auxch(auxch, 9, address, buf, size); - if (ret) - return ret; + const u32 unksel = 1; /* nfi which to use, or if it matters.. */ + const u32 ureq = unksel ? 0x00100000 : 0x00200000; + const u32 urep = unksel ? 0x01000000 : 0x02000000; + u32 ctrl, timeout; + + /* wait up to 1ms for any previous transaction to be done... */ + timeout = 1000; + do { + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("begin idle timeout 0x%08x", ctrl); + return -EBUSY; + } + } while (ctrl & 0x03010000); + + /* set some magic, and wait up to 1ms for it to appear */ + nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq); + timeout = 1000; + do { + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("magic wait 0x%08x\n", ctrl); + auxch_fini(dev, ch); + return -EBUSY; + } + } while ((ctrl & 0x03000000) != urep); return 0; } static int -auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size) +auxch_tx(struct drm_device *dev, int ch, u8 type, u32 addr, u8 *data, u8 size) { - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_i2c_chan *auxch; - int ret; + u32 ctrl, stat, timeout, retries; + u32 xbuf[4] = {}; + int ret, i; - auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); - if (!auxch) - return -ENODEV; + AUX_DBG("%d: 0x%08x %d\n", type, addr, size); - ret = nouveau_dp_auxch(auxch, 8, address, buf, size); - return ret; -} + ret = auxch_init(dev, ch); + if (ret) + goto out; -static int -nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t tmp; - int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); - - tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED | - NV50_SOR_DP_CTRL_LANE_MASK); - tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16; - if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN) - tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED; - nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); - - return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1); -} + stat = nv_rd32(dev, 0x00e4e8 + (ch * 0x50)); + if (!(stat & 0x10000000)) { + AUX_DBG("sink not detected\n"); + ret = -ENXIO; + goto out; + } -static int -nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t tmp; - int reg = 0x614300 + (nv_encoder->or * 0x800); + if (!(type & 1)) { + memcpy(xbuf, data, size); + for (i = 0; i < 16; i += 4) { + AUX_DBG("wr 0x%08x\n", xbuf[i / 4]); + nv_wr32(dev, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]); + } + } - tmp = nv_rd32(dev, reg); - tmp &= 0xfff3ffff; - if (cmd == DP_LINK_BW_2_7) - tmp |= 0x00040000; - nv_wr32(dev, reg, tmp); + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + ctrl &= ~0x0001f0ff; + ctrl |= type << 12; + ctrl |= size - 1; + nv_wr32(dev, 0x00e4e0 + (ch * 0x50), addr); + + /* retry transaction a number of times on failure... */ + ret = -EREMOTEIO; + for (retries = 0; retries < 32; retries++) { + /* reset, and delay a while if this is a retry */ + nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl); + nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl); + if (retries) + udelay(400); + + /* transaction request, wait up to 1ms for it to complete */ + nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl); + + timeout = 1000; + do { + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("tx req timeout 0x%08x\n", ctrl); + goto out; + } + } while (ctrl & 0x00010000); - return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1); -} + /* read status, and check if transaction completed ok */ + stat = nv_mask(dev, 0x00e4e8 + (ch * 0x50), 0, 0); + if (!(stat & 0x000f0f00)) { + ret = 0; + break; + } -static int -nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t tmp; - uint8_t cmd; - int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); - int ret; + AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); + } - tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN; - tmp |= (pattern << 24); - nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); + if (type & 1) { + for (i = 0; i < 16; i += 4) { + xbuf[i / 4] = nv_rd32(dev, 0x00e4d0 + (ch * 0x50) + i); + AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); + } + memcpy(data, xbuf, size); + } - ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); - if (ret) - return ret; - cmd &= ~DP_TRAINING_PATTERN_MASK; - cmd |= (pattern & DP_TRAINING_PATTERN_MASK); - return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); +out: + auxch_fini(dev, ch); + return ret; } -static int -nouveau_dp_max_voltage_swing(struct drm_encoder *encoder) +static u32 +dp_link_bw_get(struct drm_device *dev, int or, int link) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; - int i, dpe_headerlen, max_vs = 0; - - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; - dpse = (void *)((char *)dpe + dpe_headerlen); + u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800)); + if (!(ctrl & 0x000c0000)) + return 162000; + return 270000; +} - for (i = 0; i < dpe_headerlen; i++, dpse++) { - if (dpse->vs_level > max_vs) - max_vs = dpse->vs_level; +static int +dp_lane_count_get(struct drm_device *dev, int or, int link) +{ + u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); + switch (ctrl & 0x000f0000) { + case 0x00010000: return 1; + case 0x00030000: return 2; + default: + return 4; } - - return max_vs; } -static int -nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs) +void +nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; - int i, dpe_headerlen, max_pre = 0; + const u32 symbol = 100000; + int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; + int TU, VTUi, VTUf, VTUa; + u64 link_data_rate, link_ratio, unk; + u32 best_diff = 64 * symbol; + u32 link_nr, link_bw, r; + + /* calculate packed data rate for each lane */ + link_nr = dp_lane_count_get(dev, or, link); + link_data_rate = (clk * bpp / 8) / link_nr; + + /* calculate ratio of packed data rate to link symbol rate */ + link_bw = dp_link_bw_get(dev, or, link); + link_ratio = link_data_rate * symbol; + r = do_div(link_ratio, link_bw); + + for (TU = 64; TU >= 32; TU--) { + /* calculate average number of valid symbols in each TU */ + u32 tu_valid = link_ratio * TU; + u32 calc, diff; + + /* find a hw representation for the fraction.. */ + VTUi = tu_valid / symbol; + calc = VTUi * symbol; + diff = tu_valid - calc; + if (diff) { + if (diff >= (symbol / 2)) { + VTUf = symbol / (symbol - diff); + if (symbol - (VTUf * diff)) + VTUf++; + + if (VTUf <= 15) { + VTUa = 1; + calc += symbol - (symbol / VTUf); + } else { + VTUa = 0; + VTUf = 1; + calc += symbol; + } + } else { + VTUa = 0; + VTUf = min((int)(symbol / diff), 15); + calc += symbol / VTUf; + } - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; - dpse = (void *)((char *)dpe + dpe_headerlen); + diff = calc - tu_valid; + } else { + /* no remainder, but the hw doesn't like the fractional + * part to be zero. decrement the integer part and + * have the fraction add a whole symbol back + */ + VTUa = 0; + VTUf = 1; + VTUi--; + } - for (i = 0; i < dpe_headerlen; i++, dpse++) { - if (dpse->vs_level != vs) - continue; + if (diff < best_diff) { + best_diff = diff; + bestTU = TU; + bestVTUa = VTUa; + bestVTUf = VTUf; + bestVTUi = VTUi; + if (diff == 0) + break; + } + } - if (dpse->pre_level > max_pre) - max_pre = dpse->pre_level; + if (!bestTU) { + NV_ERROR(dev, "DP: unable to find suitable config\n"); + return; } - return max_pre; + /* XXX close to vbios numbers, but not right */ + unk = (symbol - link_ratio) * bestTU; + unk *= link_ratio; + r = do_div(unk, symbol); + r = do_div(unk, symbol); + unk += 6; + + nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); + nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | + bestVTUf << 16 | + bestVTUi << 8 | + unk); } -static bool -nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config) +u8 * +nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table *dpe; - int ret, i, dpe_headerlen, vs = 0, pre = 0; - uint8_t request[2]; - - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; - - ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2); - if (ret) - return false; - - NV_DEBUG_KMS(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]); - - /* Keep all lanes at the same level.. */ - for (i = 0; i < nv_encoder->dp.link_nr; i++) { - int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf; - int lane_vs = lane_req & 3; - int lane_pre = (lane_req >> 2) & 3; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct bit_entry d; + u8 *table; + int i; + + if (bit_table(dev, 'd', &d)) { + NV_ERROR(dev, "BIT 'd' table not found\n"); + return NULL; + } - if (lane_vs > vs) - vs = lane_vs; - if (lane_pre > pre) - pre = lane_pre; + if (d.version != 1) { + NV_ERROR(dev, "BIT 'd' table version %d unknown\n", d.version); + return NULL; } - if (vs >= nouveau_dp_max_voltage_swing(encoder)) { - vs = nouveau_dp_max_voltage_swing(encoder); - vs |= 4; + table = ROMPTR(bios, d.data[0]); + if (!table) { + NV_ERROR(dev, "displayport table pointer invalid\n"); + return NULL; } - if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) { - pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3); - pre |= 4; + switch (table[0]) { + case 0x20: + case 0x21: + case 0x30: + break; + default: + NV_ERROR(dev, "displayport table 0x%02x unknown\n", table[0]); + return NULL; } - /* Update the configuration for all lanes.. */ - for (i = 0; i < nv_encoder->dp.link_nr; i++) - config[i] = (pre << 3) | vs; + for (i = 0; i < table[3]; i++) { + *entry = ROMPTR(bios, table[table[1] + (i * table[2])]); + if (*entry && bios_encoder_match(dcb, ROM32((*entry)[0]))) + return table; + } - return true; + NV_ERROR(dev, "displayport encoder table not found\n"); + return NULL; } -static bool -nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; - int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); - int dpe_headerlen, ret, i; +/****************************************************************************** + * link training + *****************************************************************************/ +struct dp_state { + struct dcb_entry *dcb; + u8 *table; + u8 *entry; + int auxch; + int crtc; + int or; + int link; + u8 *dpcd; + int link_nr; + u32 link_bw; + u8 stat[6]; + u8 conf[4]; +}; - NV_DEBUG_KMS(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n", - config[0], config[1], config[2], config[3]); +static void +dp_set_link_config(struct drm_device *dev, struct dp_state *dp) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + int or = dp->or, link = dp->link; + u8 *entry, sink[2]; + u32 dp_ctrl; + u16 script; + + NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); + + /* set selected link rate on source */ + switch (dp->link_bw) { + case 270000: + nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000); + sink[0] = DP_LINK_BW_2_7; + break; + default: + nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000); + sink[0] = DP_LINK_BW_1_62; + break; + } - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; - dpse = (void *)((char *)dpe + dpe_headerlen); + /* offset +0x0a of each dp encoder table entry is a pointer to another + * table, that has (among other things) pointers to more scripts that + * need to be executed, this time depending on link speed. + */ + entry = ROMPTR(&dev_priv->vbios, dp->entry[10]); + if (entry) { + if (dp->table[0] < 0x30) { + while (dp->link_bw < (ROM16(entry[0]) * 10)) + entry += 4; + script = ROM16(entry[2]); + } else { + while (dp->link_bw < (entry[0] * 27000)) + entry += 3; + script = ROM16(entry[1]); + } - for (i = 0; i < dpe->record_nr; i++, dpse++) { - if (dpse->vs_level == (config[0] & 3) && - dpse->pre_level == ((config[0] >> 3) & 3)) - break; + nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); } - BUG_ON(i == dpe->record_nr); - - for (i = 0; i < nv_encoder->dp.link_nr; i++) { - const int shift[4] = { 16, 8, 0, 24 }; - uint32_t mask = 0xff << shift[i]; - uint32_t reg0, reg1, reg2; - - reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask; - reg0 |= (dpse->reg0 << shift[i]); - reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask; - reg1 |= (dpse->reg1 << shift[i]); - reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff; - reg2 |= (dpse->reg2 << 8); - nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0); - nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1); - nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2); + + /* configure lane count on the source */ + dp_ctrl = ((1 << dp->link_nr) - 1) << 16; + sink[1] = dp->link_nr; + if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) { + dp_ctrl |= 0x00004000; + sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; } - ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4); - if (ret) - return false; + nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); - return true; + /* inform the sink of the new configuration */ + auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); } -bool -nouveau_dp_link_train(struct drm_encoder *encoder) +static void +dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) { - struct drm_device *dev = encoder->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *nv_connector; - struct bit_displayport_encoder_table *dpe; - int dpe_headerlen; - uint8_t config[4], status[3]; - bool cr_done, cr_max_vs, eq_done, hpd_state; - int ret = 0, i, tries, voltage; + u8 sink_tp; - NV_DEBUG_KMS(dev, "link training!!\n"); + NV_DEBUG_KMS(dev, "training pattern %d\n", tp); - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (!nv_connector) - return false; + nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) { - NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or); - return false; - } + auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); + sink_tp &= ~DP_TRAINING_PATTERN_MASK; + sink_tp |= tp; + auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); +} - /* disable hotplug detect, this flips around on some panels during - * link training. - */ - hpd_state = pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); +static const u8 nv50_lane_map[] = { 16, 8, 0, 24 }; +static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 }; + +static int +dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mask = 0, drv = 0, pre = 0, unk = 0; + const u8 *shifts; + int link = dp->link; + int or = dp->or; + int i; + + if (dev_priv->chipset != 0xaf) + shifts = nv50_lane_map; + else + shifts = nvaf_lane_map; + + for (i = 0; i < dp->link_nr; i++) { + u8 *conf = dp->entry + dp->table[4]; + u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 lpre = (lane & 0x0c) >> 2; + u8 lvsw = (lane & 0x03) >> 0; + + mask |= 0xff << shifts[i]; + unk |= 1 << (shifts[i] >> 3); + + dp->conf[i] = (lpre << 3) | lvsw; + if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) + dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; + if (lpre == DP_TRAIN_PRE_EMPHASIS_9_5) + dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); + + if (dp->table[0] < 0x30) { + u8 *last = conf + (dp->entry[4] * dp->table[5]); + while (lvsw != conf[0] || lpre != conf[1]) { + conf += dp->table[5]; + if (conf >= last) + return -EINVAL; + } + + conf += 2; + } else { + /* no lookup table anymore, set entries for each + * combination of voltage swing and pre-emphasis + * level allowed by the DP spec. + */ + switch (lvsw) { + case 0: lpre += 0; break; + case 1: lpre += 4; break; + case 2: lpre += 7; break; + case 3: lpre += 9; break; + } + + conf = conf + (lpre * dp->table[5]); + conf++; + } - if (dpe->script0) { - NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); - nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), - nv_encoder->dcb); + drv |= conf[0] << shifts[i]; + pre |= conf[1] << shifts[i]; + unk = (unk & ~0x0000ff00) | (conf[2] << 8); } -train: - cr_done = eq_done = false; + nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); + nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre); + nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk); - /* set link configuration */ - NV_DEBUG_KMS(dev, "\tbegin train: bw %d, lanes %d\n", - nv_encoder->dp.link_bw, nv_encoder->dp.link_nr); + return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); +} - ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw); - if (ret) - return false; +static int +dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay) +{ + int ret; - config[0] = nv_encoder->dp.link_nr; - if (nv_encoder->dp.dpcd_version >= 0x11 && - nv_encoder->dp.enhanced_frame) - config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + udelay(delay); - ret = nouveau_dp_lane_count_set(encoder, config[0]); + ret = auxch_tx(dev, dp->auxch, 9, DP_LANE0_1_STATUS, dp->stat, 6); if (ret) - return false; + return ret; - /* clock recovery */ - NV_DEBUG_KMS(dev, "\tbegin cr\n"); - ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1); - if (ret) - goto stop; + NV_DEBUG_KMS(dev, "status %02x %02x %02x %02x %02x %02x\n", + dp->stat[0], dp->stat[1], dp->stat[2], dp->stat[3], + dp->stat[4], dp->stat[5]); + return 0; +} - tries = 0; - voltage = -1; - memset(config, 0x00, sizeof(config)); - for (;;) { - if (!nouveau_dp_link_train_commit(encoder, config)) - break; +static int +dp_link_train_cr(struct drm_device *dev, struct dp_state *dp) +{ + bool cr_done = false, abort = false; + int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + int tries = 0, i; - udelay(100); + dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1); - ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2); - if (ret) + do { + if (dp_link_train_commit(dev, dp) || + dp_link_train_update(dev, dp, 100)) break; - NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", - status[0], status[1]); cr_done = true; - cr_max_vs = false; - for (i = 0; i < nv_encoder->dp.link_nr; i++) { - int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; - + for (i = 0; i < dp->link_nr; i++) { + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; if (!(lane & DP_LANE_CR_DONE)) { cr_done = false; - if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) - cr_max_vs = true; + if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED) + abort = true; break; } } - if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { - voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { + voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; tries = 0; } + } while (!cr_done && !abort && ++tries < 5); - if (cr_done || cr_max_vs || (++tries == 5)) - break; - - if (!nouveau_dp_link_train_adjust(encoder, config)) - break; - } - - if (!cr_done) - goto stop; + return cr_done ? 0 : -1; +} - /* channel equalisation */ - NV_DEBUG_KMS(dev, "\tbegin eq\n"); - ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2); - if (ret) - goto stop; +static int +dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) +{ + bool eq_done, cr_done = true; + int tries = 0, i; - for (tries = 0; tries <= 5; tries++) { - udelay(400); + dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2); - ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3); - if (ret) + do { + if (dp_link_train_update(dev, dp, 400)) break; - NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", - status[0], status[1]); - eq_done = true; - if (!(status[2] & DP_INTERLANE_ALIGN_DONE)) - eq_done = false; - - for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) { - int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; - - if (!(lane & DP_LANE_CR_DONE)) { + eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE); + for (i = 0; i < dp->link_nr && eq_done; i++) { + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DP_LANE_CR_DONE)) cr_done = false; - break; - } - if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || - !(lane & DP_LANE_SYMBOL_LOCKED)) { + !(lane & DP_LANE_SYMBOL_LOCKED)) eq_done = false; - break; - } } - if (eq_done || !cr_done) + if (dp_link_train_commit(dev, dp)) break; + } while (!eq_done && cr_done && ++tries <= 5); - if (!nouveau_dp_link_train_adjust(encoder, config) || - !nouveau_dp_link_train_commit(encoder, config)) - break; - } + return eq_done ? 0 : -1; +} -stop: - /* end link training */ - ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE); - if (ret) +bool +nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) +{ + struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector = + nouveau_encoder_connector_get(nv_encoder); + struct drm_device *dev = encoder->dev; + struct nouveau_i2c_chan *auxch; + const u32 bw_list[] = { 270000, 162000, 0 }; + const u32 *link_bw = bw_list; + struct dp_state dp; + + auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); + if (!auxch) return false; - /* retry at a lower setting, if possible */ - if (!ret && !(eq_done && cr_done)) { - NV_DEBUG_KMS(dev, "\twe failed\n"); - if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) { - NV_DEBUG_KMS(dev, "retry link training at low rate\n"); - nv_encoder->dp.link_bw = DP_LINK_BW_1_62; - goto train; - } + dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); + if (!dp.table) + return -EINVAL; + + dp.dcb = nv_encoder->dcb; + dp.crtc = nv_crtc->index; + dp.auxch = auxch->rd; + dp.or = nv_encoder->or; + dp.link = !(nv_encoder->dcb->sorconf.link & 1); + dp.dpcd = nv_encoder->dp.dpcd; + + /* some sinks toggle hotplug in response to some of the actions + * we take during link training (DP_SET_POWER is one), we need + * to ignore them for the moment to avoid races. + */ + pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); + + /* enable down-spreading, if possible */ + if (dp.table[1] >= 16) { + u16 script = ROM16(dp.entry[14]); + if (nv_encoder->dp.dpcd[3] & 1) + script = ROM16(dp.entry[12]); + + nouveau_bios_run_init_table(dev, script, dp.dcb, dp.crtc); } - if (dpe->script1) { - NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or); - nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), - nv_encoder->dcb); + /* execute pre-train script from vbios */ + nouveau_bios_run_init_table(dev, ROM16(dp.entry[6]), dp.dcb, dp.crtc); + + /* start off at highest link rate supported by encoder and display */ + while (*link_bw > nv_encoder->dp.link_bw) + link_bw++; + + while (link_bw[0]) { + /* find minimum required lane count at this link rate */ + dp.link_nr = nv_encoder->dp.link_nr; + while ((dp.link_nr >> 1) * link_bw[0] > datarate) + dp.link_nr >>= 1; + + /* drop link rate to minimum with this lane count */ + while ((link_bw[1] * dp.link_nr) > datarate) + link_bw++; + dp.link_bw = link_bw[0]; + + /* program selected link configuration */ + dp_set_link_config(dev, &dp); + + /* attempt to train the link at this configuration */ + memset(dp.stat, 0x00, sizeof(dp.stat)); + if (!dp_link_train_cr(dev, &dp) && + !dp_link_train_eq(dev, &dp)) + break; + + /* retry at lower rate */ + link_bw++; } - /* re-enable hotplug detect */ - pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, hpd_state); + /* finish link training */ + dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE); - return eq_done; + /* execute post-train script from vbios */ + nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc); + + /* re-enable hotplug detect */ + pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); + return true; } bool @@ -447,31 +648,34 @@ nouveau_dp_detect(struct drm_encoder *encoder) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_device *dev = encoder->dev; - uint8_t dpcd[4]; + struct nouveau_i2c_chan *auxch; + u8 *dpcd = nv_encoder->dp.dpcd; int ret; - ret = auxch_rd(encoder, 0x0000, dpcd, 4); - if (ret) + auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); + if (!auxch) return false; - NV_DEBUG_KMS(dev, "encoder: link_bw %d, link_nr %d\n" - "display: link_bw %d, link_nr %d version 0x%02x\n", - nv_encoder->dcb->dpconf.link_bw, - nv_encoder->dcb->dpconf.link_nr, - dpcd[1], dpcd[2] & 0x0f, dpcd[0]); + ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8); + if (ret) + return false; - nv_encoder->dp.dpcd_version = dpcd[0]; + nv_encoder->dp.link_bw = 27000 * dpcd[1]; + nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; - nv_encoder->dp.link_bw = dpcd[1]; - if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 && - !nv_encoder->dcb->dpconf.link_bw) - nv_encoder->dp.link_bw = DP_LINK_BW_1_62; + NV_DEBUG_KMS(dev, "display: %dx%d dpcd 0x%02x\n", + nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]); + NV_DEBUG_KMS(dev, "encoder: %dx%d\n", + nv_encoder->dcb->dpconf.link_nr, + nv_encoder->dcb->dpconf.link_bw); - nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; - if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr) + if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr) nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; + if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw) + nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw; - nv_encoder->dp.enhanced_frame = (dpcd[2] & DP_ENHANCED_FRAME_CAP); + NV_DEBUG_KMS(dev, "maximum: %dx%d\n", + nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); return true; } @@ -480,105 +684,13 @@ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr) { - struct drm_device *dev = auxch->dev; - uint32_t tmp, ctrl, stat = 0, data32[4] = {}; - int ret = 0, i, index = auxch->rd; - - NV_DEBUG_KMS(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr); - - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000); - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - if (!(tmp & 0x01000000)) { - NV_ERROR(dev, "expected bit 24 == 1, got 0x%08x\n", tmp); - ret = -EIO; - goto out; - } - - for (i = 0; i < 3; i++) { - tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd)); - if (tmp & NV50_AUXCH_STAT_STATE_READY) - break; - udelay(100); - } - - if (i == 3) { - ret = -EBUSY; - goto out; - } - - if (!(cmd & 1)) { - memcpy(data32, data, data_nr); - for (i = 0; i < 4; i++) { - NV_DEBUG_KMS(dev, "wr %d: 0x%08x\n", i, data32[i]); - nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]); - } - } - - nv_wr32(dev, NV50_AUXCH_ADDR(index), addr); - ctrl = nv_rd32(dev, NV50_AUXCH_CTRL(index)); - ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN); - ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT); - ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT); - - for (i = 0; i < 16; i++) { - nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000); - nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl); - nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000); - if (!nv_wait(dev, NV50_AUXCH_CTRL(index), - 0x00010000, 0x00000000)) { - NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n", - nv_rd32(dev, NV50_AUXCH_CTRL(index))); - ret = -EBUSY; - goto out; - } - - udelay(400); - - stat = nv_rd32(dev, NV50_AUXCH_STAT(index)); - if ((stat & NV50_AUXCH_STAT_REPLY_AUX) != - NV50_AUXCH_STAT_REPLY_AUX_DEFER) - break; - } - - if (i == 16) { - NV_ERROR(dev, "auxch DEFER too many times, bailing\n"); - ret = -EREMOTEIO; - goto out; - } - - if (cmd & 1) { - if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) { - ret = -EREMOTEIO; - goto out; - } - - for (i = 0; i < 4; i++) { - data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i)); - NV_DEBUG_KMS(dev, "rd %d: 0x%08x\n", i, data32[i]); - } - memcpy(data, data32, data_nr); - } - -out: - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000); - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - if (tmp & 0x01000000) { - NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp); - ret = -EIO; - } - - udelay(400); - - return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY); + return auxch_tx(auxch->dev, auxch->rd, cmd, addr, data, data_nr); } static int nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap; - struct drm_device *dev = auxch->dev; struct i2c_msg *msg = msgs; int ret, mcnt = num; @@ -602,19 +714,6 @@ nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (ret < 0) return ret; - switch (ret & NV50_AUXCH_STAT_REPLY_I2C) { - case NV50_AUXCH_STAT_REPLY_I2C_ACK: - break; - case NV50_AUXCH_STAT_REPLY_I2C_NACK: - return -EREMOTEIO; - case NV50_AUXCH_STAT_REPLY_I2C_DEFER: - udelay(100); - continue; - default: - NV_ERROR(dev, "bad auxch reply: 0x%08x\n", ret); - return -EREMOTEIO; - } - ptr += cnt; remaining -= cnt; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 02c6f37..8c084c0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -23,6 +23,7 @@ */ #include <linux/console.h> +#include <linux/module.h> #include "drmP.h" #include "drm.h" @@ -41,7 +42,7 @@ int nouveau_agpmode = -1; module_param_named(agpmode, nouveau_agpmode, int, 0400); MODULE_PARM_DESC(modeset, "Enable kernel modesetting"); -static int nouveau_modeset = -1; /* kms */ +int nouveau_modeset = -1; module_param_named(modeset, nouveau_modeset, int, 0400); MODULE_PARM_DESC(vbios, "Override default VBIOS location"); @@ -73,7 +74,7 @@ int nouveau_ignorelid = 0; module_param_named(ignorelid, nouveau_ignorelid, int, 0400); MODULE_PARM_DESC(noaccel, "Disable all acceleration"); -int nouveau_noaccel = 0; +int nouveau_noaccel = -1; module_param_named(noaccel, nouveau_noaccel, int, 0400); MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration"); @@ -119,6 +120,10 @@ MODULE_PARM_DESC(msi, "Enable MSI (default: off)\n"); int nouveau_msi; module_param_named(msi, nouveau_msi, int, 0400); +MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n"); +int nouveau_ctxfw; +module_param_named(ctxfw, nouveau_ctxfw, int, 0400); + int nouveau_fbpercrtc; #if 0 module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400); @@ -173,8 +178,10 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - NV_INFO(dev, "Disabling fbcon acceleration...\n"); - nouveau_fbcon_save_disable_accel(dev); + if (dev->mode_config.num_crtc) { + NV_INFO(dev, "Disabling fbcon acceleration...\n"); + nouveau_fbcon_save_disable_accel(dev); + } NV_INFO(dev, "Unpinning framebuffer(s)...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -210,10 +217,13 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) pfifo->unload_context(dev); for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) { - if (dev_priv->eng[e]) { - ret = dev_priv->eng[e]->fini(dev, e); - if (ret) - goto out_abort; + if (!dev_priv->eng[e]) + continue; + + ret = dev_priv->eng[e]->fini(dev, e, true); + if (ret) { + NV_ERROR(dev, "... engine %d failed: %d\n", i, ret); + goto out_abort; } } @@ -238,10 +248,12 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) pci_set_power_state(pdev, PCI_D3hot); } - console_lock(); - nouveau_fbcon_set_suspend(dev, 1); - console_unlock(); - nouveau_fbcon_restore_accel(dev); + if (dev->mode_config.num_crtc) { + console_lock(); + nouveau_fbcon_set_suspend(dev, 1); + console_unlock(); + nouveau_fbcon_restore_accel(dev); + } return 0; out_abort: @@ -267,7 +279,8 @@ nouveau_pci_resume(struct pci_dev *pdev) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - nouveau_fbcon_save_disable_accel(dev); + if (dev->mode_config.num_crtc) + nouveau_fbcon_save_disable_accel(dev); NV_INFO(dev, "We're back, enabling device...\n"); pci_set_power_state(pdev, PCI_D0); @@ -354,7 +367,7 @@ nouveau_pci_resume(struct pci_dev *pdev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - u32 offset = nv_crtc->cursor.nvbo->bo.mem.start << PAGE_SHIFT; + u32 offset = nv_crtc->cursor.nvbo->bo.offset; nv_crtc->cursor.set_offset(nv_crtc, offset); nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, @@ -368,15 +381,18 @@ nouveau_pci_resume(struct pci_dev *pdev) nv_crtc->lut.depth = 0; } - console_lock(); - nouveau_fbcon_set_suspend(dev, 0); - console_unlock(); + if (dev->mode_config.num_crtc) { + console_lock(); + nouveau_fbcon_set_suspend(dev, 0); + console_unlock(); - nouveau_fbcon_zfill_all(dev); + nouveau_fbcon_zfill_all(dev); + } drm_helper_resume_force_mode(dev); - nouveau_fbcon_restore_accel(dev); + if (dev->mode_config.num_crtc) + nouveau_fbcon_restore_accel(dev); return 0; } @@ -389,7 +405,9 @@ static struct drm_driver driver = { .firstopen = nouveau_firstopen, .lastclose = nouveau_lastclose, .unload = nouveau_unload, + .open = nouveau_open, .preclose = nouveau_preclose, + .postclose = nouveau_postclose, #if defined(CONFIG_DRM_NOUVEAU_DEBUG) .debugfs_init = nouveau_debugfs_init, .debugfs_cleanup = nouveau_debugfs_takedown, @@ -420,6 +438,12 @@ static struct drm_driver driver = { .gem_init_object = nouveau_gem_object_new, .gem_free_object = nouveau_gem_object_del, + .gem_open_object = nouveau_gem_object_open, + .gem_close_object = nouveau_gem_object_close, + + .dumb_create = nouveau_display_dumb_create, + .dumb_map_offset = nouveau_display_dumb_map_offset, + .dumb_destroy = nouveau_display_dumb_destroy, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -450,9 +474,7 @@ static int __init nouveau_init(void) #ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force()) nouveau_modeset = 0; - else #endif - nouveau_modeset = 1; } if (!nouveau_modeset) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 9c56331..4c0be3a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -46,9 +46,17 @@ #include "ttm/ttm_module.h" struct nouveau_fpriv { - struct ttm_object_file *tfile; + spinlock_t lock; + struct list_head channels; + struct nouveau_vm *vm; }; +static inline struct nouveau_fpriv * +nouveau_fpriv(struct drm_file *file_priv) +{ + return file_priv ? file_priv->driver_priv : NULL; +} + #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) #include "nouveau_drm.h" @@ -69,7 +77,7 @@ struct nouveau_mem { struct drm_device *dev; struct nouveau_vma bar_vma; - struct nouveau_vma tmp_vma; + struct nouveau_vma vma[2]; u8 page_shift; struct drm_mm_node *tag; @@ -107,7 +115,8 @@ struct nouveau_bo { struct nouveau_channel *channel; - struct nouveau_vma vma; + struct list_head vma_list; + unsigned page_shift; uint32_t tile_mode; uint32_t tile_flags; @@ -176,9 +185,10 @@ struct nouveau_gpuobj { uint32_t flags; u32 size; - u32 pinst; - u32 cinst; - u64 vinst; + u32 pinst; /* PRAMIN BAR offset */ + u32 cinst; /* Channel offset */ + u64 vinst; /* VRAM address */ + u64 linst; /* VM address */ uint32_t engine; uint32_t class; @@ -201,6 +211,7 @@ enum nouveau_channel_mutex_class { struct nouveau_channel { struct drm_device *dev; + struct list_head list; int id; /* references to the channel data structure */ @@ -228,15 +239,18 @@ struct nouveau_channel { uint32_t sequence; uint32_t sequence_ack; atomic_t last_sequence_irq; + struct nouveau_vma vma; } fence; /* DMA push buffer */ struct nouveau_gpuobj *pushbuf; struct nouveau_bo *pushbuf_bo; + struct nouveau_vma pushbuf_vma; uint32_t pushbuf_base; /* Notifier memory */ struct nouveau_bo *notifier_bo; + struct nouveau_vma notifier_vma; struct drm_mm notifier_heap; /* PFIFO context */ @@ -278,6 +292,7 @@ struct nouveau_channel { uint32_t sw_subchannel[8]; + struct nouveau_vma dispc_vma[2]; struct { struct nouveau_gpuobj *vblsem; uint32_t vblsem_head; @@ -297,7 +312,7 @@ struct nouveau_channel { struct nouveau_exec_engine { void (*destroy)(struct drm_device *, int engine); int (*init)(struct drm_device *, int engine); - int (*fini)(struct drm_device *, int engine); + int (*fini)(struct drm_device *, int engine, bool suspend); int (*context_new)(struct nouveau_channel *, int engine); void (*context_del)(struct nouveau_channel *, int engine); int (*object_new)(struct nouveau_channel *, int engine, @@ -314,7 +329,8 @@ struct nouveau_instmem_engine { int (*suspend)(struct drm_device *dev); void (*resume)(struct drm_device *dev); - int (*get)(struct nouveau_gpuobj *, u32 size, u32 align); + int (*get)(struct nouveau_gpuobj *, struct nouveau_channel *, + u32 size, u32 align); void (*put)(struct nouveau_gpuobj *); int (*map)(struct nouveau_gpuobj *); void (*unmap)(struct nouveau_gpuobj *); @@ -398,12 +414,13 @@ struct nouveau_gpio_engine { }; struct nouveau_pm_voltage_level { - u8 voltage; - u8 vid; + u32 voltage; /* microvolts */ + u8 vid; }; struct nouveau_pm_voltage { bool supported; + u8 version; u8 vid_mask; struct nouveau_pm_voltage_level *level; @@ -412,17 +429,48 @@ struct nouveau_pm_voltage { struct nouveau_pm_memtiming { int id; - u32 reg_100220; - u32 reg_100224; - u32 reg_100228; - u32 reg_10022c; - u32 reg_100230; - u32 reg_100234; - u32 reg_100238; - u32 reg_10023c; - u32 reg_100240; + u32 reg_0; /* 0x10f290 on Fermi, 0x100220 for older */ + u32 reg_1; + u32 reg_2; + u32 reg_3; + u32 reg_4; + u32 reg_5; + u32 reg_6; + u32 reg_7; + u32 reg_8; + /* To be written to 0x1002c0 */ + u8 CL; + u8 WR; }; +struct nouveau_pm_tbl_header{ + u8 version; + u8 header_len; + u8 entry_cnt; + u8 entry_len; +}; + +struct nouveau_pm_tbl_entry{ + u8 tWR; + u8 tUNK_1; + u8 tCL; + u8 tRP; /* Byte 3 */ + u8 empty_4; + u8 tRAS; /* Byte 5 */ + u8 empty_6; + u8 tRFC; /* Byte 7 */ + u8 empty_8; + u8 tRC; /* Byte 9 */ + u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14; + u8 empty_15,empty_16,empty_17; + u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21; +}; + +/* nouveau_mem.c */ +void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_memtiming *timing); + #define NOUVEAU_PM_MAX_LEVEL 8 struct nouveau_pm_level { struct device_attribute dev_attr; @@ -432,11 +480,19 @@ struct nouveau_pm_level { u32 core; u32 memory; u32 shader; - u32 unk05; - u32 unk0a; - - u8 voltage; - u8 fanspeed; + u32 rop; + u32 copy; + u32 daemon; + u32 vdec; + u32 unk05; /* nv50:nva3, roughly.. */ + u32 unka0; /* nva3:nvc0 */ + u32 hub01; /* nvc0- */ + u32 hub06; /* nvc0- */ + u32 hub07; /* nvc0- */ + + u32 volt_min; /* microvolts */ + u32 volt_max; + u8 fanspeed; u16 memscript; struct nouveau_pm_memtiming *timing; @@ -445,9 +501,9 @@ struct nouveau_pm_level { struct nouveau_pm_temp_sensor_constants { u16 offset_constant; s16 offset_mult; - u16 offset_div; - u16 slope_mult; - u16 slope_div; + s16 offset_div; + s16 slope_mult; + s16 slope_div; }; struct nouveau_pm_threshold_temp { @@ -480,6 +536,11 @@ struct nouveau_pm_engine { void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *, u32 id, int khz); void (*clock_set)(struct drm_device *, void *); + + int (*clocks_get)(struct drm_device *, struct nouveau_pm_level *); + void *(*clocks_pre)(struct drm_device *, struct nouveau_pm_level *); + void (*clocks_set)(struct drm_device *, void *); + int (*voltage_get)(struct drm_device *); int (*voltage_set)(struct drm_device *, int voltage); int (*fanspeed_get)(struct drm_device *); @@ -488,7 +549,10 @@ struct nouveau_pm_engine { }; struct nouveau_vram_engine { + struct nouveau_mm mm; + int (*init)(struct drm_device *); + void (*takedown)(struct drm_device *dev); int (*get)(struct drm_device *, u64, u32 align, u32 size_nc, u32 type, struct nouveau_mem **); void (*put)(struct drm_device *, struct nouveau_mem **); @@ -604,17 +668,19 @@ enum nouveau_card_type { NV_40 = 0x40, NV_50 = 0x50, NV_C0 = 0xc0, + NV_D0 = 0xd0 }; struct drm_nouveau_private { struct drm_device *dev; + bool noaccel; /* the card type, takes NV_* as values */ enum nouveau_card_type card_type; /* exact chipset, derived from NV_PMC_BOOT_0 */ int chipset; - int stepping; int flags; + u32 crystal; void __iomem *mmio; @@ -700,9 +766,7 @@ struct drm_nouveau_private { /* VRAM/fb configuration */ uint64_t vram_size; uint64_t vram_sys_base; - u32 vram_rblock_size; - uint64_t fb_phys; uint64_t fb_available_size; uint64_t fb_mappable_pages; uint64_t fb_aper_free; @@ -765,6 +829,7 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo) } /* nouveau_drv.c */ +extern int nouveau_modeset; extern int nouveau_agpmode; extern int nouveau_duallink; extern int nouveau_uscript_lvds; @@ -784,12 +849,15 @@ extern int nouveau_override_conntype; extern char *nouveau_perflvl; extern int nouveau_perflvl_wr; extern int nouveau_msi; +extern int nouveau_ctxfw; extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); extern int nouveau_pci_resume(struct pci_dev *pdev); /* nouveau_state.c */ +extern int nouveau_open(struct drm_device *, struct drm_file *); extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); +extern void nouveau_postclose(struct drm_device *, struct drm_file *); extern int nouveau_load(struct drm_device *, unsigned long flags); extern int nouveau_firstopen(struct drm_device *); extern void nouveau_lastclose(struct drm_device *); @@ -802,6 +870,8 @@ extern bool nouveau_wait_eq(struct drm_device *, uint64_t timeout, uint32_t reg, uint32_t mask, uint32_t val); extern bool nouveau_wait_ne(struct drm_device *, uint64_t timeout, uint32_t reg, uint32_t mask, uint32_t val); +extern bool nouveau_wait_cb(struct drm_device *, u64 timeout, + bool (*cond)(void *), void *); extern bool nouveau_wait_for_idle(struct drm_device *); extern int nouveau_card_init(struct drm_device *); @@ -847,7 +917,7 @@ extern int nouveau_channel_alloc(struct drm_device *dev, extern struct nouveau_channel * nouveau_channel_get_unlocked(struct nouveau_channel *); extern struct nouveau_channel * -nouveau_channel_get(struct drm_device *, struct drm_file *, int id); +nouveau_channel_get(struct drm_file *, int id); extern void nouveau_channel_put_unlocked(struct nouveau_channel **); extern void nouveau_channel_put(struct nouveau_channel **); extern void nouveau_channel_ref(struct nouveau_channel *chan, @@ -984,15 +1054,15 @@ static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector /* nouveau_backlight.c */ #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT -extern int nouveau_backlight_init(struct drm_connector *); -extern void nouveau_backlight_exit(struct drm_connector *); +extern int nouveau_backlight_init(struct drm_device *); +extern void nouveau_backlight_exit(struct drm_device *); #else -static inline int nouveau_backlight_init(struct drm_connector *dev) +static inline int nouveau_backlight_init(struct drm_device *dev) { return 0; } -static inline void nouveau_backlight_exit(struct drm_connector *dev) { } +static inline void nouveau_backlight_exit(struct drm_device *dev) { } #endif /* nouveau_bios.c */ @@ -1000,7 +1070,8 @@ extern int nouveau_bios_init(struct drm_device *); extern void nouveau_bios_takedown(struct drm_device *dev); extern int nouveau_run_vbios_init(struct drm_device *); extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table, - struct dcb_entry *); + struct dcb_entry *, int crtc); +extern void nouveau_bios_init_exec(struct drm_device *, uint16_t table); extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *, enum dcb_gpio_tag); extern struct dcb_connector_table_entry * @@ -1008,11 +1079,8 @@ nouveau_bios_connector_entry(struct drm_device *, int index); extern u32 get_pll_register(struct drm_device *, enum pll_types); extern int get_pll_limits(struct drm_device *, uint32_t limit_match, struct pll_lims *); -extern int nouveau_bios_run_display_table(struct drm_device *, - struct dcb_entry *, - uint32_t script, int pxclk); -extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *, - int *length); +extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk, + struct dcb_entry *, int crtc); extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *); extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, @@ -1021,6 +1089,7 @@ extern int run_tmds_table(struct drm_device *, struct dcb_entry *, int head, int pxclk); extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head, enum LVDS_script, int pxclk); +bool bios_encoder_match(struct dcb_entry *, u32 hash); /* nouveau_ttm.c */ int nouveau_ttm_global_init(struct drm_nouveau_private *); @@ -1031,7 +1100,9 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr); bool nouveau_dp_detect(struct drm_encoder *); -bool nouveau_dp_link_train(struct drm_encoder *); +bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate); +void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); +u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); /* nv04_fb.c */ extern int nv04_fb_init(struct drm_device *); @@ -1120,7 +1191,6 @@ extern int nvc0_fifo_unload_context(struct drm_device *); /* nv04_graph.c */ extern int nv04_graph_create(struct drm_device *); -extern void nv04_graph_fifo_access(struct drm_device *, bool); extern int nv04_graph_object_new(struct nouveau_channel *, int, u32, u16); extern int nv04_graph_mthd_page_flip(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data); @@ -1158,8 +1228,8 @@ extern int nva3_copy_create(struct drm_device *dev); /* nvc0_copy.c */ extern int nvc0_copy_create(struct drm_device *dev, int engine); -/* nv40_mpeg.c */ -extern int nv40_mpeg_create(struct drm_device *dev); +/* nv31_mpeg.c */ +extern int nv31_mpeg_create(struct drm_device *dev); /* nv50_mpeg.c */ extern int nv50_mpeg_create(struct drm_device *dev); @@ -1169,7 +1239,8 @@ extern int nv04_instmem_init(struct drm_device *); extern void nv04_instmem_takedown(struct drm_device *); extern int nv04_instmem_suspend(struct drm_device *); extern void nv04_instmem_resume(struct drm_device *); -extern int nv04_instmem_get(struct nouveau_gpuobj *, u32 size, u32 align); +extern int nv04_instmem_get(struct nouveau_gpuobj *, struct nouveau_channel *, + u32 size, u32 align); extern void nv04_instmem_put(struct nouveau_gpuobj *); extern int nv04_instmem_map(struct nouveau_gpuobj *); extern void nv04_instmem_unmap(struct nouveau_gpuobj *); @@ -1180,7 +1251,8 @@ extern int nv50_instmem_init(struct drm_device *); extern void nv50_instmem_takedown(struct drm_device *); extern int nv50_instmem_suspend(struct drm_device *); extern void nv50_instmem_resume(struct drm_device *); -extern int nv50_instmem_get(struct nouveau_gpuobj *, u32 size, u32 align); +extern int nv50_instmem_get(struct nouveau_gpuobj *, struct nouveau_channel *, + u32 size, u32 align); extern void nv50_instmem_put(struct nouveau_gpuobj *); extern int nv50_instmem_map(struct nouveau_gpuobj *); extern void nv50_instmem_unmap(struct nouveau_gpuobj *); @@ -1242,15 +1314,19 @@ extern int nv04_display_create(struct drm_device *); extern int nv04_display_init(struct drm_device *); extern void nv04_display_destroy(struct drm_device *); +/* nvd0_display.c */ +extern int nvd0_display_create(struct drm_device *); +extern int nvd0_display_init(struct drm_device *); +extern void nvd0_display_destroy(struct drm_device *); + /* nv04_crtc.c */ extern int nv04_crtc_create(struct drm_device *, int index); /* nouveau_bo.c */ extern struct ttm_bo_driver nouveau_bo_driver; -extern int nouveau_bo_new(struct drm_device *, struct nouveau_channel *, - int size, int align, uint32_t flags, - uint32_t tile_mode, uint32_t tile_flags, - struct nouveau_bo **); +extern int nouveau_bo_new(struct drm_device *, int size, int align, + uint32_t flags, uint32_t tile_mode, + uint32_t tile_flags, struct nouveau_bo **); extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags); extern int nouveau_bo_unpin(struct nouveau_bo *); extern int nouveau_bo_map(struct nouveau_bo *); @@ -1265,6 +1341,12 @@ extern void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *); extern int nouveau_bo_validate(struct nouveau_bo *, bool interruptible, bool no_wait_reserve, bool no_wait_gpu); +extern struct nouveau_vma * +nouveau_bo_vma_find(struct nouveau_bo *, struct nouveau_vm *); +extern int nouveau_bo_vma_add(struct nouveau_bo *, struct nouveau_vm *, + struct nouveau_vma *); +extern void nouveau_bo_vma_del(struct nouveau_bo *, struct nouveau_vma *); + /* nouveau_fence.c */ struct nouveau_fence; extern int nouveau_fence_init(struct drm_device *); @@ -1310,12 +1392,14 @@ static inline struct nouveau_fence *nouveau_fence_ref(struct nouveau_fence *obj) } /* nouveau_gem.c */ -extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *, - int size, int align, uint32_t domain, - uint32_t tile_mode, uint32_t tile_flags, - struct nouveau_bo **); +extern int nouveau_gem_new(struct drm_device *, int size, int align, + uint32_t domain, uint32_t tile_mode, + uint32_t tile_flags, struct nouveau_bo **); extern int nouveau_gem_object_new(struct drm_gem_object *); extern void nouveau_gem_object_del(struct drm_gem_object *); +extern int nouveau_gem_object_open(struct drm_gem_object *, struct drm_file *); +extern void nouveau_gem_object_close(struct drm_gem_object *, + struct drm_file *); extern int nouveau_gem_ioctl_new(struct drm_device *, void *, struct drm_file *); extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *, @@ -1334,6 +1418,12 @@ int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event); int nouveau_finish_page_flip(struct nouveau_channel *, struct nouveau_page_flip_state *); +int nouveau_display_dumb_create(struct drm_file *, struct drm_device *, + struct drm_mode_create_dumb *args); +int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *, + uint32_t handle, uint64_t *offset); +int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *, + uint32_t handle); /* nv10_gpio.c */ int nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); @@ -1344,6 +1434,8 @@ int nv50_gpio_init(struct drm_device *dev); void nv50_gpio_fini(struct drm_device *dev); int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); +int nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); +int nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag, void (*)(void *, int), void *); void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag, @@ -1418,6 +1510,8 @@ static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val) nouveau_wait_eq(dev, 2000000000ULL, (reg), (mask), (val)) #define nv_wait_ne(dev, reg, mask, val) \ nouveau_wait_ne(dev, 2000000000ULL, (reg), (mask), (val)) +#define nv_wait_cb(dev, func, data) \ + nouveau_wait_cb(dev, 2000000000ULL, (func), (data)) /* PRAMIN access */ static inline u32 nv_ri32(struct drm_device *dev, unsigned offset) @@ -1484,6 +1578,7 @@ enum { NOUVEAU_REG_DEBUG_RMVIO = 0x80, NOUVEAU_REG_DEBUG_VGAATTR = 0x100, NOUVEAU_REG_DEBUG_EVO = 0x200, + NOUVEAU_REG_DEBUG_AUXCH = 0x400 }; #define NV_REG_DEBUG(type, dev, fmt, arg...) do { \ diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index ae69b61..e5d6e3f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -49,17 +49,17 @@ struct nouveau_encoder { union { struct { - int mc_unknown; - uint32_t unk0; - uint32_t unk1; - int dpcd_version; + u8 dpcd[8]; int link_nr; int link_bw; - bool enhanced_frame; + u32 datarate; } dp; }; }; +struct nouveau_encoder * +find_encoder(struct drm_connector *connector, int type); + static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc) { struct drm_encoder_slave *slave = to_encoder_slave(enc); @@ -83,21 +83,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder); int nv50_sor_create(struct drm_connector *, struct dcb_entry *); int nv50_dac_create(struct drm_connector *, struct dcb_entry *); -struct bit_displayport_encoder_table { - uint32_t match; - uint8_t record_nr; - uint8_t unknown; - uint16_t script0; - uint16_t script1; - uint16_t unknown_table; -} __attribute__ ((packed)); - -struct bit_displayport_encoder_table_entry { - uint8_t vs_level; - uint8_t pre_level; - uint8_t reg0; - uint8_t reg1; - uint8_t reg2; -} __attribute__ ((packed)); - #endif /* __NOUVEAU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h index a3a88ad..95c843e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fb.h +++ b/drivers/gpu/drm/nouveau/nouveau_fb.h @@ -30,6 +30,7 @@ struct nouveau_framebuffer { struct drm_framebuffer base; struct nouveau_bo *nvbo; + struct nouveau_vma vma; u32 r_dma; u32 r_format; u32 r_pitch; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index ea71f78..cc0801d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -279,6 +279,7 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev, struct fb_info *info; struct drm_framebuffer *fb; struct nouveau_framebuffer *nouveau_fb; + struct nouveau_channel *chan; struct nouveau_bo *nvbo; struct drm_mode_fb_cmd mode_cmd; struct pci_dev *pdev = dev->pdev; @@ -296,8 +297,8 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev, size = mode_cmd.pitch * mode_cmd.height; size = roundup(size, PAGE_SIZE); - ret = nouveau_gem_new(dev, dev_priv->channel, size, 0, - NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, &nvbo); + ret = nouveau_gem_new(dev, size, 0, NOUVEAU_GEM_DOMAIN_VRAM, + 0, 0x0000, &nvbo); if (ret) { NV_ERROR(dev, "failed to allocate framebuffer\n"); goto out; @@ -318,6 +319,15 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev, goto out; } + chan = nouveau_nofbaccel ? NULL : dev_priv->channel; + if (chan && dev_priv->card_type >= NV_50) { + ret = nouveau_bo_vma_add(nvbo, chan->vm, &nfbdev->nouveau_fb.vma); + if (ret) { + NV_ERROR(dev, "failed to map fb into chan: %d\n", ret); + chan = NULL; + } + } + mutex_lock(&dev->struct_mutex); info = framebuffer_alloc(0, device); @@ -448,6 +458,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev) if (nouveau_fb->nvbo) { nouveau_bo_unmap(nouveau_fb->nvbo); + nouveau_bo_vma_del(nouveau_fb->nvbo, &nouveau_fb->vma); drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem); nouveau_fb->nvbo = NULL; } @@ -476,6 +487,7 @@ int nouveau_fbcon_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fbdev *nfbdev; + int preferred_bpp; int ret; nfbdev = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL); @@ -494,7 +506,15 @@ int nouveau_fbcon_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&nfbdev->helper); - drm_fb_helper_initial_config(&nfbdev->helper, 32); + + if (dev_priv->vram_size <= 32 * 1024 * 1024) + preferred_bpp = 8; + else if (dev_priv->vram_size <= 64 * 1024 * 1024) + preferred_bpp = 16; + else + preferred_bpp = 32; + + drm_fb_helper_initial_config(&nfbdev->helper, preferred_bpp); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 56f06b0..2f6daae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -336,6 +336,7 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema) { struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct nouveau_fence *fence = NULL; + u64 offset = chan->fence.vma.offset + sema->mem->start; int ret; if (dev_priv->chipset < 0x84) { @@ -345,13 +346,10 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema) BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 3); OUT_RING (chan, NvSema); - OUT_RING (chan, sema->mem->start); + OUT_RING (chan, offset); OUT_RING (chan, 1); } else if (dev_priv->chipset < 0xc0) { - struct nouveau_vma *vma = &dev_priv->fence.bo->vma; - u64 offset = vma->offset + sema->mem->start; - ret = RING_SPACE(chan, 7); if (ret) return ret; @@ -364,9 +362,6 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema) OUT_RING (chan, 1); OUT_RING (chan, 1); /* ACQUIRE_EQ */ } else { - struct nouveau_vma *vma = &dev_priv->fence.bo->vma; - u64 offset = vma->offset + sema->mem->start; - ret = RING_SPACE(chan, 5); if (ret) return ret; @@ -394,6 +389,7 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema) { struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct nouveau_fence *fence = NULL; + u64 offset = chan->fence.vma.offset + sema->mem->start; int ret; if (dev_priv->chipset < 0x84) { @@ -403,14 +399,11 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema) BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 2); OUT_RING (chan, NvSema); - OUT_RING (chan, sema->mem->start); + OUT_RING (chan, offset); BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_RELEASE, 1); OUT_RING (chan, 1); } else if (dev_priv->chipset < 0xc0) { - struct nouveau_vma *vma = &dev_priv->fence.bo->vma; - u64 offset = vma->offset + sema->mem->start; - ret = RING_SPACE(chan, 7); if (ret) return ret; @@ -423,9 +416,6 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema) OUT_RING (chan, 1); OUT_RING (chan, 2); /* RELEASE */ } else { - struct nouveau_vma *vma = &dev_priv->fence.bo->vma; - u64 offset = vma->offset + sema->mem->start; - ret = RING_SPACE(chan, 5); if (ret) return ret; @@ -529,7 +519,7 @@ nouveau_fence_channel_init(struct nouveau_channel *chan) if (USE_SEMA(dev) && dev_priv->chipset < 0x84) { struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem; - ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, + ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY, mem->start << PAGE_SHIFT, mem->size, NV_MEM_ACCESS_RW, NV_MEM_TARGET_VRAM, &obj); @@ -540,6 +530,13 @@ nouveau_fence_channel_init(struct nouveau_channel *chan) nouveau_gpuobj_ref(NULL, &obj); if (ret) return ret; + } else + if (USE_SEMA(dev)) { + /* map fence bo into channel's vm */ + ret = nouveau_bo_vma_add(dev_priv->fence.bo, chan->vm, + &chan->fence.vma); + if (ret) + return ret; } atomic_set(&chan->fence.last_sequence_irq, 0); @@ -549,10 +546,10 @@ nouveau_fence_channel_init(struct nouveau_channel *chan) void nouveau_fence_channel_fini(struct nouveau_channel *chan) { + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct nouveau_fence *tmp, *fence; spin_lock(&chan->fence.lock); - list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) { fence->signalled = true; list_del(&fence->entry); @@ -562,8 +559,9 @@ nouveau_fence_channel_fini(struct nouveau_channel *chan) kref_put(&fence->refcount, nouveau_fence_del); } - spin_unlock(&chan->fence.lock); + + nouveau_bo_vma_del(dev_priv->fence.bo, &chan->fence.vma); } int @@ -575,7 +573,7 @@ nouveau_fence_init(struct drm_device *dev) /* Create a shared VRAM heap for cross-channel sync. */ if (USE_SEMA(dev)) { - ret = nouveau_bo_new(dev, NULL, size, 0, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(dev, size, 0, TTM_PL_FLAG_VRAM, 0, 0, &dev_priv->fence.bo); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index cee78b2..bd0b1fc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -60,9 +60,71 @@ nouveau_gem_object_del(struct drm_gem_object *gem) } int -nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan, - int size, int align, uint32_t domain, uint32_t tile_mode, - uint32_t tile_flags, struct nouveau_bo **pnvbo) +nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv) +{ + struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_vma *vma; + int ret; + + if (!fpriv->vm) + return 0; + + ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0); + if (ret) + return ret; + + vma = nouveau_bo_vma_find(nvbo, fpriv->vm); + if (!vma) { + vma = kzalloc(sizeof(*vma), GFP_KERNEL); + if (!vma) { + ret = -ENOMEM; + goto out; + } + + ret = nouveau_bo_vma_add(nvbo, fpriv->vm, vma); + if (ret) { + kfree(vma); + goto out; + } + } else { + vma->refcount++; + } + +out: + ttm_bo_unreserve(&nvbo->bo); + return ret; +} + +void +nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) +{ + struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_vma *vma; + int ret; + + if (!fpriv->vm) + return; + + ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0); + if (ret) + return; + + vma = nouveau_bo_vma_find(nvbo, fpriv->vm); + if (vma) { + if (--vma->refcount == 0) { + nouveau_bo_vma_del(nvbo, vma); + kfree(vma); + } + } + ttm_bo_unreserve(&nvbo->bo); +} + +int +nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain, + uint32_t tile_mode, uint32_t tile_flags, + struct nouveau_bo **pnvbo) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_bo *nvbo; @@ -76,7 +138,7 @@ nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan, if (!flags || domain & NOUVEAU_GEM_DOMAIN_CPU) flags |= TTM_PL_FLAG_SYSTEM; - ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode, + ret = nouveau_bo_new(dev, size, align, flags, tile_mode, tile_flags, pnvbo); if (ret) return ret; @@ -103,17 +165,28 @@ nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan, } static int -nouveau_gem_info(struct drm_gem_object *gem, struct drm_nouveau_gem_info *rep) +nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, + struct drm_nouveau_gem_info *rep) { + struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_vma *vma; if (nvbo->bo.mem.mem_type == TTM_PL_TT) rep->domain = NOUVEAU_GEM_DOMAIN_GART; else rep->domain = NOUVEAU_GEM_DOMAIN_VRAM; - rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT; rep->offset = nvbo->bo.offset; + if (fpriv->vm) { + vma = nouveau_bo_vma_find(nvbo, fpriv->vm); + if (!vma) + return -EINVAL; + + rep->offset = vma->offset; + } + + rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT; rep->map_handle = nvbo->bo.addr_space_offset; rep->tile_mode = nvbo->tile_mode; rep->tile_flags = nvbo->tile_flags; @@ -127,7 +200,6 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_gem_new *req = data; struct nouveau_bo *nvbo = NULL; - struct nouveau_channel *chan = NULL; int ret = 0; if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL)) @@ -138,28 +210,21 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, return -EINVAL; } - if (req->channel_hint) { - chan = nouveau_channel_get(dev, file_priv, req->channel_hint); - if (IS_ERR(chan)) - return PTR_ERR(chan); - } - - ret = nouveau_gem_new(dev, chan, req->info.size, req->align, + ret = nouveau_gem_new(dev, req->info.size, req->align, req->info.domain, req->info.tile_mode, req->info.tile_flags, &nvbo); - if (chan) - nouveau_channel_put(&chan); if (ret) return ret; - ret = nouveau_gem_info(nvbo->gem, &req->info); - if (ret) - goto out; - ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle); + if (ret == 0) { + ret = nouveau_gem_info(file_priv, nvbo->gem, &req->info); + if (ret) + drm_gem_handle_delete(file_priv, req->info.handle); + } + /* drop reference from allocate - handle holds it now */ drm_gem_object_unreference_unlocked(nvbo->gem); -out: return ret; } @@ -216,7 +281,8 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence) list_for_each_safe(entry, tmp, list) { nvbo = list_entry(entry, struct nouveau_bo, entry); - nouveau_bo_fence(nvbo, fence); + if (likely(fence)) + nouveau_bo_fence(nvbo, fence); if (unlikely(nvbo->validate_mapped)) { ttm_bo_kunmap(&nvbo->kmap); @@ -337,6 +403,7 @@ static int validate_list(struct nouveau_channel *chan, struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr) { + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct drm_nouveau_gem_pushbuf_bo __user *upbbo = (void __force __user *)(uintptr_t)user_pbbo_ptr; struct drm_device *dev = chan->dev; @@ -375,24 +442,26 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, return ret; } - if (nvbo->bo.offset == b->presumed.offset && - ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && - b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || - (nvbo->bo.mem.mem_type == TTM_PL_TT && - b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART))) - continue; + if (dev_priv->card_type < NV_50) { + if (nvbo->bo.offset == b->presumed.offset && + ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && + b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || + (nvbo->bo.mem.mem_type == TTM_PL_TT && + b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART))) + continue; - if (nvbo->bo.mem.mem_type == TTM_PL_TT) - b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; - else - b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; - b->presumed.offset = nvbo->bo.offset; - b->presumed.valid = 0; - relocs++; - - if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index].presumed, - &b->presumed, sizeof(b->presumed))) - return -EFAULT; + if (nvbo->bo.mem.mem_type == TTM_PL_TT) + b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; + else + b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; + b->presumed.offset = nvbo->bo.offset; + b->presumed.valid = 0; + relocs++; + + if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index].presumed, + &b->presumed, sizeof(b->presumed))) + return -EFAULT; + } } return relocs; @@ -567,7 +636,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, struct nouveau_fence *fence = NULL; int i, j, ret = 0, do_reloc = 0; - chan = nouveau_channel_get(dev, file_priv, req->channel); + chan = nouveau_channel_get(file_priv, req->channel); if (IS_ERR(chan)) return PTR_ERR(chan); @@ -801,7 +870,7 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data, if (!gem) return -ENOENT; - ret = nouveau_gem_info(gem, req); + ret = nouveau_gem_info(file_priv, gem, req); drm_gem_object_unreference_unlocked(gem); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index cb389d0..d39b220 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs */ +#include <linux/module.h> + #include "drmP.h" #include "nouveau_drv.h" #include "nouveau_i2c.h" @@ -107,6 +109,13 @@ nv4e_i2c_getsda(void *data) return !!((nv_rd32(dev, i2c->rd) >> 16) & 8); } +static const uint32_t nv50_i2c_port[] = { + 0x00e138, 0x00e150, 0x00e168, 0x00e180, + 0x00e254, 0x00e274, 0x00e764, 0x00e780, + 0x00e79c, 0x00e7b8 +}; +#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port) + static int nv50_i2c_getscl(void *data) { @@ -130,28 +139,32 @@ static void nv50_i2c_setscl(void *data, int state) { struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); + nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); } static void nv50_i2c_setsda(void *data, int state) { struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - nv_wr32(dev, i2c->wr, - (nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0)); + nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0)); i2c->data = state; } -static const uint32_t nv50_i2c_port[] = { - 0x00e138, 0x00e150, 0x00e168, 0x00e180, - 0x00e254, 0x00e274, 0x00e764, 0x00e780, - 0x00e79c, 0x00e7b8 -}; -#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port) +static int +nvd0_i2c_getscl(void *data) +{ + struct nouveau_i2c_chan *i2c = data; + return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10); +} + +static int +nvd0_i2c_getsda(void *data) +{ + struct nouveau_i2c_chan *i2c = data; + return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20); +} int nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) @@ -163,7 +176,8 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) if (entry->chan) return -EEXIST; - if (dev_priv->card_type >= NV_50 && entry->read >= NV50_I2C_PORTS) { + if (dev_priv->card_type >= NV_50 && + dev_priv->card_type <= NV_C0 && entry->read >= NV50_I2C_PORTS) { NV_ERROR(dev, "unknown i2c port %d\n", entry->read); return -EINVAL; } @@ -192,10 +206,17 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) case 5: i2c->bit.setsda = nv50_i2c_setsda; i2c->bit.setscl = nv50_i2c_setscl; - i2c->bit.getsda = nv50_i2c_getsda; - i2c->bit.getscl = nv50_i2c_getscl; - i2c->rd = nv50_i2c_port[entry->read]; - i2c->wr = i2c->rd; + if (dev_priv->card_type < NV_D0) { + i2c->bit.getsda = nv50_i2c_getsda; + i2c->bit.getscl = nv50_i2c_getscl; + i2c->rd = nv50_i2c_port[entry->read]; + i2c->wr = i2c->rd; + } else { + i2c->bit.getsda = nvd0_i2c_getsda; + i2c->bit.getscl = nvd0_i2c_getscl; + i2c->rd = 0x00d014 + (entry->read * 0x20); + i2c->wr = i2c->rd; + } break; case 6: i2c->rd = entry->read; @@ -267,7 +288,10 @@ nouveau_i2c_find(struct drm_device *dev, int index) val = 0xe001; } - nv_wr32(dev, reg, (nv_rd32(dev, reg) & ~0xf003) | val); + /* nfi, but neither auxch or i2c work if it's 1 */ + nv_mask(dev, reg + 0x0c, 0x00000001, 0x00000000); + /* nfi, but switches auxch vs normal i2c */ + nv_mask(dev, reg + 0x00, 0x0000f003, val); } if (!i2c->chan && nouveau_i2c_init(dev, i2c, index)) @@ -309,7 +333,7 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what, NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index); - for (i = 0; info[i].addr; i++) { + for (i = 0; i2c && info[i].addr; i++) { if (nouveau_probe_i2c_addr(i2c, info[i].addr) && (!match || match(i2c, &info[i]))) { NV_INFO(dev, "Detected %s: %s\n", what, info[i].type); diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c index 2ba7265..868c7fd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_irq.c +++ b/drivers/gpu/drm/nouveau/nouveau_irq.c @@ -79,7 +79,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS) int i; stat = nv_rd32(dev, NV03_PMC_INTR_0); - if (!stat) + if (stat == 0 || stat == ~0) return IRQ_NONE; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 5ee14d2..36bec48 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -397,7 +397,7 @@ nouveau_mem_vram_init(struct drm_device *dev) if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(40))) dma_bits = 40; } else - if (0 && drm_pci_device_is_pcie(dev) && + if (0 && pci_is_pcie(dev->pdev) && dev_priv->chipset > 0x40 && dev_priv->chipset != 0x45) { if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(39))) @@ -408,8 +408,6 @@ nouveau_mem_vram_init(struct drm_device *dev) if (ret) return ret; - dev_priv->fb_phys = pci_resource_start(dev->pdev, 1); - ret = nouveau_ttm_global_init(dev_priv); if (ret) return ret; @@ -423,38 +421,6 @@ nouveau_mem_vram_init(struct drm_device *dev) return ret; } - /* reserve space at end of VRAM for PRAMIN */ - if (dev_priv->card_type >= NV_50) { - dev_priv->ramin_rsvd_vram = 1 * 1024 * 1024; - } else - if (dev_priv->card_type >= NV_40) { - u32 vs = hweight8((nv_rd32(dev, 0x001540) & 0x0000ff00) >> 8); - u32 rsvd; - - /* estimate grctx size, the magics come from nv40_grctx.c */ - if (dev_priv->chipset == 0x40) rsvd = 0x6aa0 * vs; - else if (dev_priv->chipset < 0x43) rsvd = 0x4f00 * vs; - else if (nv44_graph_class(dev)) rsvd = 0x4980 * vs; - else rsvd = 0x4a40 * vs; - rsvd += 16 * 1024; - rsvd *= dev_priv->engine.fifo.channels; - - /* pciegart table */ - if (drm_pci_device_is_pcie(dev)) - rsvd += 512 * 1024; - - /* object storage */ - rsvd += 512 * 1024; - - dev_priv->ramin_rsvd_vram = round_up(rsvd, 4096); - } else { - dev_priv->ramin_rsvd_vram = 512 * 1024; - } - - ret = dev_priv->engine.vram.init(dev); - if (ret) - return ret; - NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20)); if (dev_priv->vram_sys_base) { NV_INFO(dev, "Stolen system memory at: 0x%010llx\n", @@ -479,7 +445,7 @@ nouveau_mem_vram_init(struct drm_device *dev) } if (dev_priv->card_type < NV_50) { - ret = nouveau_bo_new(dev, NULL, 256*1024, 0, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(dev, 256*1024, 0, TTM_PL_FLAG_VRAM, 0, 0, &dev_priv->vga_ram); if (ret == 0) ret = nouveau_bo_pin(dev_priv->vga_ram, @@ -536,35 +502,146 @@ nouveau_mem_gart_init(struct drm_device *dev) return 0; } +/* XXX: For now a dummy. More samples required, possibly even a card + * Called from nouveau_perf.c */ +void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_memtiming *timing) { + + NV_DEBUG(dev,"Timing entry format unknown, please contact nouveau developers"); +} + +void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_memtiming *timing) { + + timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); + + /* XXX: I don't trust the -1's and +1's... they must come + * from somewhere! */ + timing->reg_1 = (e->tWR + 2 + magic_number) << 24 | + 1 << 16 | + (e->tUNK_1 + 2 + magic_number) << 8 | + (e->tCL + 2 - magic_number); + timing->reg_2 = (magic_number << 24 | e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); + timing->reg_2 |= 0x20200000; + + NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id, + timing->reg_0, timing->reg_1,timing->reg_2); +} + +void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, uint8_t magic_number,struct nouveau_pm_memtiming *timing) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + + uint8_t unk18 = 1, + unk19 = 1, + unk20 = 0, + unk21 = 0; + + switch (min(hdr->entry_len, (u8) 22)) { + case 22: + unk21 = e->tUNK_21; + case 21: + unk20 = e->tUNK_20; + case 20: + unk19 = e->tUNK_19; + case 19: + unk18 = e->tUNK_18; + break; + } + + timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); + + /* XXX: I don't trust the -1's and +1's... they must come + * from somewhere! */ + timing->reg_1 = (e->tWR + unk19 + 1 + magic_number) << 24 | + max(unk18, (u8) 1) << 16 | + (e->tUNK_1 + unk19 + 1 + magic_number) << 8; + if (dev_priv->chipset == 0xa8) { + timing->reg_1 |= (e->tCL - 1); + } else { + timing->reg_1 |= (e->tCL + 2 - magic_number); + } + timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); + + timing->reg_5 = (e->tRAS << 24 | e->tRC); + timing->reg_5 += max(e->tUNK_10, e->tUNK_11) << 16; + + if (P->version == 1) { + timing->reg_2 |= magic_number << 24; + timing->reg_3 = (0x14 + e->tCL) << 24 | + 0x16 << 16 | + (e->tCL - 1) << 8 | + (e->tCL - 1); + timing->reg_4 = (nv_rd32(dev,0x10022c) & 0xffff0000) | e->tUNK_13 << 8 | e->tUNK_13; + timing->reg_5 |= (e->tCL + 2) << 8; + timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16; + } else { + timing->reg_2 |= (unk19 - 1) << 24; + /* XXX: reg_10022c for recentish cards pretty much unknown*/ + timing->reg_3 = e->tCL - 1; + timing->reg_4 = (unk20 << 24 | unk21 << 16 | + e->tUNK_13 << 8 | e->tUNK_13); + /* XXX: +6? */ + timing->reg_5 |= (unk19 + 6) << 8; + + /* XXX: reg_10023c currently unknown + * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */ + timing->reg_7 = 0x202; + } + + NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", timing->id, + timing->reg_0, timing->reg_1, + timing->reg_2, timing->reg_3); + NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", + timing->reg_4, timing->reg_5, + timing->reg_6, timing->reg_7); + NV_DEBUG(dev, " 240: %08x\n", timing->reg_8); +} + +void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) { + timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | e->tRAS << 8 | e->tRP); + timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tCL & 0x0f); + timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tWR << 16 | e->tUNK_1 << 8; + timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13; + timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15; + NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id, + timing->reg_0, timing->reg_1, + timing->reg_2, timing->reg_3); + NV_DEBUG(dev, " 2a0: %08x %08x %08x %08x\n", + timing->reg_4, timing->reg_5, + timing->reg_6, timing->reg_7); +} + +/** + * Processes the Memory Timing BIOS table, stores generated + * register values + * @pre init scripts were run, memtiming regs are initialized + */ void nouveau_mem_timing_init(struct drm_device *dev) { - /* cards < NVC0 only */ struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_memtimings *memtimings = &pm->memtimings; struct nvbios *bios = &dev_priv->vbios; struct bit_entry P; - u8 tUNK_0, tUNK_1, tUNK_2; - u8 tRP; /* Byte 3 */ - u8 tRAS; /* Byte 5 */ - u8 tRFC; /* Byte 7 */ - u8 tRC; /* Byte 9 */ - u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14; - u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21; - u8 magic_number = 0; /* Yeah... sorry*/ - u8 *mem = NULL, *entry; - int i, recordlen, entries; + struct nouveau_pm_tbl_header *hdr = NULL; + uint8_t magic_number; + u8 *entry; + int i; if (bios->type == NVBIOS_BIT) { if (bit_table(dev, 'P', &P)) return; if (P.version == 1) - mem = ROMPTR(bios, P.data[4]); + hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[4]); else if (P.version == 2) - mem = ROMPTR(bios, P.data[8]); + hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[8]); else { NV_WARN(dev, "unknown mem for BIT P %d\n", P.version); } @@ -573,150 +650,56 @@ nouveau_mem_timing_init(struct drm_device *dev) return; } - if (!mem) { + if (!hdr) { NV_DEBUG(dev, "memory timing table pointer invalid\n"); return; } - if (mem[0] != 0x10) { - NV_WARN(dev, "memory timing table 0x%02x unknown\n", mem[0]); + if (hdr->version != 0x10) { + NV_WARN(dev, "memory timing table 0x%02x unknown\n", hdr->version); return; } /* validate record length */ - entries = mem[2]; - recordlen = mem[3]; - if (recordlen < 15) { - NV_ERROR(dev, "mem timing table length unknown: %d\n", mem[3]); + if (hdr->entry_len < 15) { + NV_ERROR(dev, "mem timing table length unknown: %d\n", hdr->entry_len); return; } /* parse vbios entries into common format */ memtimings->timing = - kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL); + kcalloc(hdr->entry_cnt, sizeof(*memtimings->timing), GFP_KERNEL); if (!memtimings->timing) return; /* Get "some number" from the timing reg for NV_40 and NV_50 - * Used in calculations later */ - if (dev_priv->card_type >= NV_40 && dev_priv->chipset < 0x98) { + * Used in calculations later... source unknown */ + magic_number = 0; + if (P.version == 1) { magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24; } - entry = mem + mem[1]; - for (i = 0; i < entries; i++, entry += recordlen) { + entry = (u8*) hdr + hdr->header_len; + for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) { struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i]; if (entry[0] == 0) continue; - tUNK_18 = 1; - tUNK_19 = 1; - tUNK_20 = 0; - tUNK_21 = 0; - switch (min(recordlen, 22)) { - case 22: - tUNK_21 = entry[21]; - case 21: - tUNK_20 = entry[20]; - case 20: - tUNK_19 = entry[19]; - case 19: - tUNK_18 = entry[18]; - default: - tUNK_0 = entry[0]; - tUNK_1 = entry[1]; - tUNK_2 = entry[2]; - tRP = entry[3]; - tRAS = entry[5]; - tRFC = entry[7]; - tRC = entry[9]; - tUNK_10 = entry[10]; - tUNK_11 = entry[11]; - tUNK_12 = entry[12]; - tUNK_13 = entry[13]; - tUNK_14 = entry[14]; - break; - } - - timing->reg_100220 = (tRC << 24 | tRFC << 16 | tRAS << 8 | tRP); - - /* XXX: I don't trust the -1's and +1's... they must come - * from somewhere! */ - timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 | - max(tUNK_18, (u8) 1) << 16 | - (tUNK_1 + tUNK_19 + 1 + magic_number) << 8; - if (dev_priv->chipset == 0xa8) { - timing->reg_100224 |= (tUNK_2 - 1); - } else { - timing->reg_100224 |= (tUNK_2 + 2 - magic_number); - } - - timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10); - if (dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa) - timing->reg_100228 |= (tUNK_19 - 1) << 24; - else - timing->reg_100228 |= magic_number << 24; - - if (dev_priv->card_type == NV_40) { - /* NV40: don't know what the rest of the regs are.. - * And don't need to know either */ - timing->reg_100228 |= 0x20200000; - } else if (dev_priv->card_type >= NV_50) { - if (dev_priv->chipset < 0x98 || - (dev_priv->chipset == 0x98 && - dev_priv->stepping <= 0xa1)) { - timing->reg_10022c = (0x14 + tUNK_2) << 24 | - 0x16 << 16 | - (tUNK_2 - 1) << 8 | - (tUNK_2 - 1); - } else { - /* XXX: reg_10022c for recentish cards */ - timing->reg_10022c = tUNK_2 - 1; - } - - timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 | - tUNK_13 << 8 | tUNK_13); - - timing->reg_100234 = (tRAS << 24 | tRC); - timing->reg_100234 += max(tUNK_10, tUNK_11) << 16; - - if (dev_priv->chipset < 0x98 || - (dev_priv->chipset == 0x98 && - dev_priv->stepping <= 0xa1)) { - timing->reg_100234 |= (tUNK_2 + 2) << 8; - } else { - /* XXX: +6? */ - timing->reg_100234 |= (tUNK_19 + 6) << 8; - } - - /* XXX; reg_100238 - * reg_100238: 0x00?????? */ - timing->reg_10023c = 0x202; - if (dev_priv->chipset < 0x98 || - (dev_priv->chipset == 0x98 && - dev_priv->stepping <= 0xa1)) { - timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16; - } else { - /* XXX: reg_10023c - * currently unknown - * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */ - } - - /* XXX: reg_100240? */ - } timing->id = i; - - NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i, - timing->reg_100220, timing->reg_100224, - timing->reg_100228, timing->reg_10022c); - NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", - timing->reg_100230, timing->reg_100234, - timing->reg_100238, timing->reg_10023c); - NV_DEBUG(dev, " 240: %08x\n", timing->reg_100240); + timing->WR = entry[0]; + timing->CL = entry[2]; + + if(dev_priv->card_type <= NV_40) { + nv40_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]); + } else if(dev_priv->card_type == NV_50){ + nv50_mem_timing_entry(dev,&P,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]); + } else if(dev_priv->card_type == NV_C0) { + nvc0_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,&pm->memtimings.timing[i]); + } } - memtimings->nr_timing = entries; - memtimings->supported = (dev_priv->chipset <= 0x98); + memtimings->nr_timing = hdr->entry_cnt; + memtimings->supported = P.version == 1; } void @@ -725,41 +708,38 @@ nouveau_mem_timing_fini(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings; - kfree(mem->timing); + if(mem->timing) { + kfree(mem->timing); + mem->timing = NULL; + } } static int -nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long p_size) +nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) { - struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev); - struct nouveau_mm *mm; - u64 size, block, rsvd; - int ret; - - rsvd = (256 * 1024); /* vga memory */ - size = (p_size << PAGE_SHIFT) - rsvd; - block = dev_priv->vram_rblock_size; - - ret = nouveau_mm_init(&mm, rsvd >> 12, size >> 12, block >> 12); - if (ret) - return ret; - - man->priv = mm; + /* nothing to do */ return 0; } static int nouveau_vram_manager_fini(struct ttm_mem_type_manager *man) { - struct nouveau_mm *mm = man->priv; - int ret; + /* nothing to do */ + return 0; +} - ret = nouveau_mm_fini(&mm); - if (ret) - return ret; +static inline void +nouveau_mem_node_cleanup(struct nouveau_mem *node) +{ + if (node->vma[0].node) { + nouveau_vm_unmap(&node->vma[0]); + nouveau_vm_put(&node->vma[0]); + } - man->priv = NULL; - return 0; + if (node->vma[1].node) { + nouveau_vm_unmap(&node->vma[1]); + nouveau_vm_put(&node->vma[1]); + } } static void @@ -768,14 +748,9 @@ nouveau_vram_manager_del(struct ttm_mem_type_manager *man, { struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev); struct nouveau_vram_engine *vram = &dev_priv->engine.vram; - struct nouveau_mem *node = mem->mm_node; struct drm_device *dev = dev_priv->dev; - if (node->tmp_vma.node) { - nouveau_vm_unmap(&node->tmp_vma); - nouveau_vm_put(&node->tmp_vma); - } - + nouveau_mem_node_cleanup(mem->mm_node); vram->put(dev, (struct nouveau_mem **)&mem->mm_node); } @@ -794,7 +769,7 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man, int ret; if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) - size_nc = 1 << nvbo->vma.node->type; + size_nc = 1 << nvbo->page_shift; ret = vram->get(dev, mem->num_pages << PAGE_SHIFT, mem->page_alignment << PAGE_SHIFT, size_nc, @@ -804,9 +779,7 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man, return (ret == -ENOSPC) ? 0 : ret; } - node->page_shift = 12; - if (nvbo->vma.node) - node->page_shift = nvbo->vma.node->type; + node->page_shift = nvbo->page_shift; mem->mm_node = node; mem->start = node->offset >> PAGE_SHIFT; @@ -862,15 +835,9 @@ static void nouveau_gart_manager_del(struct ttm_mem_type_manager *man, struct ttm_mem_reg *mem) { - struct nouveau_mem *node = mem->mm_node; - - if (node->tmp_vma.node) { - nouveau_vm_unmap(&node->tmp_vma); - nouveau_vm_put(&node->tmp_vma); - } - + nouveau_mem_node_cleanup(mem->mm_node); + kfree(mem->mm_node); mem->mm_node = NULL; - kfree(node); } static int @@ -880,11 +847,7 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_mem_reg *mem) { struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); - struct nouveau_bo *nvbo = nouveau_bo(bo); - struct nouveau_vma *vma = &nvbo->vma; - struct nouveau_vm *vm = vma->vm; struct nouveau_mem *node; - int ret; if (unlikely((mem->num_pages << PAGE_SHIFT) >= dev_priv->gart_info.aper_size)) @@ -893,24 +856,8 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man, node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) return -ENOMEM; + node->page_shift = 12; - /* This node must be for evicting large-paged VRAM - * to system memory. Due to a nv50 limitation of - * not being able to mix large/small pages within - * the same PDE, we need to create a temporary - * small-paged VMA for the eviction. - */ - if (vma->node->type != vm->spg_shift) { - ret = nouveau_vm_get(vm, (u64)vma->node->length << 12, - vm->spg_shift, NV_MEM_ACCESS_RW, - &node->tmp_vma); - if (ret) { - kfree(node); - return ret; - } - } - - node->page_shift = nvbo->vma.node->type; mem->mm_node = node; mem->start = 0; return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.c b/drivers/gpu/drm/nouveau/nouveau_mm.c index 7609756..b29ffb3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mm.c @@ -27,7 +27,7 @@ #include "nouveau_mm.h" static inline void -region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a) +region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a) { list_del(&a->nl_entry); list_del(&a->fl_entry); @@ -35,7 +35,7 @@ region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a) } static struct nouveau_mm_node * -region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size) +region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size) { struct nouveau_mm_node *b; @@ -57,33 +57,33 @@ region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size) return b; } -#define node(root, dir) ((root)->nl_entry.dir == &rmm->nodes) ? NULL : \ +#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry) void -nouveau_mm_put(struct nouveau_mm *rmm, struct nouveau_mm_node *this) +nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this) { struct nouveau_mm_node *prev = node(this, prev); struct nouveau_mm_node *next = node(this, next); - list_add(&this->fl_entry, &rmm->free); + list_add(&this->fl_entry, &mm->free); this->type = 0; if (prev && prev->type == 0) { prev->length += this->length; - region_put(rmm, this); + region_put(mm, this); this = prev; } if (next && next->type == 0) { next->offset = this->offset; next->length += this->length; - region_put(rmm, this); + region_put(mm, this); } } int -nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, +nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc, u32 align, struct nouveau_mm_node **pnode) { struct nouveau_mm_node *prev, *this, *next; @@ -92,17 +92,17 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, u32 splitoff; u32 s, e; - list_for_each_entry(this, &rmm->free, fl_entry) { + list_for_each_entry(this, &mm->free, fl_entry) { e = this->offset + this->length; s = this->offset; prev = node(this, prev); if (prev && prev->type != type) - s = roundup(s, rmm->block_size); + s = roundup(s, mm->block_size); next = node(this, next); if (next && next->type != type) - e = rounddown(e, rmm->block_size); + e = rounddown(e, mm->block_size); s = (s + align_mask) & ~align_mask; e &= ~align_mask; @@ -110,10 +110,10 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, continue; splitoff = s - this->offset; - if (splitoff && !region_split(rmm, this, splitoff)) + if (splitoff && !region_split(mm, this, splitoff)) return -ENOMEM; - this = region_split(rmm, this, min(size, e - s)); + this = region_split(mm, this, min(size, e - s)); if (!this) return -ENOMEM; @@ -127,45 +127,49 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, } int -nouveau_mm_init(struct nouveau_mm **prmm, u32 offset, u32 length, u32 block) +nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) { - struct nouveau_mm *rmm; - struct nouveau_mm_node *heap; + struct nouveau_mm_node *node; + + if (block) { + mutex_init(&mm->mutex); + INIT_LIST_HEAD(&mm->nodes); + INIT_LIST_HEAD(&mm->free); + mm->block_size = block; + mm->heap_nodes = 0; + } - heap = kzalloc(sizeof(*heap), GFP_KERNEL); - if (!heap) + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) return -ENOMEM; - heap->offset = roundup(offset, block); - heap->length = rounddown(offset + length, block) - heap->offset; + node->offset = roundup(offset, mm->block_size); + node->length = rounddown(offset + length, mm->block_size) - node->offset; - rmm = kzalloc(sizeof(*rmm), GFP_KERNEL); - if (!rmm) { - kfree(heap); - return -ENOMEM; - } - rmm->block_size = block; - mutex_init(&rmm->mutex); - INIT_LIST_HEAD(&rmm->nodes); - INIT_LIST_HEAD(&rmm->free); - list_add(&heap->nl_entry, &rmm->nodes); - list_add(&heap->fl_entry, &rmm->free); - - *prmm = rmm; + list_add_tail(&node->nl_entry, &mm->nodes); + list_add_tail(&node->fl_entry, &mm->free); + mm->heap_nodes++; return 0; } int -nouveau_mm_fini(struct nouveau_mm **prmm) +nouveau_mm_fini(struct nouveau_mm *mm) { - struct nouveau_mm *rmm = *prmm; - struct nouveau_mm_node *heap = - list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry); - - if (!list_is_singular(&rmm->nodes)) - return -EBUSY; + struct nouveau_mm_node *node, *heap = + list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry); + int nodes = 0; + + list_for_each_entry(node, &mm->nodes, nl_entry) { + if (nodes++ == mm->heap_nodes) { + printk(KERN_ERR "nouveau_mm in use at destroy time!\n"); + list_for_each_entry(node, &mm->nodes, nl_entry) { + printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n", + node->type, node->offset, node->length); + } + WARN_ON(1); + return -EBUSY; + } + } kfree(heap); - kfree(rmm); - *prmm = NULL; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.h b/drivers/gpu/drm/nouveau/nouveau_mm.h index 1f7483a..57a600c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mm.h +++ b/drivers/gpu/drm/nouveau/nouveau_mm.h @@ -42,16 +42,18 @@ struct nouveau_mm { struct mutex mutex; u32 block_size; + int heap_nodes; }; -int nouveau_mm_init(struct nouveau_mm **, u32 offset, u32 length, u32 block); -int nouveau_mm_fini(struct nouveau_mm **); +int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block); +int nouveau_mm_fini(struct nouveau_mm *); int nouveau_mm_pre(struct nouveau_mm *); int nouveau_mm_get(struct nouveau_mm *, int type, u32 size, u32 size_nc, u32 align, struct nouveau_mm_node **); void nouveau_mm_put(struct nouveau_mm *, struct nouveau_mm_node *); int nv50_vram_init(struct drm_device *); +void nv50_vram_fini(struct drm_device *); int nv50_vram_new(struct drm_device *, u64 size, u32 align, u32 size_nc, u32 memtype, struct nouveau_mem **); void nv50_vram_del(struct drm_device *, struct nouveau_mem **); diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c index 5b39718..6abdbe6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_notifier.c +++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c @@ -34,6 +34,7 @@ int nouveau_notifier_init_channel(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_bo *ntfy = NULL; uint32_t flags, ttmpl; int ret; @@ -46,7 +47,7 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan) ttmpl = TTM_PL_FLAG_TT; } - ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags, 0, 0, &ntfy); + ret = nouveau_gem_new(dev, PAGE_SIZE, 0, flags, 0, 0, &ntfy); if (ret) return ret; @@ -58,14 +59,22 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan) if (ret) goto out_err; + if (dev_priv->card_type >= NV_50) { + ret = nouveau_bo_vma_add(ntfy, chan->vm, &chan->notifier_vma); + if (ret) + goto out_err; + } + ret = drm_mm_init(&chan->notifier_heap, 0, ntfy->bo.mem.size); if (ret) goto out_err; chan->notifier_bo = ntfy; out_err: - if (ret) + if (ret) { + nouveau_bo_vma_del(ntfy, &chan->notifier_vma); drm_gem_object_unreference_unlocked(ntfy->gem); + } return ret; } @@ -78,6 +87,7 @@ nouveau_notifier_takedown_channel(struct nouveau_channel *chan) if (!chan->notifier_bo) return; + nouveau_bo_vma_del(chan->notifier_bo, &chan->notifier_vma); nouveau_bo_unmap(chan->notifier_bo); mutex_lock(&dev->struct_mutex); nouveau_bo_unpin(chan->notifier_bo); @@ -122,10 +132,10 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle, target = NV_MEM_TARGET_VRAM; else target = NV_MEM_TARGET_GART; - offset = chan->notifier_bo->bo.mem.start << PAGE_SHIFT; + offset = chan->notifier_bo->bo.offset; } else { target = NV_MEM_TARGET_VM; - offset = chan->notifier_bo->vma.offset; + offset = chan->notifier_vma.offset; } offset += mem->start; @@ -183,7 +193,7 @@ nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data, if (unlikely(dev_priv->card_type >= NV_C0)) return -EINVAL; - chan = nouveau_channel_get(dev, file_priv, na->channel); + chan = nouveau_channel_get(file_priv, na->channel); if (IS_ERR(chan)) return PTR_ERR(chan); diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 8f97016..960c0ae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -125,7 +125,7 @@ nouveau_gpuobj_mthd_call2(struct drm_device *dev, int chid, int ret = -EINVAL; spin_lock_irqsave(&dev_priv->channels.lock, flags); - if (chid > 0 && chid < dev_priv->engine.fifo.channels) + if (chid >= 0 && chid < dev_priv->engine.fifo.channels) chan = dev_priv->channels.ptr[chid]; if (chan) ret = nouveau_gpuobj_mthd_call(chan, class, mthd, data); @@ -191,7 +191,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); spin_unlock(&dev_priv->ramin_lock); - if (chan) { + if (!(flags & NVOBJ_FLAG_VM) && chan) { ramin = drm_mm_search_free(&chan->ramin_heap, size, align, 0); if (ramin) ramin = drm_mm_get_block(ramin, size, align); @@ -208,7 +208,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, gpuobj->vinst = ramin->start + chan->ramin->vinst; gpuobj->node = ramin; } else { - ret = instmem->get(gpuobj, size, align); + ret = instmem->get(gpuobj, chan, size, align); if (ret) { nouveau_gpuobj_ref(NULL, &gpuobj); return ret; @@ -680,7 +680,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) return ret; } - ret = drm_mm_init(&chan->ramin_heap, base, size); + ret = drm_mm_init(&chan->ramin_heap, base, size - base); if (ret) { NV_ERROR(dev, "Error creating PRAMIN heap: %d\n", ret); nouveau_gpuobj_ref(NULL, &chan->ramin); @@ -690,36 +690,69 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) return 0; } -int -nouveau_gpuobj_channel_init(struct nouveau_channel *chan, - uint32_t vram_h, uint32_t tt_h) +static int +nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm) { + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *vram = NULL, *tt = NULL; + struct nouveau_gpuobj *pgd = NULL; + struct nouveau_vm_pgd *vpgd; int ret, i; - NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h); - - if (dev_priv->card_type == NV_C0) { - struct nouveau_vm *vm = dev_priv->chan_vm; - struct nouveau_vm_pgd *vpgd; + ret = nouveau_gpuobj_new(dev, NULL, 4096, 0x1000, 0, &chan->ramin); + if (ret) + return ret; - ret = nouveau_gpuobj_new(dev, NULL, 4096, 0x1000, 0, - &chan->ramin); + /* create page directory for this vm if none currently exists, + * will be destroyed automagically when last reference to the + * vm is removed + */ + if (list_empty(&vm->pgd_list)) { + ret = nouveau_gpuobj_new(dev, NULL, 65536, 0x1000, 0, &pgd); if (ret) return ret; + } + nouveau_vm_ref(vm, &chan->vm, pgd); + nouveau_gpuobj_ref(NULL, &pgd); + + /* point channel at vm's page directory */ + vpgd = list_first_entry(&vm->pgd_list, struct nouveau_vm_pgd, head); + nv_wo32(chan->ramin, 0x0200, lower_32_bits(vpgd->obj->vinst)); + nv_wo32(chan->ramin, 0x0204, upper_32_bits(vpgd->obj->vinst)); + nv_wo32(chan->ramin, 0x0208, 0xffffffff); + nv_wo32(chan->ramin, 0x020c, 0x000000ff); + + /* map display semaphore buffers into channel's vm */ + if (dev_priv->card_type >= NV_D0) + return 0; - nouveau_vm_ref(vm, &chan->vm, NULL); + for (i = 0; i < 2; i++) { + struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i]; - vpgd = list_first_entry(&vm->pgd_list, struct nouveau_vm_pgd, head); - nv_wo32(chan->ramin, 0x0200, lower_32_bits(vpgd->obj->vinst)); - nv_wo32(chan->ramin, 0x0204, upper_32_bits(vpgd->obj->vinst)); - nv_wo32(chan->ramin, 0x0208, 0xffffffff); - nv_wo32(chan->ramin, 0x020c, 0x000000ff); - return 0; + ret = nouveau_bo_vma_add(dispc->sem.bo, chan->vm, + &chan->dispc_vma[i]); + if (ret) + return ret; } + return 0; +} + +int +nouveau_gpuobj_channel_init(struct nouveau_channel *chan, + uint32_t vram_h, uint32_t tt_h) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fpriv *fpriv = nouveau_fpriv(chan->file_priv); + struct nouveau_vm *vm = fpriv ? fpriv->vm : dev_priv->chan_vm; + struct nouveau_gpuobj *vram = NULL, *tt = NULL; + int ret, i; + + NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h); + if (dev_priv->card_type >= NV_C0) + return nvc0_gpuobj_channel_init(chan, vm); + /* Allocate a chunk of memory for per-channel object storage */ ret = nouveau_gpuobj_channel_init_pramin(chan); if (ret) { @@ -731,7 +764,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, * - Allocate per-channel page-directory * - Link with shared channel VM */ - if (dev_priv->chan_vm) { + if (vm) { u32 pgd_offs = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200; u64 vm_vinst = chan->ramin->vinst + pgd_offs; u32 vm_pinst = chan->ramin->pinst; @@ -744,7 +777,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, if (ret) return ret; - nouveau_vm_ref(dev_priv->chan_vm, &chan->vm, chan->vm_pd); + nouveau_vm_ref(vm, &chan->vm, chan->vm_pd); } /* RAMHT */ @@ -764,11 +797,11 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, return ret; /* dma objects for display sync channel semaphore blocks */ - for (i = 0; i < 2; i++) { + for (i = 0; i < dev->mode_config.num_crtc; i++) { struct nouveau_gpuobj *sem = NULL; struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i]; - u64 offset = dispc->sem.bo->bo.mem.start << PAGE_SHIFT; + u64 offset = dispc->sem.bo->bo.offset; ret = nouveau_gpuobj_dma_new(chan, 0x3d, offset, 0xfff, NV_MEM_ACCESS_RW, @@ -841,10 +874,19 @@ void nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + int i; NV_DEBUG(dev, "ch%d\n", chan->id); - nouveau_ramht_ref(NULL, &chan->ramht, chan); + if (dev_priv->card_type >= NV_50 && dev_priv->card_type <= NV_C0) { + struct nv50_display *disp = nv50_display(dev); + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nv50_display_crtc *dispc = &disp->crtc[i]; + nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]); + } + } nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd); nouveau_gpuobj_ref(NULL, &chan->vm_pd); @@ -909,7 +951,7 @@ int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data, if (init->handle == ~0) return -EINVAL; - chan = nouveau_channel_get(dev, file_priv, init->channel); + chan = nouveau_channel_get(file_priv, init->channel); if (IS_ERR(chan)) return PTR_ERR(chan); @@ -936,7 +978,7 @@ int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data, struct nouveau_channel *chan; int ret; - chan = nouveau_channel_get(dev, file_priv, objfree->channel); + chan = nouveau_channel_get(file_priv, objfree->channel); if (IS_ERR(chan)) return PTR_ERR(chan); diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index ef9dec0..33d03fb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -127,13 +127,57 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, entry += ramcfg * recordlen; if (entry[1] >= pm->memtimings.nr_timing) { - NV_WARN(dev, "timingset %d does not exist\n", entry[1]); + if (entry[1] != 0xff) + NV_WARN(dev, "timingset %d does not exist\n", entry[1]); return NULL; } return &pm->memtimings.timing[entry[1]]; } +static void +nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, + struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + u8 *vmap; + int id; + + id = perflvl->volt_min; + perflvl->volt_min = 0; + + /* boards using voltage table version <0x40 store the voltage + * level directly in the perflvl entry as a multiple of 10mV + */ + if (dev_priv->engine.pm.voltage.version < 0x40) { + perflvl->volt_min = id * 10000; + perflvl->volt_max = perflvl->volt_min; + return; + } + + /* on newer ones, the perflvl stores an index into yet another + * vbios table containing a min/max voltage value for the perflvl + */ + if (P->version != 2 || P->length < 34) { + NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n", + P->version, P->length); + return; + } + + vmap = ROMPTR(bios, P->data[32]); + if (!vmap) { + NV_DEBUG(dev, "volt map table pointer invalid\n"); + return; + } + + if (id < vmap[3]) { + vmap += vmap[1] + (vmap[2] * id); + perflvl->volt_min = ROM32(vmap[0]); + perflvl->volt_max = ROM32(vmap[4]); + } +} + void nouveau_perf_init(struct drm_device *dev) { @@ -141,6 +185,8 @@ nouveau_perf_init(struct drm_device *dev) struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nvbios *bios = &dev_priv->vbios; struct bit_entry P; + struct nouveau_pm_memtimings *memtimings = &pm->memtimings; + struct nouveau_pm_tbl_header mt_hdr; u8 version, headerlen, recordlen, entries; u8 *perf, *entry; int vid, i; @@ -188,6 +234,22 @@ nouveau_perf_init(struct drm_device *dev) } entry = perf + headerlen; + + /* For version 0x15, initialize memtiming table */ + if(version == 0x15) { + memtimings->timing = + kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL); + if (!memtimings->timing) { + NV_WARN(dev,"Could not allocate memtiming table\n"); + return; + } + + mt_hdr.entry_cnt = entries; + mt_hdr.entry_len = 14; + mt_hdr.version = version; + mt_hdr.header_len = 4; + } + for (i = 0; i < entries; i++) { struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; @@ -203,7 +265,8 @@ nouveau_perf_init(struct drm_device *dev) case 0x13: case 0x15: perflvl->fanspeed = entry[55]; - perflvl->voltage = (recordlen > 56) ? entry[56] : 0; + if (recordlen > 56) + perflvl->volt_min = entry[56]; perflvl->core = ROM32(entry[1]) * 10; perflvl->memory = ROM32(entry[5]) * 20; break; @@ -211,9 +274,10 @@ nouveau_perf_init(struct drm_device *dev) case 0x23: case 0x24: perflvl->fanspeed = entry[4]; - perflvl->voltage = entry[5]; - perflvl->core = ROM16(entry[6]) * 1000; - + perflvl->volt_min = entry[5]; + perflvl->shader = ROM16(entry[6]) * 1000; + perflvl->core = perflvl->shader; + perflvl->core += (signed char)entry[8] * 1000; if (dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) perflvl->memory = ROM16(entry[11]) * 1000; @@ -223,7 +287,7 @@ nouveau_perf_init(struct drm_device *dev) break; case 0x25: perflvl->fanspeed = entry[4]; - perflvl->voltage = entry[5]; + perflvl->volt_min = entry[5]; perflvl->core = ROM16(entry[6]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; @@ -232,7 +296,7 @@ nouveau_perf_init(struct drm_device *dev) perflvl->memscript = ROM16(entry[2]); case 0x35: perflvl->fanspeed = entry[6]; - perflvl->voltage = entry[7]; + perflvl->volt_min = entry[7]; perflvl->core = ROM16(entry[8]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; @@ -240,30 +304,34 @@ nouveau_perf_init(struct drm_device *dev) perflvl->unk05 = ROM16(entry[16]) * 1000; break; case 0x40: -#define subent(n) entry[perf[2] + ((n) * perf[3])] +#define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000 perflvl->fanspeed = 0; /*XXX*/ - perflvl->voltage = entry[2]; + perflvl->volt_min = entry[2]; if (dev_priv->card_type == NV_50) { - perflvl->core = ROM16(subent(0)) & 0xfff; - perflvl->shader = ROM16(subent(1)) & 0xfff; - perflvl->memory = ROM16(subent(2)) & 0xfff; + perflvl->core = subent(0); + perflvl->shader = subent(1); + perflvl->memory = subent(2); + perflvl->vdec = subent(3); + perflvl->unka0 = subent(4); } else { - perflvl->shader = ROM16(subent(3)) & 0xfff; + perflvl->hub06 = subent(0); + perflvl->hub01 = subent(1); + perflvl->copy = subent(2); + perflvl->shader = subent(3); + perflvl->rop = subent(4); + perflvl->memory = subent(5); + perflvl->vdec = subent(6); + perflvl->daemon = subent(10); + perflvl->hub07 = subent(11); perflvl->core = perflvl->shader / 2; - perflvl->unk0a = ROM16(subent(4)) & 0xfff; - perflvl->memory = ROM16(subent(5)) & 0xfff; } - - perflvl->core *= 1000; - perflvl->shader *= 1000; - perflvl->memory *= 1000; - perflvl->unk0a *= 1000; break; } /* make sure vid is valid */ - if (pm->voltage.supported && perflvl->voltage) { - vid = nouveau_volt_vid_lookup(dev, perflvl->voltage); + nouveau_perf_voltage(dev, &P, perflvl); + if (pm->voltage.supported && perflvl->volt_min) { + vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min); if (vid < 0) { NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i); entry += recordlen; @@ -272,7 +340,11 @@ nouveau_perf_init(struct drm_device *dev) } /* get the corresponding memory timings */ - if (version > 0x15) { + if (version == 0x15) { + memtimings->timing[i].id = i; + nv30_mem_timing_entry(dev,&mt_hdr,(struct nouveau_pm_tbl_entry*) &entry[41],0,&memtimings->timing[i]); + perflvl->timing = &memtimings->timing[i]; + } else if (version > 0x15) { /* last 3 args are for < 0x40, ignored for >= 0x40 */ perflvl->timing = nouveau_perf_timing(dev, &P, diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index da8d994..a539fd2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -64,18 +64,26 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (perflvl == pm->cur) return 0; - if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) { - ret = pm->voltage_set(dev, perflvl->voltage); + if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) { + ret = pm->voltage_set(dev, perflvl->volt_min); if (ret) { NV_ERROR(dev, "voltage_set %d failed: %d\n", - perflvl->voltage, ret); + perflvl->volt_min, ret); } } - nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core); - nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader); - nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); - nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05); + if (pm->clocks_pre) { + void *state = pm->clocks_pre(dev, perflvl); + if (IS_ERR(state)) + return PTR_ERR(state); + pm->clocks_set(dev, state); + } else + if (pm->clock_set) { + nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core); + nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader); + nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); + nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05); + } pm->cur = perflvl; return 0; @@ -92,9 +100,6 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) if (nouveau_perflvl_wr != 7777) return -EPERM; - if (!pm->clock_set) - return -EINVAL; - if (!strncmp(profile, "boot", 4)) perflvl = &pm->boot; else { @@ -123,31 +128,37 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) struct nouveau_pm_engine *pm = &dev_priv->engine.pm; int ret; - if (!pm->clock_get) - return -EINVAL; - memset(perflvl, 0, sizeof(*perflvl)); - ret = pm->clock_get(dev, PLL_CORE); - if (ret > 0) - perflvl->core = ret; + if (pm->clocks_get) { + ret = pm->clocks_get(dev, perflvl); + if (ret) + return ret; + } else + if (pm->clock_get) { + ret = pm->clock_get(dev, PLL_CORE); + if (ret > 0) + perflvl->core = ret; - ret = pm->clock_get(dev, PLL_MEMORY); - if (ret > 0) - perflvl->memory = ret; + ret = pm->clock_get(dev, PLL_MEMORY); + if (ret > 0) + perflvl->memory = ret; - ret = pm->clock_get(dev, PLL_SHADER); - if (ret > 0) - perflvl->shader = ret; + ret = pm->clock_get(dev, PLL_SHADER); + if (ret > 0) + perflvl->shader = ret; - ret = pm->clock_get(dev, PLL_UNK05); - if (ret > 0) - perflvl->unk05 = ret; + ret = pm->clock_get(dev, PLL_UNK05); + if (ret > 0) + perflvl->unk05 = ret; + } if (pm->voltage.supported && pm->voltage_get) { ret = pm->voltage_get(dev); - if (ret > 0) - perflvl->voltage = ret; + if (ret > 0) { + perflvl->volt_min = ret; + perflvl->volt_max = ret; + } } return 0; @@ -156,7 +167,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) static void nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) { - char c[16], s[16], v[16], f[16], t[16]; + char c[16], s[16], v[32], f[16], t[16], m[16]; c[0] = '\0'; if (perflvl->core) @@ -166,9 +177,19 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) if (perflvl->shader) snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000); + m[0] = '\0'; + if (perflvl->memory) + snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000); + v[0] = '\0'; - if (perflvl->voltage) - snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10); + if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) { + snprintf(v, sizeof(v), " voltage %dmV-%dmV", + perflvl->volt_min / 1000, perflvl->volt_max / 1000); + } else + if (perflvl->volt_min) { + snprintf(v, sizeof(v), " voltage %dmV", + perflvl->volt_min / 1000); + } f[0] = '\0'; if (perflvl->fanspeed) @@ -178,8 +199,7 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) if (perflvl->timing) snprintf(t, sizeof(t), " timing %d", perflvl->timing->id); - snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000, - c, s, v, f, t); + snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f); } static ssize_t @@ -190,7 +210,7 @@ nouveau_pm_get_perflvl_info(struct device *d, char *ptr = buf; int len = PAGE_SIZE; - snprintf(ptr, len, "%d: ", perflvl->id); + snprintf(ptr, len, "%d:", perflvl->id); ptr += strlen(buf); len -= strlen(buf); @@ -211,9 +231,9 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) if (!pm->cur) snprintf(ptr, len, "setting: boot\n"); else if (pm->cur == &pm->boot) - snprintf(ptr, len, "setting: boot\nc: "); + snprintf(ptr, len, "setting: boot\nc:"); else - snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id); + snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id); ptr += strlen(buf); len -= strlen(buf); @@ -292,7 +312,7 @@ nouveau_sysfs_fini(struct drm_device *dev) } } -#ifdef CONFIG_HWMON +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) static ssize_t nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) { @@ -409,7 +429,7 @@ static const struct attribute_group hwmon_attrgroup = { static int nouveau_hwmon_init(struct drm_device *dev) { -#ifdef CONFIG_HWMON +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct device *hwmon_dev; @@ -442,7 +462,7 @@ nouveau_hwmon_init(struct drm_device *dev) static void nouveau_hwmon_fini(struct drm_device *dev) { -#ifdef CONFIG_HWMON +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; @@ -488,7 +508,7 @@ nouveau_pm_init(struct drm_device *dev) NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); for (i = 0; i < pm->nr_perflvl; i++) { nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); - NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); + NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info); } /* determine current ("boot") performance level */ @@ -498,7 +518,7 @@ nouveau_pm_init(struct drm_device *dev) pm->cur = &pm->boot; nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); - NV_INFO(dev, "c: %s", info); + NV_INFO(dev, "c:%s", info); } /* switch performance levels now if requested */ diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 4a9838dd..8ac02cd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -52,6 +52,11 @@ void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, u32 id, int khz); void nv04_pm_clock_set(struct drm_device *, void *); +/* nv40_pm.c */ +int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +void nv40_pm_clocks_set(struct drm_device *, void *); + /* nv50_pm.c */ int nv50_pm_clock_get(struct drm_device *, u32 id); void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, @@ -59,10 +64,12 @@ void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, void nv50_pm_clock_set(struct drm_device *, void *); /* nva3_pm.c */ -int nva3_pm_clock_get(struct drm_device *, u32 id); -void *nva3_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, - u32 id, int khz); -void nva3_pm_clock_set(struct drm_device *, void *); +int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +void nva3_pm_clocks_set(struct drm_device *, void *); + +/* nvc0_pm.c */ +int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); /* nouveau_temp.c */ void nouveau_temp_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index f18cdfc..43a96b9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -826,9 +826,12 @@ #define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000 #define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000 #define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000 -#define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084 -#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000 -#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff +#define NV50_PDISP_SOR_PWM_DIV(i) (0x0061c080 + (i) * 0x800) +#define NV50_PDISP_SOR_PWM_CTL(i) (0x0061c084 + (i) * 0x800) +#define NV50_PDISP_SOR_PWM_CTL_NEW 0x80000000 +#define NVA3_PDISP_SOR_PWM_CTL_UNK 0x40000000 +#define NV50_PDISP_SOR_PWM_CTL_VAL 0x000007ff +#define NVA3_PDISP_SOR_PWM_CTL_VAL 0x00ffffff #define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_CTRL_ENABLED 0x00000001 #define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000 @@ -843,7 +846,7 @@ #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 #define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) -#define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index ca6028f..c8a463b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -12,8 +12,8 @@ struct nouveau_sgdma_be { struct drm_device *dev; dma_addr_t *pages; - bool *ttm_alloced; unsigned nr_pages; + bool unmap_pages; u64 offset; bool bound; @@ -26,43 +26,28 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages, { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; struct drm_device *dev = nvbe->dev; + int i; NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages); - if (nvbe->pages) - return -EINVAL; - - nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL); - if (!nvbe->pages) - return -ENOMEM; + nvbe->pages = dma_addrs; + nvbe->nr_pages = num_pages; + nvbe->unmap_pages = true; - nvbe->ttm_alloced = kmalloc(sizeof(bool) * num_pages, GFP_KERNEL); - if (!nvbe->ttm_alloced) { - kfree(nvbe->pages); - nvbe->pages = NULL; - return -ENOMEM; + /* this code path isn't called and is incorrect anyways */ + if (0) { /* dma_addrs[0] != DMA_ERROR_CODE) { */ + nvbe->unmap_pages = false; + return 0; } - nvbe->nr_pages = 0; - while (num_pages--) { - /* this code path isn't called and is incorrect anyways */ - if (0) { /*dma_addrs[nvbe->nr_pages] != DMA_ERROR_CODE)*/ - nvbe->pages[nvbe->nr_pages] = - dma_addrs[nvbe->nr_pages]; - nvbe->ttm_alloced[nvbe->nr_pages] = true; - } else { - nvbe->pages[nvbe->nr_pages] = - pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->pdev, - nvbe->pages[nvbe->nr_pages])) { - be->func->clear(be); - return -EFAULT; - } - nvbe->ttm_alloced[nvbe->nr_pages] = false; + for (i = 0; i < num_pages; i++) { + nvbe->pages[i] = pci_map_page(dev->pdev, pages[i], 0, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(dev->pdev, nvbe->pages[i])) { + nvbe->nr_pages = --i; + be->func->clear(be); + return -EFAULT; } - - nvbe->nr_pages++; } return 0; @@ -72,26 +57,20 @@ static void nouveau_sgdma_clear(struct ttm_backend *be) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; - struct drm_device *dev; - - if (nvbe && nvbe->pages) { - dev = nvbe->dev; - NV_DEBUG(dev, "\n"); + struct drm_device *dev = nvbe->dev; - if (nvbe->bound) - be->func->unbind(be); + if (nvbe->bound) + be->func->unbind(be); + if (nvbe->unmap_pages) { while (nvbe->nr_pages--) { - if (!nvbe->ttm_alloced[nvbe->nr_pages]) - pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages], + pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages], PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); } - kfree(nvbe->pages); - kfree(nvbe->ttm_alloced); - nvbe->pages = NULL; - nvbe->ttm_alloced = NULL; - nvbe->nr_pages = 0; + nvbe->unmap_pages = false; } + + nvbe->pages = NULL; } static void @@ -129,7 +108,7 @@ nv04_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++, pte++) { nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 3); - dma_offset += NV_CTXDMA_PAGE_SIZE; + offset_l += NV_CTXDMA_PAGE_SIZE; } } @@ -432,7 +411,7 @@ nouveau_sgdma_init(struct drm_device *dev) u32 aper_size, align; int ret; - if (dev_priv->card_type >= NV_40 && drm_pci_device_is_pcie(dev)) + if (dev_priv->card_type >= NV_40 && pci_is_pcie(dev->pdev)) aper_size = 512 * 1024 * 1024; else aper_size = 64 * 1024 * 1024; @@ -461,7 +440,7 @@ nouveau_sgdma_init(struct drm_device *dev) dev_priv->gart_info.type = NOUVEAU_GART_HW; dev_priv->gart_info.func = &nv50_sgdma_backend; } else - if (0 && drm_pci_device_is_pcie(dev) && + if (0 && pci_is_pcie(dev->pdev) && dev_priv->chipset > 0x40 && dev_priv->chipset != 0x45) { if (nv44_graph_class(dev)) { dev_priv->gart_info.func = &nv44_sgdma_backend; diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 731acea..01adcfb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -46,6 +46,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_engine *engine = &dev_priv->engine; + u32 pclass = dev->pdev->class >> 8; switch (dev_priv->chipset & 0xf0) { case 0x00: @@ -91,6 +92,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_pre = nv04_pm_clock_pre; engine->pm.clock_set = nv04_pm_clock_set; engine->vram.init = nouveau_mem_detect; + engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; case 0x10: @@ -139,6 +141,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_pre = nv04_pm_clock_pre; engine->pm.clock_set = nv04_pm_clock_set; engine->vram.init = nouveau_mem_detect; + engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; case 0x20: @@ -187,6 +190,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_pre = nv04_pm_clock_pre; engine->pm.clock_set = nv04_pm_clock_set; engine->vram.init = nouveau_mem_detect; + engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; case 0x30: @@ -237,6 +241,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->vram.init = nouveau_mem_detect; + engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; case 0x40: @@ -282,19 +287,20 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->gpio.get = nv10_gpio_get; engine->gpio.set = nv10_gpio_set; engine->gpio.irq_enable = NULL; - engine->pm.clock_get = nv04_pm_clock_get; - engine->pm.clock_pre = nv04_pm_clock_pre; - engine->pm.clock_set = nv04_pm_clock_set; + engine->pm.clocks_get = nv40_pm_clocks_get; + engine->pm.clocks_pre = nv40_pm_clocks_pre; + engine->pm.clocks_set = nv40_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->pm.temp_get = nv40_temp_get; engine->vram.init = nouveau_mem_detect; + engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; case 0x50: case 0x80: /* gotta love NVIDIA's consistency.. */ case 0x90: - case 0xA0: + case 0xa0: engine->instmem.init = nv50_instmem_init; engine->instmem.takedown = nv50_instmem_takedown; engine->instmem.suspend = nv50_instmem_suspend; @@ -354,9 +360,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_set = nv50_pm_clock_set; break; default: - engine->pm.clock_get = nva3_pm_clock_get; - engine->pm.clock_pre = nva3_pm_clock_pre; - engine->pm.clock_set = nva3_pm_clock_set; + engine->pm.clocks_get = nva3_pm_clocks_get; + engine->pm.clocks_pre = nva3_pm_clocks_pre; + engine->pm.clocks_set = nva3_pm_clocks_set; break; } engine->pm.voltage_get = nouveau_voltage_gpio_get; @@ -366,11 +372,12 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) else engine->pm.temp_get = nv40_temp_get; engine->vram.init = nv50_vram_init; + engine->vram.takedown = nv50_vram_fini; engine->vram.get = nv50_vram_new; engine->vram.put = nv50_vram_del; engine->vram.flags_valid = nv50_vram_flags_valid; break; - case 0xC0: + case 0xc0: engine->instmem.init = nvc0_instmem_init; engine->instmem.takedown = nvc0_instmem_takedown; engine->instmem.suspend = nvc0_instmem_suspend; @@ -411,15 +418,79 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->gpio.irq_unregister = nv50_gpio_irq_unregister; engine->gpio.irq_enable = nv50_gpio_irq_enable; engine->vram.init = nvc0_vram_init; + engine->vram.takedown = nv50_vram_fini; engine->vram.get = nvc0_vram_new; engine->vram.put = nv50_vram_del; engine->vram.flags_valid = nvc0_vram_flags_valid; + engine->pm.temp_get = nv84_temp_get; + engine->pm.clocks_get = nvc0_pm_clocks_get; + engine->pm.voltage_get = nouveau_voltage_gpio_get; + engine->pm.voltage_set = nouveau_voltage_gpio_set; + break; + case 0xd0: + engine->instmem.init = nvc0_instmem_init; + engine->instmem.takedown = nvc0_instmem_takedown; + engine->instmem.suspend = nvc0_instmem_suspend; + engine->instmem.resume = nvc0_instmem_resume; + engine->instmem.get = nv50_instmem_get; + engine->instmem.put = nv50_instmem_put; + engine->instmem.map = nv50_instmem_map; + engine->instmem.unmap = nv50_instmem_unmap; + engine->instmem.flush = nv84_instmem_flush; + engine->mc.init = nv50_mc_init; + engine->mc.takedown = nv50_mc_takedown; + engine->timer.init = nv04_timer_init; + engine->timer.read = nv04_timer_read; + engine->timer.takedown = nv04_timer_takedown; + engine->fb.init = nvc0_fb_init; + engine->fb.takedown = nvc0_fb_takedown; + engine->fifo.channels = 128; + engine->fifo.init = nvc0_fifo_init; + engine->fifo.takedown = nvc0_fifo_takedown; + engine->fifo.disable = nvc0_fifo_disable; + engine->fifo.enable = nvc0_fifo_enable; + engine->fifo.reassign = nvc0_fifo_reassign; + engine->fifo.channel_id = nvc0_fifo_channel_id; + engine->fifo.create_context = nvc0_fifo_create_context; + engine->fifo.destroy_context = nvc0_fifo_destroy_context; + engine->fifo.load_context = nvc0_fifo_load_context; + engine->fifo.unload_context = nvc0_fifo_unload_context; + engine->display.early_init = nouveau_stub_init; + engine->display.late_takedown = nouveau_stub_takedown; + engine->display.create = nvd0_display_create; + engine->display.init = nvd0_display_init; + engine->display.destroy = nvd0_display_destroy; + engine->gpio.init = nv50_gpio_init; + engine->gpio.takedown = nouveau_stub_takedown; + engine->gpio.get = nvd0_gpio_get; + engine->gpio.set = nvd0_gpio_set; + engine->gpio.irq_register = nv50_gpio_irq_register; + engine->gpio.irq_unregister = nv50_gpio_irq_unregister; + engine->gpio.irq_enable = nv50_gpio_irq_enable; + engine->vram.init = nvc0_vram_init; + engine->vram.takedown = nv50_vram_fini; + engine->vram.get = nvc0_vram_new; + engine->vram.put = nv50_vram_del; + engine->vram.flags_valid = nvc0_vram_flags_valid; + engine->pm.clocks_get = nvc0_pm_clocks_get; + engine->pm.voltage_get = nouveau_voltage_gpio_get; + engine->pm.voltage_set = nouveau_voltage_gpio_set; break; default: NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); return 1; } + /* headless mode */ + if (nouveau_modeset == 2 || + (nouveau_modeset < 0 && pclass != PCI_CLASS_DISPLAY_VGA)) { + engine->display.early_init = nouveau_stub_init; + engine->display.late_takedown = nouveau_stub_takedown; + engine->display.create = nouveau_stub_init; + engine->display.init = nouveau_stub_init; + engine->display.destroy = nouveau_stub_takedown; + } + return 0; } @@ -441,21 +512,6 @@ nouveau_vga_set_decode(void *priv, bool state) return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; } -static int -nouveau_card_init_channel(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int ret; - - ret = nouveau_channel_alloc(dev, &dev_priv->channel, - (struct drm_file *)-2, NvDmaFB, NvDmaTT); - if (ret) - return ret; - - mutex_unlock(&dev_priv->channel->mutex); - return 0; -} - static void nouveau_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) { @@ -525,9 +581,17 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_display_early; + /* workaround an odd issue on nvc1 by disabling the device's + * nosnoop capability. hopefully won't cause issues until a + * better fix is found - assuming there is one... + */ + if (dev_priv->chipset == 0xc1) { + nv_mask(dev, 0x00088080, 0x00000800, 0x00000000); + } + nouveau_pm_init(dev); - ret = nouveau_mem_vram_init(dev); + ret = engine->vram.init(dev); if (ret) goto out_bios; @@ -539,10 +603,14 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_gpuobj; - ret = nouveau_mem_gart_init(dev); + ret = nouveau_mem_vram_init(dev); if (ret) goto out_instmem; + ret = nouveau_mem_gart_init(dev); + if (ret) + goto out_ttmvram; + /* PMC */ ret = engine->mc.init(dev); if (ret) @@ -563,7 +631,7 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_timer; - if (!nouveau_noaccel) { + if (!dev_priv->noaccel) { switch (dev_priv->card_type) { case NV_04: nv04_graph_create(dev); @@ -618,8 +686,11 @@ nouveau_card_init(struct drm_device *dev) break; } - if (dev_priv->card_type == NV_40) - nv40_mpeg_create(dev); + if (dev_priv->card_type == NV_40 || + dev_priv->chipset == 0x31 || + dev_priv->chipset == 0x34 || + dev_priv->chipset == 0x36) + nv31_mpeg_create(dev); else if (dev_priv->card_type == NV_50 && (dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0)) @@ -639,50 +710,78 @@ nouveau_card_init(struct drm_device *dev) goto out_engine; } - ret = engine->display.create(dev); + ret = nouveau_irq_init(dev); if (ret) goto out_fifo; - ret = drm_vblank_init(dev, nv_two_heads(dev) ? 2 : 1); - if (ret) - goto out_vblank; + /* initialise general modesetting */ + drm_mode_config_init(dev); + drm_mode_create_scaling_mode_property(dev); + drm_mode_create_dithering_property(dev); + dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; + dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + if (dev_priv->card_type < NV_10) { + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + } else + if (dev_priv->card_type < NV_50) { + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + } else { + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; + } - ret = nouveau_irq_init(dev); + ret = engine->display.create(dev); if (ret) - goto out_vblank; + goto out_irq; - /* what about PVIDEO/PCRTC/PRAMDAC etc? */ + nouveau_backlight_init(dev); if (dev_priv->eng[NVOBJ_ENGINE_GR]) { ret = nouveau_fence_init(dev); if (ret) - goto out_irq; + goto out_disp; - ret = nouveau_card_init_channel(dev); + ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL, + NvDmaFB, NvDmaTT); if (ret) goto out_fence; + + mutex_unlock(&dev_priv->channel->mutex); + } + + if (dev->mode_config.num_crtc) { + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret) + goto out_chan; + + nouveau_fbcon_init(dev); + drm_kms_helper_poll_init(dev); } - nouveau_fbcon_init(dev); - drm_kms_helper_poll_init(dev); return 0; +out_chan: + nouveau_channel_put_unlocked(&dev_priv->channel); out_fence: nouveau_fence_fini(dev); +out_disp: + nouveau_backlight_exit(dev); + engine->display.destroy(dev); out_irq: nouveau_irq_fini(dev); -out_vblank: - drm_vblank_cleanup(dev); - engine->display.destroy(dev); out_fifo: - if (!nouveau_noaccel) + if (!dev_priv->noaccel) engine->fifo.takedown(dev); out_engine: - if (!nouveau_noaccel) { + if (!dev_priv->noaccel) { for (e = e - 1; e >= 0; e--) { if (!dev_priv->eng[e]) continue; - dev_priv->eng[e]->fini(dev, e); + dev_priv->eng[e]->fini(dev, e, false); dev_priv->eng[e]->destroy(dev,e ); } } @@ -696,12 +795,14 @@ out_mc: engine->mc.takedown(dev); out_gart: nouveau_mem_gart_fini(dev); +out_ttmvram: + nouveau_mem_vram_fini(dev); out_instmem: engine->instmem.takedown(dev); out_gpuobj: nouveau_gpuobj_takedown(dev); out_vram: - nouveau_mem_vram_fini(dev); + engine->vram.takedown(dev); out_bios: nouveau_pm_fini(dev); nouveau_bios_takedown(dev); @@ -718,16 +819,26 @@ static void nouveau_card_takedown(struct drm_device *dev) struct nouveau_engine *engine = &dev_priv->engine; int e; + if (dev->mode_config.num_crtc) { + drm_kms_helper_poll_fini(dev); + nouveau_fbcon_fini(dev); + drm_vblank_cleanup(dev); + } + if (dev_priv->channel) { - nouveau_fence_fini(dev); nouveau_channel_put_unlocked(&dev_priv->channel); + nouveau_fence_fini(dev); } - if (!nouveau_noaccel) { + nouveau_backlight_exit(dev); + engine->display.destroy(dev); + drm_mode_config_cleanup(dev); + + if (!dev_priv->noaccel) { engine->fifo.takedown(dev); for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) { if (dev_priv->eng[e]) { - dev_priv->eng[e]->fini(dev, e); + dev_priv->eng[e]->fini(dev, e, false); dev_priv->eng[e]->destroy(dev,e ); } } @@ -748,13 +859,13 @@ static void nouveau_card_takedown(struct drm_device *dev) ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT); mutex_unlock(&dev->struct_mutex); nouveau_mem_gart_fini(dev); + nouveau_mem_vram_fini(dev); engine->instmem.takedown(dev); nouveau_gpuobj_takedown(dev); - nouveau_mem_vram_fini(dev); + engine->vram.takedown(dev); nouveau_irq_fini(dev); - drm_vblank_cleanup(dev); nouveau_pm_fini(dev); nouveau_bios_takedown(dev); @@ -762,6 +873,41 @@ static void nouveau_card_takedown(struct drm_device *dev) vga_client_register(dev->pdev, NULL, NULL, NULL); } +int +nouveau_open(struct drm_device *dev, struct drm_file *file_priv) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fpriv *fpriv; + int ret; + + fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL); + if (unlikely(!fpriv)) + return -ENOMEM; + + spin_lock_init(&fpriv->lock); + INIT_LIST_HEAD(&fpriv->channels); + + if (dev_priv->card_type == NV_50) { + ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0020000000ULL, + &fpriv->vm); + if (ret) { + kfree(fpriv); + return ret; + } + } else + if (dev_priv->card_type >= NV_C0) { + ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL, + &fpriv->vm); + if (ret) { + kfree(fpriv); + return ret; + } + } + + file_priv->driver_priv = fpriv; + return 0; +} + /* here a client dies, release the stuff that was allocated for its * file_priv */ void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv) @@ -769,6 +915,14 @@ void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv) nouveau_channel_cleanup(dev, file_priv); } +void +nouveau_postclose(struct drm_device *dev, struct drm_file *file_priv) +{ + struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); + nouveau_vm_ref(NULL, &fpriv->vm, NULL); + kfree(fpriv); +} + /* first module load, setup the mmio/fb mapping */ /* KMS: we need mmio at load time, not when the first drm client opens. */ int nouveau_firstopen(struct drm_device *dev) @@ -844,7 +998,7 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev) int nouveau_load(struct drm_device *dev, unsigned long flags) { struct drm_nouveau_private *dev_priv; - uint32_t reg0; + uint32_t reg0, strap; resource_size_t mmio_start_offs; int ret; @@ -888,13 +1042,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) /* Time to determine the card architecture */ reg0 = nv_rd32(dev, NV03_PMC_BOOT_0); - dev_priv->stepping = 0; /* XXX: add stepping for pre-NV10? */ /* We're dealing with >=NV10 */ if ((reg0 & 0x0f000000) > 0) { /* Bit 27-20 contain the architecture in hex */ dev_priv->chipset = (reg0 & 0xff00000) >> 20; - dev_priv->stepping = (reg0 & 0xff); /* NV04 or NV05 */ } else if ((reg0 & 0xff00fff0) == 0x20004000) { if (reg0 & 0x00f00000) @@ -924,6 +1076,9 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) case 0xc0: dev_priv->card_type = NV_C0; break; + case 0xd0: + dev_priv->card_type = NV_D0; + break; default: NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0); ret = -EINVAL; @@ -933,6 +1088,43 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", dev_priv->card_type, reg0); + /* determine frequency of timing crystal */ + strap = nv_rd32(dev, 0x101000); + if ( dev_priv->chipset < 0x17 || + (dev_priv->chipset >= 0x20 && dev_priv->chipset <= 0x25)) + strap &= 0x00000040; + else + strap &= 0x00400040; + + switch (strap) { + case 0x00000000: dev_priv->crystal = 13500; break; + case 0x00000040: dev_priv->crystal = 14318; break; + case 0x00400000: dev_priv->crystal = 27000; break; + case 0x00400040: dev_priv->crystal = 25000; break; + } + + NV_DEBUG(dev, "crystal freq: %dKHz\n", dev_priv->crystal); + + /* Determine whether we'll attempt acceleration or not, some + * cards are disabled by default here due to them being known + * non-functional, or never been tested due to lack of hw. + */ + dev_priv->noaccel = !!nouveau_noaccel; + if (nouveau_noaccel == -1) { + switch (dev_priv->chipset) { +#if 0 + case 0xXX: /* known broken */ + NV_INFO(dev, "acceleration disabled by default, pass " + "noaccel=0 to force enable\n"); + dev_priv->noaccel = true; + break; +#endif + default: + dev_priv->noaccel = false; + break; + } + } + ret = nouveau_remove_conflicting_drivers(dev); if (ret) goto err_mmio; @@ -948,7 +1140,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) ioremap(pci_resource_start(dev->pdev, ramin_bar), dev_priv->ramin_size); if (!dev_priv->ramin) { - NV_ERROR(dev, "Failed to PRAMIN BAR"); + NV_ERROR(dev, "Failed to map PRAMIN BAR\n"); ret = -ENOMEM; goto err_mmio; } @@ -997,11 +1189,7 @@ void nouveau_lastclose(struct drm_device *dev) int nouveau_unload(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_engine *engine = &dev_priv->engine; - drm_kms_helper_poll_fini(dev); - nouveau_fbcon_fini(dev); - engine->display.destroy(dev); nouveau_card_takedown(dev); iounmap(dev_priv->mmio); @@ -1031,7 +1219,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data, case NOUVEAU_GETPARAM_BUS_TYPE: if (drm_pci_device_is_agp(dev)) getparam->value = NV_AGP; - else if (drm_pci_device_is_pcie(dev)) + else if (pci_is_pcie(dev->pdev)) getparam->value = NV_PCIE; else getparam->value = NV_PCI; @@ -1052,7 +1240,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data, getparam->value = 1; break; case NOUVEAU_GETPARAM_HAS_PAGEFLIP: - getparam->value = 1; + getparam->value = dev_priv->card_type < NV_D0; break; case NOUVEAU_GETPARAM_GRAPH_UNITS: /* NV40 and NV50 versions are quite different, but register @@ -1120,6 +1308,23 @@ nouveau_wait_ne(struct drm_device *dev, uint64_t timeout, return false; } +/* Wait until cond(data) == true, up until timeout has hit */ +bool +nouveau_wait_cb(struct drm_device *dev, u64 timeout, + bool (*cond)(void *), void *data) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + u64 start = ptimer->read(dev); + + do { + if (cond(data) == true) + return true; + } while (ptimer->read(dev) - start < timeout); + + return false; +} + /* Waits for PGRAPH to go completely idle */ bool nouveau_wait_for_idle(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 649b041..5a46446 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -22,6 +22,8 @@ * Authors: Martin Peres */ +#include <linux/module.h> + #include "drmP.h" #include "nouveau_drv.h" @@ -43,7 +45,7 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) /* Set the default sensor's contants */ sensor->offset_constant = 0; - sensor->offset_mult = 1; + sensor->offset_mult = 0; sensor->offset_div = 1; sensor->slope_mult = 1; sensor->slope_div = 1; @@ -99,6 +101,13 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) sensor->slope_mult = 431; sensor->slope_div = 10000; break; + + case 0x67: + sensor->offset_mult = -26149; + sensor->offset_div = 100; + sensor->slope_mult = 484; + sensor->slope_div = 10000; + break; } } @@ -109,7 +118,7 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) /* Read the entries from the table */ for (i = 0; i < entries; i++) { - u16 value = ROM16(temp[1]); + s16 value = ROM16(temp[1]); switch (temp[0]) { case 0x01: @@ -160,8 +169,8 @@ nv40_sensor_setup(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; - u32 offset = sensor->offset_mult / sensor->offset_div; - u32 sensor_calibration; + s32 offset = sensor->offset_mult / sensor->offset_div; + s32 sensor_calibration; /* set up the sensors */ sensor_calibration = 120 - offset - sensor->offset_constant; diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c index 519a6b4..ef0832b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.c +++ b/drivers/gpu/drm/nouveau/nouveau_vm.c @@ -172,9 +172,9 @@ nouveau_vm_unmap_pgt(struct nouveau_vm *vm, int big, u32 fpde, u32 lpde) vm->map_pgt(vpgd->obj, pde, vpgt->obj); } - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); nouveau_gpuobj_ref(NULL, &pgt); - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); } } @@ -191,18 +191,18 @@ nouveau_vm_map_pgt(struct nouveau_vm *vm, u32 pde, u32 type) pgt_size = (1 << (vm->pgt_bits + 12)) >> type; pgt_size *= 8; - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); ret = nouveau_gpuobj_new(vm->dev, NULL, pgt_size, 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &pgt); - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); if (unlikely(ret)) return ret; /* someone beat us to filling the PDE while we didn't have the lock */ if (unlikely(vpgt->refcount[big]++)) { - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); nouveau_gpuobj_ref(NULL, &pgt); - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); return 0; } @@ -223,10 +223,10 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift, u32 fpde, lpde, pde; int ret; - mutex_lock(&vm->mm->mutex); - ret = nouveau_mm_get(vm->mm, page_shift, msize, 0, align, &vma->node); + mutex_lock(&vm->mm.mutex); + ret = nouveau_mm_get(&vm->mm, page_shift, msize, 0, align, &vma->node); if (unlikely(ret != 0)) { - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); return ret; } @@ -245,13 +245,13 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift, if (ret) { if (pde != fpde) nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1); - nouveau_mm_put(vm->mm, vma->node); - mutex_unlock(&vm->mm->mutex); + nouveau_mm_put(&vm->mm, vma->node); + mutex_unlock(&vm->mm.mutex); vma->node = NULL; return ret; } } - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); vma->vm = vm; vma->offset = (u64)vma->node->offset << 12; @@ -270,11 +270,11 @@ nouveau_vm_put(struct nouveau_vma *vma) fpde = (vma->node->offset >> vm->pgt_bits); lpde = (vma->node->offset + vma->node->length - 1) >> vm->pgt_bits; - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde); - nouveau_mm_put(vm->mm, vma->node); + nouveau_mm_put(&vm->mm, vma->node); vma->node = NULL; - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); } int @@ -306,7 +306,7 @@ nouveau_vm_new(struct drm_device *dev, u64 offset, u64 length, u64 mm_offset, block = length; } else - if (dev_priv->card_type == NV_C0) { + if (dev_priv->card_type >= NV_C0) { vm->map_pgt = nvc0_vm_map_pgt; vm->map = nvc0_vm_map; vm->map_sg = nvc0_vm_map_sg; @@ -360,32 +360,35 @@ nouveau_vm_link(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd) nouveau_gpuobj_ref(pgd, &vpgd->obj); - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); for (i = vm->fpde; i <= vm->lpde; i++) vm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj); list_add(&vpgd->head, &vm->pgd_list); - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); return 0; } static void -nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd) +nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd) { struct nouveau_vm_pgd *vpgd, *tmp; + struct nouveau_gpuobj *pgd = NULL; - if (!pgd) + if (!mpgd) return; - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) { - if (vpgd->obj != pgd) - continue; - - list_del(&vpgd->head); - nouveau_gpuobj_ref(NULL, &vpgd->obj); - kfree(vpgd); + if (vpgd->obj == mpgd) { + pgd = vpgd->obj; + list_del(&vpgd->head); + kfree(vpgd); + break; + } } - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); + + nouveau_gpuobj_ref(NULL, &pgd); } static void @@ -396,8 +399,8 @@ nouveau_vm_del(struct nouveau_vm *vm) list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) { nouveau_vm_unlink(vm, vpgd->obj); } - WARN_ON(nouveau_mm_fini(&vm->mm) != 0); + nouveau_mm_fini(&vm->mm); kfree(vm->pgt); kfree(vm); } diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h index c48a9fc..6ce995f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.h +++ b/drivers/gpu/drm/nouveau/nouveau_vm.h @@ -41,6 +41,8 @@ struct nouveau_vm_pgd { }; struct nouveau_vma { + struct list_head head; + int refcount; struct nouveau_vm *vm; struct nouveau_mm_node *node; u64 offset; @@ -49,7 +51,7 @@ struct nouveau_vma { struct nouveau_vm { struct drm_device *dev; - struct nouveau_mm *mm; + struct nouveau_mm mm; int refcount; struct list_head pgd_list; diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index 75e87274..86d03e1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -27,7 +27,7 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" -static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a }; +static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 }; static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]); int @@ -170,6 +170,13 @@ nouveau_volt_init(struct drm_device *dev) */ vidshift = 2; break; + case 0x40: + headerlen = volt[1]; + recordlen = volt[2]; + entries = volt[3]; /* not a clue what the entries are for.. */ + vidmask = volt[11]; /* guess.. */ + vidshift = 0; + break; default: NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]); return; @@ -197,16 +204,37 @@ nouveau_volt_init(struct drm_device *dev) } /* parse vbios entries into common format */ - voltage->level = kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL); - if (!voltage->level) - return; + voltage->version = volt[0]; + if (voltage->version < 0x40) { + voltage->nr_level = entries; + voltage->level = + kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL); + if (!voltage->level) + return; - entry = volt + headerlen; - for (i = 0; i < entries; i++, entry += recordlen) { - voltage->level[i].voltage = entry[0]; - voltage->level[i].vid = entry[1] >> vidshift; + entry = volt + headerlen; + for (i = 0; i < entries; i++, entry += recordlen) { + voltage->level[i].voltage = entry[0] * 10000; + voltage->level[i].vid = entry[1] >> vidshift; + } + } else { + u32 volt_uv = ROM32(volt[4]); + s16 step_uv = ROM16(volt[8]); + u8 vid; + + voltage->nr_level = voltage->vid_mask + 1; + voltage->level = kcalloc(voltage->nr_level, + sizeof(*voltage->level), GFP_KERNEL); + if (!voltage->level) + return; + + for (vid = 0; vid <= voltage->vid_mask; vid++) { + voltage->level[vid].voltage = volt_uv; + voltage->level[vid].vid = vid; + volt_uv += step_uv; + } } - voltage->nr_level = entries; + voltage->supported = true; } diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index f1a3ae4..5e45398 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -781,11 +781,20 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; - struct drm_framebuffer *drm_fb = nv_crtc->base.fb; - struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); + struct drm_framebuffer *drm_fb; + struct nouveau_framebuffer *fb; int arb_burst, arb_lwm; int ret; + NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + + /* no fb bound */ + if (!atomic && !crtc->fb) { + NV_DEBUG_KMS(dev, "No FB bound\n"); + return 0; + } + + /* If atomic, we want to switch to the fb we were passed, so * now we update pointers to do that. (We don't pin; just * assume we're already pinned and update the base address.) @@ -794,6 +803,8 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, drm_fb = passed_fb; fb = nouveau_framebuffer(passed_fb); } else { + drm_fb = crtc->fb; + fb = nouveau_framebuffer(crtc->fb); /* If not atomic, we can go ahead and pin, and unpin the * old fb we were passed. */ @@ -1035,7 +1046,7 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num) drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs); drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); - ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, 0, 0x0000, &nv_crtc->cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 1715e14..6bd8518 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -126,27 +126,6 @@ nv04_display_create(struct drm_device *dev) nouveau_hw_save_vga_fonts(dev, 1); - drm_mode_config_init(dev); - drm_mode_create_scaling_mode_property(dev); - drm_mode_create_dithering_property(dev); - - dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; - - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - switch (dev_priv->card_type) { - case NV_04: - dev->mode_config.max_width = 2048; - dev->mode_config.max_height = 2048; - break; - default: - dev->mode_config.max_width = 4096; - dev->mode_config.max_height = 4096; - break; - } - - dev->mode_config.fb_base = dev_priv->fb_phys; - nv04_crtc_create(dev, 0); if (nv_two_heads(dev)) nv04_crtc_create(dev, 1); @@ -235,8 +214,6 @@ nv04_display_destroy(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) crtc->funcs->restore(crtc); - drm_mode_config_cleanup(dev); - nouveau_hw_save_vga_fonts(dev, 0); } diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c index 3626ee7..dbdea8e 100644 --- a/drivers/gpu/drm/nouveau/nv04_graph.c +++ b/drivers/gpu/drm/nouveau/nv04_graph.c @@ -450,13 +450,13 @@ nv04_graph_context_del(struct nouveau_channel *chan, int engine) unsigned long flags; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv04_graph_fifo_access(dev, false); + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); /* Unload the context if it's the currently active one */ if (nv04_graph_channel(dev) == chan) nv04_graph_unload_context(dev); - nv04_graph_fifo_access(dev, true); + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); /* Free the context resources */ @@ -538,24 +538,18 @@ nv04_graph_init(struct drm_device *dev, int engine) } static int -nv04_graph_fini(struct drm_device *dev, int engine) +nv04_graph_fini(struct drm_device *dev, int engine, bool suspend) { + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) { + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + return -EBUSY; + } nv04_graph_unload_context(dev); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000); return 0; } -void -nv04_graph_fifo_access(struct drm_device *dev, bool enabled) -{ - if (enabled) - nv_wr32(dev, NV04_PGRAPH_FIFO, - nv_rd32(dev, NV04_PGRAPH_FIFO) | 1); - else - nv_wr32(dev, NV04_PGRAPH_FIFO, - nv_rd32(dev, NV04_PGRAPH_FIFO) & ~1); -} - static int nv04_graph_mthd_set_ref(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c index b8611b9..c1248e0 100644 --- a/drivers/gpu/drm/nouveau/nv04_instmem.c +++ b/drivers/gpu/drm/nouveau/nv04_instmem.c @@ -28,6 +28,31 @@ int nv04_instmem_init(struct drm_device *dev) /* RAMIN always available */ dev_priv->ramin_available = true; + /* Reserve space at end of VRAM for PRAMIN */ + if (dev_priv->card_type >= NV_40) { + u32 vs = hweight8((nv_rd32(dev, 0x001540) & 0x0000ff00) >> 8); + u32 rsvd; + + /* estimate grctx size, the magics come from nv40_grctx.c */ + if (dev_priv->chipset == 0x40) rsvd = 0x6aa0 * vs; + else if (dev_priv->chipset < 0x43) rsvd = 0x4f00 * vs; + else if (nv44_graph_class(dev)) rsvd = 0x4980 * vs; + else rsvd = 0x4a40 * vs; + rsvd += 16 * 1024; + rsvd *= dev_priv->engine.fifo.channels; + + /* pciegart table */ + if (pci_is_pcie(dev->pdev)) + rsvd += 512 * 1024; + + /* object storage */ + rsvd += 512 * 1024; + + dev_priv->ramin_rsvd_vram = round_up(rsvd, 4096); + } else { + dev_priv->ramin_rsvd_vram = 512 * 1024; + } + /* Setup shared RAMHT */ ret = nouveau_gpuobj_new_fake(dev, 0x10000, ~0, 4096, NVOBJ_FLAG_ZERO_ALLOC, &ramht); @@ -112,7 +137,8 @@ nv04_instmem_resume(struct drm_device *dev) } int -nv04_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align) +nv04_instmem_get(struct nouveau_gpuobj *gpuobj, struct nouveau_channel *chan, + u32 size, u32 align) { struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private; struct drm_mm_node *ramin = NULL; diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c index eb1c70d..9ae92a8 100644 --- a/drivers/gpu/drm/nouveau/nv04_pm.c +++ b/drivers/gpu/drm/nouveau/nv04_pm.c @@ -68,6 +68,7 @@ void nv04_pm_clock_set(struct drm_device *dev, void *pre_state) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; struct nv04_pm_state *state = pre_state; u32 reg = state->pll.reg; @@ -85,6 +86,9 @@ nv04_pm_clock_set(struct drm_device *dev, void *pre_state) nv_mask(dev, 0x1002c0, 0, 1 << 8); } + if (reg == NV_PRAMDAC_NVPLL_COEFF) + ptimer->init(dev); + kfree(state); } diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c index 1d09ddd..263301b 100644 --- a/drivers/gpu/drm/nouveau/nv04_timer.c +++ b/drivers/gpu/drm/nouveau/nv04_timer.c @@ -6,43 +6,75 @@ int nv04_timer_init(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 m, n, d; + nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000); nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF); - /* Just use the pre-existing values when possible for now; these regs - * are not written in nv (driver writer missed a /4 on the address), and - * writing 8 and 3 to the correct regs breaks the timings on the LVDS - * hardware sequencing microcode. - * A correct solution (involving calculations with the GPU PLL) can - * be done when kernel modesetting lands - */ - if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) || - !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) { - nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008); - nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003); + /* aim for 31.25MHz, which gives us nanosecond timestamps */ + d = 1000000 / 32; + + /* determine base clock for timer source */ + if (dev_priv->chipset < 0x40) { + n = dev_priv->engine.pm.clock_get(dev, PLL_CORE); + } else + if (dev_priv->chipset == 0x40) { + /*XXX: figure this out */ + n = 0; + } else { + n = dev_priv->crystal; + m = 1; + while (n < (d * 2)) { + n += (n / m); + m++; + } + + nv_wr32(dev, 0x009220, m - 1); + } + + if (!n) { + NV_WARN(dev, "PTIMER: unknown input clock freq\n"); + if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) || + !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) { + nv_wr32(dev, NV04_PTIMER_NUMERATOR, 1); + nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 1); + } + return 0; + } + + /* reduce ratio to acceptable values */ + while (((n % 5) == 0) && ((d % 5) == 0)) { + n /= 5; + d /= 5; } + while (((n % 2) == 0) && ((d % 2) == 0)) { + n /= 2; + d /= 2; + } + + while (n > 0xffff || d > 0xffff) { + n >>= 1; + d >>= 1; + } + + nv_wr32(dev, NV04_PTIMER_NUMERATOR, n); + nv_wr32(dev, NV04_PTIMER_DENOMINATOR, d); return 0; } -uint64_t +u64 nv04_timer_read(struct drm_device *dev) { - uint32_t low; - /* From kmmio dumps on nv28 this looks like how the blob does this. - * It reads the high dword twice, before and after. - * The only explanation seems to be that the 64-bit timer counter - * advances between high and low dword reads and may corrupt the - * result. Not confirmed. - */ - uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); - uint32_t high1; + u32 hi, lo; + do { - high1 = high2; - low = nv_rd32(dev, NV04_PTIMER_TIME_0); - high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); - } while (high1 != high2); - return (((uint64_t)high2) << 32) | (uint64_t)low; + hi = nv_rd32(dev, NV04_PTIMER_TIME_1); + lo = nv_rd32(dev, NV04_PTIMER_TIME_0); + } while (hi != nv_rd32(dev, NV04_PTIMER_TIME_1)); + + return ((u64)hi << 32 | lo); } void diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c index 0930c6c..7255e4a 100644 --- a/drivers/gpu/drm/nouveau/nv10_graph.c +++ b/drivers/gpu/drm/nouveau/nv10_graph.c @@ -708,8 +708,8 @@ static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan, 0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c); nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst); nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000); - nv04_graph_fifo_access(dev, true); - nv04_graph_fifo_access(dev, false); + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); /* Restore the FIFO state */ for (i = 0; i < ARRAY_SIZE(fifo); i++) @@ -879,13 +879,13 @@ nv10_graph_context_del(struct nouveau_channel *chan, int engine) unsigned long flags; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv04_graph_fifo_access(dev, false); + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); /* Unload the context if it's the currently active one */ if (nv10_graph_channel(dev) == chan) nv10_graph_unload_context(dev); - nv04_graph_fifo_access(dev, true); + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); /* Free the context resources */ @@ -957,8 +957,13 @@ nv10_graph_init(struct drm_device *dev, int engine) } static int -nv10_graph_fini(struct drm_device *dev, int engine) +nv10_graph_fini(struct drm_device *dev, int engine, bool suspend) { + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) { + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + return -EBUSY; + } nv10_graph_unload_context(dev); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000); return 0; diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c index affc7d7..183e375 100644 --- a/drivers/gpu/drm/nouveau/nv20_graph.c +++ b/drivers/gpu/drm/nouveau/nv20_graph.c @@ -454,13 +454,13 @@ nv20_graph_context_del(struct nouveau_channel *chan, int engine) unsigned long flags; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv04_graph_fifo_access(dev, false); + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); /* Unload the context if it's the currently active one */ if (nv10_graph_channel(dev) == chan) nv20_graph_unload_context(dev); - nv04_graph_fifo_access(dev, true); + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); /* Free the context resources */ @@ -654,8 +654,13 @@ nv30_graph_init(struct drm_device *dev, int engine) } int -nv20_graph_fini(struct drm_device *dev, int engine) +nv20_graph_fini(struct drm_device *dev, int engine, bool suspend) { + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) { + nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + return -EBUSY; + } nv20_graph_unload_context(dev); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000); return 0; @@ -753,6 +758,7 @@ nv20_graph_create(struct drm_device *dev) break; default: NV_ERROR(dev, "PGRAPH: unknown chipset\n"); + kfree(pgraph); return 0; } } else { @@ -774,6 +780,7 @@ nv20_graph_create(struct drm_device *dev) break; default: NV_ERROR(dev, "PGRAPH: unknown chipset\n"); + kfree(pgraph); return 0; } } diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c index 5beb01b..ba14a93 100644 --- a/drivers/gpu/drm/nouveau/nv40_graph.c +++ b/drivers/gpu/drm/nouveau/nv40_graph.c @@ -35,89 +35,6 @@ struct nv40_graph_engine { u32 grctx_size; }; -static struct nouveau_channel * -nv40_graph_channel(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *grctx; - uint32_t inst; - int i; - - inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR); - if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED)) - return NULL; - inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4; - - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { - if (!dev_priv->channels.ptr[i]) - continue; - - grctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_GR]; - if (grctx && grctx->pinst == inst) - return dev_priv->channels.ptr[i]; - } - - return NULL; -} - -static int -nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save) -{ - uint32_t old_cp, tv = 1000, tmp; - int i; - - old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER); - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); - - tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310); - tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE : - NV40_PGRAPH_CTXCTL_0310_XFER_LOAD; - nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp); - - tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304); - tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX; - nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp); - - nouveau_wait_for_idle(dev); - - for (i = 0; i < tv; i++) { - if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0) - break; - } - - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp); - - if (i == tv) { - uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT); - NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save); - NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n", - ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT, - ucstat & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK); - NV_ERROR(dev, "0x40030C = 0x%08x\n", - nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C)); - return -EBUSY; - } - - return 0; -} - -static int -nv40_graph_unload_context(struct drm_device *dev) -{ - uint32_t inst; - int ret; - - inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR); - if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED)) - return 0; - inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE; - - ret = nv40_graph_transfer_context(dev, inst, 1); - - nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst); - return ret; -} - static int nv40_graph_context_new(struct nouveau_channel *chan, int engine) { @@ -163,16 +80,16 @@ nv40_graph_context_del(struct nouveau_channel *chan, int engine) struct nouveau_gpuobj *grctx = chan->engctx[engine]; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 inst = 0x01000000 | (grctx->pinst >> 4); unsigned long flags; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv04_graph_fifo_access(dev, false); - - /* Unload the context if it's the currently active one */ - if (nv40_graph_channel(dev) == chan) - nv40_graph_unload_context(dev); - - nv04_graph_fifo_access(dev, true); + nv_mask(dev, 0x400720, 0x00000000, 0x00000001); + if (nv_rd32(dev, 0x40032c) == inst) + nv_mask(dev, 0x40032c, 0x01000000, 0x00000000); + if (nv_rd32(dev, 0x400330) == inst) + nv_mask(dev, 0x400330, 0x01000000, 0x00000000); + nv_mask(dev, 0x400720, 0x00000001, 0x00000001); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); /* Free the context resources */ @@ -429,9 +346,20 @@ nv40_graph_init(struct drm_device *dev, int engine) } static int -nv40_graph_fini(struct drm_device *dev, int engine) +nv40_graph_fini(struct drm_device *dev, int engine, bool suspend) { - nv40_graph_unload_context(dev); + u32 inst = nv_rd32(dev, 0x40032c); + if (inst & 0x01000000) { + nv_wr32(dev, 0x400720, 0x00000000); + nv_wr32(dev, 0x400784, inst); + nv_mask(dev, 0x400310, 0x00000020, 0x00000020); + nv_mask(dev, 0x400304, 0x00000001, 0x00000001); + if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000)) { + u32 insn = nv_rd32(dev, 0x400308); + NV_ERROR(dev, "PGRAPH: ctxprog timeout 0x%08x\n", insn); + } + nv_mask(dev, 0x40032c, 0x01000000, 0x00000000); + } return 0; } diff --git a/drivers/gpu/drm/nouveau/nv40_mpeg.c b/drivers/gpu/drm/nouveau/nv40_mpeg.c deleted file mode 100644 index 6d2af29..0000000 --- a/drivers/gpu/drm/nouveau/nv40_mpeg.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2011 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: Ben Skeggs - */ - -#include "drmP.h" -#include "nouveau_drv.h" -#include "nouveau_ramht.h" - -struct nv40_mpeg_engine { - struct nouveau_exec_engine base; -}; - -static int -nv40_mpeg_context_new(struct nouveau_channel *chan, int engine) -{ - struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *ctx = NULL; - unsigned long flags; - int ret; - - NV_DEBUG(dev, "ch%d\n", chan->id); - - ret = nouveau_gpuobj_new(dev, NULL, 264 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &ctx); - if (ret) - return ret; - - nv_wo32(ctx, 0x78, 0x02001ec1); - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv_mask(dev, 0x002500, 0x00000001, 0x00000000); - if ((nv_rd32(dev, 0x003204) & 0x1f) == chan->id) - nv_wr32(dev, 0x00330c, ctx->pinst >> 4); - nv_wo32(chan->ramfc, 0x54, ctx->pinst >> 4); - nv_mask(dev, 0x002500, 0x00000001, 0x00000001); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - - chan->engctx[engine] = ctx; - return 0; -} - -static void -nv40_mpeg_context_del(struct nouveau_channel *chan, int engine) -{ - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nouveau_gpuobj *ctx = chan->engctx[engine]; - struct drm_device *dev = chan->dev; - unsigned long flags; - u32 inst = 0x80000000 | (ctx->pinst >> 4); - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); - if (nv_rd32(dev, 0x00b318) == inst) - nv_mask(dev, 0x00b318, 0x80000000, 0x00000000); - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - - nouveau_gpuobj_ref(NULL, &ctx); - chan->engctx[engine] = NULL; -} - -static int -nv40_mpeg_object_new(struct nouveau_channel *chan, int engine, - u32 handle, u16 class) -{ - struct drm_device *dev = chan->dev; - struct nouveau_gpuobj *obj = NULL; - int ret; - - ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &obj); - if (ret) - return ret; - obj->engine = 2; - obj->class = class; - - nv_wo32(obj, 0x00, class); - - ret = nouveau_ramht_insert(chan, handle, obj); - nouveau_gpuobj_ref(NULL, &obj); - return ret; -} - -static int -nv40_mpeg_init(struct drm_device *dev, int engine) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); - int i; - - /* VPE init */ - nv_mask(dev, 0x000200, 0x00000002, 0x00000000); - nv_mask(dev, 0x000200, 0x00000002, 0x00000002); - nv_wr32(dev, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ - nv_wr32(dev, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ - - for (i = 0; i < dev_priv->engine.fb.num_tiles; i++) - pmpeg->base.set_tile_region(dev, i); - - /* PMPEG init */ - nv_wr32(dev, 0x00b32c, 0x00000000); - nv_wr32(dev, 0x00b314, 0x00000100); - nv_wr32(dev, 0x00b220, 0x00000044); - nv_wr32(dev, 0x00b300, 0x02001ec1); - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); - - nv_wr32(dev, 0x00b100, 0xffffffff); - nv_wr32(dev, 0x00b140, 0xffffffff); - - if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) { - NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200)); - return -EBUSY; - } - - return 0; -} - -static int -nv40_mpeg_fini(struct drm_device *dev, int engine) -{ - /*XXX: context save? */ - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); - nv_wr32(dev, 0x00b140, 0x00000000); - return 0; -} - -static int -nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) -{ - struct drm_device *dev = chan->dev; - u32 inst = data << 4; - u32 dma0 = nv_ri32(dev, inst + 0); - u32 dma1 = nv_ri32(dev, inst + 4); - u32 dma2 = nv_ri32(dev, inst + 8); - u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); - u32 size = dma1 + 1; - - /* only allow linear DMA objects */ - if (!(dma0 & 0x00002000)) - return -EINVAL; - - if (mthd == 0x0190) { - /* DMA_CMD */ - nv_mask(dev, 0x00b300, 0x00030000, (dma0 & 0x00030000)); - nv_wr32(dev, 0x00b334, base); - nv_wr32(dev, 0x00b324, size); - } else - if (mthd == 0x01a0) { - /* DMA_DATA */ - nv_mask(dev, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2); - nv_wr32(dev, 0x00b360, base); - nv_wr32(dev, 0x00b364, size); - } else { - /* DMA_IMAGE, VRAM only */ - if (dma0 & 0x000c0000) - return -EINVAL; - - nv_wr32(dev, 0x00b370, base); - nv_wr32(dev, 0x00b374, size); - } - - return 0; -} - -static int -nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *ctx; - unsigned long flags; - int i; - - spin_lock_irqsave(&dev_priv->channels.lock, flags); - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { - if (!dev_priv->channels.ptr[i]) - continue; - - ctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_MPEG]; - if (ctx && ctx->pinst == inst) - break; - } - spin_unlock_irqrestore(&dev_priv->channels.lock, flags); - return i; -} - -static void -nv40_vpe_set_tile_region(struct drm_device *dev, int i) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; - - nv_wr32(dev, 0x00b008 + (i * 0x10), tile->pitch); - nv_wr32(dev, 0x00b004 + (i * 0x10), tile->limit); - nv_wr32(dev, 0x00b000 + (i * 0x10), tile->addr); -} - -static void -nv40_mpeg_isr(struct drm_device *dev) -{ - u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4; - u32 chid = nv40_mpeg_isr_chid(dev, inst); - u32 stat = nv_rd32(dev, 0x00b100); - u32 type = nv_rd32(dev, 0x00b230); - u32 mthd = nv_rd32(dev, 0x00b234); - u32 data = nv_rd32(dev, 0x00b238); - u32 show = stat; - - if (stat & 0x01000000) { - /* happens on initial binding of the object */ - if (type == 0x00000020 && mthd == 0x0000) { - nv_mask(dev, 0x00b308, 0x00000000, 0x00000000); - show &= ~0x01000000; - } - - if (type == 0x00000010) { - if (!nouveau_gpuobj_mthd_call2(dev, chid, 0x3174, mthd, data)) - show &= ~0x01000000; - } - } - - nv_wr32(dev, 0x00b100, stat); - nv_wr32(dev, 0x00b230, 0x00000001); - - if (show && nouveau_ratelimit()) { - NV_INFO(dev, "PMPEG: Ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", - chid, inst, stat, type, mthd, data); - } -} - -static void -nv40_vpe_isr(struct drm_device *dev) -{ - if (nv_rd32(dev, 0x00b100)) - nv40_mpeg_isr(dev); - - if (nv_rd32(dev, 0x00b800)) { - u32 stat = nv_rd32(dev, 0x00b800); - NV_INFO(dev, "PMSRCH: 0x%08x\n", stat); - nv_wr32(dev, 0xb800, stat); - } -} - -static void -nv40_mpeg_destroy(struct drm_device *dev, int engine) -{ - struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); - - nouveau_irq_unregister(dev, 0); - - NVOBJ_ENGINE_DEL(dev, MPEG); - kfree(pmpeg); -} - -int -nv40_mpeg_create(struct drm_device *dev) -{ - struct nv40_mpeg_engine *pmpeg; - - pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); - if (!pmpeg) - return -ENOMEM; - - pmpeg->base.destroy = nv40_mpeg_destroy; - pmpeg->base.init = nv40_mpeg_init; - pmpeg->base.fini = nv40_mpeg_fini; - pmpeg->base.context_new = nv40_mpeg_context_new; - pmpeg->base.context_del = nv40_mpeg_context_del; - pmpeg->base.object_new = nv40_mpeg_object_new; - - /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between - * all VPE engines, for this driver's purposes the PMPEG engine - * will be treated as the "master" and handle the global VPE - * bits too - */ - pmpeg->base.set_tile_region = nv40_vpe_set_tile_region; - nouveau_irq_register(dev, 0, nv40_vpe_isr); - - NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); - NVOBJ_CLASS(dev, 0x3174, MPEG); - NVOBJ_MTHD (dev, 0x3174, 0x0190, nv40_mpeg_mthd_dma); - NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv40_mpeg_mthd_dma); - NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv40_mpeg_mthd_dma); - -#if 0 - NVOBJ_ENGINE_ADD(dev, ME, &pme->base); - NVOBJ_CLASS(dev, 0x4075, ME); -#endif - return 0; - -} diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index ebabacf..882080e 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -104,7 +104,7 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked) OUT_RING(evo, nv_crtc->lut.depth == 8 ? NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON); - OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.start << PAGE_SHIFT) >> 8); + OUT_RING(evo, nv_crtc->lut.nvbo->bo.offset >> 8); if (dev_priv->chipset != 0x50) { BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1); OUT_RING(evo, NvEvoVRAM); @@ -329,8 +329,6 @@ nv50_crtc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(&nv_crtc->base); - nv50_cursor_fini(nv_crtc); - nouveau_bo_unmap(nv_crtc->lut.nvbo); nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); nouveau_bo_unmap(nv_crtc->cursor.nvbo); @@ -372,7 +370,7 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, nouveau_bo_unmap(cursor); - nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.mem.start << PAGE_SHIFT); + nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset); nv_crtc->cursor.show(nv_crtc, true); out: @@ -519,12 +517,18 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, struct drm_device *dev = nv_crtc->base.dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *evo = nv50_display(dev)->master; - struct drm_framebuffer *drm_fb = nv_crtc->base.fb; - struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); + struct drm_framebuffer *drm_fb; + struct nouveau_framebuffer *fb; int ret; NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + /* no fb bound */ + if (!atomic && !crtc->fb) { + NV_DEBUG_KMS(dev, "No FB bound\n"); + return 0; + } + /* If atomic, we want to switch to the fb we were passed, so * now we update pointers to do that. (We don't pin; just * assume we're already pinned and update the base address.) @@ -533,6 +537,8 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, drm_fb = passed_fb; fb = nouveau_framebuffer(passed_fb); } else { + drm_fb = crtc->fb; + fb = nouveau_framebuffer(crtc->fb); /* If not atomic, we can go ahead and pin, and unpin the * old fb we were passed. */ @@ -546,7 +552,7 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, } } - nv_crtc->fb.offset = fb->nvbo->bo.mem.start << PAGE_SHIFT; + nv_crtc->fb.offset = fb->nvbo->bo.offset; nv_crtc->fb.tile_flags = nouveau_bo_tile_layout(fb->nvbo); nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8; if (!nv_crtc->fb.blanked && dev_priv->chipset != 0x50) { @@ -747,7 +753,7 @@ nv50_crtc_create(struct drm_device *dev, int index) } nv_crtc->lut.depth = 0; - ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(dev, 4096, 0x100, TTM_PL_FLAG_VRAM, 0, 0x0000, &nv_crtc->lut.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); @@ -773,7 +779,7 @@ nv50_crtc_create(struct drm_device *dev, int index) drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs); drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); - ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, 0, 0x0000, &nv_crtc->cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c index 9752c35..adfc9b6 100644 --- a/drivers/gpu/drm/nouveau/nv50_cursor.c +++ b/drivers/gpu/drm/nouveau/nv50_cursor.c @@ -137,21 +137,3 @@ nv50_cursor_init(struct nouveau_crtc *nv_crtc) nv_crtc->cursor.show = nv50_cursor_show; return 0; } - -void -nv50_cursor_fini(struct nouveau_crtc *nv_crtc) -{ - struct drm_device *dev = nv_crtc->base.dev; - int idx = nv_crtc->index; - - NV_DEBUG_KMS(dev, "\n"); - - nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0); - if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), - NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { - NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); - NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", - nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx))); - } -} - diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 08da478..06de250 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -247,6 +247,16 @@ static int nv50_display_disable(struct drm_device *dev) } } + for (i = 0; i < 2; i++) { + nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0); + if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), + NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { + NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); + NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", + nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); + } + } + nv50_evo_fini(dev); for (i = 0; i < 3; i++) { @@ -286,23 +296,6 @@ int nv50_display_create(struct drm_device *dev) return -ENOMEM; dev_priv->engine.display.priv = priv; - /* init basic kernel modesetting */ - drm_mode_config_init(dev); - - /* Initialise some optional connector properties. */ - drm_mode_create_scaling_mode_property(dev); - drm_mode_create_dithering_property(dev); - - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - - dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; - - dev->mode_config.max_width = 8192; - dev->mode_config.max_height = 8192; - - dev->mode_config.fb_base = dev_priv->fb_phys; - /* Create CRTC objects */ for (i = 0; i < 2; i++) nv50_crtc_create(dev, i); @@ -364,8 +357,6 @@ nv50_display_destroy(struct drm_device *dev) NV_DEBUG_KMS(dev, "\n"); - drm_mode_config_cleanup(dev); - nv50_display_disable(dev); nouveau_irq_unregister(dev, 26); kfree(disp); @@ -415,8 +406,6 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* synchronise with the rendering channel, if necessary */ if (likely(chan)) { - u64 offset = dispc->sem.bo->vma.offset + dispc->sem.offset; - ret = RING_SPACE(chan, 10); if (ret) { WIND_RING(evo); @@ -438,6 +427,8 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, else OUT_RING (chan, chan->vram_handle); } else { + u64 offset = chan->dispc_vma[nv_crtc->index].offset; + offset += dispc->sem.offset; BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset)); @@ -484,7 +475,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, OUT_RING (evo, 0x00000000); OUT_RING (evo, 0x00000000); BEGIN_RING(evo, 0, 0x0800, 5); - OUT_RING (evo, (nv_fb->nvbo->bo.mem.start << PAGE_SHIFT) >> 8); + OUT_RING (evo, nv_fb->nvbo->bo.offset >> 8); OUT_RING (evo, 0); OUT_RING (evo, (fb->height << 16) | fb->width); OUT_RING (evo, nv_fb->r_pitch); @@ -625,7 +616,7 @@ nv50_display_unk10_handler(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_display *disp = nv50_display(dev); u32 unk30 = nv_rd32(dev, 0x610030), mc; - int i, crtc, or, type = OUTPUT_ANY; + int i, crtc, or = 0, type = OUTPUT_ANY; NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); disp->irq.dcb = NULL; @@ -698,7 +689,7 @@ nv50_display_unk10_handler(struct drm_device *dev) struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i]; if (dcb->type == type && (dcb->or & (1 << or))) { - nouveau_bios_run_display_table(dev, dcb, 0, -1); + nouveau_bios_run_display_table(dev, 0, -1, dcb, -1); disp->irq.dcb = dcb; goto ack; } @@ -711,49 +702,18 @@ ack: } static void -nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb) -{ - int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1); - struct drm_encoder *encoder; - uint32_t tmp, unk0 = 0, unk1 = 0; - - if (dcb->type != OUTPUT_DP) - return; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - - if (nv_encoder->dcb == dcb) { - unk0 = nv_encoder->dp.unk0; - unk1 = nv_encoder->dp.unk1; - break; - } - } - - if (unk0 || unk1) { - tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - tmp &= 0xfffffe03; - nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0); - - tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); - tmp &= 0xfef080c0; - nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1); - } -} - -static void nv50_display_unk20_handler(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_display *disp = nv50_display(dev); u32 unk30 = nv_rd32(dev, 0x610030), tmp, pclk, script, mc = 0; struct dcb_entry *dcb; - int i, crtc, or, type = OUTPUT_ANY; + int i, crtc, or = 0, type = OUTPUT_ANY; NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); dcb = disp->irq.dcb; if (dcb) { - nouveau_bios_run_display_table(dev, dcb, 0, -2); + nouveau_bios_run_display_table(dev, 0, -2, dcb, -1); disp->irq.dcb = NULL; } @@ -837,9 +797,15 @@ nv50_display_unk20_handler(struct drm_device *dev) } script = nv50_display_script_select(dev, dcb, mc, pclk); - nouveau_bios_run_display_table(dev, dcb, script, pclk); + nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); - nv50_display_unk20_dp_hack(dev, dcb); + if (type == OUTPUT_DP) { + int link = !(dcb->dpconf.sor.link & 1); + if ((mc & 0x000f0000) == 0x00020000) + nouveau_dp_tu_update(dev, or, link, pclk, 18); + else + nouveau_dp_tu_update(dev, or, link, pclk, 24); + } if (dcb->type != OUTPUT_ANALOG) { tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); @@ -904,7 +870,7 @@ nv50_display_unk40_handler(struct drm_device *dev) if (!dcb) goto ack; - nouveau_bios_run_display_table(dev, dcb, script, -pclk); + nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1); nv50_display_unk40_dp_set_tmds(dev, dcb); ack: diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c index c8e83c1..c99d975 100644 --- a/drivers/gpu/drm/nouveau/nv50_evo.c +++ b/drivers/gpu/drm/nouveau/nv50_evo.c @@ -38,6 +38,7 @@ nv50_evo_channel_del(struct nouveau_channel **pevo) return; *pevo = NULL; + nouveau_ramht_ref(NULL, &evo->ramht, evo); nouveau_gpuobj_channel_takedown(evo); nouveau_bo_unmap(evo->pushbuf_bo); nouveau_bo_ref(NULL, &evo->pushbuf_bo); @@ -116,7 +117,7 @@ nv50_evo_channel_new(struct drm_device *dev, int chid, evo->user_get = 4; evo->user_put = 0; - ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, + ret = nouveau_bo_new(dev, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, &evo->pushbuf_bo); if (ret == 0) ret = nouveau_bo_pin(evo->pushbuf_bo, TTM_PL_FLAG_VRAM); @@ -153,7 +154,7 @@ nv50_evo_channel_init(struct nouveau_channel *evo) { struct drm_device *dev = evo->dev; int id = evo->id, ret, i; - u64 pushbuf = evo->pushbuf_bo->bo.mem.start << PAGE_SHIFT; + u64 pushbuf = evo->pushbuf_bo->bo.offset; u32 tmp; tmp = nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id)); @@ -331,16 +332,15 @@ nv50_evo_create(struct drm_device *dev) if (ret) goto err; - ret = nouveau_bo_new(dev, NULL, 4096, 0x1000, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, 0, 0x0000, &dispc->sem.bo); if (!ret) { - offset = dispc->sem.bo->bo.mem.start << PAGE_SHIFT; - ret = nouveau_bo_pin(dispc->sem.bo, TTM_PL_FLAG_VRAM); if (!ret) ret = nouveau_bo_map(dispc->sem.bo); if (ret) nouveau_bo_ref(NULL, &dispc->sem.bo); + offset = dispc->sem.bo->bo.offset; } if (ret) diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c index 791ded1..dc75a72 100644 --- a/drivers/gpu/drm/nouveau/nv50_fbcon.c +++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c @@ -159,7 +159,7 @@ nv50_fbcon_accel_init(struct fb_info *info) struct drm_device *dev = nfbdev->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = dev_priv->channel; - struct nouveau_bo *nvbo = nfbdev->nouveau_fb.nvbo; + struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb; int ret, format; switch (info->var.bits_per_pixel) { @@ -247,8 +247,8 @@ nv50_fbcon_accel_init(struct fb_info *info) OUT_RING(chan, info->fix.line_length); OUT_RING(chan, info->var.xres_virtual); OUT_RING(chan, info->var.yres_virtual); - OUT_RING(chan, upper_32_bits(nvbo->vma.offset)); - OUT_RING(chan, lower_32_bits(nvbo->vma.offset)); + OUT_RING(chan, upper_32_bits(fb->vma.offset)); + OUT_RING(chan, lower_32_bits(fb->vma.offset)); BEGIN_RING(chan, NvSub2D, 0x0230, 2); OUT_RING(chan, format); OUT_RING(chan, 1); @@ -256,8 +256,8 @@ nv50_fbcon_accel_init(struct fb_info *info) OUT_RING(chan, info->fix.line_length); OUT_RING(chan, info->var.xres_virtual); OUT_RING(chan, info->var.yres_virtual); - OUT_RING(chan, upper_32_bits(nvbo->vma.offset)); - OUT_RING(chan, lower_32_bits(nvbo->vma.offset)); + OUT_RING(chan, upper_32_bits(fb->vma.offset)); + OUT_RING(chan, lower_32_bits(fb->vma.offset)); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c index d4f4206..793a5cc 100644 --- a/drivers/gpu/drm/nouveau/nv50_gpio.c +++ b/drivers/gpu/drm/nouveau/nv50_gpio.c @@ -98,6 +98,37 @@ nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) } int +nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +{ + struct dcb_gpio_entry *gpio; + u32 v; + + gpio = nouveau_bios_gpio_entry(dev, tag); + if (!gpio) + return -ENOENT; + + v = nv_rd32(dev, 0x00d610 + (gpio->line * 4)); + v &= 0x00004000; + return (!!v == (gpio->state[1] & 1)); +} + +int +nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +{ + struct dcb_gpio_entry *gpio; + u32 v; + + gpio = nouveau_bios_gpio_entry(dev, tag); + if (!gpio) + return -ENOENT; + + v = gpio->state[state] ^ 2; + + nv_mask(dev, 0x00d610 + (gpio->line * 4), 0x00003000, v << 12); + return 0; +} + +int nv50_gpio_irq_register(struct drm_device *dev, enum dcb_gpio_tag tag, void (*handler)(void *, int), void *data) { diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index e25cbb4..ac601f7 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -31,7 +31,6 @@ #include "nouveau_grctx.h" #include "nouveau_dma.h" #include "nouveau_vm.h" -#include "nouveau_ramht.h" #include "nv50_evo.h" struct nv50_graph_engine { @@ -121,71 +120,62 @@ nv50_graph_unload_context(struct drm_device *dev) return 0; } -static void -nv50_graph_init_reset(struct drm_device *dev) -{ - uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21); - - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e); - nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e); -} - -static void -nv50_graph_init_intr(struct drm_device *dev) -{ - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff); - nv_wr32(dev, 0x400138, 0xffffffff); - nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff); -} - -static void -nv50_graph_init_regs__nv(struct drm_device *dev) +static int +nv50_graph_init(struct drm_device *dev, int engine) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t units = nv_rd32(dev, 0x1540); + struct nv50_graph_engine *pgraph = nv_engine(dev, engine); + u32 units = nv_rd32(dev, 0x001540); int i; NV_DEBUG(dev, "\n"); + /* master reset */ + nv_mask(dev, 0x000200, 0x00201000, 0x00000000); + nv_mask(dev, 0x000200, 0x00201000, 0x00201000); + nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */ + + /* reset/enable traps and interrupts */ nv_wr32(dev, 0x400804, 0xc0000000); nv_wr32(dev, 0x406800, 0xc0000000); nv_wr32(dev, 0x400c04, 0xc0000000); nv_wr32(dev, 0x401800, 0xc0000000); nv_wr32(dev, 0x405018, 0xc0000000); nv_wr32(dev, 0x402000, 0xc0000000); - for (i = 0; i < 16; i++) { - if (units & 1 << i) { - if (dev_priv->chipset < 0xa0) { - nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000); - nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000); - nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000); - } else { - nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000); - nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000); - nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000); - } + if (!(units & (1 << i))) + continue; + + if (dev_priv->chipset < 0xa0) { + nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000); + nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000); + nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000); + } else { + nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000); + nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000); + nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000); } } nv_wr32(dev, 0x400108, 0xffffffff); - - nv_wr32(dev, 0x400824, 0x00004000); + nv_wr32(dev, 0x400138, 0xffffffff); + nv_wr32(dev, 0x400100, 0xffffffff); + nv_wr32(dev, 0x40013c, 0xffffffff); nv_wr32(dev, 0x400500, 0x00010001); -} - -static void -nv50_graph_init_zcull(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int i; - - NV_DEBUG(dev, "\n"); + /* upload context program, initialise ctxctl defaults */ + nv_wr32(dev, 0x400324, 0x00000000); + for (i = 0; i < pgraph->ctxprog_size; i++) + nv_wr32(dev, 0x400328, pgraph->ctxprog[i]); + nv_wr32(dev, 0x400824, 0x00000000); + nv_wr32(dev, 0x400828, 0x00000000); + nv_wr32(dev, 0x40082c, 0x00000000); + nv_wr32(dev, 0x400830, 0x00000000); + nv_wr32(dev, 0x400724, 0x00000000); + nv_wr32(dev, 0x40032c, 0x00000000); + nv_wr32(dev, 0x400320, 4); /* CTXCTL_CMD = NEWCTXDMA */ + + /* some unknown zcull magic */ switch (dev_priv->chipset & 0xf0) { case 0x50: case 0x80: @@ -214,50 +204,18 @@ nv50_graph_init_zcull(struct drm_device *dev) nv_wr32(dev, 0x402c28 + (i * 8), 0x00000000); nv_wr32(dev, 0x402c2c + (i * 8), 0x00000000); } -} - -static int -nv50_graph_init_ctxctl(struct drm_device *dev) -{ - struct nv50_graph_engine *pgraph = nv_engine(dev, NVOBJ_ENGINE_GR); - int i; - - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0); - for (i = 0; i < pgraph->ctxprog_size; i++) - nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, pgraph->ctxprog[i]); - - nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */ - nv_wr32(dev, 0x400320, 4); - nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0); - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0); - return 0; -} - -static int -nv50_graph_init(struct drm_device *dev, int engine) -{ - int ret; - NV_DEBUG(dev, "\n"); - - nv50_graph_init_reset(dev); - nv50_graph_init_regs__nv(dev); - nv50_graph_init_zcull(dev); - - ret = nv50_graph_init_ctxctl(dev); - if (ret) - return ret; - - nv50_graph_init_intr(dev); return 0; } static int -nv50_graph_fini(struct drm_device *dev, int engine) +nv50_graph_fini(struct drm_device *dev, int engine, bool suspend) { - NV_DEBUG(dev, "\n"); + nv_mask(dev, 0x400500, 0x00010001, 0x00000000); + if (!nv_wait(dev, 0x400700, ~0, 0) && suspend) { + nv_mask(dev, 0x400500, 0x00010001, 0x00010001); + return -EBUSY; + } nv50_graph_unload_context(dev); nv_wr32(dev, 0x40013c, 0x00000000); return 0; diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c index de9abff..4b46d69 100644 --- a/drivers/gpu/drm/nouveau/nv50_grctx.c +++ b/drivers/gpu/drm/nouveau/nv50_grctx.c @@ -40,6 +40,12 @@ #define CP_FLAG_UNK0B ((0 * 32) + 0xb) #define CP_FLAG_UNK0B_CLEAR 0 #define CP_FLAG_UNK0B_SET 1 +#define CP_FLAG_XFER_SWITCH ((0 * 32) + 0xe) +#define CP_FLAG_XFER_SWITCH_DISABLE 0 +#define CP_FLAG_XFER_SWITCH_ENABLE 1 +#define CP_FLAG_STATE ((0 * 32) + 0x1c) +#define CP_FLAG_STATE_STOPPED 0 +#define CP_FLAG_STATE_RUNNING 1 #define CP_FLAG_UNK1D ((0 * 32) + 0x1d) #define CP_FLAG_UNK1D_CLEAR 0 #define CP_FLAG_UNK1D_SET 1 @@ -194,6 +200,9 @@ nv50_grctx_init(struct nouveau_grctx *ctx) "the devs.\n"); return -ENOSYS; } + + cp_set (ctx, STATE, RUNNING); + cp_set (ctx, XFER_SWITCH, ENABLE); /* decide whether we're loading/unloading the context */ cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save); cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save); @@ -260,6 +269,8 @@ nv50_grctx_init(struct nouveau_grctx *ctx) cp_name(ctx, cp_exit); cp_set (ctx, USER_SAVE, NOT_PENDING); cp_set (ctx, USER_LOAD, NOT_PENDING); + cp_set (ctx, XFER_SWITCH, DISABLE); + cp_set (ctx, STATE, STOPPED); cp_out (ctx, CP_END); ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */ @@ -590,7 +601,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) gr_def(ctx, offset + 0x1c, 0x00880000); break; case 0x86: - gr_def(ctx, offset + 0x1c, 0x008c0000); + gr_def(ctx, offset + 0x1c, 0x018c0000); break; case 0x92: case 0x96: diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c index 4f95a1e..a7c12c9 100644 --- a/drivers/gpu/drm/nouveau/nv50_instmem.c +++ b/drivers/gpu/drm/nouveau/nv50_instmem.c @@ -305,9 +305,9 @@ struct nv50_gpuobj_node { u32 align; }; - int -nv50_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align) +nv50_instmem_get(struct nouveau_gpuobj *gpuobj, struct nouveau_channel *chan, + u32 size, u32 align) { struct drm_device *dev = gpuobj->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -336,7 +336,7 @@ nv50_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align) if (!(gpuobj->flags & NVOBJ_FLAG_VM_USER)) flags |= NV_MEM_ACCESS_SYS; - ret = nouveau_vm_get(dev_priv->chan_vm, size, 12, flags, + ret = nouveau_vm_get(chan->vm, size, 12, flags, &node->chan_vma); if (ret) { vram->put(dev, &node->vram); @@ -345,7 +345,7 @@ nv50_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align) } nouveau_vm_map(&node->chan_vma, node->vram); - gpuobj->vinst = node->chan_vma.offset; + gpuobj->linst = node->chan_vma.offset; } gpuobj->size = size; diff --git a/drivers/gpu/drm/nouveau/nv50_mpeg.c b/drivers/gpu/drm/nouveau/nv50_mpeg.c index 1dc5913..b57a2d1 100644 --- a/drivers/gpu/drm/nouveau/nv50_mpeg.c +++ b/drivers/gpu/drm/nouveau/nv50_mpeg.c @@ -160,7 +160,7 @@ nv50_mpeg_init(struct drm_device *dev, int engine) } static int -nv50_mpeg_fini(struct drm_device *dev, int engine) +nv50_mpeg_fini(struct drm_device *dev, int engine, bool suspend) { /*XXX: context save for s/r */ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 8a28100..3d5a86b 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -115,15 +115,15 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state) BIT_M.version == 1 && BIT_M.length >= 0x0b) { script = ROM16(BIT_M.data[0x05]); if (script) - nouveau_bios_run_init_table(dev, script, NULL); + nouveau_bios_run_init_table(dev, script, NULL, -1); script = ROM16(BIT_M.data[0x07]); if (script) - nouveau_bios_run_init_table(dev, script, NULL); + nouveau_bios_run_init_table(dev, script, NULL, -1); script = ROM16(BIT_M.data[0x09]); if (script) - nouveau_bios_run_init_table(dev, script, NULL); + nouveau_bios_run_init_table(dev, script, NULL, -1); - nouveau_bios_run_init_table(dev, perflvl->memscript, NULL); + nouveau_bios_run_init_table(dev, perflvl->memscript, NULL, -1); } if (state->type == PLL_MEMORY) { diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index c25c5938..2633aa8 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -124,7 +124,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) if (mode == DRM_MODE_DPMS_ON) { u8 status = DP_SET_POWER_D0; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); - nouveau_dp_link_train(encoder); + nouveau_dp_link_train(encoder, nv_encoder->dp.datarate); } else { u8 status = DP_SET_POWER_D3; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); @@ -187,14 +187,13 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_device *dev = encoder->dev; struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; uint32_t mode_ctl = 0; int ret; NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n", nv_encoder->or, nv_encoder->dcb->type, crtc->index); - nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); - switch (nv_encoder->dcb->type) { case OUTPUT_TMDS: if (nv_encoder->dcb->sorconf.link & 1) { @@ -206,7 +205,15 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, mode_ctl = 0x0200; break; case OUTPUT_DP: - mode_ctl |= (nv_encoder->dp.mc_unknown << 16); + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (nv_connector && nv_connector->base.display_info.bpc == 6) { + nv_encoder->dp.datarate = crtc->mode->clock * 18 / 8; + mode_ctl |= 0x00020000; + } else { + nv_encoder->dp.datarate = crtc->mode->clock * 24 / 8; + mode_ctl |= 0x00050000; + } + if (nv_encoder->dcb->sorconf.link & 1) mode_ctl |= 0x00000800; else @@ -227,6 +234,8 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; + nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); + ret = RING_SPACE(evo, 2); if (ret) { NV_ERROR(dev, "no space while connecting SOR\n"); @@ -313,29 +322,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry) encoder->possible_crtcs = entry->heads; encoder->possible_clones = 0; - if (nv_encoder->dcb->type == OUTPUT_DP) { - int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1); - uint32_t tmp; - - tmp = nv_rd32(dev, 0x61c700 + (or * 0x800)); - - switch ((tmp & 0x00000f00) >> 8) { - case 8: - case 9: - nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16; - tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - nv_encoder->dp.unk0 = tmp & 0x000001fc; - tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); - nv_encoder->dp.unk1 = tmp & 0x010f7f3f; - break; - default: - break; - } - - if (!nv_encoder->dp.mc_unknown) - nv_encoder->dp.mc_unknown = 5; - } - drm_mode_connector_attach_encoder(connector, encoder); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c index 1a0dd49..40b84f2 100644 --- a/drivers/gpu/drm/nouveau/nv50_vm.c +++ b/drivers/gpu/drm/nouveau/nv50_vm.c @@ -156,7 +156,7 @@ nv50_vm_flush(struct nouveau_vm *vm) pinstmem->flush(vm->dev); /* BAR */ - if (vm != dev_priv->chan_vm) { + if (vm == dev_priv->bar1_vm || vm == dev_priv->bar3_vm) { nv50_vm_flush_engine(vm->dev, 6); return; } diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c index ffbc3d8..2e45e57 100644 --- a/drivers/gpu/drm/nouveau/nv50_vram.c +++ b/drivers/gpu/drm/nouveau/nv50_vram.c @@ -51,9 +51,7 @@ void nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; - struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM]; - struct nouveau_mm *mm = man->priv; + struct nouveau_mm *mm = &dev_priv->engine.vram.mm; struct nouveau_mm_node *this; struct nouveau_mem *mem; @@ -84,9 +82,7 @@ nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc, u32 memtype, struct nouveau_mem **pmem) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; - struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM]; - struct nouveau_mm *mm = man->priv; + struct nouveau_mm *mm = &dev_priv->engine.vram.mm; struct nouveau_mm_node *r; struct nouveau_mem *mem; int comp = (memtype & 0x300) >> 8; @@ -164,7 +160,7 @@ nv50_vram_rblock(struct drm_device *dev) colbits = (r4 & 0x0000f000) >> 12; rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; - banks = ((r4 & 0x01000000) ? 8 : 4); + banks = 1 << (((r4 & 0x03000000) >> 24) + 2); rowsize = parts * banks * (1 << colbits) * 8; predicted = rowsize << rowbitsa; @@ -190,22 +186,35 @@ int nv50_vram_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_vram_engine *vram = &dev_priv->engine.vram; + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 rblock, length; dev_priv->vram_size = nv_rd32(dev, 0x10020c); dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32; dev_priv->vram_size &= 0xffffffff00ULL; - switch (dev_priv->chipset) { - case 0xaa: - case 0xac: - case 0xaf: + /* IGPs, no funky reordering happens here, they don't have VRAM */ + if (dev_priv->chipset == 0xaa || + dev_priv->chipset == 0xac || + dev_priv->chipset == 0xaf) { dev_priv->vram_sys_base = (u64)nv_rd32(dev, 0x100e10) << 12; - dev_priv->vram_rblock_size = 4096; - break; - default: - dev_priv->vram_rblock_size = nv50_vram_rblock(dev); - break; + rblock = 4096 >> 12; + } else { + rblock = nv50_vram_rblock(dev) >> 12; } - return 0; + length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail; + + return nouveau_mm_init(&vram->mm, rsvd_head, length, rblock); +} + +void +nv50_vram_fini(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_vram_engine *vram = &dev_priv->engine.vram; + + nouveau_mm_fini(&vram->mm); } diff --git a/drivers/gpu/drm/nouveau/nv84_crypt.c b/drivers/gpu/drm/nouveau/nv84_crypt.c index 75b809a..edece9c 100644 --- a/drivers/gpu/drm/nouveau/nv84_crypt.c +++ b/drivers/gpu/drm/nouveau/nv84_crypt.c @@ -138,7 +138,7 @@ nv84_crypt_isr(struct drm_device *dev) } static int -nv84_crypt_fini(struct drm_device *dev, int engine) +nv84_crypt_fini(struct drm_device *dev, int engine, bool suspend) { nv_wr32(dev, 0x102140, 0x00000000); return 0; diff --git a/drivers/gpu/drm/nouveau/nva3_copy.c b/drivers/gpu/drm/nouveau/nva3_copy.c index b86820a..8f356d5 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.c +++ b/drivers/gpu/drm/nouveau/nva3_copy.c @@ -140,7 +140,7 @@ nva3_copy_init(struct drm_device *dev, int engine) } static int -nva3_copy_fini(struct drm_device *dev, int engine) +nva3_copy_fini(struct drm_device *dev, int engine, bool suspend) { nv_mask(dev, 0x104048, 0x00000003, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc b/drivers/gpu/drm/nouveau/nva3_copy.fuc index eaf35f8..d894731 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.fuc +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc @@ -118,9 +118,9 @@ dispatch_dma: // mthd 0x030c-0x0340, various stuff .b16 0xc3 14 .b32 ctx_src_address_high ~0x000000ff -.b32 ctx_src_address_low ~0xfffffff0 +.b32 ctx_src_address_low ~0xffffffff .b32 ctx_dst_address_high ~0x000000ff -.b32 ctx_dst_address_low ~0xfffffff0 +.b32 ctx_dst_address_low ~0xffffffff .b32 ctx_src_pitch ~0x0007ffff .b32 ctx_dst_pitch ~0x0007ffff .b32 ctx_xcnt ~0x0000ffff diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h index 2731de2..e2a0e88 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h @@ -1,37 +1,72 @@ -uint32_t nva3_pcopy_data[] = { +u32 nva3_pcopy_data[] = { +/* 0x0000: ctx_object */ 0x00000000, +/* 0x0004: ctx_dma */ +/* 0x0004: ctx_dma_query */ 0x00000000, +/* 0x0008: ctx_dma_src */ 0x00000000, +/* 0x000c: ctx_dma_dst */ 0x00000000, +/* 0x0010: ctx_query_address_high */ 0x00000000, +/* 0x0014: ctx_query_address_low */ 0x00000000, +/* 0x0018: ctx_query_counter */ 0x00000000, +/* 0x001c: ctx_src_address_high */ 0x00000000, +/* 0x0020: ctx_src_address_low */ 0x00000000, +/* 0x0024: ctx_src_pitch */ 0x00000000, +/* 0x0028: ctx_src_tile_mode */ 0x00000000, +/* 0x002c: ctx_src_xsize */ 0x00000000, +/* 0x0030: ctx_src_ysize */ 0x00000000, +/* 0x0034: ctx_src_zsize */ 0x00000000, +/* 0x0038: ctx_src_zoff */ 0x00000000, +/* 0x003c: ctx_src_xoff */ 0x00000000, +/* 0x0040: ctx_src_yoff */ 0x00000000, +/* 0x0044: ctx_src_cpp */ 0x00000000, +/* 0x0048: ctx_dst_address_high */ 0x00000000, +/* 0x004c: ctx_dst_address_low */ 0x00000000, +/* 0x0050: ctx_dst_pitch */ 0x00000000, +/* 0x0054: ctx_dst_tile_mode */ 0x00000000, +/* 0x0058: ctx_dst_xsize */ 0x00000000, +/* 0x005c: ctx_dst_ysize */ 0x00000000, +/* 0x0060: ctx_dst_zsize */ 0x00000000, +/* 0x0064: ctx_dst_zoff */ 0x00000000, +/* 0x0068: ctx_dst_xoff */ 0x00000000, +/* 0x006c: ctx_dst_yoff */ 0x00000000, +/* 0x0070: ctx_dst_cpp */ 0x00000000, +/* 0x0074: ctx_format */ 0x00000000, +/* 0x0078: ctx_swz_const0 */ 0x00000000, +/* 0x007c: ctx_swz_const1 */ 0x00000000, +/* 0x0080: ctx_xcnt */ 0x00000000, +/* 0x0084: ctx_ycnt */ 0x00000000, 0x00000000, 0x00000000, @@ -63,6 +98,7 @@ uint32_t nva3_pcopy_data[] = { 0x00000000, 0x00000000, 0x00000000, +/* 0x0100: dispatch_table */ 0x00010000, 0x00000000, 0x00000000, @@ -73,6 +109,7 @@ uint32_t nva3_pcopy_data[] = { 0x00010162, 0x00000000, 0x00030060, +/* 0x0128: dispatch_dma */ 0x00010170, 0x00000000, 0x00010170, @@ -118,11 +155,11 @@ uint32_t nva3_pcopy_data[] = { 0x0000001c, 0xffffff00, 0x00000020, - 0x0000000f, + 0x00000000, 0x00000048, 0xffffff00, 0x0000004c, - 0x0000000f, + 0x00000000, 0x00000024, 0xfff80000, 0x00000050, @@ -146,7 +183,8 @@ uint32_t nva3_pcopy_data[] = { 0x00000800, }; -uint32_t nva3_pcopy_code[] = { +u32 nva3_pcopy_code[] = { +/* 0x0000: main */ 0x04fe04bd, 0x3517f000, 0xf10010fe, @@ -158,23 +196,31 @@ uint32_t nva3_pcopy_code[] = { 0x17f11031, 0x27f01200, 0x0012d003, +/* 0x002f: spin */ 0xf40031f4, 0x0ef40028, +/* 0x0035: ih */ 0x8001cffd, 0xf40812c4, 0x21f4060b, +/* 0x0041: ih_no_chsw */ 0x0412c472, 0xf4060bf4, +/* 0x004a: ih_no_cmd */ 0x11c4c321, 0x4001d00c, +/* 0x0052: swctx */ 0x47f101f8, 0x4bfe7700, 0x0007fe00, 0xf00204b9, 0x01f40643, 0x0604fa09, +/* 0x006b: swctx_load */ 0xfa060ef4, +/* 0x006e: swctx_done */ 0x03f80504, +/* 0x0072: chsw */ 0x27f100f8, 0x23cf1400, 0x1e3fc800, @@ -183,18 +229,22 @@ uint32_t nva3_pcopy_code[] = { 0x1e3af052, 0xf00023d0, 0x24d00147, +/* 0x0093: chsw_no_unload */ 0xcf00f880, 0x3dc84023, 0x220bf41e, 0xf40131f4, 0x57f05221, 0x0367f004, +/* 0x00a8: chsw_load_ctx_dma */ 0xa07856bc, 0xb6018068, 0x87d00884, 0x0162b600, +/* 0x00bb: chsw_finish_load */ 0xf0f018f4, 0x23d00237, +/* 0x00c3: dispatch */ 0xf100f880, 0xcf190037, 0x33cf4032, @@ -202,6 +252,7 @@ uint32_t nva3_pcopy_code[] = { 0x1024b607, 0x010057f1, 0x74bd64bd, +/* 0x00dc: dispatch_loop */ 0x58005658, 0x50b60157, 0x0446b804, @@ -211,6 +262,7 @@ uint32_t nva3_pcopy_code[] = { 0xb60276bb, 0x57bb0374, 0xdf0ef400, +/* 0x0100: dispatch_valid_mthd */ 0xb60246bb, 0x45bb0344, 0x01459800, @@ -220,31 +272,41 @@ uint32_t nva3_pcopy_code[] = { 0xb0014658, 0x1bf40064, 0x00538009, +/* 0x0127: dispatch_cmd */ 0xf4300ef4, 0x55f90132, 0xf40c01f4, +/* 0x0132: dispatch_invalid_bitfield */ 0x25f0250e, +/* 0x0135: dispatch_illegal_mthd */ 0x0125f002, +/* 0x0138: dispatch_error */ 0x100047f1, 0xd00042d0, 0x27f04043, 0x0002d040, +/* 0x0148: hostirq_wait */ 0xf08002cf, 0x24b04024, 0xf71bf400, +/* 0x0154: dispatch_done */ 0x1d0027f1, 0xd00137f0, 0x00f80023, +/* 0x0160: cmd_nop */ +/* 0x0162: cmd_pm_trigger */ 0x27f100f8, 0x34bd2200, 0xd00233f0, 0x00f80023, +/* 0x0170: cmd_dma */ 0x012842b7, 0xf00145b6, 0x43801e39, 0x0040b701, 0x0644b606, 0xf80043d0, +/* 0x0189: cmd_exec_set_format */ 0xf030f400, 0xb00001b0, 0x01b00101, @@ -256,20 +318,26 @@ uint32_t nva3_pcopy_code[] = { 0x70b63847, 0x0232f401, 0x94bd84bd, +/* 0x01b4: ncomp_loop */ 0xb60f4ac4, 0xb4bd0445, +/* 0x01bc: bpc_loop */ 0xf404a430, 0xa5ff0f18, 0x00cbbbc0, 0xf40231f4, +/* 0x01ce: cmp_c0 */ 0x1bf4220e, 0x10c7f00c, 0xf400cbbb, +/* 0x01da: cmp_c1 */ 0xa430160e, 0x0c18f406, 0xbb14c7f0, 0x0ef400cb, +/* 0x01e9: cmp_zero */ 0x80c7f107, +/* 0x01ed: bpc_next */ 0x01c83800, 0xb60180b6, 0xb5b801b0, @@ -280,6 +348,7 @@ uint32_t nva3_pcopy_code[] = { 0x98110680, 0x68fd2008, 0x0502f400, +/* 0x0216: dst_xcnt */ 0x75fd64bd, 0x1c078000, 0xf10078fd, @@ -304,6 +373,7 @@ uint32_t nva3_pcopy_code[] = { 0x980056d0, 0x56d01f06, 0x1030f440, +/* 0x0276: cmd_exec_set_surface_tiled */ 0x579800f8, 0x6879c70a, 0xb66478c7, @@ -311,9 +381,11 @@ uint32_t nva3_pcopy_code[] = { 0x0e76b060, 0xf0091bf4, 0x0ef40477, +/* 0x0291: xtile64 */ 0x027cf00f, 0xfd1170b6, 0x77f00947, +/* 0x029d: xtileok */ 0x0f5a9806, 0xfd115b98, 0xb7f000ab, @@ -371,6 +443,7 @@ uint32_t nva3_pcopy_code[] = { 0x67d00600, 0x0060b700, 0x0068d004, +/* 0x0382: cmd_exec_set_surface_linear */ 0x6cf000f8, 0x0260b702, 0x0864b602, @@ -381,13 +454,16 @@ uint32_t nva3_pcopy_code[] = { 0xb70067d0, 0x98040060, 0x67d00957, +/* 0x03ab: cmd_exec_wait */ 0xf900f800, 0xf110f900, 0xb6080007, +/* 0x03b6: loop */ 0x01cf0604, 0x0114f000, 0xfcfa1bf4, 0xf800fc10, +/* 0x03c5: cmd_exec_query */ 0x0d34c800, 0xf5701bf4, 0xf103ab21, @@ -417,6 +493,7 @@ uint32_t nva3_pcopy_code[] = { 0x47f10153, 0x44b60800, 0x0045d006, +/* 0x0438: query_counter */ 0x03ab21f5, 0x080c47f1, 0x980644b6, @@ -439,11 +516,13 @@ uint32_t nva3_pcopy_code[] = { 0x47f10153, 0x44b60800, 0x0045d006, +/* 0x0492: cmd_exec */ 0x21f500f8, 0x3fc803ab, 0x0e0bf400, 0x018921f5, 0x020047f1, +/* 0x04a7: cmd_exec_no_format */ 0xf11e0ef4, 0xb6081067, 0x77f00664, @@ -451,19 +530,24 @@ uint32_t nva3_pcopy_code[] = { 0x981c0780, 0x67d02007, 0x4067d000, +/* 0x04c2: cmd_exec_init_src_surface */ 0x32f444bd, 0xc854bd02, 0x0bf4043f, 0x8221f50a, 0x0a0ef403, +/* 0x04d4: src_tiled */ 0x027621f5, +/* 0x04db: cmd_exec_init_dst_surface */ 0xf40749f0, 0x57f00231, 0x083fc82c, 0xf50a0bf4, 0xf4038221, +/* 0x04ee: dst_tiled */ 0x21f50a0e, 0x49f00276, +/* 0x04f5: cmd_exec_kick */ 0x0057f108, 0x0654b608, 0xd0210698, @@ -473,6 +557,8 @@ uint32_t nva3_pcopy_code[] = { 0xc80054d0, 0x0bf40c3f, 0xc521f507, +/* 0x0519: cmd_exec_done */ +/* 0x051b: cmd_wrcache_flush */ 0xf100f803, 0xbd220027, 0x0133f034, diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index e4b2b9e..618c144 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -27,178 +27,316 @@ #include "nouveau_bios.h" #include "nouveau_pm.h" -/* This is actually a lot more complex than it appears here, but hopefully - * this should be able to deal with what the VBIOS leaves for us.. - * - * If not, well, I'll jump off that bridge when I come to it. - */ +static u32 read_clk(struct drm_device *, int, bool); +static u32 read_pll(struct drm_device *, int, u32); -struct nva3_pm_state { - enum pll_types type; - u32 src0; - u32 src1; - u32 ctrl; - u32 coef; - u32 old_pnm; - u32 new_pnm; - u32 new_div; -}; +static u32 +read_vco(struct drm_device *dev, int clk) +{ + u32 sctl = nv_rd32(dev, 0x4120 + (clk * 4)); + if ((sctl & 0x00000030) != 0x00000030) + return read_pll(dev, 0x41, 0x00e820); + return read_pll(dev, 0x42, 0x00e8a0); +} -static int -nva3_pm_pll_offset(u32 id) +static u32 +read_clk(struct drm_device *dev, int clk, bool ignore_en) { - static const u32 pll_map[] = { - 0x00, PLL_CORE, - 0x01, PLL_SHADER, - 0x02, PLL_MEMORY, - 0x00, 0x00 - }; - const u32 *map = pll_map; - - while (map[1]) { - if (id == map[1]) - return map[0]; - map += 2; + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 sctl, sdiv, sclk; + + /* refclk for the 0xe8xx plls is a fixed frequency */ + if (clk >= 0x40) { + if (dev_priv->chipset == 0xaf) { + /* no joke.. seriously.. sigh.. */ + return nv_rd32(dev, 0x00471c) * 1000; + } + + return dev_priv->crystal; } - return -ENOENT; + sctl = nv_rd32(dev, 0x4120 + (clk * 4)); + if (!ignore_en && !(sctl & 0x00000100)) + return 0; + + switch (sctl & 0x00003000) { + case 0x00000000: + return dev_priv->crystal; + case 0x00002000: + if (sctl & 0x00000040) + return 108000; + return 100000; + case 0x00003000: + sclk = read_vco(dev, clk); + sdiv = ((sctl & 0x003f0000) >> 16) + 2; + return (sclk * 2) / sdiv; + default: + return 0; + } } -int -nva3_pm_clock_get(struct drm_device *dev, u32 id) +static u32 +read_pll(struct drm_device *dev, int clk, u32 pll) +{ + u32 ctrl = nv_rd32(dev, pll + 0); + u32 sclk = 0, P = 1, N = 1, M = 1; + + if (!(ctrl & 0x00000008)) { + if (ctrl & 0x00000001) { + u32 coef = nv_rd32(dev, pll + 4); + M = (coef & 0x000000ff) >> 0; + N = (coef & 0x0000ff00) >> 8; + P = (coef & 0x003f0000) >> 16; + + /* no post-divider on these.. */ + if ((pll & 0x00ff00) == 0x00e800) + P = 1; + + sclk = read_clk(dev, 0x00 + clk, false); + } + } else { + sclk = read_clk(dev, 0x10 + clk, false); + } + + return sclk * N / (M * P); +} + +struct creg { + u32 clk; + u32 pll; +}; + +static int +calc_clk(struct drm_device *dev, int clk, u32 pll, u32 khz, struct creg *reg) { - u32 src0, src1, ctrl, coef; - struct pll_lims pll; - int ret, off; - int P, N, M; + struct pll_lims limits; + u32 oclk, sclk, sdiv; + int P, N, M, diff; + int ret; + + reg->pll = 0; + reg->clk = 0; + if (!khz) { + NV_DEBUG(dev, "no clock for 0x%04x/0x%02x\n", pll, clk); + return 0; + } - ret = get_pll_limits(dev, id, &pll); + switch (khz) { + case 27000: + reg->clk = 0x00000100; + return khz; + case 100000: + reg->clk = 0x00002100; + return khz; + case 108000: + reg->clk = 0x00002140; + return khz; + default: + sclk = read_vco(dev, clk); + sdiv = min((sclk * 2) / (khz - 2999), (u32)65); + /* if the clock has a PLL attached, and we can get a within + * [-2, 3) MHz of a divider, we'll disable the PLL and use + * the divider instead. + * + * divider can go as low as 2, limited here because NVIDIA + * and the VBIOS on my NVA8 seem to prefer using the PLL + * for 810MHz - is there a good reason? + */ + if (sdiv > 4) { + oclk = (sclk * 2) / sdiv; + diff = khz - oclk; + if (!pll || (diff >= -2000 && diff < 3000)) { + reg->clk = (((sdiv - 2) << 16) | 0x00003100); + return oclk; + } + } + + if (!pll) { + NV_ERROR(dev, "bad freq %02x: %d %d\n", clk, khz, sclk); + return -ERANGE; + } + + break; + } + + ret = get_pll_limits(dev, pll, &limits); if (ret) return ret; - off = nva3_pm_pll_offset(id); - if (off < 0) - return off; + limits.refclk = read_clk(dev, clk - 0x10, true); + if (!limits.refclk) + return -EINVAL; + + ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P); + if (ret >= 0) { + reg->clk = nv_rd32(dev, 0x4120 + (clk * 4)); + reg->pll = (P << 16) | (N << 8) | M; + } + return ret; +} + +static void +prog_pll(struct drm_device *dev, int clk, u32 pll, struct creg *reg) +{ + const u32 src0 = 0x004120 + (clk * 4); + const u32 src1 = 0x004160 + (clk * 4); + const u32 ctrl = pll + 0; + const u32 coef = pll + 4; + u32 cntl; + + if (!reg->clk && !reg->pll) { + NV_DEBUG(dev, "no clock for %02x\n", clk); + return; + } - src0 = nv_rd32(dev, 0x4120 + (off * 4)); - src1 = nv_rd32(dev, 0x4160 + (off * 4)); - ctrl = nv_rd32(dev, pll.reg + 0); - coef = nv_rd32(dev, pll.reg + 4); - NV_DEBUG(dev, "PLL %02x: 0x%08x 0x%08x 0x%08x 0x%08x\n", - id, src0, src1, ctrl, coef); + cntl = nv_rd32(dev, ctrl) & 0xfffffff2; + if (reg->pll) { + nv_mask(dev, src0, 0x00000101, 0x00000101); + nv_wr32(dev, coef, reg->pll); + nv_wr32(dev, ctrl, cntl | 0x00000015); + nv_mask(dev, src1, 0x00000100, 0x00000000); + nv_mask(dev, src1, 0x00000001, 0x00000000); + } else { + nv_mask(dev, src1, 0x003f3141, 0x00000101 | reg->clk); + nv_wr32(dev, ctrl, cntl | 0x0000001d); + nv_mask(dev, ctrl, 0x00000001, 0x00000000); + nv_mask(dev, src0, 0x00000100, 0x00000000); + nv_mask(dev, src0, 0x00000001, 0x00000000); + } +} - if (ctrl & 0x00000008) { - u32 div = ((src1 & 0x003c0000) >> 18) + 1; - return (pll.refclk * 2) / div; +static void +prog_clk(struct drm_device *dev, int clk, struct creg *reg) +{ + if (!reg->clk) { + NV_DEBUG(dev, "no clock for %02x\n", clk); + return; } - P = (coef & 0x003f0000) >> 16; - N = (coef & 0x0000ff00) >> 8; - M = (coef & 0x000000ff); - return pll.refclk * N / M / P; + nv_mask(dev, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | reg->clk); +} + +int +nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + perflvl->core = read_pll(dev, 0x00, 0x4200); + perflvl->shader = read_pll(dev, 0x01, 0x4220); + perflvl->memory = read_pll(dev, 0x02, 0x4000); + perflvl->unka0 = read_clk(dev, 0x20, false); + perflvl->vdec = read_clk(dev, 0x21, false); + perflvl->daemon = read_clk(dev, 0x25, false); + perflvl->copy = perflvl->core; + return 0; } +struct nva3_pm_state { + struct creg nclk; + struct creg sclk; + struct creg mclk; + struct creg vdec; + struct creg unka0; +}; + void * -nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl, - u32 id, int khz) +nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - struct nva3_pm_state *pll; - struct pll_lims limits; - int N, M, P, diff; - int ret, off; + struct nva3_pm_state *info; + int ret; - ret = get_pll_limits(dev, id, &limits); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + ret = calc_clk(dev, 0x10, 0x4200, perflvl->core, &info->nclk); if (ret < 0) - return (ret == -ENOENT) ? NULL : ERR_PTR(ret); + goto out; - off = nva3_pm_pll_offset(id); - if (id < 0) - return ERR_PTR(-EINVAL); + ret = calc_clk(dev, 0x11, 0x4220, perflvl->shader, &info->sclk); + if (ret < 0) + goto out; + ret = calc_clk(dev, 0x12, 0x4000, perflvl->memory, &info->mclk); + if (ret < 0) + goto out; - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) - return ERR_PTR(-ENOMEM); - pll->type = id; - pll->src0 = 0x004120 + (off * 4); - pll->src1 = 0x004160 + (off * 4); - pll->ctrl = limits.reg + 0; - pll->coef = limits.reg + 4; - - /* If target clock is within [-2, 3) MHz of a divisor, we'll - * use that instead of calculating MNP values - */ - pll->new_div = min((limits.refclk * 2) / (khz - 2999), 16); - if (pll->new_div) { - diff = khz - ((limits.refclk * 2) / pll->new_div); - if (diff < -2000 || diff >= 3000) - pll->new_div = 0; - } + ret = calc_clk(dev, 0x20, 0x0000, perflvl->unka0, &info->unka0); + if (ret < 0) + goto out; - if (!pll->new_div) { - ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P); - if (ret < 0) - return ERR_PTR(ret); + ret = calc_clk(dev, 0x21, 0x0000, perflvl->vdec, &info->vdec); + if (ret < 0) + goto out; - pll->new_pnm = (P << 16) | (N << 8) | M; - pll->new_div = 2 - 1; - } else { - pll->new_pnm = 0; - pll->new_div--; +out: + if (ret < 0) { + kfree(info); + info = ERR_PTR(ret); } + return info; +} + +static bool +nva3_pm_grcp_idle(void *data) +{ + struct drm_device *dev = data; - if ((nv_rd32(dev, pll->src1) & 0x00000101) != 0x00000101) - pll->old_pnm = nv_rd32(dev, pll->coef); - return pll; + if (!(nv_rd32(dev, 0x400304) & 0x00000001)) + return true; + if (nv_rd32(dev, 0x400308) == 0x0050001c) + return true; + return false; } void -nva3_pm_clock_set(struct drm_device *dev, void *pre_state) +nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) { - struct nva3_pm_state *pll = pre_state; - u32 ctrl = 0; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nva3_pm_state *info = pre_state; + unsigned long flags; - /* For the memory clock, NVIDIA will build a "script" describing - * the reclocking process and ask PDAEMON to execute it. - */ - if (pll->type == PLL_MEMORY) { - nv_wr32(dev, 0x100210, 0); - nv_wr32(dev, 0x1002dc, 1); - nv_wr32(dev, 0x004018, 0x00001000); - ctrl = 0x18000100; + /* prevent any new grctx switches from starting */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_wr32(dev, 0x400324, 0x00000000); + nv_wr32(dev, 0x400328, 0x0050001c); /* wait flag 0x1c */ + /* wait for any pending grctx switches to complete */ + if (!nv_wait_cb(dev, nva3_pm_grcp_idle, dev)) { + NV_ERROR(dev, "pm: ctxprog didn't go idle\n"); + goto cleanup; } - - if (pll->old_pnm || !pll->new_pnm) { - nv_mask(dev, pll->src1, 0x003c0101, 0x00000101 | - (pll->new_div << 18)); - nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl); - nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000); + /* freeze PFIFO */ + nv_mask(dev, 0x002504, 0x00000001, 0x00000001); + if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) { + NV_ERROR(dev, "pm: fifo didn't go idle\n"); + goto cleanup; } - if (pll->new_pnm) { - nv_mask(dev, pll->src0, 0x00000101, 0x00000101); - nv_wr32(dev, pll->coef, pll->new_pnm); - nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl); - nv_mask(dev, pll->ctrl, 0x00000010, 0x00000000); - nv_mask(dev, pll->ctrl, 0x00020010, 0x00020010); - nv_wr32(dev, pll->ctrl, 0x00010015 | ctrl); - nv_mask(dev, pll->src1, 0x00000100, 0x00000000); - nv_mask(dev, pll->src1, 0x00000001, 0x00000000); - if (pll->type == PLL_MEMORY) - nv_wr32(dev, 0x4018, 0x10005000); - } else { - nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000); - nv_mask(dev, pll->src0, 0x00000100, 0x00000000); - nv_mask(dev, pll->src0, 0x00000001, 0x00000000); - if (pll->type == PLL_MEMORY) - nv_wr32(dev, 0x4018, 0x1000d000); - } + prog_pll(dev, 0x00, 0x004200, &info->nclk); + prog_pll(dev, 0x01, 0x004220, &info->sclk); + prog_clk(dev, 0x20, &info->unka0); + prog_clk(dev, 0x21, &info->vdec); - if (pll->type == PLL_MEMORY) { + if (info->mclk.clk || info->mclk.pll) { + nv_wr32(dev, 0x100210, 0); + nv_wr32(dev, 0x1002dc, 1); + nv_wr32(dev, 0x004018, 0x00001000); + prog_pll(dev, 0x02, 0x004000, &info->mclk); + if (nv_rd32(dev, 0x4000) & 0x00000008) + nv_wr32(dev, 0x004018, 0x1000d000); + else + nv_wr32(dev, 0x004018, 0x10005000); nv_wr32(dev, 0x1002dc, 0); nv_wr32(dev, 0x100210, 0x80000000); } - kfree(pll); +cleanup: + /* unfreeze PFIFO */ + nv_mask(dev, 0x002504, 0x00000001, 0x00000000); + /* restore ctxprog to normal */ + nv_wr32(dev, 0x400324, 0x00000000); + nv_wr32(dev, 0x400328, 0x0070009c); /* set flag 0x1c */ + /* unblock it if necessary */ + if (nv_rd32(dev, 0x400308) == 0x0050001c) + nv_mask(dev, 0x400824, 0x10000000, 0x10000000); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + kfree(info); } - diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.c b/drivers/gpu/drm/nouveau/nvc0_copy.c index 208fa7a..dddf006 100644 --- a/drivers/gpu/drm/nouveau/nvc0_copy.c +++ b/drivers/gpu/drm/nouveau/nvc0_copy.c @@ -48,14 +48,14 @@ nvc0_copy_context_new(struct nouveau_channel *chan, int engine) struct nouveau_gpuobj *ctx = NULL; int ret; - ret = nouveau_gpuobj_new(dev, NULL, 256, 256, + ret = nouveau_gpuobj_new(dev, chan, 256, 256, NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER | NVOBJ_FLAG_ZERO_ALLOC, &ctx); if (ret) return ret; - nv_wo32(ramin, pcopy->ctx + 0, lower_32_bits(ctx->vinst)); - nv_wo32(ramin, pcopy->ctx + 4, upper_32_bits(ctx->vinst)); + nv_wo32(ramin, pcopy->ctx + 0, lower_32_bits(ctx->linst)); + nv_wo32(ramin, pcopy->ctx + 4, upper_32_bits(ctx->linst)); dev_priv->engine.instmem.flush(dev); chan->engctx[engine] = ctx; @@ -127,7 +127,7 @@ nvc0_copy_init(struct drm_device *dev, int engine) } static int -nvc0_copy_fini(struct drm_device *dev, int engine) +nvc0_copy_fini(struct drm_device *dev, int engine, bool suspend) { struct nvc0_copy_engine *pcopy = nv_engine(dev, engine); diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h index 4199038..9e87036 100644 --- a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h @@ -1,34 +1,65 @@ -uint32_t nvc0_pcopy_data[] = { +u32 nvc0_pcopy_data[] = { +/* 0x0000: ctx_object */ 0x00000000, +/* 0x0004: ctx_query_address_high */ 0x00000000, +/* 0x0008: ctx_query_address_low */ 0x00000000, +/* 0x000c: ctx_query_counter */ 0x00000000, +/* 0x0010: ctx_src_address_high */ 0x00000000, +/* 0x0014: ctx_src_address_low */ 0x00000000, +/* 0x0018: ctx_src_pitch */ 0x00000000, +/* 0x001c: ctx_src_tile_mode */ 0x00000000, +/* 0x0020: ctx_src_xsize */ 0x00000000, +/* 0x0024: ctx_src_ysize */ 0x00000000, +/* 0x0028: ctx_src_zsize */ 0x00000000, +/* 0x002c: ctx_src_zoff */ 0x00000000, +/* 0x0030: ctx_src_xoff */ 0x00000000, +/* 0x0034: ctx_src_yoff */ 0x00000000, +/* 0x0038: ctx_src_cpp */ 0x00000000, +/* 0x003c: ctx_dst_address_high */ 0x00000000, +/* 0x0040: ctx_dst_address_low */ 0x00000000, +/* 0x0044: ctx_dst_pitch */ 0x00000000, +/* 0x0048: ctx_dst_tile_mode */ 0x00000000, +/* 0x004c: ctx_dst_xsize */ 0x00000000, +/* 0x0050: ctx_dst_ysize */ 0x00000000, +/* 0x0054: ctx_dst_zsize */ 0x00000000, +/* 0x0058: ctx_dst_zoff */ 0x00000000, +/* 0x005c: ctx_dst_xoff */ 0x00000000, +/* 0x0060: ctx_dst_yoff */ 0x00000000, +/* 0x0064: ctx_dst_cpp */ 0x00000000, +/* 0x0068: ctx_format */ 0x00000000, +/* 0x006c: ctx_swz_const0 */ 0x00000000, +/* 0x0070: ctx_swz_const1 */ 0x00000000, +/* 0x0074: ctx_xcnt */ 0x00000000, +/* 0x0078: ctx_ycnt */ 0x00000000, 0x00000000, 0x00000000, @@ -63,6 +94,7 @@ uint32_t nvc0_pcopy_data[] = { 0x00000000, 0x00000000, 0x00000000, +/* 0x0100: dispatch_table */ 0x00010000, 0x00000000, 0x00000000, @@ -111,11 +143,11 @@ uint32_t nvc0_pcopy_data[] = { 0x00000010, 0xffffff00, 0x00000014, - 0x0000000f, + 0x00000000, 0x0000003c, 0xffffff00, 0x00000040, - 0x0000000f, + 0x00000000, 0x00000018, 0xfff80000, 0x00000044, @@ -139,7 +171,8 @@ uint32_t nvc0_pcopy_data[] = { 0x00000800, }; -uint32_t nvc0_pcopy_code[] = { +u32 nvc0_pcopy_code[] = { +/* 0x0000: main */ 0x04fe04bd, 0x3517f000, 0xf10010fe, @@ -151,15 +184,20 @@ uint32_t nvc0_pcopy_code[] = { 0x17f11031, 0x27f01200, 0x0012d003, +/* 0x002f: spin */ 0xf40031f4, 0x0ef40028, +/* 0x0035: ih */ 0x8001cffd, 0xf40812c4, 0x21f4060b, +/* 0x0041: ih_no_chsw */ 0x0412c4ca, 0xf5070bf4, +/* 0x004b: ih_no_cmd */ 0xc4010221, 0x01d00c11, +/* 0x0053: swctx */ 0xf101f840, 0xfe770047, 0x47f1004b, @@ -188,8 +226,11 @@ uint32_t nvc0_pcopy_code[] = { 0xf00204b9, 0x01f40643, 0x0604fa09, +/* 0x00c3: swctx_load */ 0xfa060ef4, +/* 0x00c6: swctx_done */ 0x03f80504, +/* 0x00ca: chsw */ 0x27f100f8, 0x23cf1400, 0x1e3fc800, @@ -198,18 +239,22 @@ uint32_t nvc0_pcopy_code[] = { 0x1e3af053, 0xf00023d0, 0x24d00147, +/* 0x00eb: chsw_no_unload */ 0xcf00f880, 0x3dc84023, 0x090bf41e, 0xf40131f4, +/* 0x00fa: chsw_finish_load */ 0x37f05321, 0x8023d002, +/* 0x0102: dispatch */ 0x37f100f8, 0x32cf1900, 0x0033cf40, 0x07ff24e4, 0xf11024b6, 0xbd010057, +/* 0x011b: dispatch_loop */ 0x5874bd64, 0x57580056, 0x0450b601, @@ -219,6 +264,7 @@ uint32_t nvc0_pcopy_code[] = { 0xbb0f08f4, 0x74b60276, 0x0057bb03, +/* 0x013f: dispatch_valid_mthd */ 0xbbdf0ef4, 0x44b60246, 0x0045bb03, @@ -229,24 +275,33 @@ uint32_t nvc0_pcopy_code[] = { 0x64b00146, 0x091bf400, 0xf4005380, +/* 0x0166: dispatch_cmd */ 0x32f4300e, 0xf455f901, 0x0ef40c01, +/* 0x0171: dispatch_invalid_bitfield */ 0x0225f025, +/* 0x0174: dispatch_illegal_mthd */ +/* 0x0177: dispatch_error */ 0xf10125f0, 0xd0100047, 0x43d00042, 0x4027f040, +/* 0x0187: hostirq_wait */ 0xcf0002d0, 0x24f08002, 0x0024b040, +/* 0x0193: dispatch_done */ 0xf1f71bf4, 0xf01d0027, 0x23d00137, +/* 0x019f: cmd_nop */ 0xf800f800, +/* 0x01a1: cmd_pm_trigger */ 0x0027f100, 0xf034bd22, 0x23d00233, +/* 0x01af: cmd_exec_set_format */ 0xf400f800, 0x01b0f030, 0x0101b000, @@ -258,20 +313,26 @@ uint32_t nvc0_pcopy_code[] = { 0x3847c701, 0xf40170b6, 0x84bd0232, +/* 0x01da: ncomp_loop */ 0x4ac494bd, 0x0445b60f, +/* 0x01e2: bpc_loop */ 0xa430b4bd, 0x0f18f404, 0xbbc0a5ff, 0x31f400cb, 0x220ef402, +/* 0x01f4: cmp_c0 */ 0xf00c1bf4, 0xcbbb10c7, 0x160ef400, +/* 0x0200: cmp_c1 */ 0xf406a430, 0xc7f00c18, 0x00cbbb14, +/* 0x020f: cmp_zero */ 0xf1070ef4, +/* 0x0213: bpc_next */ 0x380080c7, 0x80b601c8, 0x01b0b601, @@ -283,6 +344,7 @@ uint32_t nvc0_pcopy_code[] = { 0x1d08980e, 0xf40068fd, 0x64bd0502, +/* 0x023c: dst_xcnt */ 0x800075fd, 0x78fd1907, 0x1057f100, @@ -307,15 +369,18 @@ uint32_t nvc0_pcopy_code[] = { 0x1c069800, 0xf44056d0, 0x00f81030, +/* 0x029c: cmd_exec_set_surface_tiled */ 0xc7075798, 0x78c76879, 0x0380b664, 0xb06077c7, 0x1bf40e76, 0x0477f009, +/* 0x02b7: xtile64 */ 0xf00f0ef4, 0x70b6027c, 0x0947fd11, +/* 0x02c3: xtileok */ 0x980677f0, 0x5b980c5a, 0x00abfd0e, @@ -374,6 +439,7 @@ uint32_t nvc0_pcopy_code[] = { 0xb70067d0, 0xd0040060, 0x00f80068, +/* 0x03a8: cmd_exec_set_surface_linear */ 0xb7026cf0, 0xb6020260, 0x57980864, @@ -384,12 +450,15 @@ uint32_t nvc0_pcopy_code[] = { 0x0060b700, 0x06579804, 0xf80067d0, +/* 0x03d1: cmd_exec_wait */ 0xf900f900, 0x0007f110, 0x0604b608, +/* 0x03dc: loop */ 0xf00001cf, 0x1bf40114, 0xfc10fcfa, +/* 0x03eb: cmd_exec_query */ 0xc800f800, 0x1bf40d34, 0xd121f570, @@ -419,6 +488,7 @@ uint32_t nvc0_pcopy_code[] = { 0x0153f026, 0x080047f1, 0xd00644b6, +/* 0x045e: query_counter */ 0x21f50045, 0x47f103d1, 0x44b6080c, @@ -442,11 +512,13 @@ uint32_t nvc0_pcopy_code[] = { 0x080047f1, 0xd00644b6, 0x00f80045, +/* 0x04b8: cmd_exec */ 0x03d121f5, 0xf4003fc8, 0x21f50e0b, 0x47f101af, 0x0ef40200, +/* 0x04cd: cmd_exec_no_format */ 0x1067f11e, 0x0664b608, 0x800177f0, @@ -454,18 +526,23 @@ uint32_t nvc0_pcopy_code[] = { 0x1d079819, 0xd00067d0, 0x44bd4067, +/* 0x04e8: cmd_exec_init_src_surface */ 0xbd0232f4, 0x043fc854, 0xf50a0bf4, 0xf403a821, +/* 0x04fa: src_tiled */ 0x21f50a0e, 0x49f0029c, +/* 0x0501: cmd_exec_init_dst_surface */ 0x0231f407, 0xc82c57f0, 0x0bf4083f, 0xa821f50a, 0x0a0ef403, +/* 0x0514: dst_tiled */ 0x029c21f5, +/* 0x051b: cmd_exec_kick */ 0xf10849f0, 0xb6080057, 0x06980654, @@ -475,7 +552,9 @@ uint32_t nvc0_pcopy_code[] = { 0x54d00546, 0x0c3fc800, 0xf5070bf4, +/* 0x053f: cmd_exec_done */ 0xf803eb21, +/* 0x0541: cmd_wrcache_flush */ 0x0027f100, 0xf034bd22, 0x23d00133, diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c index 26a9960..5bf5503 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fb.c +++ b/drivers/gpu/drm/nouveau/nvc0_fb.c @@ -1,5 +1,5 @@ /* - * Copyright 2010 Red Hat Inc. + * Copyright 2011 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -23,16 +23,107 @@ */ #include "drmP.h" - +#include "drm.h" #include "nouveau_drv.h" +#include "nouveau_drm.h" + +struct nvc0_fb_priv { + struct page *r100c10_page; + dma_addr_t r100c10; +}; + +static inline void +nvc0_mfb_subp_isr(struct drm_device *dev, int unit, int subp) +{ + u32 subp_base = 0x141000 + (unit * 0x2000) + (subp * 0x400); + u32 stat = nv_rd32(dev, subp_base + 0x020); + + if (stat) { + NV_INFO(dev, "PMFB%d_SUBP%d: 0x%08x\n", unit, subp, stat); + nv_wr32(dev, subp_base + 0x020, stat); + } +} + +static void +nvc0_mfb_isr(struct drm_device *dev) +{ + u32 units = nv_rd32(dev, 0x00017c); + while (units) { + u32 subp, unit = ffs(units) - 1; + for (subp = 0; subp < 2; subp++) + nvc0_mfb_subp_isr(dev, unit, subp); + units &= ~(1 << unit); + } +} + +static void +nvc0_fb_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + struct nvc0_fb_priv *priv = pfb->priv; + + nouveau_irq_unregister(dev, 25); + + if (priv->r100c10_page) { + pci_unmap_page(dev->pdev, priv->r100c10, PAGE_SIZE, + PCI_DMA_BIDIRECTIONAL); + __free_page(priv->r100c10_page); + } + + kfree(priv); + pfb->priv = NULL; +} + +static int +nvc0_fb_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + struct nvc0_fb_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + pfb->priv = priv; + + priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!priv->r100c10_page) { + nvc0_fb_destroy(dev); + return -ENOMEM; + } + + priv->r100c10 = pci_map_page(dev->pdev, priv->r100c10_page, 0, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(dev->pdev, priv->r100c10)) { + nvc0_fb_destroy(dev); + return -EFAULT; + } + + nouveau_irq_register(dev, 25, nvc0_mfb_isr); + return 0; +} int nvc0_fb_init(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_fb_priv *priv; + int ret; + + if (!dev_priv->engine.fb.priv) { + ret = nvc0_fb_create(dev); + if (ret) + return ret; + } + priv = dev_priv->engine.fb.priv; + + nv_wr32(dev, 0x100c10, priv->r100c10 >> 8); return 0; } void nvc0_fb_takedown(struct drm_device *dev) { + nvc0_fb_destroy(dev); } diff --git a/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/drivers/gpu/drm/nouveau/nvc0_fbcon.c index fa5d4c2..a495e48 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fbcon.c +++ b/drivers/gpu/drm/nouveau/nvc0_fbcon.c @@ -159,7 +159,7 @@ nvc0_fbcon_accel_init(struct fb_info *info) struct drm_device *dev = nfbdev->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = dev_priv->channel; - struct nouveau_bo *nvbo = nfbdev->nouveau_fb.nvbo; + struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb; int ret, format; ret = nouveau_gpuobj_gr_new(chan, 0x902d, 0x902d); @@ -203,8 +203,8 @@ nvc0_fbcon_accel_init(struct fb_info *info) BEGIN_NVC0(chan, 2, NvSub2D, 0x0000, 1); OUT_RING (chan, 0x0000902d); BEGIN_NVC0(chan, 2, NvSub2D, 0x0104, 2); - OUT_RING (chan, upper_32_bits(chan->notifier_bo->bo.offset)); - OUT_RING (chan, lower_32_bits(chan->notifier_bo->bo.offset)); + OUT_RING (chan, upper_32_bits(chan->notifier_vma.offset)); + OUT_RING (chan, lower_32_bits(chan->notifier_vma.offset)); BEGIN_NVC0(chan, 2, NvSub2D, 0x0290, 1); OUT_RING (chan, 0); BEGIN_NVC0(chan, 2, NvSub2D, 0x0888, 1); @@ -249,8 +249,8 @@ nvc0_fbcon_accel_init(struct fb_info *info) OUT_RING (chan, info->fix.line_length); OUT_RING (chan, info->var.xres_virtual); OUT_RING (chan, info->var.yres_virtual); - OUT_RING (chan, upper_32_bits(nvbo->vma.offset)); - OUT_RING (chan, lower_32_bits(nvbo->vma.offset)); + OUT_RING (chan, upper_32_bits(fb->vma.offset)); + OUT_RING (chan, lower_32_bits(fb->vma.offset)); BEGIN_NVC0(chan, 2, NvSub2D, 0x0230, 10); OUT_RING (chan, format); OUT_RING (chan, 1); @@ -260,8 +260,8 @@ nvc0_fbcon_accel_init(struct fb_info *info) OUT_RING (chan, info->fix.line_length); OUT_RING (chan, info->var.xres_virtual); OUT_RING (chan, info->var.yres_virtual); - OUT_RING (chan, upper_32_bits(nvbo->vma.offset)); - OUT_RING (chan, lower_32_bits(nvbo->vma.offset)); + OUT_RING (chan, upper_32_bits(fb->vma.offset)); + OUT_RING (chan, lower_32_bits(fb->vma.offset)); FIRE_RING (chan); return 0; diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c index fb4f594..dcbe0d5 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fifo.c +++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c @@ -210,10 +210,10 @@ nvc0_fifo_unload_context(struct drm_device *dev) int i; for (i = 0; i < 128; i++) { - if (!(nv_rd32(dev, 0x003004 + (i * 4)) & 1)) + if (!(nv_rd32(dev, 0x003004 + (i * 8)) & 1)) continue; - nv_mask(dev, 0x003004 + (i * 4), 0x00000001, 0x00000000); + nv_mask(dev, 0x003004 + (i * 8), 0x00000001, 0x00000000); nv_wr32(dev, 0x002634, i); if (!nv_wait(dev, 0x002634, 0xffffffff, i)) { NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n", @@ -322,7 +322,7 @@ nvc0_fifo_init(struct drm_device *dev) } /* PSUBFIFO[n] */ - for (i = 0; i < 3; i++) { + for (i = 0; i < priv->spoon_nr; i++) { nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */ diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index ca6db20..ecfafd7 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -23,12 +23,40 @@ */ #include <linux/firmware.h> +#include <linux/module.h> #include "drmP.h" #include "nouveau_drv.h" #include "nouveau_mm.h" + #include "nvc0_graph.h" +#include "nvc0_grhub.fuc.h" +#include "nvc0_grgpc.fuc.h" + +static void +nvc0_graph_ctxctl_debug_unit(struct drm_device *dev, u32 base) +{ + NV_INFO(dev, "PGRAPH: %06x - done 0x%08x\n", base, + nv_rd32(dev, base + 0x400)); + NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, + nv_rd32(dev, base + 0x800), nv_rd32(dev, base + 0x804), + nv_rd32(dev, base + 0x808), nv_rd32(dev, base + 0x80c)); + NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, + nv_rd32(dev, base + 0x810), nv_rd32(dev, base + 0x814), + nv_rd32(dev, base + 0x818), nv_rd32(dev, base + 0x81c)); +} + +static void +nvc0_graph_ctxctl_debug(struct drm_device *dev) +{ + u32 gpcnr = nv_rd32(dev, 0x409604) & 0xffff; + u32 gpc; + + nvc0_graph_ctxctl_debug_unit(dev, 0x409000); + for (gpc = 0; gpc < gpcnr; gpc++) + nvc0_graph_ctxctl_debug_unit(dev, 0x502000 + (gpc * 0x8000)); +} static int nvc0_graph_load_context(struct nouveau_channel *chan) @@ -72,24 +100,44 @@ nvc0_graph_construct_context(struct nouveau_channel *chan) if (!ctx) return -ENOMEM; - nvc0_graph_load_context(chan); - - nv_wo32(grch->grctx, 0x1c, 1); - nv_wo32(grch->grctx, 0x20, 0); - nv_wo32(grch->grctx, 0x28, 0); - nv_wo32(grch->grctx, 0x2c, 0); - dev_priv->engine.instmem.flush(dev); - - ret = nvc0_grctx_generate(chan); - if (ret) { - kfree(ctx); - return ret; + if (!nouveau_ctxfw) { + nv_wr32(dev, 0x409840, 0x80000000); + nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12); + nv_wr32(dev, 0x409504, 0x00000001); + if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) { + NV_ERROR(dev, "PGRAPH: HUB_SET_CHAN timeout\n"); + nvc0_graph_ctxctl_debug(dev); + ret = -EBUSY; + goto err; + } + } else { + nvc0_graph_load_context(chan); + + nv_wo32(grch->grctx, 0x1c, 1); + nv_wo32(grch->grctx, 0x20, 0); + nv_wo32(grch->grctx, 0x28, 0); + nv_wo32(grch->grctx, 0x2c, 0); + dev_priv->engine.instmem.flush(dev); } - ret = nvc0_graph_unload_context_to(dev, chan->ramin->vinst); - if (ret) { - kfree(ctx); - return ret; + ret = nvc0_grctx_generate(chan); + if (ret) + goto err; + + if (!nouveau_ctxfw) { + nv_wr32(dev, 0x409840, 0x80000000); + nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12); + nv_wr32(dev, 0x409504, 0x00000002); + if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) { + NV_ERROR(dev, "PGRAPH: HUB_CTX_SAVE timeout\n"); + nvc0_graph_ctxctl_debug(dev); + ret = -EBUSY; + goto err; + } + } else { + ret = nvc0_graph_unload_context_to(dev, chan->ramin->vinst); + if (ret) + goto err; } for (i = 0; i < priv->grctx_size; i += 4) @@ -97,6 +145,10 @@ nvc0_graph_construct_context(struct nouveau_channel *chan) priv->grctx_vals = ctx; return 0; + +err: + kfree(ctx); + return ret; } static int @@ -105,64 +157,87 @@ nvc0_graph_create_context_mmio_list(struct nouveau_channel *chan) struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR); struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; int i = 0, gpc, tp, ret; - u32 magic; - ret = nouveau_gpuobj_new(dev, NULL, 0x2000, 256, NVOBJ_FLAG_VM, + ret = nouveau_gpuobj_new(dev, chan, 0x2000, 256, NVOBJ_FLAG_VM, &grch->unk408004); if (ret) return ret; - ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 256, NVOBJ_FLAG_VM, + ret = nouveau_gpuobj_new(dev, chan, 0x8000, 256, NVOBJ_FLAG_VM, &grch->unk40800c); if (ret) return ret; - ret = nouveau_gpuobj_new(dev, NULL, 384 * 1024, 4096, + ret = nouveau_gpuobj_new(dev, chan, 384 * 1024, 4096, NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER, &grch->unk418810); if (ret) return ret; - ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0, NVOBJ_FLAG_VM, + ret = nouveau_gpuobj_new(dev, chan, 0x1000, 0, NVOBJ_FLAG_VM, &grch->mmio); if (ret) return ret; nv_wo32(grch->mmio, i++ * 4, 0x00408004); - nv_wo32(grch->mmio, i++ * 4, grch->unk408004->vinst >> 8); + nv_wo32(grch->mmio, i++ * 4, grch->unk408004->linst >> 8); nv_wo32(grch->mmio, i++ * 4, 0x00408008); nv_wo32(grch->mmio, i++ * 4, 0x80000018); nv_wo32(grch->mmio, i++ * 4, 0x0040800c); - nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->vinst >> 8); + nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->linst >> 8); nv_wo32(grch->mmio, i++ * 4, 0x00408010); nv_wo32(grch->mmio, i++ * 4, 0x80000000); nv_wo32(grch->mmio, i++ * 4, 0x00418810); - nv_wo32(grch->mmio, i++ * 4, 0x80000000 | grch->unk418810->vinst >> 12); + nv_wo32(grch->mmio, i++ * 4, 0x80000000 | grch->unk418810->linst >> 12); nv_wo32(grch->mmio, i++ * 4, 0x00419848); - nv_wo32(grch->mmio, i++ * 4, 0x10000000 | grch->unk418810->vinst >> 12); + nv_wo32(grch->mmio, i++ * 4, 0x10000000 | grch->unk418810->linst >> 12); nv_wo32(grch->mmio, i++ * 4, 0x00419004); - nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->vinst >> 8); + nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->linst >> 8); nv_wo32(grch->mmio, i++ * 4, 0x00419008); nv_wo32(grch->mmio, i++ * 4, 0x00000000); nv_wo32(grch->mmio, i++ * 4, 0x00418808); - nv_wo32(grch->mmio, i++ * 4, grch->unk408004->vinst >> 8); + nv_wo32(grch->mmio, i++ * 4, grch->unk408004->linst >> 8); nv_wo32(grch->mmio, i++ * 4, 0x0041880c); nv_wo32(grch->mmio, i++ * 4, 0x80000018); - magic = 0x02180000; - nv_wo32(grch->mmio, i++ * 4, 0x00405830); - nv_wo32(grch->mmio, i++ * 4, magic); - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - for (tp = 0; tp < priv->tp_nr[gpc]; tp++, magic += 0x02fc) { - u32 reg = 0x504520 + (gpc * 0x8000) + (tp * 0x0800); - nv_wo32(grch->mmio, i++ * 4, reg); - nv_wo32(grch->mmio, i++ * 4, magic); + if (dev_priv->chipset != 0xc1) { + u32 magic = 0x02180000; + nv_wo32(grch->mmio, i++ * 4, 0x00405830); + nv_wo32(grch->mmio, i++ * 4, magic); + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + for (tp = 0; tp < priv->tp_nr[gpc]; tp++) { + u32 reg = TP_UNIT(gpc, tp, 0x520); + nv_wo32(grch->mmio, i++ * 4, reg); + nv_wo32(grch->mmio, i++ * 4, magic); + magic += 0x0324; + } + } + } else { + u32 magic = 0x02180000; + nv_wo32(grch->mmio, i++ * 4, 0x00405830); + nv_wo32(grch->mmio, i++ * 4, magic | 0x0000218); + nv_wo32(grch->mmio, i++ * 4, 0x004064c4); + nv_wo32(grch->mmio, i++ * 4, 0x0086ffff); + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + for (tp = 0; tp < priv->tp_nr[gpc]; tp++) { + u32 reg = TP_UNIT(gpc, tp, 0x520); + nv_wo32(grch->mmio, i++ * 4, reg); + nv_wo32(grch->mmio, i++ * 4, (1 << 28) | magic); + magic += 0x0324; + } + for (tp = 0; tp < priv->tp_nr[gpc]; tp++) { + u32 reg = TP_UNIT(gpc, tp, 0x544); + nv_wo32(grch->mmio, i++ * 4, reg); + nv_wo32(grch->mmio, i++ * 4, magic); + magic += 0x0324; + } } } @@ -186,7 +261,7 @@ nvc0_graph_context_new(struct nouveau_channel *chan, int engine) return -ENOMEM; chan->engctx[NVOBJ_ENGINE_GR] = grch; - ret = nouveau_gpuobj_new(dev, NULL, priv->grctx_size, 256, + ret = nouveau_gpuobj_new(dev, chan, priv->grctx_size, 256, NVOBJ_FLAG_VM | NVOBJ_FLAG_ZERO_ALLOC, &grch->grctx); if (ret) @@ -197,8 +272,8 @@ nvc0_graph_context_new(struct nouveau_channel *chan, int engine) if (ret) goto error; - nv_wo32(chan->ramin, 0x0210, lower_32_bits(grctx->vinst) | 4); - nv_wo32(chan->ramin, 0x0214, upper_32_bits(grctx->vinst)); + nv_wo32(chan->ramin, 0x0210, lower_32_bits(grctx->linst) | 4); + nv_wo32(chan->ramin, 0x0214, upper_32_bits(grctx->linst)); pinstmem->flush(dev); if (!priv->grctx_vals) { @@ -210,15 +285,20 @@ nvc0_graph_context_new(struct nouveau_channel *chan, int engine) for (i = 0; i < priv->grctx_size; i += 4) nv_wo32(grctx, i, priv->grctx_vals[i / 4]); - nv_wo32(grctx, 0xf4, 0); - nv_wo32(grctx, 0xf8, 0); - nv_wo32(grctx, 0x10, grch->mmio_nr); - nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->vinst)); - nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->vinst)); - nv_wo32(grctx, 0x1c, 1); - nv_wo32(grctx, 0x20, 0); - nv_wo32(grctx, 0x28, 0); - nv_wo32(grctx, 0x2c, 0); + if (!nouveau_ctxfw) { + nv_wo32(grctx, 0x00, grch->mmio_nr); + nv_wo32(grctx, 0x04, grch->mmio->linst >> 8); + } else { + nv_wo32(grctx, 0xf4, 0); + nv_wo32(grctx, 0xf8, 0); + nv_wo32(grctx, 0x10, grch->mmio_nr); + nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->linst)); + nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->linst)); + nv_wo32(grctx, 0x1c, 1); + nv_wo32(grctx, 0x20, 0); + nv_wo32(grctx, 0x28, 0); + nv_wo32(grctx, 0x2c, 0); + } pinstmem->flush(dev); return 0; @@ -248,7 +328,7 @@ nvc0_graph_object_new(struct nouveau_channel *chan, int engine, } static int -nvc0_graph_fini(struct drm_device *dev, int engine) +nvc0_graph_fini(struct drm_device *dev, int engine, bool suspend) { return 0; } @@ -296,10 +376,13 @@ static void nvc0_graph_init_gpc_0(struct drm_device *dev) { struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tp_total); u32 data[TP_MAX / 8]; u8 tpnr[GPC_MAX]; int i, gpc, tpc; + nv_wr32(dev, TP_UNIT(0, 0, 0x5c), 1); /* affects TFB offset queries */ + /* * TP ROP UNKVAL(magic_not_rop_nr) * 450: 4/0/0/0 2 3 @@ -307,13 +390,6 @@ nvc0_graph_init_gpc_0(struct drm_device *dev) * 465: 3/4/4/0 4 7 * 470: 3/3/4/4 5 5 * 480: 3/4/4/4 6 6 - * - * magicgpc918 - * 450: 00200000 00000000001000000000000000000000 - * 460: 00124925 00000000000100100100100100100101 - * 465: 000ba2e9 00000000000010111010001011101001 - * 470: 00092493 00000000000010010010010010010011 - * 480: 00088889 00000000000010001000100010001001 */ memset(data, 0x00, sizeof(data)); @@ -336,11 +412,11 @@ nvc0_graph_init_gpc_0(struct drm_device *dev) nv_wr32(dev, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 | priv->tp_nr[gpc]); nv_wr32(dev, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tp_total); - nv_wr32(dev, GPC_UNIT(gpc, 0x0918), priv->magicgpc918); + nv_wr32(dev, GPC_UNIT(gpc, 0x0918), magicgpc918); } - nv_wr32(dev, GPC_BCAST(0x1bd4), priv->magicgpc918); - nv_wr32(dev, GPC_BCAST(0x08ac), priv->rop_nr); + nv_wr32(dev, GPC_BCAST(0x1bd4), magicgpc918); + nv_wr32(dev, GPC_BCAST(0x08ac), nv_rd32(dev, 0x100800)); } static void @@ -419,8 +495,51 @@ nvc0_graph_init_fuc(struct drm_device *dev, u32 fuc_base, static int nvc0_graph_init_ctxctl(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); u32 r000260; + int i; + + if (!nouveau_ctxfw) { + /* load HUB microcode */ + r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000); + nv_wr32(dev, 0x4091c0, 0x01000000); + for (i = 0; i < sizeof(nvc0_grhub_data) / 4; i++) + nv_wr32(dev, 0x4091c4, nvc0_grhub_data[i]); + + nv_wr32(dev, 0x409180, 0x01000000); + for (i = 0; i < sizeof(nvc0_grhub_code) / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(dev, 0x409188, i >> 6); + nv_wr32(dev, 0x409184, nvc0_grhub_code[i]); + } + + /* load GPC microcode */ + nv_wr32(dev, 0x41a1c0, 0x01000000); + for (i = 0; i < sizeof(nvc0_grgpc_data) / 4; i++) + nv_wr32(dev, 0x41a1c4, nvc0_grgpc_data[i]); + + nv_wr32(dev, 0x41a180, 0x01000000); + for (i = 0; i < sizeof(nvc0_grgpc_code) / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(dev, 0x41a188, i >> 6); + nv_wr32(dev, 0x41a184, nvc0_grgpc_code[i]); + } + nv_wr32(dev, 0x000260, r000260); + + /* start HUB ucode running, it'll init the GPCs */ + nv_wr32(dev, 0x409800, dev_priv->chipset); + nv_wr32(dev, 0x40910c, 0x00000000); + nv_wr32(dev, 0x409100, 0x00000002); + if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) { + NV_ERROR(dev, "PGRAPH: HUB_INIT timed out\n"); + nvc0_graph_ctxctl_debug(dev); + return -EBUSY; + } + + priv->grctx_size = nv_rd32(dev, 0x409804); + return 0; + } /* load fuc microcode */ r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000); @@ -528,6 +647,22 @@ nvc0_graph_isr_chid(struct drm_device *dev, u64 inst) } static void +nvc0_graph_ctxctl_isr(struct drm_device *dev) +{ + u32 ustat = nv_rd32(dev, 0x409c18); + + if (ustat & 0x00000001) + NV_INFO(dev, "PGRAPH: CTXCTRL ucode error\n"); + if (ustat & 0x00080000) + NV_INFO(dev, "PGRAPH: CTXCTRL watchdog timeout\n"); + if (ustat & ~0x00080001) + NV_INFO(dev, "PGRAPH: CTXCTRL 0x%08x\n", ustat); + + nvc0_graph_ctxctl_debug(dev); + nv_wr32(dev, 0x409c20, ustat); +} + +static void nvc0_graph_isr(struct drm_device *dev) { u64 inst = (u64)(nv_rd32(dev, 0x409b00) & 0x0fffffff) << 12; @@ -578,11 +713,7 @@ nvc0_graph_isr(struct drm_device *dev) } if (stat & 0x00080000) { - u32 ustat = nv_rd32(dev, 0x409c18); - - NV_INFO(dev, "PGRAPH: CTXCTRL ustat 0x%08x\n", ustat); - - nv_wr32(dev, 0x409c20, ustat); + nvc0_graph_ctxctl_isr(dev); nv_wr32(dev, 0x400100, 0x00080000); stat &= ~0x00080000; } @@ -595,22 +726,6 @@ nvc0_graph_isr(struct drm_device *dev) nv_wr32(dev, 0x400500, 0x00010001); } -static void -nvc0_runk140_isr(struct drm_device *dev) -{ - u32 units = nv_rd32(dev, 0x00017c) & 0x1f; - - while (units) { - u32 unit = ffs(units) - 1; - u32 reg = 0x140000 + unit * 0x2000; - u32 st0 = nv_mask(dev, reg + 0x1020, 0, 0); - u32 st1 = nv_mask(dev, reg + 0x1420, 0, 0); - - NV_INFO(dev, "PRUNK140: %d 0x%08x 0x%08x\n", unit, st0, st1); - units &= ~(1 << unit); - } -} - static int nvc0_graph_create_fw(struct drm_device *dev, const char *fwname, struct nvc0_graph_fuc *fuc) @@ -651,13 +766,14 @@ nvc0_graph_destroy(struct drm_device *dev, int engine) { struct nvc0_graph_priv *priv = nv_engine(dev, engine); - nvc0_graph_destroy_fw(&priv->fuc409c); - nvc0_graph_destroy_fw(&priv->fuc409d); - nvc0_graph_destroy_fw(&priv->fuc41ac); - nvc0_graph_destroy_fw(&priv->fuc41ad); + if (nouveau_ctxfw) { + nvc0_graph_destroy_fw(&priv->fuc409c); + nvc0_graph_destroy_fw(&priv->fuc409d); + nvc0_graph_destroy_fw(&priv->fuc41ac); + nvc0_graph_destroy_fw(&priv->fuc41ad); + } nouveau_irq_unregister(dev, 12); - nouveau_irq_unregister(dev, 25); nouveau_gpuobj_ref(NULL, &priv->unk4188b8); nouveau_gpuobj_ref(NULL, &priv->unk4188b4); @@ -675,13 +791,10 @@ nvc0_graph_create(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvc0_graph_priv *priv; int ret, gpc, i; + u32 fermi; - switch (dev_priv->chipset) { - case 0xc0: - case 0xc3: - case 0xc4: - break; - default: + fermi = nvc0_graph_class(dev); + if (!fermi) { NV_ERROR(dev, "PGRAPH: unsupported chipset, please report!\n"); return 0; } @@ -699,17 +812,18 @@ nvc0_graph_create(struct drm_device *dev) NVOBJ_ENGINE_ADD(dev, GR, &priv->base); nouveau_irq_register(dev, 12, nvc0_graph_isr); - nouveau_irq_register(dev, 25, nvc0_runk140_isr); - if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) || - nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) || - nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) || - nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) { - ret = 0; - goto error; + if (nouveau_ctxfw) { + NV_INFO(dev, "PGRAPH: using external firmware\n"); + if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) || + nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) || + nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) || + nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) { + ret = 0; + goto error; + } } - ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4); if (ret) goto error; @@ -735,25 +849,31 @@ nvc0_graph_create(struct drm_device *dev) case 0xc0: if (priv->tp_total == 11) { /* 465, 3/4/4/0, 4 */ priv->magic_not_rop_nr = 0x07; - /* filled values up to tp_total, the rest 0 */ - priv->magicgpc918 = 0x000ba2e9; } else if (priv->tp_total == 14) { /* 470, 3/3/4/4, 5 */ priv->magic_not_rop_nr = 0x05; - priv->magicgpc918 = 0x00092493; } else if (priv->tp_total == 15) { /* 480, 3/4/4/4, 6 */ priv->magic_not_rop_nr = 0x06; - priv->magicgpc918 = 0x00088889; } break; case 0xc3: /* 450, 4/0/0/0, 2 */ priv->magic_not_rop_nr = 0x03; - priv->magicgpc918 = 0x00200000; break; case 0xc4: /* 460, 3/4/0/0, 4 */ priv->magic_not_rop_nr = 0x01; - priv->magicgpc918 = 0x00124925; + break; + case 0xc1: /* 2/0/0/0, 1 */ + priv->magic_not_rop_nr = 0x01; + break; + case 0xc8: /* 4/4/3/4, 5 */ + priv->magic_not_rop_nr = 0x06; + break; + case 0xce: /* 4/4/0/0, 4 */ + priv->magic_not_rop_nr = 0x03; + break; + case 0xcf: /* 4/0/0/0, 3 */ + priv->magic_not_rop_nr = 0x03; break; } @@ -763,13 +883,16 @@ nvc0_graph_create(struct drm_device *dev) priv->tp_nr[3], priv->rop_nr); /* use 0xc3's values... */ priv->magic_not_rop_nr = 0x03; - priv->magicgpc918 = 0x00200000; } NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */ NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */ NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip); NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */ + if (fermi >= 0x9197) + NVOBJ_CLASS(dev, 0x9197, GR); /* 3D (NVC1-) */ + if (fermi >= 0x9297) + NVOBJ_CLASS(dev, 0x9297, GR); /* 3D (NVC8-) */ NVOBJ_CLASS(dev, 0x90c0, GR); /* COMPUTE */ return 0; @@ -777,20 +900,3 @@ error: nvc0_graph_destroy(dev, NVOBJ_ENGINE_GR); return ret; } - -MODULE_FIRMWARE("nouveau/nvc0_fuc409c"); -MODULE_FIRMWARE("nouveau/nvc0_fuc409d"); -MODULE_FIRMWARE("nouveau/nvc0_fuc41ac"); -MODULE_FIRMWARE("nouveau/nvc0_fuc41ad"); -MODULE_FIRMWARE("nouveau/nvc3_fuc409c"); -MODULE_FIRMWARE("nouveau/nvc3_fuc409d"); -MODULE_FIRMWARE("nouveau/nvc3_fuc41ac"); -MODULE_FIRMWARE("nouveau/nvc3_fuc41ad"); -MODULE_FIRMWARE("nouveau/nvc4_fuc409c"); -MODULE_FIRMWARE("nouveau/nvc4_fuc409d"); -MODULE_FIRMWARE("nouveau/nvc4_fuc41ac"); -MODULE_FIRMWARE("nouveau/nvc4_fuc41ad"); -MODULE_FIRMWARE("nouveau/fuc409c"); -MODULE_FIRMWARE("nouveau/fuc409d"); -MODULE_FIRMWARE("nouveau/fuc41ac"); -MODULE_FIRMWARE("nouveau/fuc41ad"); diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.h b/drivers/gpu/drm/nouveau/nvc0_graph.h index f5d184e0..636fe98 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.h +++ b/drivers/gpu/drm/nouveau/nvc0_graph.h @@ -57,8 +57,7 @@ struct nvc0_graph_priv { struct nouveau_gpuobj *unk4188b4; struct nouveau_gpuobj *unk4188b8; - u8 magic_not_rop_nr; - u32 magicgpc918; + u8 magic_not_rop_nr; }; struct nvc0_graph_chan { @@ -72,4 +71,26 @@ struct nvc0_graph_chan { int nvc0_grctx_generate(struct nouveau_channel *); +/* nvc0_graph.c uses this also to determine supported chipsets */ +static inline u32 +nvc0_graph_class(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + switch (dev_priv->chipset) { + case 0xc0: + case 0xc3: + case 0xc4: + case 0xce: /* guess, mmio trace shows only 0x9097 state */ + case 0xcf: /* guess, mmio trace shows only 0x9097 state */ + return 0x9097; + case 0xc1: + return 0x9197; + case 0xc8: + return 0x9297; + default: + return 0; + } +} + #endif diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c index 6df0661..96b0b93 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grctx.c +++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c @@ -45,6 +45,9 @@ nv_mthd(struct drm_device *dev, u32 class, u32 mthd, u32 data) static void nvc0_grctx_generate_9097(struct drm_device *dev) { + u32 fermi = nvc0_graph_class(dev); + u32 mthd; + nv_mthd(dev, 0x9097, 0x0800, 0x00000000); nv_mthd(dev, 0x9097, 0x0840, 0x00000000); nv_mthd(dev, 0x9097, 0x0880, 0x00000000); @@ -824,134 +827,10 @@ nvc0_grctx_generate_9097(struct drm_device *dev) nv_mthd(dev, 0x9097, 0x1eb8, 0x00000001); nv_mthd(dev, 0x9097, 0x1ed8, 0x00000001); nv_mthd(dev, 0x9097, 0x1ef8, 0x00000001); - nv_mthd(dev, 0x9097, 0x3400, 0x00000000); - nv_mthd(dev, 0x9097, 0x3404, 0x00000000); - nv_mthd(dev, 0x9097, 0x3408, 0x00000000); - nv_mthd(dev, 0x9097, 0x340c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3410, 0x00000000); - nv_mthd(dev, 0x9097, 0x3414, 0x00000000); - nv_mthd(dev, 0x9097, 0x3418, 0x00000000); - nv_mthd(dev, 0x9097, 0x341c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3420, 0x00000000); - nv_mthd(dev, 0x9097, 0x3424, 0x00000000); - nv_mthd(dev, 0x9097, 0x3428, 0x00000000); - nv_mthd(dev, 0x9097, 0x342c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3430, 0x00000000); - nv_mthd(dev, 0x9097, 0x3434, 0x00000000); - nv_mthd(dev, 0x9097, 0x3438, 0x00000000); - nv_mthd(dev, 0x9097, 0x343c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3440, 0x00000000); - nv_mthd(dev, 0x9097, 0x3444, 0x00000000); - nv_mthd(dev, 0x9097, 0x3448, 0x00000000); - nv_mthd(dev, 0x9097, 0x344c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3450, 0x00000000); - nv_mthd(dev, 0x9097, 0x3454, 0x00000000); - nv_mthd(dev, 0x9097, 0x3458, 0x00000000); - nv_mthd(dev, 0x9097, 0x345c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3460, 0x00000000); - nv_mthd(dev, 0x9097, 0x3464, 0x00000000); - nv_mthd(dev, 0x9097, 0x3468, 0x00000000); - nv_mthd(dev, 0x9097, 0x346c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3470, 0x00000000); - nv_mthd(dev, 0x9097, 0x3474, 0x00000000); - nv_mthd(dev, 0x9097, 0x3478, 0x00000000); - nv_mthd(dev, 0x9097, 0x347c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3480, 0x00000000); - nv_mthd(dev, 0x9097, 0x3484, 0x00000000); - nv_mthd(dev, 0x9097, 0x3488, 0x00000000); - nv_mthd(dev, 0x9097, 0x348c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3490, 0x00000000); - nv_mthd(dev, 0x9097, 0x3494, 0x00000000); - nv_mthd(dev, 0x9097, 0x3498, 0x00000000); - nv_mthd(dev, 0x9097, 0x349c, 0x00000000); - nv_mthd(dev, 0x9097, 0x34a0, 0x00000000); - nv_mthd(dev, 0x9097, 0x34a4, 0x00000000); - nv_mthd(dev, 0x9097, 0x34a8, 0x00000000); - nv_mthd(dev, 0x9097, 0x34ac, 0x00000000); - nv_mthd(dev, 0x9097, 0x34b0, 0x00000000); - nv_mthd(dev, 0x9097, 0x34b4, 0x00000000); - nv_mthd(dev, 0x9097, 0x34b8, 0x00000000); - nv_mthd(dev, 0x9097, 0x34bc, 0x00000000); - nv_mthd(dev, 0x9097, 0x34c0, 0x00000000); - nv_mthd(dev, 0x9097, 0x34c4, 0x00000000); - nv_mthd(dev, 0x9097, 0x34c8, 0x00000000); - nv_mthd(dev, 0x9097, 0x34cc, 0x00000000); - nv_mthd(dev, 0x9097, 0x34d0, 0x00000000); - nv_mthd(dev, 0x9097, 0x34d4, 0x00000000); - nv_mthd(dev, 0x9097, 0x34d8, 0x00000000); - nv_mthd(dev, 0x9097, 0x34dc, 0x00000000); - nv_mthd(dev, 0x9097, 0x34e0, 0x00000000); - nv_mthd(dev, 0x9097, 0x34e4, 0x00000000); - nv_mthd(dev, 0x9097, 0x34e8, 0x00000000); - nv_mthd(dev, 0x9097, 0x34ec, 0x00000000); - nv_mthd(dev, 0x9097, 0x34f0, 0x00000000); - nv_mthd(dev, 0x9097, 0x34f4, 0x00000000); - nv_mthd(dev, 0x9097, 0x34f8, 0x00000000); - nv_mthd(dev, 0x9097, 0x34fc, 0x00000000); - nv_mthd(dev, 0x9097, 0x3500, 0x00000000); - nv_mthd(dev, 0x9097, 0x3504, 0x00000000); - nv_mthd(dev, 0x9097, 0x3508, 0x00000000); - nv_mthd(dev, 0x9097, 0x350c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3510, 0x00000000); - nv_mthd(dev, 0x9097, 0x3514, 0x00000000); - nv_mthd(dev, 0x9097, 0x3518, 0x00000000); - nv_mthd(dev, 0x9097, 0x351c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3520, 0x00000000); - nv_mthd(dev, 0x9097, 0x3524, 0x00000000); - nv_mthd(dev, 0x9097, 0x3528, 0x00000000); - nv_mthd(dev, 0x9097, 0x352c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3530, 0x00000000); - nv_mthd(dev, 0x9097, 0x3534, 0x00000000); - nv_mthd(dev, 0x9097, 0x3538, 0x00000000); - nv_mthd(dev, 0x9097, 0x353c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3540, 0x00000000); - nv_mthd(dev, 0x9097, 0x3544, 0x00000000); - nv_mthd(dev, 0x9097, 0x3548, 0x00000000); - nv_mthd(dev, 0x9097, 0x354c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3550, 0x00000000); - nv_mthd(dev, 0x9097, 0x3554, 0x00000000); - nv_mthd(dev, 0x9097, 0x3558, 0x00000000); - nv_mthd(dev, 0x9097, 0x355c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3560, 0x00000000); - nv_mthd(dev, 0x9097, 0x3564, 0x00000000); - nv_mthd(dev, 0x9097, 0x3568, 0x00000000); - nv_mthd(dev, 0x9097, 0x356c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3570, 0x00000000); - nv_mthd(dev, 0x9097, 0x3574, 0x00000000); - nv_mthd(dev, 0x9097, 0x3578, 0x00000000); - nv_mthd(dev, 0x9097, 0x357c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3580, 0x00000000); - nv_mthd(dev, 0x9097, 0x3584, 0x00000000); - nv_mthd(dev, 0x9097, 0x3588, 0x00000000); - nv_mthd(dev, 0x9097, 0x358c, 0x00000000); - nv_mthd(dev, 0x9097, 0x3590, 0x00000000); - nv_mthd(dev, 0x9097, 0x3594, 0x00000000); - nv_mthd(dev, 0x9097, 0x3598, 0x00000000); - nv_mthd(dev, 0x9097, 0x359c, 0x00000000); - nv_mthd(dev, 0x9097, 0x35a0, 0x00000000); - nv_mthd(dev, 0x9097, 0x35a4, 0x00000000); - nv_mthd(dev, 0x9097, 0x35a8, 0x00000000); - nv_mthd(dev, 0x9097, 0x35ac, 0x00000000); - nv_mthd(dev, 0x9097, 0x35b0, 0x00000000); - nv_mthd(dev, 0x9097, 0x35b4, 0x00000000); - nv_mthd(dev, 0x9097, 0x35b8, 0x00000000); - nv_mthd(dev, 0x9097, 0x35bc, 0x00000000); - nv_mthd(dev, 0x9097, 0x35c0, 0x00000000); - nv_mthd(dev, 0x9097, 0x35c4, 0x00000000); - nv_mthd(dev, 0x9097, 0x35c8, 0x00000000); - nv_mthd(dev, 0x9097, 0x35cc, 0x00000000); - nv_mthd(dev, 0x9097, 0x35d0, 0x00000000); - nv_mthd(dev, 0x9097, 0x35d4, 0x00000000); - nv_mthd(dev, 0x9097, 0x35d8, 0x00000000); - nv_mthd(dev, 0x9097, 0x35dc, 0x00000000); - nv_mthd(dev, 0x9097, 0x35e0, 0x00000000); - nv_mthd(dev, 0x9097, 0x35e4, 0x00000000); - nv_mthd(dev, 0x9097, 0x35e8, 0x00000000); - nv_mthd(dev, 0x9097, 0x35ec, 0x00000000); - nv_mthd(dev, 0x9097, 0x35f0, 0x00000000); - nv_mthd(dev, 0x9097, 0x35f4, 0x00000000); - nv_mthd(dev, 0x9097, 0x35f8, 0x00000000); - nv_mthd(dev, 0x9097, 0x35fc, 0x00000000); + if (fermi == 0x9097) { + for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4) + nv_mthd(dev, 0x9097, mthd, 0x00000000); + } nv_mthd(dev, 0x9097, 0x030c, 0x00000001); nv_mthd(dev, 0x9097, 0x1944, 0x00000000); nv_mthd(dev, 0x9097, 0x1514, 0x00000000); @@ -1321,6 +1200,37 @@ nvc0_grctx_generate_9097(struct drm_device *dev) } static void +nvc0_grctx_generate_9197(struct drm_device *dev) +{ + u32 fermi = nvc0_graph_class(dev); + u32 mthd; + + if (fermi == 0x9197) { + for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4) + nv_mthd(dev, 0x9197, mthd, 0x00000000); + } + nv_mthd(dev, 0x9197, 0x02e4, 0x0000b001); +} + +static void +nvc0_grctx_generate_9297(struct drm_device *dev) +{ + u32 fermi = nvc0_graph_class(dev); + u32 mthd; + + if (fermi == 0x9297) { + for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4) + nv_mthd(dev, 0x9297, mthd, 0x00000000); + } + nv_mthd(dev, 0x9297, 0x036c, 0x00000000); + nv_mthd(dev, 0x9297, 0x0370, 0x00000000); + nv_mthd(dev, 0x9297, 0x07a4, 0x00000000); + nv_mthd(dev, 0x9297, 0x07a8, 0x00000000); + nv_mthd(dev, 0x9297, 0x0374, 0x00000000); + nv_mthd(dev, 0x9297, 0x0378, 0x00000020); +} + +static void nvc0_grctx_generate_902d(struct drm_device *dev) { nv_mthd(dev, 0x902d, 0x0200, 0x000000cf); @@ -1559,8 +1469,15 @@ nvc0_grctx_generate_unk47xx(struct drm_device *dev) static void nvc0_grctx_generate_shaders(struct drm_device *dev) { - nv_wr32(dev, 0x405800, 0x078000bf); - nv_wr32(dev, 0x405830, 0x02180000); + struct drm_nouveau_private *dev_priv = dev->dev_private; + + if (dev_priv->chipset != 0xc1) { + nv_wr32(dev, 0x405800, 0x078000bf); + nv_wr32(dev, 0x405830, 0x02180000); + } else { + nv_wr32(dev, 0x405800, 0x0f8000bf); + nv_wr32(dev, 0x405830, 0x02180218); + } nv_wr32(dev, 0x405834, 0x00000000); nv_wr32(dev, 0x405838, 0x00000000); nv_wr32(dev, 0x405854, 0x00000000); @@ -1586,10 +1503,16 @@ nvc0_grctx_generate_unk60xx(struct drm_device *dev) static void nvc0_grctx_generate_unk64xx(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + nv_wr32(dev, 0x4064a8, 0x00000000); nv_wr32(dev, 0x4064ac, 0x00003fff); nv_wr32(dev, 0x4064b4, 0x00000000); nv_wr32(dev, 0x4064b8, 0x00000000); + if (dev_priv->chipset == 0xc1) { + nv_wr32(dev, 0x4064c0, 0x80140078); + nv_wr32(dev, 0x4064c4, 0x0086ffff); + } } static void @@ -1622,21 +1545,14 @@ static void nvc0_grctx_generate_rop(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + int chipset = dev_priv->chipset; /* ROPC_BROADCAST */ nv_wr32(dev, 0x408800, 0x02802a3c); nv_wr32(dev, 0x408804, 0x00000040); - nv_wr32(dev, 0x408808, 0x0003e00d); - switch (dev_priv->chipset) { - case 0xc0: - nv_wr32(dev, 0x408900, 0x0080b801); - break; - case 0xc3: - case 0xc4: - nv_wr32(dev, 0x408900, 0x3080b801); - break; - } - nv_wr32(dev, 0x408904, 0x02000001); + nv_wr32(dev, 0x408808, chipset != 0xc1 ? 0x0003e00d : 0x1003e005); + nv_wr32(dev, 0x408900, 0x3080b801); + nv_wr32(dev, 0x408904, chipset != 0xc1 ? 0x02000001 : 0x62000001); nv_wr32(dev, 0x408908, 0x00c80929); nv_wr32(dev, 0x40890c, 0x00000000); nv_wr32(dev, 0x408980, 0x0000011d); @@ -1645,6 +1561,8 @@ nvc0_grctx_generate_rop(struct drm_device *dev) static void nvc0_grctx_generate_gpc(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + int chipset = dev_priv->chipset; int i; /* GPC_BROADCAST */ @@ -1676,7 +1594,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x41880c, 0x00000000); nv_wr32(dev, 0x418810, 0x00000000); nv_wr32(dev, 0x418828, 0x00008442); - nv_wr32(dev, 0x418830, 0x00000001); + nv_wr32(dev, 0x418830, chipset != 0xc1 ? 0x00000001 : 0x10000001); nv_wr32(dev, 0x4188d8, 0x00000008); nv_wr32(dev, 0x4188e0, 0x01000000); nv_wr32(dev, 0x4188e8, 0x00000000); @@ -1684,7 +1602,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x4188f0, 0x00000000); nv_wr32(dev, 0x4188f4, 0x00000000); nv_wr32(dev, 0x4188f8, 0x00000000); - nv_wr32(dev, 0x4188fc, 0x00100000); + nv_wr32(dev, 0x4188fc, chipset != 0xc1 ? 0x00100000 : 0x00100018); nv_wr32(dev, 0x41891c, 0x00ff00ff); nv_wr32(dev, 0x418924, 0x00000000); nv_wr32(dev, 0x418928, 0x00ffff00); @@ -1715,6 +1633,8 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418c24, 0x00000000); nv_wr32(dev, 0x418c28, 0x00000000); nv_wr32(dev, 0x418c2c, 0x00000000); + if (chipset == 0xc1) + nv_wr32(dev, 0x418c6c, 0x00000001); nv_wr32(dev, 0x418c80, 0x20200004); nv_wr32(dev, 0x418c8c, 0x00000001); nv_wr32(dev, 0x419000, 0x00000780); @@ -1727,10 +1647,13 @@ static void nvc0_grctx_generate_tp(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + int chipset = dev_priv->chipset; /* GPC_BROADCAST.TP_BROADCAST */ + nv_wr32(dev, 0x419818, 0x00000000); + nv_wr32(dev, 0x41983c, 0x00038bc7); nv_wr32(dev, 0x419848, 0x00000000); - nv_wr32(dev, 0x419864, 0x0000012a); + nv_wr32(dev, 0x419864, chipset != 0xc1 ? 0x0000012a : 0x00000129); nv_wr32(dev, 0x419888, 0x00000000); nv_wr32(dev, 0x419a00, 0x000001f0); nv_wr32(dev, 0x419a04, 0x00000001); @@ -1740,8 +1663,8 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419a14, 0x00000200); nv_wr32(dev, 0x419a1c, 0x00000000); nv_wr32(dev, 0x419a20, 0x00000800); - if (dev_priv->chipset != 0xc0) - nv_wr32(dev, 0x00419ac4, 0x0007f440); /* 0xc3 */ + if (chipset != 0xc0 && chipset != 0xc8) + nv_wr32(dev, 0x00419ac4, 0x0007f440); nv_wr32(dev, 0x419b00, 0x0a418820); nv_wr32(dev, 0x419b04, 0x062080e6); nv_wr32(dev, 0x419b08, 0x020398a4); @@ -1749,17 +1672,22 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419b10, 0x0a418820); nv_wr32(dev, 0x419b14, 0x000000e6); nv_wr32(dev, 0x419bd0, 0x00900103); - nv_wr32(dev, 0x419be0, 0x00000001); + nv_wr32(dev, 0x419be0, chipset != 0xc1 ? 0x00000001 : 0x00400001); nv_wr32(dev, 0x419be4, 0x00000000); nv_wr32(dev, 0x419c00, 0x00000002); nv_wr32(dev, 0x419c04, 0x00000006); nv_wr32(dev, 0x419c08, 0x00000002); nv_wr32(dev, 0x419c20, 0x00000000); - nv_wr32(dev, 0x419cbc, 0x28137606); + if (chipset == 0xce || chipset == 0xcf) + nv_wr32(dev, 0x419cb0, 0x00020048); + else + nv_wr32(dev, 0x419cb0, 0x00060048); nv_wr32(dev, 0x419ce8, 0x00000000); nv_wr32(dev, 0x419cf4, 0x00000183); - nv_wr32(dev, 0x419d20, 0x02180000); + nv_wr32(dev, 0x419d20, chipset != 0xc1 ? 0x02180000 : 0x12180000); nv_wr32(dev, 0x419d24, 0x00001fff); + if (chipset == 0xc1) + nv_wr32(dev, 0x419d44, 0x02180218); nv_wr32(dev, 0x419e04, 0x00000000); nv_wr32(dev, 0x419e08, 0x00000000); nv_wr32(dev, 0x419e0c, 0x00000000); @@ -1785,11 +1713,11 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419e8c, 0x00000000); nv_wr32(dev, 0x419e90, 0x00000000); nv_wr32(dev, 0x419e98, 0x00000000); - if (dev_priv->chipset != 0xc0) + if (chipset != 0xc0 && chipset != 0xc8) nv_wr32(dev, 0x419ee0, 0x00011110); nv_wr32(dev, 0x419f50, 0x00000000); nv_wr32(dev, 0x419f54, 0x00000000); - if (dev_priv->chipset != 0xc0) + if (chipset != 0xc0 && chipset != 0xc8) nv_wr32(dev, 0x419f58, 0x00000000); } @@ -1801,6 +1729,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; struct drm_device *dev = chan->dev; int i, gpc, tp, id; + u32 fermi = nvc0_graph_class(dev); u32 r000260, tmp; r000260 = nv_rd32(dev, 0x000260); @@ -1857,10 +1786,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_wr32(dev, 0x40587c, 0x00000000); if (1) { - const u8 chipset_tp_max[] = { 16, 0, 0, 4, 8 }; - u8 max = chipset_tp_max[dev_priv->chipset & 0x0f]; - u8 tpnr[GPC_MAX]; - u8 data[32]; + u8 tpnr[GPC_MAX], data[TP_MAX]; memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr)); memset(data, 0x1f, sizeof(data)); @@ -1874,7 +1800,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) data[tp] = gpc; } - for (i = 0; i < max / 4; i++) + for (i = 0; i < 4; i++) nv_wr32(dev, 0x4060a8 + (i * 4), ((u32 *)data)[i]); } @@ -1886,6 +1812,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) /* calculate first set of magics */ memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr)); + gpc = -1; for (tp = 0; tp < priv->tp_total; tp++) { do { gpc = (gpc + 1) % priv->gpc_nr; @@ -1935,30 +1862,26 @@ nvc0_grctx_generate(struct nouveau_channel *chan) if (1) { u32 tp_mask = 0, tp_set = 0; - u8 tpnr[GPC_MAX]; + u8 tpnr[GPC_MAX], a, b; memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr)); for (gpc = 0; gpc < priv->gpc_nr; gpc++) tp_mask |= ((1 << priv->tp_nr[gpc]) - 1) << (gpc * 8); - gpc = -1; - for (i = 0, gpc = -1; i < 32; i++) { - int ltp = i * (priv->tp_total - 1) / 32; + for (i = 0, gpc = -1, b = -1; i < 32; i++) { + a = (i * (priv->tp_total - 1)) / 32; + if (a != b) { + b = a; + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpnr[gpc]); + tp = priv->tp_nr[gpc] - tpnr[gpc]--; - do { - gpc = (gpc + 1) % priv->gpc_nr; - } while (!tpnr[gpc]); - tp = priv->tp_nr[gpc] - tpnr[gpc]--; - - tp_set |= 1 << ((gpc * 8) + tp); + tp_set |= 1 << ((gpc * 8) + tp); + } - do { - nv_wr32(dev, 0x406800 + (i * 0x20), tp_set); - tp_set ^= tp_mask; - nv_wr32(dev, 0x406c00 + (i * 0x20), tp_set); - tp_set ^= tp_mask; - } while (ltp == (++i * (priv->tp_total - 1) / 32)); - i--; + nv_wr32(dev, 0x406800 + (i * 0x20), tp_set); + nv_wr32(dev, 0x406c00 + (i * 0x20), tp_set ^ tp_mask); } } @@ -2633,6 +2556,8 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x0000053f, 0xffff0000); nv_icmd(dev, 0x00000585, 0x0000003f); nv_icmd(dev, 0x00000576, 0x00000003); + if (dev_priv->chipset == 0xc1) + nv_icmd(dev, 0x0000057b, 0x00000059); nv_icmd(dev, 0x00000586, 0x00000040); nv_icmd(dev, 0x00000582, 0x00000080); nv_icmd(dev, 0x00000583, 0x00000080); @@ -2865,6 +2790,10 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_wr32(dev, 0x404154, 0x00000400); nvc0_grctx_generate_9097(dev); + if (fermi >= 0x9197) + nvc0_grctx_generate_9197(dev); + if (fermi >= 0x9297) + nvc0_grctx_generate_9297(dev); nvc0_grctx_generate_902d(dev); nvc0_grctx_generate_9039(dev); nvc0_grctx_generate_90c0(dev); diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c index 82357d2..b701c43 100644 --- a/drivers/gpu/drm/nouveau/nvc0_instmem.c +++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c @@ -32,7 +32,6 @@ struct nvc0_instmem_priv { struct nouveau_channel *bar1; struct nouveau_gpuobj *bar3_pgd; struct nouveau_channel *bar3; - struct nouveau_gpuobj *chan_pgd; }; int @@ -181,17 +180,11 @@ nvc0_instmem_init(struct drm_device *dev) goto error; /* channel vm */ - ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL, &vm); + ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL, + &dev_priv->chan_vm); if (ret) goto error; - ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 4096, 0, &priv->chan_pgd); - if (ret) - goto error; - - nouveau_vm_ref(vm, &dev_priv->chan_vm, priv->chan_pgd); - nouveau_vm_ref(NULL, &vm, NULL); - nvc0_instmem_resume(dev); return 0; error: @@ -211,8 +204,7 @@ nvc0_instmem_takedown(struct drm_device *dev) nv_wr32(dev, 0x1704, 0x00000000); nv_wr32(dev, 0x1714, 0x00000000); - nouveau_vm_ref(NULL, &dev_priv->chan_vm, priv->chan_pgd); - nouveau_gpuobj_ref(NULL, &priv->chan_pgd); + nouveau_vm_ref(NULL, &dev_priv->chan_vm, NULL); nvc0_channel_del(&priv->bar1); nouveau_vm_ref(NULL, &dev_priv->bar1_vm, priv->bar1_pgd); diff --git a/drivers/gpu/drm/nouveau/nvc0_vm.c b/drivers/gpu/drm/nouveau/nvc0_vm.c index a179e6c..9e35294 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vm.c +++ b/drivers/gpu/drm/nouveau/nvc0_vm.c @@ -105,7 +105,11 @@ nvc0_vm_flush(struct nouveau_vm *vm) struct drm_device *dev = vm->dev; struct nouveau_vm_pgd *vpgd; unsigned long flags; - u32 engine = (dev_priv->chan_vm == vm) ? 1 : 5; + u32 engine; + + engine = 1; + if (vm == dev_priv->bar1_vm || vm == dev_priv->bar3_vm) + engine |= 4; pinstmem->flush(vm->dev); diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c index 67c6ec6..ce984d5 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vram.c +++ b/drivers/gpu/drm/nouveau/nvc0_vram.c @@ -43,7 +43,7 @@ static const u8 types[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, - 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, + 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0 @@ -61,9 +61,7 @@ nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin, u32 type, struct nouveau_mem **pmem) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; - struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM]; - struct nouveau_mm *mm = man->priv; + struct nouveau_mm *mm = &dev_priv->engine.vram.mm; struct nouveau_mm_node *r; struct nouveau_mem *mem; int ret; @@ -105,9 +103,57 @@ int nvc0_vram_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_vram_engine *vram = &dev_priv->engine.vram; + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 parts = nv_rd32(dev, 0x121c74); + u32 bsize = nv_rd32(dev, 0x10f20c); + u32 offset, length; + bool uniform = true; + int ret, part; + + NV_DEBUG(dev, "0x100800: 0x%08x\n", nv_rd32(dev, 0x100800)); + NV_DEBUG(dev, "parts 0x%08x bcast_mem_amount 0x%08x\n", parts, bsize); + + /* read amount of vram attached to each memory controller */ + part = 0; + while (parts) { + u32 psize = nv_rd32(dev, 0x11020c + (part++ * 0x1000)); + if (psize == 0) + continue; + parts--; + + if (psize != bsize) { + if (psize < bsize) + bsize = psize; + uniform = false; + } + + NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", part, psize); + dev_priv->vram_size += (u64)psize << 20; + } + + /* if all controllers have the same amount attached, there's no holes */ + if (uniform) { + offset = rsvd_head; + length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail; + return nouveau_mm_init(&vram->mm, offset, length, 1); + } + + /* otherwise, address lowest common amount from 0GiB */ + ret = nouveau_mm_init(&vram->mm, rsvd_head, (bsize << 8) * parts, 1); + if (ret) + return ret; + + /* and the rest starting from (8GiB + common_size) */ + offset = (0x0200000000ULL >> 12) + (bsize << 8); + length = (dev_priv->vram_size >> 12) - (bsize << 8) - rsvd_tail; + + ret = nouveau_mm_init(&vram->mm, offset, length, 0); + if (ret) { + nouveau_mm_fini(&vram->mm); + return ret; + } - dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20; - dev_priv->vram_size *= nv_rd32(dev, 0x121c74); - dev_priv->vram_rblock_size = 4096; return 0; } diff --git a/drivers/gpu/drm/r128/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c index 570e190..bcac90b 100644 --- a/drivers/gpu/drm/r128/r128_cce.c +++ b/drivers/gpu/drm/r128/r128_cce.c @@ -32,6 +32,7 @@ #include <linux/firmware.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/module.h> #include "drmP.h" #include "drm.h" diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index b9e8efd..4c8796b 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -29,6 +29,8 @@ * Gareth Hughes <gareth@valinux.com> */ +#include <linux/module.h> + #include "drmP.h" #include "drm.h" #include "r128_drm.h" diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 3896ef8..cf8b4bc 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -5,6 +5,7 @@ ccflags-y := -Iinclude/drm hostprogs-y := mkregtable +clean-files := rn50_reg_safe.h r100_reg_safe.h r200_reg_safe.h rv515_reg_safe.h r300_reg_safe.h r420_reg_safe.h rs600_reg_safe.h r600_reg_safe.h evergreen_reg_safe.h cayman_reg_safe.h quiet_cmd_mkregtable = MKREGTABLE $@ cmd_mkregtable = $(obj)/mkregtable $< > $@ @@ -69,7 +70,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \ evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \ - radeon_trace_points.o ni.o cayman_blit_shaders.o + radeon_trace_points.o ni.o cayman_blit_shaders.o atombios_encoders.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index a1df7d7..afb351a 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -1259,6 +1259,9 @@ struct atom_context *atom_parse(struct card_info *card, void *bios) char name[512]; int i; + if (!ctx) + return NULL; + ctx->card = card; ctx->bios = bios; diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index 1b50ad8..4760466 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -101,6 +101,7 @@ #define ATOM_LCD_SELFTEST_START (ATOM_DISABLE+5) #define ATOM_LCD_SELFTEST_STOP (ATOM_ENABLE+5) #define ATOM_ENCODER_INIT (ATOM_DISABLE+7) +#define ATOM_INIT (ATOM_DISABLE+7) #define ATOM_GET_STATUS (ATOM_DISABLE+8) #define ATOM_BLANKING 1 @@ -251,25 +252,25 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ USHORT SetEngineClock; //Function Table,directly used by various SW components,latest version 1.1 USHORT SetMemoryClock; //Function Table,directly used by various SW components,latest version 1.1 USHORT SetPixelClock; //Function Table,directly used by various SW components,latest version 1.2 - USHORT DynamicClockGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT EnableDispPowerGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init USHORT ResetMemoryDLL; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock USHORT ResetMemoryDevice; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock - USHORT MemoryPLLInit; - USHORT AdjustDisplayPll; //only used by Bios + USHORT MemoryPLLInit; //Atomic Table, used only by Bios + USHORT AdjustDisplayPll; //Atomic Table, used by various SW componentes. USHORT AdjustMemoryController; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock USHORT EnableASIC_StaticPwrMgt; //Atomic Table, only used by Bios USHORT ASIC_StaticPwrMgtStatusChange; //Obsolete , only used by Bios USHORT DAC_LoadDetection; //Atomic Table, directly used by various SW components,latest version 1.2 USHORT LVTMAEncoderControl; //Atomic Table,directly used by various SW components,latest version 1.3 - USHORT LCD1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT HW_Misc_Operation; //Atomic Table, directly used by various SW components,latest version 1.1 USHORT DAC1EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 USHORT DAC2EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 USHORT DVOOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 USHORT CV1OutputControl; //Atomic Table, Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead - USHORT GetConditionalGoldenSetting; //only used by Bios + USHORT GetConditionalGoldenSetting; //Only used by Bios USHORT TVEncoderControl; //Function Table,directly used by various SW components,latest version 1.1 - USHORT TMDSAEncoderControl; //Atomic Table, directly used by various SW components,latest version 1.3 - USHORT LVDSEncoderControl; //Atomic Table, directly used by various SW components,latest version 1.3 + USHORT PatchMCSetting; //only used by BIOS + USHORT MC_SEQ_Control; //only used by BIOS USHORT TV1OutputControl; //Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead USHORT EnableScaler; //Atomic Table, used only by Bios USHORT BlankCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 @@ -282,7 +283,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ USHORT SetCRTC_Replication; //Atomic Table, used only by Bios USHORT SelectCRTC_Source; //Atomic Table, directly used by various SW components,latest version 1.1 USHORT EnableGraphSurfaces; //Atomic Table, used only by Bios - USHORT UpdateCRTC_DoubleBufferRegisters; + USHORT UpdateCRTC_DoubleBufferRegisters; //Atomic Table, used only by Bios USHORT LUT_AutoFill; //Atomic Table, only used by Bios USHORT EnableHW_IconCursor; //Atomic Table, only used by Bios USHORT GetMemoryClock; //Atomic Table, directly used by various SW components,latest version 1.1 @@ -308,27 +309,36 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ USHORT SetVoltage; //Function Table,directly and/or indirectly used by various SW components,latest version 1.1 USHORT DAC1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 USHORT DAC2OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 - USHORT SetupHWAssistedI2CStatus; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" + USHORT ComputeMemoryClockParam; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" USHORT ClockSource; //Atomic Table, indirectly used by various SW components,called from ASIC_Init USHORT MemoryDeviceInit; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock - USHORT EnableYUV; //Atomic Table, indirectly used by various SW components,called from EnableVGARender + USHORT GetDispObjectInfo; //Atomic Table, indirectly used by various SW components,called from EnableVGARender USHORT DIG1EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 USHORT DIG2EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 USHORT DIG1TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 USHORT DIG2TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 USHORT ProcessAuxChannelTransaction; //Function Table,only used by Bios USHORT DPEncoderService; //Function Table,only used by Bios + USHORT GetVoltageInfo; //Function Table,only used by Bios since SI }ATOM_MASTER_LIST_OF_COMMAND_TABLES; // For backward compatible #define ReadEDIDFromHWAssistedI2C ProcessI2cChannelTransaction -#define UNIPHYTransmitterControl DIG1TransmitterControl -#define LVTMATransmitterControl DIG2TransmitterControl +#define DPTranslatorControl DIG2EncoderControl +#define UNIPHYTransmitterControl DIG1TransmitterControl +#define LVTMATransmitterControl DIG2TransmitterControl #define SetCRTC_DPM_State GetConditionalGoldenSetting #define SetUniphyInstance ASIC_StaticPwrMgtStatusChange #define HPDInterruptService ReadHWAssistedI2CStatus #define EnableVGA_Access GetSCLKOverMCLKRatio -#define GetDispObjectInfo EnableYUV +#define EnableYUV GetDispObjectInfo +#define DynamicClockGating EnableDispPowerGating +#define SetupHWAssistedI2CStatus ComputeMemoryClockParam + +#define TMDSAEncoderControl PatchMCSetting +#define LVDSEncoderControl MC_SEQ_Control +#define LCD1OutputControl HW_Misc_Operation + typedef struct _ATOM_MASTER_COMMAND_TABLE { @@ -495,6 +505,34 @@ typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5 // ucInputFlag #define ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN 1 // 1-StrobeMode, 0-PerformanceMode +// use for ComputeMemoryClockParamTable +typedef struct _COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1 +{ + union + { + ULONG ulClock; + ATOM_S_MPLL_FB_DIVIDER ulFbDiv; //Output:UPPER_WORD=FB_DIV_INTEGER, LOWER_WORD=FB_DIV_FRAC shl (16-FB_FRACTION_BITS) + }; + UCHAR ucDllSpeed; //Output + UCHAR ucPostDiv; //Output + union{ + UCHAR ucInputFlag; //Input : ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN: 1-StrobeMode, 0-PerformanceMode + UCHAR ucPllCntlFlag; //Output: + }; + UCHAR ucBWCntl; +}COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1; + +// definition of ucInputFlag +#define MPLL_INPUT_FLAG_STROBE_MODE_EN 0x01 +// definition of ucPllCntlFlag +#define MPLL_CNTL_FLAG_VCO_MODE_MASK 0x03 +#define MPLL_CNTL_FLAG_BYPASS_DQ_PLL 0x04 +#define MPLL_CNTL_FLAG_QDR_ENABLE 0x08 +#define MPLL_CNTL_FLAG_AD_HALF_RATE 0x10 + +//MPLL_CNTL_FLAG_BYPASS_AD_PLL has a wrong name, should be BYPASS_DQ_PLL +#define MPLL_CNTL_FLAG_BYPASS_AD_PLL 0x04 + typedef struct _DYNAMICE_MEMORY_SETTINGS_PARAMETER { ATOM_COMPUTE_CLOCK_FREQ ulClock; @@ -562,6 +600,16 @@ typedef struct _DYNAMIC_CLOCK_GATING_PARAMETERS #define DYNAMIC_CLOCK_GATING_PS_ALLOCATION DYNAMIC_CLOCK_GATING_PARAMETERS /****************************************************************************/ +// Structure used by EnableDispPowerGatingTable.ctb +/****************************************************************************/ +typedef struct _ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 +{ + UCHAR ucDispPipeId; // ATOM_CRTC1, ATOM_CRTC2, ... + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[2]; +}ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1; + +/****************************************************************************/ // Structure used by EnableASIC_StaticPwrMgtTable.ctb /****************************************************************************/ typedef struct _ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS @@ -807,6 +855,7 @@ typedef struct _ATOM_DIG_ENCODER_CONFIG_V4 #define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_1_62GHZ 0x00 #define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ 0x01 #define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ 0x02 +#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_3_24GHZ 0x03 #define ATOM_ENCODER_CONFIG_V4_ENCODER_SEL 0x70 #define ATOM_ENCODER_CONFIG_V4_DIG0_ENCODER 0x00 #define ATOM_ENCODER_CONFIG_V4_DIG1_ENCODER 0x10 @@ -814,6 +863,7 @@ typedef struct _ATOM_DIG_ENCODER_CONFIG_V4 #define ATOM_ENCODER_CONFIG_V4_DIG3_ENCODER 0x30 #define ATOM_ENCODER_CONFIG_V4_DIG4_ENCODER 0x40 #define ATOM_ENCODER_CONFIG_V4_DIG5_ENCODER 0x50 +#define ATOM_ENCODER_CONFIG_V4_DIG6_ENCODER 0x60 typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V4 { @@ -1171,6 +1221,106 @@ typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 #define ATOM_TRANSMITTER_CONFIG_V4_TRANSMITTER3 0x80 //EF +typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V5 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucReservd1:1; + UCHAR ucHPDSel:3; + UCHAR ucPhyClkSrcId:2; + UCHAR ucCoherentMode:1; + UCHAR ucReserved:1; +#else + UCHAR ucReserved:1; + UCHAR ucCoherentMode:1; + UCHAR ucPhyClkSrcId:2; + UCHAR ucHPDSel:3; + UCHAR ucReservd1:1; +#endif +}ATOM_DIG_TRANSMITTER_CONFIG_V5; + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 +{ + USHORT usSymClock; // Encoder Clock in 10kHz,(DP mode)= linkclock/10, (TMDS/LVDS/HDMI)= pixel clock, (HDMI deep color), =pixel clock * deep_color_ratio + UCHAR ucPhyId; // 0=UNIPHYA, 1=UNIPHYB, 2=UNIPHYC, 3=UNIPHYD, 4= UNIPHYE 5=UNIPHYF + UCHAR ucAction; // define as ATOM_TRANSMITER_ACTION_xxx + UCHAR ucLaneNum; // indicate lane number 1-8 + UCHAR ucConnObjId; // Connector Object Id defined in ObjectId.h + UCHAR ucDigMode; // indicate DIG mode + union{ + ATOM_DIG_TRANSMITTER_CONFIG_V5 asConfig; + UCHAR ucConfig; + }; + UCHAR ucDigEncoderSel; // indicate DIG front end encoder + UCHAR ucDPLaneSet; + UCHAR ucReserved; + UCHAR ucReserved1; +}DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5; + +//ucPhyId +#define ATOM_PHY_ID_UNIPHYA 0 +#define ATOM_PHY_ID_UNIPHYB 1 +#define ATOM_PHY_ID_UNIPHYC 2 +#define ATOM_PHY_ID_UNIPHYD 3 +#define ATOM_PHY_ID_UNIPHYE 4 +#define ATOM_PHY_ID_UNIPHYF 5 +#define ATOM_PHY_ID_UNIPHYG 6 + +// ucDigEncoderSel +#define ATOM_TRANMSITTER_V5__DIGA_SEL 0x01 +#define ATOM_TRANMSITTER_V5__DIGB_SEL 0x02 +#define ATOM_TRANMSITTER_V5__DIGC_SEL 0x04 +#define ATOM_TRANMSITTER_V5__DIGD_SEL 0x08 +#define ATOM_TRANMSITTER_V5__DIGE_SEL 0x10 +#define ATOM_TRANMSITTER_V5__DIGF_SEL 0x20 +#define ATOM_TRANMSITTER_V5__DIGG_SEL 0x40 + +// ucDigMode +#define ATOM_TRANSMITTER_DIGMODE_V5_DP 0 +#define ATOM_TRANSMITTER_DIGMODE_V5_LVDS 1 +#define ATOM_TRANSMITTER_DIGMODE_V5_DVI 2 +#define ATOM_TRANSMITTER_DIGMODE_V5_HDMI 3 +#define ATOM_TRANSMITTER_DIGMODE_V5_SDVO 4 +#define ATOM_TRANSMITTER_DIGMODE_V5_DP_MST 5 + +// ucDPLaneSet +#define DP_LANE_SET__0DB_0_4V 0x00 +#define DP_LANE_SET__0DB_0_6V 0x01 +#define DP_LANE_SET__0DB_0_8V 0x02 +#define DP_LANE_SET__0DB_1_2V 0x03 +#define DP_LANE_SET__3_5DB_0_4V 0x08 +#define DP_LANE_SET__3_5DB_0_6V 0x09 +#define DP_LANE_SET__3_5DB_0_8V 0x0a +#define DP_LANE_SET__6DB_0_4V 0x10 +#define DP_LANE_SET__6DB_0_6V 0x11 +#define DP_LANE_SET__9_5DB_0_4V 0x18 + +// ATOM_DIG_TRANSMITTER_CONFIG_V5 asConfig; +// Bit1 +#define ATOM_TRANSMITTER_CONFIG_V5_COHERENT 0x02 + +// Bit3:2 +#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SEL_MASK 0x0c +#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SEL_SHIFT 0x02 + +#define ATOM_TRANSMITTER_CONFIG_V5_P1PLL 0x00 +#define ATOM_TRANSMITTER_CONFIG_V5_P2PLL 0x04 +#define ATOM_TRANSMITTER_CONFIG_V5_P0PLL 0x08 +#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT 0x0c +// Bit6:4 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD_SEL_MASK 0x70 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD_SEL_SHIFT 0x04 + +#define ATOM_TRANSMITTER_CONFIG_V5_NO_HPD_SEL 0x00 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL 0x10 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL 0x20 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL 0x30 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL 0x40 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL 0x50 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL 0x60 + +#define DIG_TRANSMITTER_CONTROL_PS_ALLOCATION_V1_5 DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 + + /****************************************************************************/ // Structures used by ExternalEncoderControlTable V1.3 // ASIC Families: Evergreen, Llano, NI @@ -1793,6 +1943,7 @@ typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 #define ATOM_PPLL_SS_TYPE_V3_P1PLL 0x00 #define ATOM_PPLL_SS_TYPE_V3_P2PLL 0x04 #define ATOM_PPLL_SS_TYPE_V3_DCPLL 0x08 +#define ATOM_PPLL_SS_TYPE_V3_P0PLL ATOM_PPLL_SS_TYPE_V3_DCPLL #define ATOM_PPLL_SS_AMOUNT_V3_FBDIV_MASK 0x00FF #define ATOM_PPLL_SS_AMOUNT_V3_FBDIV_SHIFT 0 #define ATOM_PPLL_SS_AMOUNT_V3_NFRAC_MASK 0x0F00 @@ -2030,12 +2181,77 @@ typedef struct _SET_VOLTAGE_PARAMETERS_V2 USHORT usVoltageLevel; // real voltage level }SET_VOLTAGE_PARAMETERS_V2; + +typedef struct _SET_VOLTAGE_PARAMETERS_V1_3 +{ + UCHAR ucVoltageType; // To tell which voltage to set up, VDDC/MVDDC/MVDDQ/VDDCI + UCHAR ucVoltageMode; // Indicate action: Set voltage level + USHORT usVoltageLevel; // real voltage level in unit of mv or Voltage Phase (0, 1, 2, .. ) +}SET_VOLTAGE_PARAMETERS_V1_3; + +//ucVoltageType +#define VOLTAGE_TYPE_VDDC 1 +#define VOLTAGE_TYPE_MVDDC 2 +#define VOLTAGE_TYPE_MVDDQ 3 +#define VOLTAGE_TYPE_VDDCI 4 + +//SET_VOLTAGE_PARAMETERS_V3.ucVoltageMode +#define ATOM_SET_VOLTAGE 0 //Set voltage Level +#define ATOM_INIT_VOLTAGE_REGULATOR 3 //Init Regulator +#define ATOM_SET_VOLTAGE_PHASE 4 //Set Vregulator Phase +#define ATOM_GET_MAX_VOLTAGE 6 //Get Max Voltage, not used in SetVoltageTable v1.3 +#define ATOM_GET_VOLTAGE_LEVEL 6 //Get Voltage level from vitual voltage ID + +// define vitual voltage id in usVoltageLevel +#define ATOM_VIRTUAL_VOLTAGE_ID0 0xff01 +#define ATOM_VIRTUAL_VOLTAGE_ID1 0xff02 +#define ATOM_VIRTUAL_VOLTAGE_ID2 0xff03 +#define ATOM_VIRTUAL_VOLTAGE_ID3 0xff04 + typedef struct _SET_VOLTAGE_PS_ALLOCATION { SET_VOLTAGE_PARAMETERS sASICSetVoltage; WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; }SET_VOLTAGE_PS_ALLOCATION; +// New Added from SI for GetVoltageInfoTable, input parameter structure +typedef struct _GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_1 +{ + UCHAR ucVoltageType; // Input: To tell which voltage to set up, VDDC/MVDDC/MVDDQ/VDDCI + UCHAR ucVoltageMode; // Input: Indicate action: Get voltage info + USHORT usVoltageLevel; // Input: real voltage level in unit of mv or Voltage Phase (0, 1, 2, .. ) or Leakage Id + ULONG ulReserved; +}GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_1; + +// New Added from SI for GetVoltageInfoTable, output parameter structure when ucVotlageMode == ATOM_GET_VOLTAGE_VID +typedef struct _GET_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1 +{ + ULONG ulVotlageGpioState; + ULONG ulVoltageGPioMask; +}GET_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1; + +// New Added from SI for GetVoltageInfoTable, output parameter structure when ucVotlageMode == ATOM_GET_VOLTAGE_STATEx_LEAKAGE_VID +typedef struct _GET_LEAKAGE_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1 +{ + USHORT usVoltageLevel; + USHORT usVoltageId; // Voltage Id programmed in Voltage Regulator + ULONG ulReseved; +}GET_LEAKAGE_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1; + + +// GetVoltageInfo v1.1 ucVoltageMode +#define ATOM_GET_VOLTAGE_VID 0x00 +#define ATOM_GET_VOTLAGE_INIT_SEQ 0x03 +#define ATOM_GET_VOLTTAGE_PHASE_PHASE_VID 0x04 +// for SI, this state map to 0xff02 voltage state in Power Play table, which is power boost state +#define ATOM_GET_VOLTAGE_STATE0_LEAKAGE_VID 0x10 + +// for SI, this state map to 0xff01 voltage state in Power Play table, which is performance state +#define ATOM_GET_VOLTAGE_STATE1_LEAKAGE_VID 0x11 +// undefined power state +#define ATOM_GET_VOLTAGE_STATE2_LEAKAGE_VID 0x12 +#define ATOM_GET_VOLTAGE_STATE3_LEAKAGE_VID 0x13 + /****************************************************************************/ // Structures used by TVEncoderControlTable /****************************************************************************/ @@ -2065,9 +2281,9 @@ typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES USHORT MultimediaConfigInfo; // Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios USHORT StandardVESA_Timing; // Only used by Bios USHORT FirmwareInfo; // Shared by various SW components,latest version 1.4 - USHORT DAC_Info; // Will be obsolete from R600 + USHORT PaletteData; // Only used by BIOS USHORT LCD_Info; // Shared by various SW components,latest version 1.3, was called LVDS_Info - USHORT TMDS_Info; // Will be obsolete from R600 + USHORT DIGTransmitterInfo; // Internal used by VBIOS only version 3.1 USHORT AnalogTV_Info; // Shared by various SW components,latest version 1.1 USHORT SupportedDevicesInfo; // Will be obsolete from R600 USHORT GPIO_I2C_Info; // Shared by various SW components,latest version 1.2 will be used from R600 @@ -2096,15 +2312,16 @@ typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES USHORT PowerSourceInfo; // Shared by various SW components, latest versoin 1.1 }ATOM_MASTER_LIST_OF_DATA_TABLES; -// For backward compatible -#define LVDS_Info LCD_Info - typedef struct _ATOM_MASTER_DATA_TABLE { ATOM_COMMON_TABLE_HEADER sHeader; ATOM_MASTER_LIST_OF_DATA_TABLES ListOfDataTables; }ATOM_MASTER_DATA_TABLE; +// For backward compatible +#define LVDS_Info LCD_Info +#define DAC_Info PaletteData +#define TMDS_Info DIGTransmitterInfo /****************************************************************************/ // Structure used in MultimediaCapabilityInfoTable @@ -2171,7 +2388,9 @@ typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO typedef struct _ATOM_FIRMWARE_CAPABILITY { #if ATOM_BIG_ENDIAN - USHORT Reserved:3; + USHORT Reserved:1; + USHORT SCL2Redefined:1; + USHORT PostWithoutModeSet:1; USHORT HyperMemory_Size:4; USHORT HyperMemory_Support:1; USHORT PPMode_Assigned:1; @@ -2193,7 +2412,9 @@ typedef struct _ATOM_FIRMWARE_CAPABILITY USHORT PPMode_Assigned:1; USHORT HyperMemory_Support:1; USHORT HyperMemory_Size:4; - USHORT Reserved:3; + USHORT PostWithoutModeSet:1; + USHORT SCL2Redefined:1; + USHORT Reserved:1; #endif }ATOM_FIRMWARE_CAPABILITY; @@ -2418,7 +2639,8 @@ typedef struct _ATOM_FIRMWARE_INFO_V2_2 USHORT usLcdMaxPixelClockPLL_Output; // In MHz unit ULONG ulReserved4; //Was ulAsicMaximumVoltage ULONG ulMinPixelClockPLL_Output; //In 10Khz unit - ULONG ulReserved5; //Was usMinEngineClockPLL_Input and usMaxEngineClockPLL_Input + UCHAR ucRemoteDisplayConfig; + UCHAR ucReserved5[3]; //Was usMinEngineClockPLL_Input and usMaxEngineClockPLL_Input ULONG ulReserved6; //Was usMinEngineClockPLL_Output and usMinMemoryClockPLL_Input ULONG ulReserved7; //Was usMaxMemoryClockPLL_Input and usMinMemoryClockPLL_Output USHORT usReserved11; //Was usMaxPixelClock; //In 10Khz unit, Max. Pclk used only for DAC @@ -2438,6 +2660,11 @@ typedef struct _ATOM_FIRMWARE_INFO_V2_2 #define ATOM_FIRMWARE_INFO_LAST ATOM_FIRMWARE_INFO_V2_2 + +// definition of ucRemoteDisplayConfig +#define REMOTE_DISPLAY_DISABLE 0x00 +#define REMOTE_DISPLAY_ENABLE 0x01 + /****************************************************************************/ // Structures used in IntegratedSystemInfoTable /****************************************************************************/ @@ -2660,8 +2887,9 @@ usMinDownStreamHTLinkWidth: same as above. #define INTEGRATED_SYSTEM_INFO__AMD_CPU__GREYHOUND 2 #define INTEGRATED_SYSTEM_INFO__AMD_CPU__K8 3 #define INTEGRATED_SYSTEM_INFO__AMD_CPU__PHARAOH 4 +#define INTEGRATED_SYSTEM_INFO__AMD_CPU__OROCHI 5 -#define INTEGRATED_SYSTEM_INFO__AMD_CPU__MAX_CODE INTEGRATED_SYSTEM_INFO__AMD_CPU__PHARAOH // this deff reflects max defined CPU code +#define INTEGRATED_SYSTEM_INFO__AMD_CPU__MAX_CODE INTEGRATED_SYSTEM_INFO__AMD_CPU__OROCHI // this deff reflects max defined CPU code #define SYSTEM_CONFIG_POWEREXPRESS_ENABLE 0x00000001 #define SYSTEM_CONFIG_RUN_AT_OVERDRIVE_ENGINE 0x00000002 @@ -2753,6 +2981,7 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V5 #define ASIC_INT_DIG4_ENCODER_ID 0x0b #define ASIC_INT_DIG5_ENCODER_ID 0x0c #define ASIC_INT_DIG6_ENCODER_ID 0x0d +#define ASIC_INT_DIG7_ENCODER_ID 0x0e //define Encoder attribute #define ATOM_ANALOG_ENCODER 0 @@ -3226,15 +3455,23 @@ typedef struct _ATOM_LCD_INFO_V13 UCHAR ucPowerSequenceDIGONtoDE_in4Ms; UCHAR ucPowerSequenceDEtoVARY_BL_in4Ms; - UCHAR ucPowerSequenceDEtoDIGON_in4Ms; UCHAR ucPowerSequenceVARY_BLtoDE_in4Ms; + UCHAR ucPowerSequenceDEtoDIGON_in4Ms; UCHAR ucOffDelay_in4Ms; UCHAR ucPowerSequenceVARY_BLtoBLON_in4Ms; UCHAR ucPowerSequenceBLONtoVARY_BL_in4Ms; UCHAR ucReserved1; - ULONG ulReserved[4]; + UCHAR ucDPCD_eDP_CONFIGURATION_CAP; // dpcd 0dh + UCHAR ucDPCD_MAX_LINK_RATE; // dpcd 01h + UCHAR ucDPCD_MAX_LANE_COUNT; // dpcd 02h + UCHAR ucDPCD_MAX_DOWNSPREAD; // dpcd 03h + + USHORT usMaxPclkFreqInSingleLink; // Max PixelClock frequency in single link mode. + UCHAR uceDPToLVDSRxId; + UCHAR ucLcdReservd; + ULONG ulReserved[2]; }ATOM_LCD_INFO_V13; #define ATOM_LCD_INFO_LAST ATOM_LCD_INFO_V13 @@ -3273,6 +3510,11 @@ typedef struct _ATOM_LCD_INFO_V13 //Use this cap bit for a quick reference whether an embadded panel (LCD1 ) is LVDS or eDP. #define LCDPANEL_CAP_V13_eDP 0x4 // = LCDPANEL_CAP_eDP no change comparing to previous version +//uceDPToLVDSRxId +#define eDP_TO_LVDS_RX_DISABLE 0x00 // no eDP->LVDS translator chip +#define eDP_TO_LVDS_COMMON_ID 0x01 // common eDP->LVDS translator chip without AMD SW init +#define eDP_TO_LVDS_RT_ID 0x02 // RT tanslator which require AMD SW init + typedef struct _ATOM_PATCH_RECORD_MODE { UCHAR ucRecordType; @@ -3317,6 +3559,7 @@ typedef struct _ATOM_PANEL_RESOLUTION_PATCH_RECORD #define LCD_CAP_RECORD_TYPE 3 #define LCD_FAKE_EDID_PATCH_RECORD_TYPE 4 #define LCD_PANEL_RESOLUTION_RECORD_TYPE 5 +#define LCD_EDID_OFFSET_PATCH_RECORD_TYPE 6 #define ATOM_RECORD_END_TYPE 0xFF /****************************Spread Spectrum Info Table Definitions **********************/ @@ -3528,6 +3771,7 @@ else //Non VGA case CAIL needs to claim an reserved area defined by FBAccessAreaOffset and usFBUsedbyDrvInKB in non VGA case.*/ +/***********************************************************************************/ #define ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO 1 typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO @@ -3818,13 +4062,17 @@ typedef struct _EXT_DISPLAY_PATH ATOM_DP_CONN_CHANNEL_MAPPING asDPMapping; ATOM_DVI_CONN_CHANNEL_MAPPING asDVIMapping; }; - UCHAR ucReserved; - USHORT usReserved[2]; + UCHAR ucChPNInvert; // bit vector for up to 8 lanes, =0: P and N is not invert, =1 P and N is inverted + USHORT usCaps; + USHORT usReserved; }EXT_DISPLAY_PATH; #define NUMBER_OF_UCHAR_FOR_GUID 16 #define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7 +//usCaps +#define EXT_DISPLAY_PATH_CAPS__HBR2_DISABLE 0x01 + typedef struct _ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO { ATOM_COMMON_TABLE_HEADER sHeader; @@ -3832,7 +4080,9 @@ typedef struct _ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO EXT_DISPLAY_PATH sPath[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; // total of fixed 7 entries. UCHAR ucChecksum; // a simple Checksum of the sum of whole structure equal to 0x0. UCHAR uc3DStereoPinId; // use for eDP panel - UCHAR Reserved [6]; // for potential expansion + UCHAR ucRemoteDisplayConfig; + UCHAR uceDPToLVDSRxId; + UCHAR Reserved[4]; // for potential expansion }ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO; //Related definitions, all records are different but they have a commond header @@ -3977,6 +4227,7 @@ typedef struct _ATOM_OBJECT_GPIO_CNTL_RECORD #define GPIO_PIN_STATE_ACTIVE_HIGH 0x1 // Indexes to GPIO array in GLSync record +// GLSync record is for Frame Lock/Gen Lock feature. #define ATOM_GPIO_INDEX_GLSYNC_REFCLK 0 #define ATOM_GPIO_INDEX_GLSYNC_HSYNC 1 #define ATOM_GPIO_INDEX_GLSYNC_VSYNC 2 @@ -3984,7 +4235,9 @@ typedef struct _ATOM_OBJECT_GPIO_CNTL_RECORD #define ATOM_GPIO_INDEX_GLSYNC_SWAP_GNT 4 #define ATOM_GPIO_INDEX_GLSYNC_INTERRUPT 5 #define ATOM_GPIO_INDEX_GLSYNC_V_RESET 6 -#define ATOM_GPIO_INDEX_GLSYNC_MAX 7 +#define ATOM_GPIO_INDEX_GLSYNC_SWAP_CNTL 7 +#define ATOM_GPIO_INDEX_GLSYNC_SWAP_SEL 8 +#define ATOM_GPIO_INDEX_GLSYNC_MAX 9 typedef struct _ATOM_ENCODER_DVO_CF_RECORD { @@ -3994,7 +4247,8 @@ typedef struct _ATOM_ENCODER_DVO_CF_RECORD }ATOM_ENCODER_DVO_CF_RECORD; // Bit maps for ATOM_ENCODER_CAP_RECORD.ucEncoderCap -#define ATOM_ENCODER_CAP_RECORD_HBR2 0x01 // DP1.2 HBR2 is supported by this path +#define ATOM_ENCODER_CAP_RECORD_HBR2 0x01 // DP1.2 HBR2 is supported by HW encoder +#define ATOM_ENCODER_CAP_RECORD_HBR2_EN 0x02 // DP1.2 HBR2 setting is qualified and HBR2 can be enabled typedef struct _ATOM_ENCODER_CAP_RECORD { @@ -4003,11 +4257,13 @@ typedef struct _ATOM_ENCODER_CAP_RECORD USHORT usEncoderCap; struct { #if ATOM_BIG_ENDIAN - USHORT usReserved:15; // Bit1-15 may be defined for other capability in future + USHORT usReserved:14; // Bit1-15 may be defined for other capability in future + USHORT usHBR2En:1; // Bit1 is for DP1.2 HBR2 enable USHORT usHBR2Cap:1; // Bit0 is for DP1.2 HBR2 capability. #else USHORT usHBR2Cap:1; // Bit0 is for DP1.2 HBR2 capability. - USHORT usReserved:15; // Bit1-15 may be defined for other capability in future + USHORT usHBR2En:1; // Bit1 is for DP1.2 HBR2 enable + USHORT usReserved:14; // Bit1-15 may be defined for other capability in future #endif }; }; @@ -4157,6 +4413,7 @@ typedef struct _ATOM_VOLTAGE_CONTROL #define VOLTAGE_CONTROL_ID_VT1556M 0x07 #define VOLTAGE_CONTROL_ID_CHL822x 0x08 #define VOLTAGE_CONTROL_ID_VT1586M 0x09 +#define VOLTAGE_CONTROL_ID_UP1637 0x0A typedef struct _ATOM_VOLTAGE_OBJECT { @@ -4193,6 +4450,69 @@ typedef struct _ATOM_LEAKID_VOLTAGE USHORT usVoltage; }ATOM_LEAKID_VOLTAGE; +typedef struct _ATOM_VOLTAGE_OBJECT_HEADER_V3{ + UCHAR ucVoltageType; //Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI + UCHAR ucVoltageMode; //Indicate voltage control mode: Init/Set/Leakage/Set phase + USHORT usSize; //Size of Object +}ATOM_VOLTAGE_OBJECT_HEADER_V3; + +typedef struct _VOLTAGE_LUT_ENTRY_V2 +{ + ULONG ulVoltageId; // The Voltage ID which is used to program GPIO register + USHORT usVoltageValue; // The corresponding Voltage Value, in mV +}VOLTAGE_LUT_ENTRY_V2; + +typedef struct _LEAKAGE_VOLTAGE_LUT_ENTRY_V2 +{ + USHORT usVoltageLevel; // The Voltage ID which is used to program GPIO register + USHORT usVoltageId; + USHORT usLeakageId; // The corresponding Voltage Value, in mV +}LEAKAGE_VOLTAGE_LUT_ENTRY_V2; + +typedef struct _ATOM_I2C_VOLTAGE_OBJECT_V3 +{ + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + UCHAR ucVoltageRegulatorId; //Indicate Voltage Regulator Id + UCHAR ucVoltageControlI2cLine; + UCHAR ucVoltageControlAddress; + UCHAR ucVoltageControlOffset; + ULONG ulReserved; + VOLTAGE_LUT_ENTRY asVolI2cLut[1]; // end with 0xff +}ATOM_I2C_VOLTAGE_OBJECT_V3; + +typedef struct _ATOM_GPIO_VOLTAGE_OBJECT_V3 +{ + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + UCHAR ucVoltageGpioCntlId; // default is 0 which indicate control through CG VID mode + UCHAR ucGpioEntryNum; // indiate the entry numbers of Votlage/Gpio value Look up table + UCHAR ucPhaseDelay; // phase delay in unit of micro second + UCHAR ucReserved; + ULONG ulGpioMaskVal; // GPIO Mask value + VOLTAGE_LUT_ENTRY_V2 asVolGpioLut[1]; +}ATOM_GPIO_VOLTAGE_OBJECT_V3; + +typedef struct _ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 +{ + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + UCHAR ucLeakageCntlId; // default is 0 + UCHAR ucLeakageEntryNum; // indicate the entry number of LeakageId/Voltage Lut table + UCHAR ucReserved[2]; + ULONG ulMaxVoltageLevel; + LEAKAGE_VOLTAGE_LUT_ENTRY_V2 asLeakageIdLut[1]; +}ATOM_LEAKAGE_VOLTAGE_OBJECT_V3; + +typedef union _ATOM_VOLTAGE_OBJECT_V3{ + ATOM_GPIO_VOLTAGE_OBJECT_V3 asGpioVoltageObj; + ATOM_I2C_VOLTAGE_OBJECT_V3 asI2cVoltageObj; + ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 asLeakageObj; +}ATOM_VOLTAGE_OBJECT_V3; + +typedef struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VOLTAGE_OBJECT_V3 asVoltageObj[3]; //Info for Voltage control +}ATOM_VOLTAGE_OBJECT_INFO_V3_1; + typedef struct _ATOM_ASIC_PROFILE_VOLTAGE { UCHAR ucProfileId; @@ -4305,7 +4625,18 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 USHORT usHDMISSpreadRateIn10Hz; USHORT usDVISSPercentage; USHORT usDVISSpreadRateIn10Hz; - ULONG ulReserved3[21]; + ULONG SclkDpmBoostMargin; + ULONG SclkDpmThrottleMargin; + USHORT SclkDpmTdpLimitPG; + USHORT SclkDpmTdpLimitBoost; + ULONG ulBoostEngineCLock; + UCHAR ulBoostVid_2bit; + UCHAR EnableBoost; + USHORT GnbTdpLimit; + USHORT usMaxLVDSPclkFreqInSingleLink; + UCHAR ucLvdsMisc; + UCHAR ucLVDSReserved; + ULONG ulReserved3[15]; ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo; }ATOM_INTEGRATED_SYSTEM_INFO_V6; @@ -4313,9 +4644,16 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 #define INTEGRATED_SYSTEM_INFO_V6_GPUCAPINFO__TMDSHDMI_COHERENT_SINGLEPLL_MODE 0x01 #define INTEGRATED_SYSTEM_INFO_V6_GPUCAPINFO__DISABLE_AUX_HW_MODE_DETECTION 0x08 -// ulOtherDisplayMisc -#define INTEGRATED_SYSTEM_INFO__GET_EDID_CALLBACK_FUNC_SUPPORT 0x01 +//ucLVDSMisc: +#define SYS_INFO_LVDSMISC__888_FPDI_MODE 0x01 +#define SYS_INFO_LVDSMISC__DL_CH_SWAP 0x02 +#define SYS_INFO_LVDSMISC__888_BPC 0x04 +#define SYS_INFO_LVDSMISC__OVERRIDE_EN 0x08 +#define SYS_INFO_LVDSMISC__BLON_ACTIVE_LOW 0x10 +// not used any more +#define SYS_INFO_LVDSMISC__VSYNC_ACTIVE_LOW 0x04 +#define SYS_INFO_LVDSMISC__HSYNC_ACTIVE_LOW 0x08 /********************************************************************************************************************** ATOM_INTEGRATED_SYSTEM_INFO_V6 Description @@ -4384,7 +4722,208 @@ ucUMAChannelNumber: System memory channel numbers. ulCSR_M3_ARB_CNTL_DEFAULT[10]: Arrays with values for CSR M3 arbiter for default ulCSR_M3_ARB_CNTL_UVD[10]: Arrays with values for CSR M3 arbiter for UVD playback. ulCSR_M3_ARB_CNTL_FS3D[10]: Arrays with values for CSR M3 arbiter for Full Screen 3D applications. -sAvail_SCLK[5]: Arrays to provide available list of SLCK and corresponding voltage, order from low to high +sAvail_SCLK[5]: Arrays to provide availabe list of SLCK and corresponding voltage, order from low to high +ulGMCRestoreResetTime: GMC power restore and GMC reset time to calculate data reconnection latency. Unit in ns. +ulMinimumNClk: Minimum NCLK speed among all NB-Pstates to calcualte data reconnection latency. Unit in 10kHz. +ulIdleNClk: NCLK speed while memory runs in self-refresh state. Unit in 10kHz. +ulDDR_DLL_PowerUpTime: DDR PHY DLL power up time. Unit in ns. +ulDDR_PLL_PowerUpTime: DDR PHY PLL power up time. Unit in ns. +usPCIEClkSSPercentage: PCIE Clock Spred Spectrum Percentage in unit 0.01%; 100 mean 1%. +usPCIEClkSSType: PCIE Clock Spred Spectrum Type. 0 for Down spread(default); 1 for Center spread. +usLvdsSSPercentage: LVDS panel ( not include eDP ) Spread Spectrum Percentage in unit of 0.01%, =0, use VBIOS default setting. +usLvdsSSpreadRateIn10Hz: LVDS panel ( not include eDP ) Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usHDMISSPercentage: HDMI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. +usHDMISSpreadRateIn10Hz: HDMI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usDVISSPercentage: DVI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. +usDVISSpreadRateIn10Hz: DVI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usMaxLVDSPclkFreqInSingleLink: Max pixel clock LVDS panel single link, if=0 means VBIOS use default threhold, right now it is 85Mhz +ucLVDSMisc: [bit0] LVDS 888bit panel mode =0: LVDS 888 panel in LDI mode, =1: LVDS 888 panel in FPDI mode + [bit1] LVDS panel lower and upper link mapping =0: lower link and upper link not swap, =1: lower link and upper link are swapped + [bit2] LVDS 888bit per color mode =0: 666 bit per color =1:888 bit per color + [bit3] LVDS parameter override enable =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used + [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low ) +**********************************************************************************************************************/ + +// this Table is used for Liano/Ontario APU +typedef struct _ATOM_FUSION_SYSTEM_INFO_V1 +{ + ATOM_INTEGRATED_SYSTEM_INFO_V6 sIntegratedSysInfo; + ULONG ulPowerplayTable[128]; +}ATOM_FUSION_SYSTEM_INFO_V1; +/********************************************************************************************************************** + ATOM_FUSION_SYSTEM_INFO_V1 Description +sIntegratedSysInfo: refer to ATOM_INTEGRATED_SYSTEM_INFO_V6 definition. +ulPowerplayTable[128]: This 512 bytes memory is used to save ATOM_PPLIB_POWERPLAYTABLE3, starting form ulPowerplayTable[0] +**********************************************************************************************************************/ + +// this IntegrateSystemInfoTable is used for Trinity APU +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; + ULONG ulDentistVCOFreq; + ULONG ulBootUpUMAClock; + ATOM_CLK_VOLT_CAPABILITY sDISPCLK_Voltage[4]; + ULONG ulBootUpReqDisplayVector; + ULONG ulOtherDisplayMisc; + ULONG ulGPUCapInfo; + ULONG ulSB_MMIO_Base_Addr; + USHORT usRequestedPWMFreqInHz; + UCHAR ucHtcTmpLmt; + UCHAR ucHtcHystLmt; + ULONG ulMinEngineClock; + ULONG ulSystemConfig; + ULONG ulCPUCapInfo; + USHORT usNBP0Voltage; + USHORT usNBP1Voltage; + USHORT usBootUpNBVoltage; + USHORT usExtDispConnInfoOffset; + USHORT usPanelRefreshRateRange; + UCHAR ucMemoryType; + UCHAR ucUMAChannelNumber; + UCHAR strVBIOSMsg[40]; + ULONG ulReserved[20]; + ATOM_AVAILABLE_SCLK_LIST sAvail_SCLK[5]; + ULONG ulGMCRestoreResetTime; + ULONG ulMinimumNClk; + ULONG ulIdleNClk; + ULONG ulDDR_DLL_PowerUpTime; + ULONG ulDDR_PLL_PowerUpTime; + USHORT usPCIEClkSSPercentage; + USHORT usPCIEClkSSType; + USHORT usLvdsSSPercentage; + USHORT usLvdsSSpreadRateIn10Hz; + USHORT usHDMISSPercentage; + USHORT usHDMISSpreadRateIn10Hz; + USHORT usDVISSPercentage; + USHORT usDVISSpreadRateIn10Hz; + ULONG SclkDpmBoostMargin; + ULONG SclkDpmThrottleMargin; + USHORT SclkDpmTdpLimitPG; + USHORT SclkDpmTdpLimitBoost; + ULONG ulBoostEngineCLock; + UCHAR ulBoostVid_2bit; + UCHAR EnableBoost; + USHORT GnbTdpLimit; + USHORT usMaxLVDSPclkFreqInSingleLink; + UCHAR ucLvdsMisc; + UCHAR ucLVDSReserved; + UCHAR ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + UCHAR ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + UCHAR ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + UCHAR ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + UCHAR ucLVDSOffToOnDelay_in4Ms; + UCHAR ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + UCHAR ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + UCHAR ucLVDSReserved1; + ULONG ulLCDBitDepthControlVal; + ULONG ulNbpStateMemclkFreq[4]; + USHORT usNBP2Voltage; + USHORT usNBP3Voltage; + ULONG ulNbpStateNClkFreq[4]; + UCHAR ucNBDPMEnable; + UCHAR ucReserved[3]; + UCHAR ucDPMState0VclkFid; + UCHAR ucDPMState0DclkFid; + UCHAR ucDPMState1VclkFid; + UCHAR ucDPMState1DclkFid; + UCHAR ucDPMState2VclkFid; + UCHAR ucDPMState2DclkFid; + UCHAR ucDPMState3VclkFid; + UCHAR ucDPMState3DclkFid; + ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo; +}ATOM_INTEGRATED_SYSTEM_INFO_V1_7; + +// ulOtherDisplayMisc +#define INTEGRATED_SYSTEM_INFO__GET_EDID_CALLBACK_FUNC_SUPPORT 0x01 +#define INTEGRATED_SYSTEM_INFO__GET_BOOTUP_DISPLAY_CALLBACK_FUNC_SUPPORT 0x02 +#define INTEGRATED_SYSTEM_INFO__GET_EXPANSION_CALLBACK_FUNC_SUPPORT 0x04 +#define INTEGRATED_SYSTEM_INFO__FAST_BOOT_SUPPORT 0x08 + +// ulGPUCapInfo +#define SYS_INFO_GPUCAPS__TMDSHDMI_COHERENT_SINGLEPLL_MODE 0x01 +#define SYS_INFO_GPUCAPS__DP_SINGLEPLL_MODE 0x02 +#define SYS_INFO_GPUCAPS__DISABLE_AUX_MODE_DETECT 0x08 + +/********************************************************************************************************************** + ATOM_INTEGRATED_SYSTEM_INFO_V1_7 Description +ulBootUpEngineClock: VBIOS bootup Engine clock frequency, in 10kHz unit. if it is equal 0, then VBIOS use pre-defined bootup engine clock +ulDentistVCOFreq: Dentist VCO clock in 10kHz unit. +ulBootUpUMAClock: System memory boot up clock frequency in 10Khz unit. +sDISPCLK_Voltage: Report Display clock voltage requirement. + +ulBootUpReqDisplayVector: VBIOS boot up display IDs, following are supported devices in Trinity projects: + ATOM_DEVICE_CRT1_SUPPORT 0x0001 + ATOM_DEVICE_DFP1_SUPPORT 0x0008 + ATOM_DEVICE_DFP6_SUPPORT 0x0040 + ATOM_DEVICE_DFP2_SUPPORT 0x0080 + ATOM_DEVICE_DFP3_SUPPORT 0x0200 + ATOM_DEVICE_DFP4_SUPPORT 0x0400 + ATOM_DEVICE_DFP5_SUPPORT 0x0800 + ATOM_DEVICE_LCD1_SUPPORT 0x0002 +ulOtherDisplayMisc: bit[0]=0: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is not supported by SBIOS. + =1: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is supported by SBIOS. + bit[1]=0: INT15 callback function Get boot display( ax=4e08, bl=01h) is not supported by SBIOS + =1: INT15 callback function Get boot display( ax=4e08, bl=01h) is supported by SBIOS + bit[2]=0: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is not supported by SBIOS + =1: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is supported by SBIOS + bit[3]=0: VBIOS fast boot is disable + =1: VBIOS fast boot is enable. ( VBIOS skip display device detection in every set mode if LCD panel is connect and LID is open) +ulGPUCapInfo: bit[0]=0: TMDS/HDMI Coherent Mode use cascade PLL mode. + =1: TMDS/HDMI Coherent Mode use signel PLL mode. + bit[1]=0: DP mode use cascade PLL mode ( New for Trinity ) + =1: DP mode use single PLL mode + bit[3]=0: Enable AUX HW mode detection logic + =1: Disable AUX HW mode detection logic + +ulSB_MMIO_Base_Addr: Physical Base address to SB MMIO space. Driver needs to initialize it for SMU usage. + +usRequestedPWMFreqInHz: When it's set to 0x0 by SBIOS: the LCD BackLight is not controlled by GPU(SW). + Any attempt to change BL using VBIOS function or enable VariBri from PP table is not effective since ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==0; + + When it's set to a non-zero frequency, the BackLight is controlled by GPU (SW) in one of two ways below: + 1. SW uses the GPU BL PWM output to control the BL, in chis case, this non-zero frequency determines what freq GPU should use; + VBIOS will set up proper PWM frequency and ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1,as the result, + Changing BL using VBIOS function is functional in both driver and non-driver present environment; + and enabling VariBri under the driver environment from PP table is optional. + + 2. SW uses other means to control BL (like DPCD),this non-zero frequency serves as a flag only indicating + that BL control from GPU is expected. + VBIOS will NOT set up PWM frequency but make ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1 + Changing BL using VBIOS function could be functional in both driver and non-driver present environment,but + it's per platform + and enabling VariBri under the driver environment from PP table is optional. + +ucHtcTmpLmt: Refer to D18F3x64 bit[22:16], HtcTmpLmt. + Threshold on value to enter HTC_active state. +ucHtcHystLmt: Refer to D18F3x64 bit[27:24], HtcHystLmt. + To calculate threshold off value to exit HTC_active state, which is Threshold on vlaue minus ucHtcHystLmt. +ulMinEngineClock: Minimum SCLK allowed in 10kHz unit. This is calculated based on WRCK Fuse settings. +ulSystemConfig: Bit[0]=0: PCIE Power Gating Disabled + =1: PCIE Power Gating Enabled + Bit[1]=0: DDR-DLL shut-down feature disabled. + 1: DDR-DLL shut-down feature enabled. + Bit[2]=0: DDR-PLL Power down feature disabled. + 1: DDR-PLL Power down feature enabled. +ulCPUCapInfo: TBD +usNBP0Voltage: VID for voltage on NB P0 State +usNBP1Voltage: VID for voltage on NB P1 State +usNBP2Voltage: VID for voltage on NB P2 State +usNBP3Voltage: VID for voltage on NB P3 State +usBootUpNBVoltage: Voltage Index of GNB voltage configured by SBIOS, which is suffcient to support VBIOS DISPCLK requirement. +usExtDispConnInfoOffset: Offset to sExtDispConnInfo inside the structure +usPanelRefreshRateRange: Bit vector for LCD supported refresh rate range. If DRR is requestd by the platform, at least two bits need to be set + to indicate a range. + SUPPORTED_LCD_REFRESHRATE_30Hz 0x0004 + SUPPORTED_LCD_REFRESHRATE_40Hz 0x0008 + SUPPORTED_LCD_REFRESHRATE_50Hz 0x0010 + SUPPORTED_LCD_REFRESHRATE_60Hz 0x0020 +ucMemoryType: [3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved. +ucUMAChannelNumber: System memory channel numbers. +ulCSR_M3_ARB_CNTL_DEFAULT[10]: Arrays with values for CSR M3 arbiter for default +ulCSR_M3_ARB_CNTL_UVD[10]: Arrays with values for CSR M3 arbiter for UVD playback. +ulCSR_M3_ARB_CNTL_FS3D[10]: Arrays with values for CSR M3 arbiter for Full Screen 3D applications. +sAvail_SCLK[5]: Arrays to provide availabe list of SLCK and corresponding voltage, order from low to high ulGMCRestoreResetTime: GMC power restore and GMC reset time to calculate data reconnection latency. Unit in ns. ulMinimumNClk: Minimum NCLK speed among all NB-Pstates to calcualte data reconnection latency. Unit in 10kHz. ulIdleNClk: NCLK speed while memory runs in self-refresh state. Unit in 10kHz. @@ -4398,6 +4937,41 @@ usHDMISSPercentage: HDMI Spread Spectrum Percentage in unit 0.01%; usHDMISSpreadRateIn10Hz: HDMI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. usDVISSPercentage: DVI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. usDVISSpreadRateIn10Hz: DVI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usMaxLVDSPclkFreqInSingleLink: Max pixel clock LVDS panel single link, if=0 means VBIOS use default threhold, right now it is 85Mhz +ucLVDSMisc: [bit0] LVDS 888bit panel mode =0: LVDS 888 panel in LDI mode, =1: LVDS 888 panel in FPDI mode + [bit1] LVDS panel lower and upper link mapping =0: lower link and upper link not swap, =1: lower link and upper link are swapped + [bit2] LVDS 888bit per color mode =0: 666 bit per color =1:888 bit per color + [bit3] LVDS parameter override enable =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used + [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low ) +ucLVDSPwrOnSeqDIGONtoDE_in4Ms: LVDS power up sequence time in unit of 4ms, time delay from DIGON signal active to data enable signal active( DE ). + =0 mean use VBIOS default which is 8 ( 32ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. +ucLVDSPwrOnDEtoVARY_BL_in4Ms: LVDS power up sequence time in unit of 4ms., time delay from DE( data enable ) active to Vary Brightness enable signal active( VARY_BL ). + =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOffVARY_BLtoDE_in4Ms: LVDS power down sequence time in unit of 4ms, time delay from data enable ( DE ) signal off to LCDVCC (DIGON) off. + =0 mean use VBIOS default delay which is 8 ( 32ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOffDEtoDIGON_in4Ms: LVDS power down sequence time in unit of 4ms, time delay from vary brightness enable signal( VARY_BL) off to data enable ( DE ) signal off. + =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSOffToOnDelay_in4Ms: LVDS power down sequence time in unit of 4ms. Time delay from DIGON signal off to DIGON signal active. + =0 means to use VBIOS default delay which is 125 ( 500ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOnVARY_BLtoBLON_in4Ms: LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active. + =0 means to use VBIOS default delay which is 0 ( 0ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOffBLONtoVARY_BL_in4Ms: LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off. + =0 means to use VBIOS default delay which is 0 ( 0ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ulNbpStateMemclkFreq[4]: system memory clock frequncey in unit of 10Khz in different NB pstate. + **********************************************************************************************************************/ /**************************************************************************/ @@ -4459,6 +5033,7 @@ typedef struct _ATOM_ASIC_SS_ASSIGNMENT #define ASIC_INTERNAL_SS_ON_DP 7 #define ASIC_INTERNAL_SS_ON_DCPLL 8 #define ASIC_EXTERNAL_SS_ON_DP_CLOCK 9 +#define ASIC_INTERNAL_VCE_SS 10 typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V2 { @@ -4520,7 +5095,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 #define ATOM_DOS_MODE_INFO_DEF 7 #define ATOM_I2C_CHANNEL_STATUS_DEF 8 #define ATOM_I2C_CHANNEL_STATUS1_DEF 9 - +#define ATOM_INTERNAL_TIMER_DEF 10 // BIOS_0_SCRATCH Definition #define ATOM_S0_CRT1_MONO 0x00000001L @@ -4648,6 +5223,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 #define ATOM_S2_DEVICE_DPMS_MASKw1 0x3FF #define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASKb3 0x0C #define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGEb3 0x10 +#define ATOM_S2_TMDS_COHERENT_MODEb3 0x10 // used by VBIOS code only, use coherent mode for TMDS/HDMI mode #define ATOM_S2_VRI_BRIGHT_ENABLEb3 0x20 #define ATOM_S2_ROTATION_STATE_MASKb3 0xC0 @@ -5038,6 +5614,23 @@ typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_3 USHORT usDeviceId; // Active Device Id for this surface. If no device, set to 0. }ENABLE_GRAPH_SURFACE_PARAMETERS_V1_3; +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_4 +{ + USHORT usHight; // Image Hight + USHORT usWidth; // Image Width + USHORT usGraphPitch; + UCHAR ucColorDepth; + UCHAR ucPixelFormat; + UCHAR ucSurface; // Surface 1 or 2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucModeType; + UCHAR ucReserved; +}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_4; + +// ucEnable +#define ATOM_GRAPH_CONTROL_SET_PITCH 0x0f +#define ATOM_GRAPH_CONTROL_SET_DISP_START 0x10 + typedef struct _ENABLE_GRAPH_SURFACE_PS_ALLOCATION { ENABLE_GRAPH_SURFACE_PARAMETERS sSetSurface; @@ -5057,6 +5650,58 @@ typedef struct _GET_DISPLAY_SURFACE_SIZE_PARAMETERS USHORT usY_Size; }GET_DISPLAY_SURFACE_SIZE_PARAMETERS; +typedef struct _GET_DISPLAY_SURFACE_SIZE_PARAMETERS_V2 +{ + union{ + USHORT usX_Size; //When use as input parameter, usX_Size indicates which CRTC + USHORT usSurface; + }; + USHORT usY_Size; + USHORT usDispXStart; + USHORT usDispYStart; +}GET_DISPLAY_SURFACE_SIZE_PARAMETERS_V2; + + +typedef struct _PALETTE_DATA_CONTROL_PARAMETERS_V3 +{ + UCHAR ucLutId; + UCHAR ucAction; + USHORT usLutStartIndex; + USHORT usLutLength; + USHORT usLutOffsetInVram; +}PALETTE_DATA_CONTROL_PARAMETERS_V3; + +// ucAction: +#define PALETTE_DATA_AUTO_FILL 1 +#define PALETTE_DATA_READ 2 +#define PALETTE_DATA_WRITE 3 + + +typedef struct _INTERRUPT_SERVICE_PARAMETERS_V2 +{ + UCHAR ucInterruptId; + UCHAR ucServiceId; + UCHAR ucStatus; + UCHAR ucReserved; +}INTERRUPT_SERVICE_PARAMETER_V2; + +// ucInterruptId +#define HDP1_INTERRUPT_ID 1 +#define HDP2_INTERRUPT_ID 2 +#define HDP3_INTERRUPT_ID 3 +#define HDP4_INTERRUPT_ID 4 +#define HDP5_INTERRUPT_ID 5 +#define HDP6_INTERRUPT_ID 6 +#define SW_INTERRUPT_ID 11 + +// ucAction +#define INTERRUPT_SERVICE_GEN_SW_INT 1 +#define INTERRUPT_SERVICE_GET_STATUS 2 + + // ucStatus +#define INTERRUPT_STATUS__INT_TRIGGER 1 +#define INTERRUPT_STATUS__HPD_HIGH 2 + typedef struct _INDIRECT_IO_ACCESS { ATOM_COMMON_TABLE_HEADER sHeader; @@ -5189,7 +5834,7 @@ typedef struct _ATOM_INIT_REG_BLOCK{ #define END_OF_REG_INDEX_BLOCK 0x0ffff #define END_OF_REG_DATA_BLOCK 0x00000000 -#define ATOM_INIT_REG_MASK_FLAG 0x80 +#define ATOM_INIT_REG_MASK_FLAG 0x80 //Not used in BIOS #define CLOCK_RANGE_HIGHEST 0x00ffffff #define VALUE_DWORD SIZEOF ULONG @@ -5229,6 +5874,7 @@ typedef struct _ATOM_MC_INIT_PARAM_TABLE #define _128Mx8 0x51 #define _128Mx16 0x52 #define _256Mx8 0x61 +#define _256Mx16 0x62 #define SAMSUNG 0x1 #define INFINEON 0x2 @@ -5585,7 +6231,7 @@ typedef struct _ATOM_VRAM_MODULE_V7 ULONG ulChannelMapCfg; // mmMC_SHARED_CHREMAP USHORT usModuleSize; // Size of ATOM_VRAM_MODULE_V7 USHORT usPrivateReserved; // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) - USHORT usReserved; + USHORT usEnableChannels; // bit vector which indicate which channels are enabled UCHAR ucExtMemoryID; // Current memory module ID UCHAR ucMemoryType; // MEM_TYPE_DDR2/DDR3/GDDR3/GDDR5 UCHAR ucChannelNum; // Number of mem. channels supported in this module @@ -5597,7 +6243,8 @@ typedef struct _ATOM_VRAM_MODULE_V7 UCHAR ucNPL_RT; // Round trip delay (MC_SEQ_CAS_TIMING [28:24]:TCL=CL+NPL_RT-2). Always 2. UCHAR ucPreamble; // [7:4] Write Preamble, [3:0] Read Preamble UCHAR ucMemorySize; // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros - UCHAR ucReserved[3]; + USHORT usSEQSettingOffset; + UCHAR ucReserved; // Memory Module specific values USHORT usEMRS2Value; // EMRS2/MR2 Value. USHORT usEMRS3Value; // EMRS3/MR3 Value. @@ -5633,10 +6280,10 @@ typedef struct _ATOM_VRAM_INFO_V3 typedef struct _ATOM_VRAM_INFO_V4 { ATOM_COMMON_TABLE_HEADER sHeader; - USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting - USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting - USHORT usRerseved; - UCHAR ucMemDQ7_0ByteRemap; // DQ line byte remap, =0: Memory Data line BYTE0, =1: BYTE1, =2: BYTE2, =3: BYTE3 + USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting + USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting + USHORT usRerseved; + UCHAR ucMemDQ7_0ByteRemap; // DQ line byte remap, =0: Memory Data line BYTE0, =1: BYTE1, =2: BYTE2, =3: BYTE3 ULONG ulMemDQ7_0BitRemap; // each DQ line ( 7~0) use 3bits, like: DQ0=Bit[2:0], DQ1:[5:3], ... DQ7:[23:21] UCHAR ucReservde[4]; UCHAR ucNumOfVRAMModule; @@ -5648,9 +6295,10 @@ typedef struct _ATOM_VRAM_INFO_V4 typedef struct _ATOM_VRAM_INFO_HEADER_V2_1 { ATOM_COMMON_TABLE_HEADER sHeader; - USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting - USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting - USHORT usReserved[4]; + USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting + USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting + USHORT usPerBytePresetOffset; // offset of ATOM_INIT_REG_BLOCK structure for Per Byte Offset Preset Settings + USHORT usReserved[3]; UCHAR ucNumOfVRAMModule; // indicate number of VRAM module UCHAR ucMemoryClkPatchTblVer; // version of memory AC timing register list UCHAR ucVramModuleVer; // indicate ATOM_VRAM_MODUE version @@ -5935,6 +6583,52 @@ typedef struct _ATOM_DISP_OUT_INFO_V2 ASIC_ENCODER_INFO asEncoderInfo[1]; }ATOM_DISP_OUT_INFO_V2; + +typedef struct _ATOM_DISP_CLOCK_ID { + UCHAR ucPpllId; + UCHAR ucPpllAttribute; +}ATOM_DISP_CLOCK_ID; + +// ucPpllAttribute +#define CLOCK_SOURCE_SHAREABLE 0x01 +#define CLOCK_SOURCE_DP_MODE 0x02 +#define CLOCK_SOURCE_NONE_DP_MODE 0x04 + +//DispOutInfoTable +typedef struct _ASIC_TRANSMITTER_INFO_V2 +{ + USHORT usTransmitterObjId; + USHORT usDispClkIdOffset; // point to clock source id list supported by Encoder Object + UCHAR ucTransmitterCmdTblId; + UCHAR ucConfig; + UCHAR ucEncoderID; // available 1st encoder ( default ) + UCHAR ucOptionEncoderID; // available 2nd encoder ( optional ) + UCHAR uc2ndEncoderID; + UCHAR ucReserved; +}ASIC_TRANSMITTER_INFO_V2; + +typedef struct _ATOM_DISP_OUT_INFO_V3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT ptrTransmitterInfo; + USHORT ptrEncoderInfo; + USHORT ptrMainCallParserFar; // direct address of main parser call in VBIOS binary. + USHORT usReserved; + UCHAR ucDCERevision; + UCHAR ucMaxDispEngineNum; + UCHAR ucMaxActiveDispEngineNum; + UCHAR ucMaxPPLLNum; + UCHAR ucCoreRefClkSource; // value of CORE_REF_CLK_SOURCE + UCHAR ucReserved[3]; + ASIC_TRANSMITTER_INFO_V2 asTransmitterInfo[1]; // for alligment only +}ATOM_DISP_OUT_INFO_V3; + +typedef enum CORE_REF_CLK_SOURCE{ + CLOCK_SRC_XTALIN=0, + CLOCK_SRC_XO_IN=1, + CLOCK_SRC_XO_IN2=2, +}CORE_REF_CLK_SOURCE; + // DispDevicePriorityInfo typedef struct _ATOM_DISPLAY_DEVICE_PRIORITY_INFO { @@ -6070,6 +6764,39 @@ typedef struct _PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS #define HW_I2C_READ 0 #define I2C_2BYTE_ADDR 0x02 +/****************************************************************************/ +// Structures used by HW_Misc_OperationTable +/****************************************************************************/ +typedef struct _ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1 +{ + UCHAR ucCmd; // Input: To tell which action to take + UCHAR ucReserved[3]; + ULONG ulReserved; +}ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1; + +typedef struct _ATOM_HW_MISC_OPERATION_OUTPUT_PARAMETER_V1_1 +{ + UCHAR ucReturnCode; // Output: Return value base on action was taken + UCHAR ucReserved[3]; + ULONG ulReserved; +}ATOM_HW_MISC_OPERATION_OUTPUT_PARAMETER_V1_1; + +// Actions code +#define ATOM_GET_SDI_SUPPORT 0xF0 + +// Return code +#define ATOM_UNKNOWN_CMD 0 +#define ATOM_FEATURE_NOT_SUPPORTED 1 +#define ATOM_FEATURE_SUPPORTED 2 + +typedef struct _ATOM_HW_MISC_OPERATION_PS_ALLOCATION +{ + ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1 sInput_Output; + PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS sReserved; +}ATOM_HW_MISC_OPERATION_PS_ALLOCATION; + +/****************************************************************************/ + typedef struct _SET_HWBLOCK_INSTANCE_PARAMETER_V2 { UCHAR ucHWBlkInst; // HW block instance, 0, 1, 2, ... @@ -6090,6 +6817,52 @@ typedef struct _SET_HWBLOCK_INSTANCE_PARAMETER_V2 #define SELECT_CRTC_PIXEL_RATE 7 #define SELECT_VGA_BLK 8 +// DIGTransmitterInfoTable structure used to program UNIPHY settings +typedef struct _DIG_TRANSMITTER_INFO_HEADER_V3_1{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDPVsPreEmphSettingOffset; // offset of PHY_ANALOG_SETTING_INFO * with DP Voltage Swing and Pre-Emphasis for each Link clock + USHORT usPhyAnalogRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with None-DP mode Analog Setting's register Info + USHORT usPhyAnalogSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with None-DP mode Analog Setting for each link clock range + USHORT usPhyPllRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with Phy Pll register Info + USHORT usPhyPllSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy Pll Settings +}DIG_TRANSMITTER_INFO_HEADER_V3_1; + +typedef struct _CLOCK_CONDITION_REGESTER_INFO{ + USHORT usRegisterIndex; + UCHAR ucStartBit; + UCHAR ucEndBit; +}CLOCK_CONDITION_REGESTER_INFO; + +typedef struct _CLOCK_CONDITION_SETTING_ENTRY{ + USHORT usMaxClockFreq; + UCHAR ucEncodeMode; + UCHAR ucPhySel; + ULONG ulAnalogSetting[1]; +}CLOCK_CONDITION_SETTING_ENTRY; + +typedef struct _CLOCK_CONDITION_SETTING_INFO{ + USHORT usEntrySize; + CLOCK_CONDITION_SETTING_ENTRY asClkCondSettingEntry[1]; +}CLOCK_CONDITION_SETTING_INFO; + +typedef struct _PHY_CONDITION_REG_VAL{ + ULONG ulCondition; + ULONG ulRegVal; +}PHY_CONDITION_REG_VAL; + +typedef struct _PHY_CONDITION_REG_INFO{ + USHORT usRegIndex; + USHORT usSize; + PHY_CONDITION_REG_VAL asRegVal[1]; +}PHY_CONDITION_REG_INFO; + +typedef struct _PHY_ANALOG_SETTING_INFO{ + UCHAR ucEncodeMode; + UCHAR ucPhySel; + USHORT usSize; + PHY_CONDITION_REG_INFO asAnalogSetting[1]; +}PHY_ANALOG_SETTING_INFO; + /****************************************************************************/ //Portion VI: Definitinos for vbios MC scratch registers that driver used /****************************************************************************/ @@ -7020,4 +7793,68 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table #pragma pack() // BIOS data must use byte aligment +// +// AMD ACPI Table +// +#pragma pack(1) + +typedef struct { + ULONG Signature; + ULONG TableLength; //Length + UCHAR Revision; + UCHAR Checksum; + UCHAR OemId[6]; + UCHAR OemTableId[8]; //UINT64 OemTableId; + ULONG OemRevision; + ULONG CreatorId; + ULONG CreatorRevision; +} AMD_ACPI_DESCRIPTION_HEADER; +/* +//EFI_ACPI_DESCRIPTION_HEADER from AcpiCommon.h +typedef struct { + UINT32 Signature; //0x0 + UINT32 Length; //0x4 + UINT8 Revision; //0x8 + UINT8 Checksum; //0x9 + UINT8 OemId[6]; //0xA + UINT64 OemTableId; //0x10 + UINT32 OemRevision; //0x18 + UINT32 CreatorId; //0x1C + UINT32 CreatorRevision; //0x20 +}EFI_ACPI_DESCRIPTION_HEADER; +*/ +typedef struct { + AMD_ACPI_DESCRIPTION_HEADER SHeader; + UCHAR TableUUID[16]; //0x24 + ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture. + ULONG Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture. + ULONG Reserved[4]; //0x3C +}UEFI_ACPI_VFCT; + +typedef struct { + ULONG PCIBus; //0x4C + ULONG PCIDevice; //0x50 + ULONG PCIFunction; //0x54 + USHORT VendorID; //0x58 + USHORT DeviceID; //0x5A + USHORT SSVID; //0x5C + USHORT SSID; //0x5E + ULONG Revision; //0x60 + ULONG ImageLength; //0x64 +}VFCT_IMAGE_HEADER; + + +typedef struct { + VFCT_IMAGE_HEADER VbiosHeader; + UCHAR VbiosContent[1]; +}GOP_VBIOS_CONTENT; + +typedef struct { + VFCT_IMAGE_HEADER Lib1Header; + UCHAR Lib1Content[1]; +}GOP_LIB1_CONTENT; + +#pragma pack() + + #endif /* _ATOMBIOS_H */ diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 071ded1..76c4f2a 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -302,8 +302,10 @@ atombios_set_crtc_dtd_timing(struct drm_crtc *crtc, misc |= ATOM_COMPOSITESYNC; if (mode->flags & DRM_MODE_FLAG_INTERLACE) misc |= ATOM_INTERLACE; - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + if (mode->flags & DRM_MODE_FLAG_DBLCLK) misc |= ATOM_DOUBLE_CLOCK_MODE; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + misc |= ATOM_H_REPLICATIONBY2 | ATOM_V_REPLICATIONBY2; args.susModeMiscInfo.usAccess = cpu_to_le16(misc); args.ucCRTC = radeon_crtc->crtc_id; @@ -346,8 +348,10 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc, misc |= ATOM_COMPOSITESYNC; if (mode->flags & DRM_MODE_FLAG_INTERLACE) misc |= ATOM_INTERLACE; - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + if (mode->flags & DRM_MODE_FLAG_DBLCLK) misc |= ATOM_DOUBLE_CLOCK_MODE; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + misc |= ATOM_H_REPLICATIONBY2 | ATOM_V_REPLICATIONBY2; args.susModeMiscInfo.usAccess = cpu_to_le16(misc); args.ucCRTC = radeon_crtc->crtc_id; @@ -466,7 +470,7 @@ static void atombios_crtc_program_ss(struct drm_crtc *crtc, return; } args.v2.ucEnable = enable; - if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK)) + if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK) || ASIC_IS_DCE41(rdev)) args.v2.ucEnable = ATOM_DISABLE; } else if (ASIC_IS_DCE3(rdev)) { args.v1.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); @@ -541,6 +545,14 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (rdev->family < CHIP_RV770) pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; + /* use frac fb div on APUs */ + if (ASIC_IS_DCE41(rdev)) + pll->flags |= RADEON_PLL_USE_FRAC_FB_DIV; + /* use frac fb div on RS780/RS880 */ + if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) + pll->flags |= RADEON_PLL_USE_FRAC_FB_DIV; + if (ASIC_IS_DCE32(rdev) && mode->clock > 165000) + pll->flags |= RADEON_PLL_USE_FRAC_FB_DIV; } else { pll->flags |= RADEON_PLL_LEGACY; @@ -558,7 +570,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, bpc = connector->display_info.bpc; encoder_mode = atombios_get_encoder_mode(encoder); if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || - radeon_encoder_is_dp_bridge(encoder)) { + (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { if (connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector_atom_dig *dig_connector = @@ -638,44 +650,29 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (ss_enabled && ss->percentage) args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_SS_ENABLE; - if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT) || - radeon_encoder_is_dp_bridge(encoder)) { + if (ENCODER_MODE_IS_DP(encoder_mode)) { + args.v3.sInput.ucDispPllConfig |= + DISPPLL_CONFIG_COHERENT_MODE; + /* 16200 or 27000 */ + args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10); + } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - if (encoder_mode == ATOM_ENCODER_MODE_DP) { + if (encoder_mode == ATOM_ENCODER_MODE_HDMI) + /* deep color support */ + args.v3.sInput.usPixelClock = + cpu_to_le16((mode->clock * bpc / 8) / 10); + if (dig->coherent_mode) args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_COHERENT_MODE; - /* 16200 or 27000 */ - args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10); - } else { - if (encoder_mode == ATOM_ENCODER_MODE_HDMI) { - /* deep color support */ - args.v3.sInput.usPixelClock = - cpu_to_le16((mode->clock * bpc / 8) / 10); - } - if (dig->coherent_mode) - args.v3.sInput.ucDispPllConfig |= - DISPPLL_CONFIG_COHERENT_MODE; - if (mode->clock > 165000) - args.v3.sInput.ucDispPllConfig |= - DISPPLL_CONFIG_DUAL_LINK; - } - } else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { - if (encoder_mode == ATOM_ENCODER_MODE_DP) { + if (mode->clock > 165000) args.v3.sInput.ucDispPllConfig |= - DISPPLL_CONFIG_COHERENT_MODE; - /* 16200 or 27000 */ - args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10); - } else if (encoder_mode != ATOM_ENCODER_MODE_LVDS) { - if (mode->clock > 165000) - args.v3.sInput.ucDispPllConfig |= - DISPPLL_CONFIG_DUAL_LINK; - } + DISPPLL_CONFIG_DUAL_LINK; } - if (radeon_encoder_is_dp_bridge(encoder)) { - struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder); - struct radeon_encoder *ext_radeon_encoder = to_radeon_encoder(ext_encoder); - args.v3.sInput.ucExtTransmitterID = ext_radeon_encoder->encoder_id; - } else + if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != + ENCODER_OBJECT_ID_NONE) + args.v3.sInput.ucExtTransmitterID = + radeon_encoder_get_dp_bridge_encoder_id(encoder); + else args.v3.sInput.ucExtTransmitterID = 0; atom_execute_table(rdev->mode_info.atom_context, @@ -764,7 +761,7 @@ static void atombios_crtc_set_dcpll(struct drm_crtc *crtc, } static void atombios_crtc_program_pll(struct drm_crtc *crtc, - int crtc_id, + u32 crtc_id, int pll_id, u32 encoder_mode, u32 encoder_id, @@ -837,22 +834,23 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc, args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */ if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC; - switch (bpc) { - case 8: - default: - args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP; - break; - case 10: - args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP; - break; + if (encoder_mode == ATOM_ENCODER_MODE_HDMI) { + switch (bpc) { + case 8: + default: + args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP; + break; + case 10: + args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP; + break; + } } args.v5.ucTransmitterID = encoder_id; args.v5.ucEncoderMode = encoder_mode; args.v5.ucPpll = pll_id; break; case 6: - args.v6.ulCrtcPclkFreq.ucCRTC = crtc_id; - args.v6.ulCrtcPclkFreq.ulPixelClock = cpu_to_le32(clock / 10); + args.v6.ulDispEngClkFreq = cpu_to_le32(crtc_id << 24 | clock / 10); args.v6.ucRefDiv = ref_div; args.v6.usFbDiv = cpu_to_le16(fb_div); args.v6.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000); @@ -860,20 +858,22 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc, args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */ if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; - switch (bpc) { - case 8: - default: - args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP; - break; - case 10: - args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP; - break; - case 12: - args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP; - break; - case 16: - args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP; - break; + if (encoder_mode == ATOM_ENCODER_MODE_HDMI) { + switch (bpc) { + case 8: + default: + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP; + break; + case 10: + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP; + break; + case 12: + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP; + break; + case 16: + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP; + break; + } } args.v6.ucTransmitterID = encoder_id; args.v6.ucEncoderMode = encoder_mode; @@ -933,8 +933,8 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode break; } - if (radeon_encoder->active_device & - (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) { + if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || + (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); @@ -946,6 +946,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode bpc = connector->display_info.bpc; switch (encoder_mode) { + case ATOM_ENCODER_MODE_DP_MST: case ATOM_ENCODER_MODE_DP: /* DP/eDP */ dp_clock = dig_connector->dp_clock / 10; @@ -963,10 +964,13 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode ss_enabled = radeon_atombios_get_ppll_ss_info(rdev, &ss, ATOM_DP_SS_ID1); - } else + } else { ss_enabled = radeon_atombios_get_ppll_ss_info(rdev, &ss, ATOM_DP_SS_ID1); + } + /* disable spread spectrum on DCE3 DP */ + ss_enabled = false; } break; case ATOM_ENCODER_MODE_LVDS: @@ -1122,9 +1126,40 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, return -EINVAL; } - if (tiling_flags & RADEON_TILING_MACRO) + if (tiling_flags & RADEON_TILING_MACRO) { + if (rdev->family >= CHIP_CAYMAN) + tmp = rdev->config.cayman.tile_config; + else + tmp = rdev->config.evergreen.tile_config; + + switch ((tmp & 0xf0) >> 4) { + case 0: /* 4 banks */ + fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_4_BANK); + break; + case 1: /* 8 banks */ + default: + fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_8_BANK); + break; + case 2: /* 16 banks */ + fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_16_BANK); + break; + } + + switch ((tmp & 0xf000) >> 12) { + case 0: /* 1KB rows */ + default: + fb_format |= EVERGREEN_GRPH_TILE_SPLIT(EVERGREEN_ADDR_SURF_TILE_SPLIT_1KB); + break; + case 1: /* 2KB rows */ + fb_format |= EVERGREEN_GRPH_TILE_SPLIT(EVERGREEN_ADDR_SURF_TILE_SPLIT_2KB); + break; + case 2: /* 4KB rows */ + fb_format |= EVERGREEN_GRPH_TILE_SPLIT(EVERGREEN_ADDR_SURF_TILE_SPLIT_4KB); + break; + } + fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1); - else if (tiling_flags & RADEON_TILING_MICRO) + } else if (tiling_flags & RADEON_TILING_MICRO) fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1); switch (radeon_crtc->crtc_id) { @@ -1451,9 +1486,11 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) * PPLL/DCPLL programming and only program the DP DTO for the * crtc virtual pixel clock. */ - if (atombios_get_encoder_mode(test_encoder) == ATOM_ENCODER_MODE_DP) { - if (ASIC_IS_DCE5(rdev) || rdev->clock.dp_extclk) + if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) { + if (rdev->clock.dp_extclk) return ATOM_PPLL_INVALID; + else if (ASIC_IS_DCE5(rdev)) + return ATOM_DCPLL; } } } @@ -1537,12 +1574,6 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct drm_device *dev = crtc->dev; - struct radeon_device *rdev = dev->dev_private; - - /* adjust pm to upcoming mode change */ - radeon_pm_compute_clocks(rdev); - if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) return false; return true; @@ -1568,10 +1599,25 @@ static void atombios_crtc_commit(struct drm_crtc *crtc) static void atombios_crtc_disable(struct drm_crtc *crtc) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_atom_ss ss; + int i; atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->mode_info.crtcs[i] && + rdev->mode_info.crtcs[i]->enabled && + i != radeon_crtc->crtc_id && + radeon_crtc->pll_id == rdev->mode_info.crtcs[i]->pll_id) { + /* one other crtc is using this pll don't turn + * off the pll + */ + goto done; + } + } + switch (radeon_crtc->pll_id) { case ATOM_PPLL1: case ATOM_PPLL2: @@ -1582,6 +1628,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc) default: break; } +done: radeon_crtc->pll_id = -1; } diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index efc2b21..3e34f1e 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -89,7 +89,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, /* flags not zero */ if (args.v1.ucReplyStatus == 2) { DRM_DEBUG_KMS("dp_aux_ch flags not zero\n"); - return -EBUSY; + return -EIO; } /* error */ @@ -483,7 +483,8 @@ static int radeon_dp_get_dp_link_clock(struct drm_connector *connector, int bpp = convert_bpc_to_bpp(connector->display_info.bpc); int lane_num, max_pix_clock; - if (radeon_connector_encoder_is_dp_bridge(connector)) + if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == + ENCODER_OBJECT_ID_NUTMEG) return 270000; lane_num = radeon_dp_get_dp_lane_number(connector, dpcd, pix_clock); @@ -549,8 +550,8 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) return false; } -static void radeon_dp_set_panel_mode(struct drm_encoder *encoder, - struct drm_connector *connector) +int radeon_dp_get_panel_mode(struct drm_encoder *encoder, + struct drm_connector *connector) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; @@ -558,24 +559,33 @@ static void radeon_dp_set_panel_mode(struct drm_encoder *encoder, int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; if (!ASIC_IS_DCE4(rdev)) - return; + return panel_mode; - if (radeon_connector_encoder_is_dp_bridge(connector)) + if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == + ENCODER_OBJECT_ID_NUTMEG) panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; - else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == + ENCODER_OBJECT_ID_TRAVIS) { + u8 id[6]; + int i; + for (i = 0; i < 6; i++) + id[i] = radeon_read_dpcd_reg(radeon_connector, 0x503 + i); + if (id[0] == 0x73 && + id[1] == 0x69 && + id[2] == 0x76 && + id[3] == 0x61 && + id[4] == 0x72 && + id[5] == 0x54) + panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; + else + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + } else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { u8 tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP); if (tmp & 1) panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; } - atombios_dig_encoder_setup(encoder, - ATOM_ENCODER_CMD_SETUP_PANEL_MODE, - panel_mode); - - if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) && - (panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { - radeon_write_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_SET, 1); - } + return panel_mode; } void radeon_dp_set_link_config(struct drm_connector *connector, @@ -712,6 +722,8 @@ static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp) static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(dp_info->encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; u8 tmp; /* power up the sink */ @@ -727,7 +739,8 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) radeon_write_dpcd_reg(dp_info->radeon_connector, DP_DOWNSPREAD_CTRL, 0); - radeon_dp_set_panel_mode(dp_info->encoder, dp_info->connector); + if (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE) + radeon_write_dpcd_reg(dp_info->radeon_connector, DP_EDP_CONFIGURATION_SET, 1); /* set the lane count on the sink */ tmp = dp_info->dp_lane_count; diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 87ff586..5d78973 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -37,9 +37,19 @@ #define EVERGREEN_PFP_UCODE_SIZE 1120 #define EVERGREEN_PM4_UCODE_SIZE 1376 +static const u32 crtc_offsets[6] = +{ + EVERGREEN_CRTC0_REGISTER_OFFSET, + EVERGREEN_CRTC1_REGISTER_OFFSET, + EVERGREEN_CRTC2_REGISTER_OFFSET, + EVERGREEN_CRTC3_REGISTER_OFFSET, + EVERGREEN_CRTC4_REGISTER_OFFSET, + EVERGREEN_CRTC5_REGISTER_OFFSET +}; + static void evergreen_gpu_init(struct radeon_device *rdev); void evergreen_fini(struct radeon_device *rdev); -static void evergreen_pcie_gen2_enable(struct radeon_device *rdev); +void evergreen_pcie_gen2_enable(struct radeon_device *rdev); void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev) { @@ -66,6 +76,27 @@ void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev) } } +void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc) +{ + int i; + + if (crtc >= rdev->num_crtc) + return; + + if (RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[crtc]) & EVERGREEN_CRTC_MASTER_EN) { + for (i = 0; i < rdev->usec_timeout; i++) { + if (!(RREG32(EVERGREEN_CRTC_STATUS + crtc_offsets[crtc]) & EVERGREEN_CRTC_V_BLANK)) + break; + udelay(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(EVERGREEN_CRTC_STATUS + crtc_offsets[crtc]) & EVERGREEN_CRTC_V_BLANK) + break; + udelay(1); + } + } +} + void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc) { /* enable the pflip int */ @@ -162,6 +193,57 @@ int sumo_get_temp(struct radeon_device *rdev) return actual_temp * 1000; } +void sumo_pm_init_profile(struct radeon_device *rdev) +{ + int idx; + + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0; + + /* low,mid sh/mh */ + if (rdev->flags & RADEON_IS_MOBILITY) + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); + else + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0; + + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0; + + /* high sh/mh */ + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = + rdev->pm.power_state[idx].num_clock_modes - 1; + + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = + rdev->pm.power_state[idx].num_clock_modes - 1; +} + void evergreen_pm_misc(struct radeon_device *rdev) { int req_ps_idx = rdev->pm.requested_power_state_index; @@ -801,7 +883,7 @@ static void evergreen_program_watermarks(struct radeon_device *rdev, !evergreen_average_bandwidth_vs_available_bandwidth(&wm) || !evergreen_check_latency_hiding(&wm) || (rdev->disp_priority == 2)) { - DRM_INFO("force priority to high\n"); + DRM_DEBUG_KMS("force priority to high\n"); priority_a_cnt |= PRIORITY_ALWAYS_ON; priority_b_cnt |= PRIORITY_ALWAYS_ON; } @@ -926,7 +1008,7 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev) u32 tmp; int r; - if (rdev->gart.table.vram.robj == NULL) { + if (rdev->gart.robj == NULL) { dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); return -EINVAL; } @@ -973,6 +1055,9 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev) WREG32(VM_CONTEXT1_CNTL, 0); evergreen_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); rdev->gart.ready = true; return 0; } @@ -980,7 +1065,6 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev) void evergreen_pcie_gart_disable(struct radeon_device *rdev) { u32 tmp; - int r; /* Disable all tables */ WREG32(VM_CONTEXT0_CNTL, 0); @@ -1000,14 +1084,7 @@ void evergreen_pcie_gart_disable(struct radeon_device *rdev) WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); - if (rdev->gart.table.vram.robj) { - r = radeon_bo_reserve(rdev->gart.table.vram.robj, false); - if (likely(r == 0)) { - radeon_bo_kunmap(rdev->gart.table.vram.robj); - radeon_bo_unpin(rdev->gart.table.vram.robj); - radeon_bo_unreserve(rdev->gart.table.vram.robj); - } - } + radeon_gart_table_vram_unpin(rdev); } void evergreen_pcie_gart_fini(struct radeon_device *rdev) @@ -1046,51 +1123,46 @@ void evergreen_agp_enable(struct radeon_device *rdev) void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save) { + u32 crtc_enabled, tmp, frame_count, blackout; + int i, j; + save->vga_render_control = RREG32(VGA_RENDER_CONTROL); save->vga_hdp_control = RREG32(VGA_HDP_CONTROL); - /* Stop all video */ + /* disable VGA render */ WREG32(VGA_RENDER_CONTROL, 0); - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1); - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1); - if (rdev->num_crtc >= 4) { - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1); - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1); - } - if (rdev->num_crtc >= 6) { - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1); - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1); - } - WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); - WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); - if (rdev->num_crtc >= 4) { - WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); - WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); - } - if (rdev->num_crtc >= 6) { - WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); - WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); - } - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); - if (rdev->num_crtc >= 4) { - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); - } - if (rdev->num_crtc >= 6) { - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); - WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); + /* blank the display controllers */ + for (i = 0; i < rdev->num_crtc; i++) { + crtc_enabled = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]) & EVERGREEN_CRTC_MASTER_EN; + if (crtc_enabled) { + save->crtc_enabled[i] = true; + tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]); + if (!(tmp & EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE)) { + dce4_wait_for_vblank(rdev, i); + tmp |= EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp); + } + /* wait for the next frame */ + frame_count = radeon_get_vblank_counter(rdev, i); + for (j = 0; j < rdev->usec_timeout; j++) { + if (radeon_get_vblank_counter(rdev, i) != frame_count) + break; + udelay(1); + } + } else { + save->crtc_enabled[i] = false; + } } - WREG32(D1VGA_CONTROL, 0); - WREG32(D2VGA_CONTROL, 0); - if (rdev->num_crtc >= 4) { - WREG32(EVERGREEN_D3VGA_CONTROL, 0); - WREG32(EVERGREEN_D4VGA_CONTROL, 0); - } - if (rdev->num_crtc >= 6) { - WREG32(EVERGREEN_D5VGA_CONTROL, 0); - WREG32(EVERGREEN_D6VGA_CONTROL, 0); + evergreen_mc_wait_for_idle(rdev); + + blackout = RREG32(MC_SHARED_BLACKOUT_CNTL); + if ((blackout & BLACKOUT_MODE_MASK) != 1) { + /* Block CPU access */ + WREG32(BIF_FB_EN, 0); + /* blackout the MC */ + blackout &= ~BLACKOUT_MODE_MASK; + WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1); } /* wait for the MC to settle */ udelay(100); @@ -1098,66 +1170,45 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save) { - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC0_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC0_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC0_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC0_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC1_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC1_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC1_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC1_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - - if (rdev->num_crtc >= 4) { - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC2_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC2_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC2_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC2_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC3_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC3_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC3_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC3_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - } - if (rdev->num_crtc >= 6) { - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC4_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC4_REGISTER_OFFSET, - upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC4_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC4_REGISTER_OFFSET, - (u32)rdev->mc.vram_start); + u32 tmp, frame_count; + int i, j; - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC5_REGISTER_OFFSET, + /* update crtc base addresses */ + for (i = 0; i < rdev->num_crtc; i++) { + WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i], upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC5_REGISTER_OFFSET, + WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i], upper_32_bits(rdev->mc.vram_start)); - WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC5_REGISTER_OFFSET, + WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + crtc_offsets[i], (u32)rdev->mc.vram_start); - WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC5_REGISTER_OFFSET, + WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + crtc_offsets[i], (u32)rdev->mc.vram_start); } - WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH, upper_32_bits(rdev->mc.vram_start)); WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS, (u32)rdev->mc.vram_start); - /* Unlock host access */ + + /* unblackout the MC */ + tmp = RREG32(MC_SHARED_BLACKOUT_CNTL); + tmp &= ~BLACKOUT_MODE_MASK; + WREG32(MC_SHARED_BLACKOUT_CNTL, tmp); + /* allow CPU access */ + WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN); + + for (i = 0; i < rdev->num_crtc; i++) { + if (save->crtc_enabled[i]) { + tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]); + tmp &= ~EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp); + /* wait for the next frame */ + frame_count = radeon_get_vblank_counter(rdev, i); + for (j = 0; j < rdev->usec_timeout; j++) { + if (radeon_get_vblank_counter(rdev, i) != frame_count) + break; + udelay(1); + } + } + } + /* Unlock vga access */ WREG32(VGA_HDP_CONTROL, save->vga_hdp_control); mdelay(1); WREG32(VGA_RENDER_CONTROL, save->vga_render_control); @@ -1206,7 +1257,7 @@ void evergreen_mc_program(struct radeon_device *rdev) WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end >> 12); } - WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0); + WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12); if (rdev->flags & RADEON_IS_IGP) { tmp = RREG32(MC_FUS_VM_FB_OFFSET) & 0x000FFFFF; tmp |= ((rdev->mc.vram_end >> 20) & 0xF) << 24; @@ -1392,9 +1443,6 @@ int evergreen_cp_resume(struct radeon_device *rdev) /* set the wb address wether it's enabled or not */ WREG32(CP_RB_RPTR_ADDR, -#ifdef __BIG_ENDIAN - RB_RPTR_SWAP(2) | -#endif ((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC)); WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF); WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF); @@ -1711,7 +1759,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) case CHIP_SUMO: rdev->config.evergreen.num_ses = 1; rdev->config.evergreen.max_pipes = 4; - rdev->config.evergreen.max_tile_pipes = 2; + rdev->config.evergreen.max_tile_pipes = 4; if (rdev->pdev->device == 0x9648) rdev->config.evergreen.max_simds = 3; else if ((rdev->pdev->device == 0x9647) || @@ -1800,7 +1848,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) break; case CHIP_CAICOS: rdev->config.evergreen.num_ses = 1; - rdev->config.evergreen.max_pipes = 4; + rdev->config.evergreen.max_pipes = 2; rdev->config.evergreen.max_tile_pipes = 2; rdev->config.evergreen.max_simds = 2; rdev->config.evergreen.max_backends = 1 * rdev->config.evergreen.num_ses; @@ -2008,14 +2056,26 @@ static void evergreen_gpu_init(struct radeon_device *rdev) /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */ if (rdev->flags & RADEON_IS_IGP) rdev->config.evergreen.tile_config |= 1 << 4; - else - rdev->config.evergreen.tile_config |= - ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4; + else { + switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) { + case 0: /* four banks */ + rdev->config.evergreen.tile_config |= 0 << 4; + break; + case 1: /* eight banks */ + rdev->config.evergreen.tile_config |= 1 << 4; + break; + case 2: /* sixteen banks */ + default: + rdev->config.evergreen.tile_config |= 2 << 4; + break; + } + } rdev->config.evergreen.tile_config |= ((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) << 8; rdev->config.evergreen.tile_config |= ((gb_addr_config & 0x30000000) >> 28) << 12; + rdev->config.evergreen.backend_map = gb_backend_map; WREG32(GB_BACKEND_MAP, gb_backend_map); WREG32(GB_ADDR_CONFIG, gb_addr_config); WREG32(DMIF_ADDR_CONFIG, gb_addr_config); @@ -2073,6 +2133,9 @@ static void evergreen_gpu_init(struct radeon_device *rdev) smx_dc_ctl0 |= NUMBER_OF_SETS(rdev->config.evergreen.sx_num_of_sets); WREG32(SMX_DC_CTL0, smx_dc_ctl0); + if (rdev->family <= CHIP_SUMO2) + WREG32(SMX_SAR_CTL0, 0x00010000); + WREG32(SX_EXPORT_BUFFER_SIZES, (COLOR_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_size / 4) - 1) | POSITION_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_pos_size / 4) - 1) | SMX_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_smx_size / 4) - 1))); @@ -2568,10 +2631,13 @@ int evergreen_irq_set(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, hpd5); WREG32(DC_HPD6_INT_CONTROL, hpd6); + /* posting read */ + RREG32(SRBM_STATUS); + return 0; } -static inline void evergreen_irq_ack(struct radeon_device *rdev) +static void evergreen_irq_ack(struct radeon_device *rdev) { u32 tmp; @@ -2682,7 +2748,7 @@ void evergreen_irq_suspend(struct radeon_device *rdev) r600_rlc_stop(rdev); } -static inline u32 evergreen_get_ih_wptr(struct radeon_device *rdev) +static u32 evergreen_get_ih_wptr(struct radeon_device *rdev) { u32 wptr, tmp; @@ -2702,6 +2768,7 @@ static inline u32 evergreen_get_ih_wptr(struct radeon_device *rdev) tmp = RREG32(IH_RB_CNTL); tmp |= IH_WPTR_OVERFLOW_CLEAR; WREG32(IH_RB_CNTL, tmp); + wptr &= ~RB_OVERFLOW; } return (wptr & rdev->ih.ptr_mask); } @@ -2728,6 +2795,9 @@ int evergreen_irq_process(struct radeon_device *rdev) return IRQ_NONE; } restart_ih: + /* Order reading of wptr vs. reading of IH ring data */ + rmb(); + /* display interrupts */ evergreen_irq_ack(rdev); @@ -2985,8 +3055,9 @@ static int evergreen_startup(struct radeon_device *rdev) int r; /* enable pcie gen2 link */ - if (!ASIC_IS_DCE5(rdev)) - evergreen_pcie_gen2_enable(rdev); + evergreen_pcie_gen2_enable(rdev); + + evergreen_mc_program(rdev); if (ASIC_IS_DCE5(rdev)) { if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) { @@ -3011,7 +3082,10 @@ static int evergreen_startup(struct radeon_device *rdev) } } - evergreen_mc_program(rdev); + r = r600_vram_scratch_init(rdev); + if (r) + return r; + if (rdev->flags & RADEON_IS_AGP) { evergreen_agp_enable(rdev); } else { @@ -3023,7 +3097,7 @@ static int evergreen_startup(struct radeon_device *rdev) r = evergreen_blit_init(rdev); if (r) { - evergreen_blit_fini(rdev); + r600_blit_fini(rdev); rdev->asic->copy = NULL; dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); } @@ -3034,6 +3108,12 @@ static int evergreen_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + r = r600_irq_init(rdev); if (r) { DRM_ERROR("radeon: IH init failed (%d).\n", r); @@ -3089,48 +3169,17 @@ int evergreen_resume(struct radeon_device *rdev) int evergreen_suspend(struct radeon_device *rdev) { - int r; - /* FIXME: we should wait for ring to be empty */ r700_cp_stop(rdev); rdev->cp.ready = false; evergreen_irq_suspend(rdev); radeon_wb_disable(rdev); evergreen_pcie_gart_disable(rdev); - - /* unpin shaders bo */ - r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); - if (likely(r == 0)) { - radeon_bo_unpin(rdev->r600_blit.shader_obj); - radeon_bo_unreserve(rdev->r600_blit.shader_obj); - } + r600_blit_suspend(rdev); return 0; } -int evergreen_copy_blit(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence *fence) -{ - int r; - - mutex_lock(&rdev->r600_blit.mutex); - rdev->r600_blit.vb_ib = NULL; - r = evergreen_blit_prepare_copy(rdev, num_gpu_pages * RADEON_GPU_PAGE_SIZE); - if (r) { - if (rdev->r600_blit.vb_ib) - radeon_ib_free(rdev, &rdev->r600_blit.vb_ib); - mutex_unlock(&rdev->r600_blit.mutex); - return r; - } - evergreen_kms_blit_copy(rdev, src_offset, dst_offset, num_gpu_pages * RADEON_GPU_PAGE_SIZE); - evergreen_blit_done_copy(rdev, fence); - mutex_unlock(&rdev->r600_blit.mutex); - return 0; -} - /* Plan is to move initialization in that function and use * helper function so that radeon_device_init pretty much * do nothing more than calling asic specific function. This @@ -3197,10 +3246,6 @@ int evergreen_init(struct radeon_device *rdev) if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; - rdev->cp.ring_obj = NULL; r600_ring_init(rdev, 1024 * 1024); @@ -3251,13 +3296,14 @@ int evergreen_init(struct radeon_device *rdev) void evergreen_fini(struct radeon_device *rdev) { - evergreen_blit_fini(rdev); + r600_blit_fini(rdev); r700_cp_fini(rdev); r600_irq_fini(rdev); radeon_wb_fini(rdev); radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); evergreen_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); radeon_fence_driver_fini(rdev); radeon_agp_fini(rdev); @@ -3267,7 +3313,7 @@ void evergreen_fini(struct radeon_device *rdev) rdev->bios = NULL; } -static void evergreen_pcie_gen2_enable(struct radeon_device *rdev) +void evergreen_pcie_gen2_enable(struct radeon_device *rdev) { u32 link_width_cntl, speed_cntl; diff --git a/drivers/gpu/drm/radeon/evergreen_blit_kms.c b/drivers/gpu/drm/radeon/evergreen_blit_kms.c index 2eb2518..914e5af 100644 --- a/drivers/gpu/drm/radeon/evergreen_blit_kms.c +++ b/drivers/gpu/drm/radeon/evergreen_blit_kms.c @@ -56,7 +56,9 @@ set_render_target(struct radeon_device *rdev, int format, if (h < 8) h = 8; - cb_color_info = ((format << 2) | (1 << 24) | (1 << 8)); + cb_color_info = CB_FORMAT(format) | + CB_SOURCE_FORMAT(CB_SF_EXPORT_NORM) | + CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1); pitch = (w / 8) - 1; slice = ((w * h) / 64) - 1; @@ -67,7 +69,7 @@ set_render_target(struct radeon_device *rdev, int format, radeon_ring_write(rdev, slice); radeon_ring_write(rdev, 0); radeon_ring_write(rdev, cb_color_info); - radeon_ring_write(rdev, (1 << 4)); + radeon_ring_write(rdev, 0); radeon_ring_write(rdev, (w - 1) | ((h - 1) << 16)); radeon_ring_write(rdev, 0); radeon_ring_write(rdev, 0); @@ -92,6 +94,15 @@ cp_set_surface_sync(struct radeon_device *rdev, else cp_coher_size = ((size + 255) >> 8); + if (rdev->family >= CHIP_CAYMAN) { + /* CP_COHER_CNTL2 has to be set manually when submitting a surface_sync + * to the RB directly. For IBs, the CP programs this as part of the + * surface_sync packet. + */ + radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(rdev, (0x85e8 - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(rdev, 0); /* CP_COHER_CNTL2 */ + } radeon_ring_write(rdev, PACKET3(PACKET3_SURFACE_SYNC, 3)); radeon_ring_write(rdev, sync_type); radeon_ring_write(rdev, cp_coher_size); @@ -133,12 +144,16 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr) u32 sq_vtx_constant_word2, sq_vtx_constant_word3; /* high addr, stride */ - sq_vtx_constant_word2 = ((upper_32_bits(gpu_addr) & 0xff) | (16 << 8)); + sq_vtx_constant_word2 = SQ_VTXC_BASE_ADDR_HI(upper_32_bits(gpu_addr) & 0xff) | + SQ_VTXC_STRIDE(16); #ifdef __BIG_ENDIAN - sq_vtx_constant_word2 |= (2 << 30); + sq_vtx_constant_word2 |= SQ_VTXC_ENDIAN_SWAP(SQ_ENDIAN_8IN32); #endif /* xyzw swizzles */ - sq_vtx_constant_word3 = (0 << 3) | (1 << 6) | (2 << 9) | (3 << 12); + sq_vtx_constant_word3 = SQ_VTCX_SEL_X(SQ_SEL_X) | + SQ_VTCX_SEL_Y(SQ_SEL_Y) | + SQ_VTCX_SEL_Z(SQ_SEL_Z) | + SQ_VTCX_SEL_W(SQ_SEL_W); radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 8)); radeon_ring_write(rdev, 0x580); @@ -149,7 +164,7 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr) radeon_ring_write(rdev, 0); radeon_ring_write(rdev, 0); radeon_ring_write(rdev, 0); - radeon_ring_write(rdev, SQ_TEX_VTX_VALID_BUFFER << 30); + radeon_ring_write(rdev, S__SQ_CONSTANT_TYPE(SQ_TEX_VTX_VALID_BUFFER)); if ((rdev->family == CHIP_CEDAR) || (rdev->family == CHIP_PALM) || @@ -168,7 +183,7 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr) static void set_tex_resource(struct radeon_device *rdev, int format, int w, int h, int pitch, - u64 gpu_addr) + u64 gpu_addr, u32 size) { u32 sq_tex_resource_word0, sq_tex_resource_word1; u32 sq_tex_resource_word4, sq_tex_resource_word7; @@ -176,14 +191,22 @@ set_tex_resource(struct radeon_device *rdev, if (h < 1) h = 1; - sq_tex_resource_word0 = (1 << 0); /* 2D */ + sq_tex_resource_word0 = TEX_DIM(SQ_TEX_DIM_2D); sq_tex_resource_word0 |= ((((pitch >> 3) - 1) << 6) | ((w - 1) << 18)); - sq_tex_resource_word1 = ((h - 1) << 0) | (1 << 28); + sq_tex_resource_word1 = ((h - 1) << 0) | + TEX_ARRAY_MODE(ARRAY_1D_TILED_THIN1); /* xyzw swizzles */ - sq_tex_resource_word4 = (0 << 16) | (1 << 19) | (2 << 22) | (3 << 25); + sq_tex_resource_word4 = TEX_DST_SEL_X(SQ_SEL_X) | + TEX_DST_SEL_Y(SQ_SEL_Y) | + TEX_DST_SEL_Z(SQ_SEL_Z) | + TEX_DST_SEL_W(SQ_SEL_W); + + sq_tex_resource_word7 = format | + S__SQ_CONSTANT_TYPE(SQ_TEX_VTX_VALID_TEXTURE); - sq_tex_resource_word7 = format | (SQ_TEX_VTX_VALID_TEXTURE << 30); + cp_set_surface_sync(rdev, + PACKET3_TC_ACTION_ENA, size, gpu_addr); radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 8)); radeon_ring_write(rdev, 0); @@ -584,31 +607,6 @@ set_default_state(struct radeon_device *rdev) } -static inline uint32_t i2f(uint32_t input) -{ - u32 result, i, exponent, fraction; - - if ((input & 0x3fff) == 0) - result = 0; /* 0 is a special case */ - else { - exponent = 140; /* exponent biased by 127; */ - fraction = (input & 0x3fff) << 10; /* cheat and only - handle numbers below 2^^15 */ - for (i = 0; i < 14; i++) { - if (fraction & 0x800000) - break; - else { - fraction = fraction << 1; /* keep - shifting left until top bit = 1 */ - exponent = exponent - 1; - } - } - result = exponent << 23 | (fraction & 0x7fffff); /* mask - off top bit; assumed 1 */ - } - return result; -} - int evergreen_blit_init(struct radeon_device *rdev) { u32 obj_size; @@ -617,6 +615,26 @@ int evergreen_blit_init(struct radeon_device *rdev) u32 packet2s[16]; int num_packet2s = 0; + rdev->r600_blit.primitives.set_render_target = set_render_target; + rdev->r600_blit.primitives.cp_set_surface_sync = cp_set_surface_sync; + rdev->r600_blit.primitives.set_shaders = set_shaders; + rdev->r600_blit.primitives.set_vtx_resource = set_vtx_resource; + rdev->r600_blit.primitives.set_tex_resource = set_tex_resource; + rdev->r600_blit.primitives.set_scissors = set_scissors; + rdev->r600_blit.primitives.draw_auto = draw_auto; + rdev->r600_blit.primitives.set_default_state = set_default_state; + + rdev->r600_blit.ring_size_common = 55; /* shaders + def state */ + rdev->r600_blit.ring_size_common += 16; /* fence emit for VB IB */ + rdev->r600_blit.ring_size_common += 5; /* done copy */ + rdev->r600_blit.ring_size_common += 16; /* fence emit for done copy */ + + rdev->r600_blit.ring_size_per_loop = 74; + if (rdev->family >= CHIP_CAYMAN) + rdev->r600_blit.ring_size_per_loop += 9; /* additional DWs for surface sync */ + + rdev->r600_blit.max_dim = 16384; + /* pin copy shader into vram if already initialized */ if (rdev->r600_blit.shader_obj) goto done; @@ -712,277 +730,3 @@ done: radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size); return 0; } - -void evergreen_blit_fini(struct radeon_device *rdev) -{ - int r; - - radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); - if (rdev->r600_blit.shader_obj == NULL) - return; - /* If we can't reserve the bo, unref should be enough to destroy - * it when it becomes idle. - */ - r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); - if (!r) { - radeon_bo_unpin(rdev->r600_blit.shader_obj); - radeon_bo_unreserve(rdev->r600_blit.shader_obj); - } - radeon_bo_unref(&rdev->r600_blit.shader_obj); -} - -static int evergreen_vb_ib_get(struct radeon_device *rdev) -{ - int r; - r = radeon_ib_get(rdev, &rdev->r600_blit.vb_ib); - if (r) { - DRM_ERROR("failed to get IB for vertex buffer\n"); - return r; - } - - rdev->r600_blit.vb_total = 64*1024; - rdev->r600_blit.vb_used = 0; - return 0; -} - -static void evergreen_vb_ib_put(struct radeon_device *rdev) -{ - radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence); - radeon_ib_free(rdev, &rdev->r600_blit.vb_ib); -} - -int evergreen_blit_prepare_copy(struct radeon_device *rdev, int size_bytes) -{ - int r; - int ring_size, line_size; - int max_size; - /* loops of emits + fence emit possible */ - int dwords_per_loop = 74, num_loops; - - r = evergreen_vb_ib_get(rdev); - if (r) - return r; - - /* 8 bpp vs 32 bpp for xfer unit */ - if (size_bytes & 3) - line_size = 8192; - else - line_size = 8192 * 4; - - max_size = 8192 * line_size; - - /* major loops cover the max size transfer */ - num_loops = ((size_bytes + max_size) / max_size); - /* minor loops cover the extra non aligned bits */ - num_loops += ((size_bytes % line_size) ? 1 : 0); - /* calculate number of loops correctly */ - ring_size = num_loops * dwords_per_loop; - /* set default + shaders */ - ring_size += 55; /* shaders + def state */ - ring_size += 10; /* fence emit for VB IB */ - ring_size += 5; /* done copy */ - ring_size += 10; /* fence emit for done copy */ - r = radeon_ring_lock(rdev, ring_size); - if (r) - return r; - - set_default_state(rdev); /* 36 */ - set_shaders(rdev); /* 16 */ - return 0; -} - -void evergreen_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence) -{ - int r; - - if (rdev->r600_blit.vb_ib) - evergreen_vb_ib_put(rdev); - - if (fence) - r = radeon_fence_emit(rdev, fence); - - radeon_ring_unlock_commit(rdev); -} - -void evergreen_kms_blit_copy(struct radeon_device *rdev, - u64 src_gpu_addr, u64 dst_gpu_addr, - int size_bytes) -{ - int max_bytes; - u64 vb_gpu_addr; - u32 *vb; - - DRM_DEBUG("emitting copy %16llx %16llx %d %d\n", src_gpu_addr, dst_gpu_addr, - size_bytes, rdev->r600_blit.vb_used); - vb = (u32 *)(rdev->r600_blit.vb_ib->ptr + rdev->r600_blit.vb_used); - if ((size_bytes & 3) || (src_gpu_addr & 3) || (dst_gpu_addr & 3)) { - max_bytes = 8192; - - while (size_bytes) { - int cur_size = size_bytes; - int src_x = src_gpu_addr & 255; - int dst_x = dst_gpu_addr & 255; - int h = 1; - src_gpu_addr = src_gpu_addr & ~255ULL; - dst_gpu_addr = dst_gpu_addr & ~255ULL; - - if (!src_x && !dst_x) { - h = (cur_size / max_bytes); - if (h > 8192) - h = 8192; - if (h == 0) - h = 1; - else - cur_size = max_bytes; - } else { - if (cur_size > max_bytes) - cur_size = max_bytes; - if (cur_size > (max_bytes - dst_x)) - cur_size = (max_bytes - dst_x); - if (cur_size > (max_bytes - src_x)) - cur_size = (max_bytes - src_x); - } - - if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) { - WARN_ON(1); - } - - vb[0] = i2f(dst_x); - vb[1] = 0; - vb[2] = i2f(src_x); - vb[3] = 0; - - vb[4] = i2f(dst_x); - vb[5] = i2f(h); - vb[6] = i2f(src_x); - vb[7] = i2f(h); - - vb[8] = i2f(dst_x + cur_size); - vb[9] = i2f(h); - vb[10] = i2f(src_x + cur_size); - vb[11] = i2f(h); - - /* src 10 */ - set_tex_resource(rdev, FMT_8, - src_x + cur_size, h, src_x + cur_size, - src_gpu_addr); - - /* 5 */ - cp_set_surface_sync(rdev, - PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr); - - - /* dst 17 */ - set_render_target(rdev, COLOR_8, - dst_x + cur_size, h, - dst_gpu_addr); - - /* scissors 12 */ - set_scissors(rdev, dst_x, 0, dst_x + cur_size, h); - - /* 15 */ - vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used; - set_vtx_resource(rdev, vb_gpu_addr); - - /* draw 10 */ - draw_auto(rdev); - - /* 5 */ - cp_set_surface_sync(rdev, - PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA, - cur_size * h, dst_gpu_addr); - - vb += 12; - rdev->r600_blit.vb_used += 12 * 4; - - src_gpu_addr += cur_size * h; - dst_gpu_addr += cur_size * h; - size_bytes -= cur_size * h; - } - } else { - max_bytes = 8192 * 4; - - while (size_bytes) { - int cur_size = size_bytes; - int src_x = (src_gpu_addr & 255); - int dst_x = (dst_gpu_addr & 255); - int h = 1; - src_gpu_addr = src_gpu_addr & ~255ULL; - dst_gpu_addr = dst_gpu_addr & ~255ULL; - - if (!src_x && !dst_x) { - h = (cur_size / max_bytes); - if (h > 8192) - h = 8192; - if (h == 0) - h = 1; - else - cur_size = max_bytes; - } else { - if (cur_size > max_bytes) - cur_size = max_bytes; - if (cur_size > (max_bytes - dst_x)) - cur_size = (max_bytes - dst_x); - if (cur_size > (max_bytes - src_x)) - cur_size = (max_bytes - src_x); - } - - if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) { - WARN_ON(1); - } - - vb[0] = i2f(dst_x / 4); - vb[1] = 0; - vb[2] = i2f(src_x / 4); - vb[3] = 0; - - vb[4] = i2f(dst_x / 4); - vb[5] = i2f(h); - vb[6] = i2f(src_x / 4); - vb[7] = i2f(h); - - vb[8] = i2f((dst_x + cur_size) / 4); - vb[9] = i2f(h); - vb[10] = i2f((src_x + cur_size) / 4); - vb[11] = i2f(h); - - /* src 10 */ - set_tex_resource(rdev, FMT_8_8_8_8, - (src_x + cur_size) / 4, - h, (src_x + cur_size) / 4, - src_gpu_addr); - /* 5 */ - cp_set_surface_sync(rdev, - PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr); - - /* dst 17 */ - set_render_target(rdev, COLOR_8_8_8_8, - (dst_x + cur_size) / 4, h, - dst_gpu_addr); - - /* scissors 12 */ - set_scissors(rdev, (dst_x / 4), 0, (dst_x + cur_size / 4), h); - - /* Vertex buffer setup 15 */ - vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used; - set_vtx_resource(rdev, vb_gpu_addr); - - /* draw 10 */ - draw_auto(rdev); - - /* 5 */ - cp_set_surface_sync(rdev, - PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA, - cur_size * h, dst_gpu_addr); - - /* 74 ring dwords per loop */ - vb += 12; - rdev->r600_blit.vb_used += 12 * 4; - - src_gpu_addr += cur_size * h; - dst_gpu_addr += cur_size * h; - size_bytes -= cur_size * h; - } - } -} - diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c index 23d3641..cd4590a 100644 --- a/drivers/gpu/drm/radeon/evergreen_cs.c +++ b/drivers/gpu/drm/radeon/evergreen_cs.c @@ -38,6 +38,7 @@ struct evergreen_cs_track { u32 group_size; u32 nbanks; u32 npipes; + u32 row_size; /* value we track */ u32 nsamples; u32 cb_color_base_last[12]; @@ -77,6 +78,44 @@ struct evergreen_cs_track { struct radeon_bo *db_s_write_bo; }; +static u32 evergreen_cs_get_aray_mode(u32 tiling_flags) +{ + if (tiling_flags & RADEON_TILING_MACRO) + return ARRAY_2D_TILED_THIN1; + else if (tiling_flags & RADEON_TILING_MICRO) + return ARRAY_1D_TILED_THIN1; + else + return ARRAY_LINEAR_GENERAL; +} + +static u32 evergreen_cs_get_num_banks(u32 nbanks) +{ + switch (nbanks) { + case 2: + return ADDR_SURF_2_BANK; + case 4: + return ADDR_SURF_4_BANK; + case 8: + default: + return ADDR_SURF_8_BANK; + case 16: + return ADDR_SURF_16_BANK; + } +} + +static u32 evergreen_cs_get_tile_split(u32 row_size) +{ + switch (row_size) { + case 1: + default: + return ADDR_SURF_TILE_SPLIT_1KB; + case 2: + return ADDR_SURF_TILE_SPLIT_2KB; + case 4: + return ADDR_SURF_TILE_SPLIT_4KB; + } +} + static void evergreen_cs_track_init(struct evergreen_cs_track *track) { int i; @@ -122,12 +161,6 @@ static void evergreen_cs_track_init(struct evergreen_cs_track *track) track->db_s_write_bo = NULL; } -static inline int evergreen_cs_track_validate_cb(struct radeon_cs_parser *p, int i) -{ - /* XXX fill in */ - return 0; -} - static int evergreen_cs_track_check(struct radeon_cs_parser *p) { struct evergreen_cs_track *track = p->track; @@ -236,28 +269,6 @@ static int evergreen_cs_packet_next_reloc(struct radeon_cs_parser *p, } /** - * evergreen_cs_packet_next_is_pkt3_nop() - test if next packet is packet3 nop for reloc - * @parser: parser structure holding parsing context. - * - * Check next packet is relocation packet3, do bo validation and compute - * GPU offset using the provided start. - **/ -static inline int evergreen_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p) -{ - struct radeon_cs_packet p3reloc; - int r; - - r = evergreen_cs_packet_parse(p, &p3reloc, p->idx); - if (r) { - return 0; - } - if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) { - return 0; - } - return 1; -} - -/** * evergreen_cs_packet_next_vline() - parse userspace VLINE packet * @parser: parser structure holding parsing context. * @@ -414,7 +425,7 @@ static int evergreen_cs_parse_packet0(struct radeon_cs_parser *p, * if register is safe. If register is not flag as safe this function * will test it against a list of register needind special handling. */ -static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) +static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) { struct evergreen_cs_track *track = (struct evergreen_cs_track *)p->track; struct radeon_cs_reloc *reloc; @@ -428,7 +439,7 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3 last_reg = ARRAY_SIZE(evergreen_reg_safe_bm); i = (reg >> 7); - if (i > last_reg) { + if (i >= last_reg) { dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); return -EINVAL; } @@ -508,21 +519,22 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3 } break; case DB_Z_INFO: - r = evergreen_cs_packet_next_reloc(p, &reloc); - if (r) { - dev_warn(p->dev, "bad SET_CONTEXT_REG " - "0x%04X\n", reg); - return -EINVAL; - } track->db_z_info = radeon_get_ib_value(p, idx); - ib[idx] &= ~Z_ARRAY_MODE(0xf); - track->db_z_info &= ~Z_ARRAY_MODE(0xf); - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { - ib[idx] |= Z_ARRAY_MODE(ARRAY_2D_TILED_THIN1); - track->db_z_info |= Z_ARRAY_MODE(ARRAY_2D_TILED_THIN1); - } else { - ib[idx] |= Z_ARRAY_MODE(ARRAY_1D_TILED_THIN1); - track->db_z_info |= Z_ARRAY_MODE(ARRAY_1D_TILED_THIN1); + if (!p->keep_tiling_flags) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] &= ~Z_ARRAY_MODE(0xf); + track->db_z_info &= ~Z_ARRAY_MODE(0xf); + ib[idx] |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + track->db_z_info |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + ib[idx] |= DB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks)); + ib[idx] |= DB_TILE_SPLIT(evergreen_cs_get_tile_split(track->row_size)); + } } break; case DB_STENCIL_INFO: @@ -635,40 +647,34 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3 case CB_COLOR5_INFO: case CB_COLOR6_INFO: case CB_COLOR7_INFO: - r = evergreen_cs_packet_next_reloc(p, &reloc); - if (r) { - dev_warn(p->dev, "bad SET_CONTEXT_REG " - "0x%04X\n", reg); - return -EINVAL; - } tmp = (reg - CB_COLOR0_INFO) / 0x3c; track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { - ib[idx] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1); - track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1); - } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { - ib[idx] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1); - track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1); + if (!p->keep_tiling_flags) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); } break; case CB_COLOR8_INFO: case CB_COLOR9_INFO: case CB_COLOR10_INFO: case CB_COLOR11_INFO: - r = evergreen_cs_packet_next_reloc(p, &reloc); - if (r) { - dev_warn(p->dev, "bad SET_CONTEXT_REG " - "0x%04X\n", reg); - return -EINVAL; - } tmp = ((reg - CB_COLOR8_INFO) / 0x1c) + 8; track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { - ib[idx] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1); - track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_2D_TILED_THIN1); - } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { - ib[idx] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1); - track->cb_color_info[tmp] |= CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1); + if (!p->keep_tiling_flags) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); } break; case CB_COLOR0_PITCH: @@ -723,6 +729,16 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3 case CB_COLOR9_ATTRIB: case CB_COLOR10_ATTRIB: case CB_COLOR11_ATTRIB: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + ib[idx] |= CB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks)); + ib[idx] |= CB_TILE_SPLIT(evergreen_cs_get_tile_split(track->row_size)); + } break; case CB_COLOR0_DIM: case CB_COLOR1_DIM: @@ -856,7 +872,6 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3 case SQ_PGM_START_PS: case SQ_PGM_START_HS: case SQ_PGM_START_LS: - case GDS_ADDR_BASE: case SQ_CONST_MEM_BASE: case SQ_ALU_CONST_CACHE_GS_0: case SQ_ALU_CONST_CACHE_GS_1: @@ -946,6 +961,34 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3 } ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); break; + case SX_MEMORY_EXPORT_BASE: + if (p->rdev->family >= CHIP_CAYMAN) { + dev_warn(p->dev, "bad SET_CONFIG_REG " + "0x%04X\n", reg); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONFIG_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; + case CAYMAN_SX_SCATTER_EXPORT_BASE: + if (p->rdev->family < CHIP_CAYMAN) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; default: dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); return -EINVAL; @@ -963,7 +1006,7 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3 * This function will check that the resource has valid field and that * the texture and mipmap bo object are big enough to cover this resource. */ -static inline int evergreen_check_texture_resource(struct radeon_cs_parser *p, u32 idx, +static int evergreen_check_texture_resource(struct radeon_cs_parser *p, u32 idx, struct radeon_bo *texture, struct radeon_bo *mipmap) { @@ -1153,6 +1196,34 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p, return r; } break; + case PACKET3_DISPATCH_DIRECT: + if (pkt->count != 3) { + DRM_ERROR("bad DISPATCH_DIRECT\n"); + return -EINVAL; + } + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx); + return r; + } + break; + case PACKET3_DISPATCH_INDIRECT: + if (pkt->count != 1) { + DRM_ERROR("bad DISPATCH_INDIRECT\n"); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad DISPATCH_INDIRECT\n"); + return -EINVAL; + } + ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff); + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; case PACKET3_WAIT_REG_MEM: if (pkt->count != 5) { DRM_ERROR("bad WAIT_REG_MEM\n"); @@ -1284,10 +1355,16 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } ib[idx+1+(i*8)+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) - ib[idx+1+(i*8)+1] |= TEX_ARRAY_MODE(ARRAY_2D_TILED_THIN1); - else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) - ib[idx+1+(i*8)+1] |= TEX_ARRAY_MODE(ARRAY_1D_TILED_THIN1); + if (!p->keep_tiling_flags) { + ib[idx+1+(i*8)+1] |= + TEX_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + ib[idx+1+(i*8)+6] |= + TEX_TILE_SPLIT(evergreen_cs_get_tile_split(track->row_size)); + ib[idx+1+(i*8)+7] |= + TEX_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks)); + } + } texture = reloc->robj; /* tex mip base */ r = evergreen_cs_packet_next_reloc(p, &reloc); @@ -1387,6 +1464,7 @@ int evergreen_cs_parse(struct radeon_cs_parser *p) { struct radeon_cs_packet pkt; struct evergreen_cs_track *track; + u32 tmp; int r; if (p->track == NULL) { @@ -1395,9 +1473,63 @@ int evergreen_cs_parse(struct radeon_cs_parser *p) if (track == NULL) return -ENOMEM; evergreen_cs_track_init(track); - track->npipes = p->rdev->config.evergreen.tiling_npipes; - track->nbanks = p->rdev->config.evergreen.tiling_nbanks; - track->group_size = p->rdev->config.evergreen.tiling_group_size; + if (p->rdev->family >= CHIP_CAYMAN) + tmp = p->rdev->config.cayman.tile_config; + else + tmp = p->rdev->config.evergreen.tile_config; + + switch (tmp & 0xf) { + case 0: + track->npipes = 1; + break; + case 1: + default: + track->npipes = 2; + break; + case 2: + track->npipes = 4; + break; + case 3: + track->npipes = 8; + break; + } + + switch ((tmp & 0xf0) >> 4) { + case 0: + track->nbanks = 4; + break; + case 1: + default: + track->nbanks = 8; + break; + case 2: + track->nbanks = 16; + break; + } + + switch ((tmp & 0xf00) >> 8) { + case 0: + track->group_size = 256; + break; + case 1: + default: + track->group_size = 512; + break; + } + + switch ((tmp & 0xf000) >> 12) { + case 0: + track->row_size = 1; + break; + case 1: + default: + track->row_size = 2; + break; + case 2: + track->row_size = 4; + break; + } + p->track = track; } do { diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h index c781c92..e022776 100644 --- a/drivers/gpu/drm/radeon/evergreen_reg.h +++ b/drivers/gpu/drm/radeon/evergreen_reg.h @@ -42,6 +42,17 @@ # define EVERGREEN_GRPH_DEPTH_8BPP 0 # define EVERGREEN_GRPH_DEPTH_16BPP 1 # define EVERGREEN_GRPH_DEPTH_32BPP 2 +# define EVERGREEN_GRPH_NUM_BANKS(x) (((x) & 0x3) << 2) +# define EVERGREEN_ADDR_SURF_2_BANK 0 +# define EVERGREEN_ADDR_SURF_4_BANK 1 +# define EVERGREEN_ADDR_SURF_8_BANK 2 +# define EVERGREEN_ADDR_SURF_16_BANK 3 +# define EVERGREEN_GRPH_Z(x) (((x) & 0x3) << 4) +# define EVERGREEN_GRPH_BANK_WIDTH(x) (((x) & 0x3) << 6) +# define EVERGREEN_ADDR_SURF_BANK_WIDTH_1 0 +# define EVERGREEN_ADDR_SURF_BANK_WIDTH_2 1 +# define EVERGREEN_ADDR_SURF_BANK_WIDTH_4 2 +# define EVERGREEN_ADDR_SURF_BANK_WIDTH_8 3 # define EVERGREEN_GRPH_FORMAT(x) (((x) & 0x7) << 8) /* 8 BPP */ # define EVERGREEN_GRPH_FORMAT_INDEXED 0 @@ -61,6 +72,24 @@ # define EVERGREEN_GRPH_FORMAT_8B_BGRA1010102 5 # define EVERGREEN_GRPH_FORMAT_RGB111110 6 # define EVERGREEN_GRPH_FORMAT_BGR101111 7 +# define EVERGREEN_GRPH_BANK_HEIGHT(x) (((x) & 0x3) << 11) +# define EVERGREEN_ADDR_SURF_BANK_HEIGHT_1 0 +# define EVERGREEN_ADDR_SURF_BANK_HEIGHT_2 1 +# define EVERGREEN_ADDR_SURF_BANK_HEIGHT_4 2 +# define EVERGREEN_ADDR_SURF_BANK_HEIGHT_8 3 +# define EVERGREEN_GRPH_TILE_SPLIT(x) (((x) & 0x7) << 13) +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_64B 0 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_128B 1 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_256B 2 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_512B 3 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_1KB 4 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_2KB 5 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_4KB 6 +# define EVERGREEN_GRPH_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 18) +# define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_1 0 +# define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_2 1 +# define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_4 2 +# define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_8 3 # define EVERGREEN_GRPH_ARRAY_MODE(x) (((x) & 0x7) << 20) # define EVERGREEN_GRPH_ARRAY_LINEAR_GENERAL 0 # define EVERGREEN_GRPH_ARRAY_LINEAR_ALIGNED 1 @@ -181,7 +210,10 @@ #define EVERGREEN_CRTC_CONTROL 0x6e70 # define EVERGREEN_CRTC_MASTER_EN (1 << 0) # define EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE (1 << 24) +#define EVERGREEN_CRTC_BLANK_CONTROL 0x6e74 +# define EVERGREEN_CRTC_BLANK_DATA_EN (1 << 8) #define EVERGREEN_CRTC_STATUS 0x6e8c +# define EVERGREEN_CRTC_V_BLANK (1 << 0) #define EVERGREEN_CRTC_STATUS_POSITION 0x6e90 #define EVERGREEN_MASTER_UPDATE_MODE 0x6ef8 #define EVERGREEN_CRTC_UPDATE_LOCK 0x6ed4 diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index ab670c3..47f3bd2 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -77,6 +77,10 @@ #define CONFIG_MEMSIZE 0x5428 +#define BIF_FB_EN 0x5490 +#define FB_READ_EN (1 << 0) +#define FB_WRITE_EN (1 << 1) + #define CP_ME_CNTL 0x86D8 #define CP_ME_HALT (1 << 28) #define CP_PFP_HALT (1 << 26) @@ -194,6 +198,9 @@ #define NOOFCHAN_MASK 0x00003000 #define MC_SHARED_CHREMAP 0x2008 +#define MC_SHARED_BLACKOUT_CNTL 0x20ac +#define BLACKOUT_MODE_MASK 0x00000007 + #define MC_ARB_RAMCFG 0x2760 #define NOOFBANK_SHIFT 0 #define NOOFBANK_MASK 0x00000003 @@ -270,6 +277,7 @@ #define SCRATCH_UMSK 0x8540 #define SCRATCH_ADDR 0x8544 +#define SMX_SAR_CTL0 0xA008 #define SMX_DC_CTL0 0xA020 #define USE_HASH_FUNCTION (1 << 0) #define NUMBER_OF_SETS(x) ((x) << 1) @@ -352,6 +360,7 @@ #define COLOR_BUFFER_SIZE(x) ((x) << 0) #define POSITION_BUFFER_SIZE(x) ((x) << 8) #define SMX_BUFFER_SIZE(x) ((x) << 16) +#define SX_MEMORY_EXPORT_BASE 0x9010 #define SX_MISC 0x28350 #define CB_PERF_CTR0_SEL_0 0x9A20 @@ -903,6 +912,10 @@ #define DB_HTILE_DATA_BASE 0x28014 #define DB_Z_INFO 0x28040 # define Z_ARRAY_MODE(x) ((x) << 4) +# define DB_TILE_SPLIT(x) (((x) & 0x7) << 8) +# define DB_NUM_BANKS(x) (((x) & 0x3) << 12) +# define DB_BANK_WIDTH(x) (((x) & 0x3) << 16) +# define DB_BANK_HEIGHT(x) (((x) & 0x3) << 20) #define DB_STENCIL_INFO 0x28044 #define DB_Z_READ_BASE 0x28048 #define DB_STENCIL_READ_BASE 0x2804c @@ -945,12 +958,39 @@ #define CB_COLOR0_SLICE 0x28c68 #define CB_COLOR0_VIEW 0x28c6c #define CB_COLOR0_INFO 0x28c70 +# define CB_FORMAT(x) ((x) << 2) # define CB_ARRAY_MODE(x) ((x) << 8) # define ARRAY_LINEAR_GENERAL 0 # define ARRAY_LINEAR_ALIGNED 1 # define ARRAY_1D_TILED_THIN1 2 # define ARRAY_2D_TILED_THIN1 4 +# define CB_SOURCE_FORMAT(x) ((x) << 24) +# define CB_SF_EXPORT_FULL 0 +# define CB_SF_EXPORT_NORM 1 #define CB_COLOR0_ATTRIB 0x28c74 +# define CB_TILE_SPLIT(x) (((x) & 0x7) << 5) +# define ADDR_SURF_TILE_SPLIT_64B 0 +# define ADDR_SURF_TILE_SPLIT_128B 1 +# define ADDR_SURF_TILE_SPLIT_256B 2 +# define ADDR_SURF_TILE_SPLIT_512B 3 +# define ADDR_SURF_TILE_SPLIT_1KB 4 +# define ADDR_SURF_TILE_SPLIT_2KB 5 +# define ADDR_SURF_TILE_SPLIT_4KB 6 +# define CB_NUM_BANKS(x) (((x) & 0x3) << 10) +# define ADDR_SURF_2_BANK 0 +# define ADDR_SURF_4_BANK 1 +# define ADDR_SURF_8_BANK 2 +# define ADDR_SURF_16_BANK 3 +# define CB_BANK_WIDTH(x) (((x) & 0x3) << 13) +# define ADDR_SURF_BANK_WIDTH_1 0 +# define ADDR_SURF_BANK_WIDTH_2 1 +# define ADDR_SURF_BANK_WIDTH_4 2 +# define ADDR_SURF_BANK_WIDTH_8 3 +# define CB_BANK_HEIGHT(x) (((x) & 0x3) << 16) +# define ADDR_SURF_BANK_HEIGHT_1 0 +# define ADDR_SURF_BANK_HEIGHT_2 1 +# define ADDR_SURF_BANK_HEIGHT_4 2 +# define ADDR_SURF_BANK_HEIGHT_8 3 #define CB_COLOR0_DIM 0x28c78 /* only CB0-7 blocks have these regs */ #define CB_COLOR0_CMASK 0x28c7c @@ -1111,14 +1151,56 @@ #define CB_COLOR7_CLEAR_WORD3 0x28e3c #define SQ_TEX_RESOURCE_WORD0_0 0x30000 +# define TEX_DIM(x) ((x) << 0) +# define SQ_TEX_DIM_1D 0 +# define SQ_TEX_DIM_2D 1 +# define SQ_TEX_DIM_3D 2 +# define SQ_TEX_DIM_CUBEMAP 3 +# define SQ_TEX_DIM_1D_ARRAY 4 +# define SQ_TEX_DIM_2D_ARRAY 5 +# define SQ_TEX_DIM_2D_MSAA 6 +# define SQ_TEX_DIM_2D_ARRAY_MSAA 7 #define SQ_TEX_RESOURCE_WORD1_0 0x30004 # define TEX_ARRAY_MODE(x) ((x) << 28) #define SQ_TEX_RESOURCE_WORD2_0 0x30008 #define SQ_TEX_RESOURCE_WORD3_0 0x3000C #define SQ_TEX_RESOURCE_WORD4_0 0x30010 +# define TEX_DST_SEL_X(x) ((x) << 16) +# define TEX_DST_SEL_Y(x) ((x) << 19) +# define TEX_DST_SEL_Z(x) ((x) << 22) +# define TEX_DST_SEL_W(x) ((x) << 25) +# define SQ_SEL_X 0 +# define SQ_SEL_Y 1 +# define SQ_SEL_Z 2 +# define SQ_SEL_W 3 +# define SQ_SEL_0 4 +# define SQ_SEL_1 5 #define SQ_TEX_RESOURCE_WORD5_0 0x30014 #define SQ_TEX_RESOURCE_WORD6_0 0x30018 +# define TEX_TILE_SPLIT(x) (((x) & 0x7) << 29) #define SQ_TEX_RESOURCE_WORD7_0 0x3001c +# define TEX_BANK_WIDTH(x) (((x) & 0x3) << 8) +# define TEX_BANK_HEIGHT(x) (((x) & 0x3) << 10) +# define TEX_NUM_BANKS(x) (((x) & 0x3) << 16) + +#define SQ_VTX_CONSTANT_WORD0_0 0x30000 +#define SQ_VTX_CONSTANT_WORD1_0 0x30004 +#define SQ_VTX_CONSTANT_WORD2_0 0x30008 +# define SQ_VTXC_BASE_ADDR_HI(x) ((x) << 0) +# define SQ_VTXC_STRIDE(x) ((x) << 8) +# define SQ_VTXC_ENDIAN_SWAP(x) ((x) << 30) +# define SQ_ENDIAN_NONE 0 +# define SQ_ENDIAN_8IN16 1 +# define SQ_ENDIAN_8IN32 2 +#define SQ_VTX_CONSTANT_WORD3_0 0x3000C +# define SQ_VTCX_SEL_X(x) ((x) << 3) +# define SQ_VTCX_SEL_Y(x) ((x) << 6) +# define SQ_VTCX_SEL_Z(x) ((x) << 9) +# define SQ_VTCX_SEL_W(x) ((x) << 12) +#define SQ_VTX_CONSTANT_WORD4_0 0x30010 +#define SQ_VTX_CONSTANT_WORD5_0 0x30014 +#define SQ_VTX_CONSTANT_WORD6_0 0x30018 +#define SQ_VTX_CONSTANT_WORD7_0 0x3001c /* cayman 3D regs */ #define CAYMAN_VGT_OFFCHIP_LDS_BASE 0x89B0 @@ -1127,6 +1209,7 @@ #define CAYMAN_PA_SC_AA_CONFIG 0x28BE0 #define CAYMAN_MSAA_NUM_SAMPLES_SHIFT 0 #define CAYMAN_MSAA_NUM_SAMPLES_MASK 0x7 +#define CAYMAN_SX_SCATTER_EXPORT_BASE 0x28358 /* cayman packet3 addition */ #define CAYMAN_PACKET3_DEALLOC_STATE 0x14 diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 0c460c4..77e6fb1 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -24,6 +24,7 @@ #include <linux/firmware.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/module.h> #include "drmP.h" #include "radeon.h" #include "radeon_asic.h" @@ -40,6 +41,7 @@ extern void evergreen_mc_program(struct radeon_device *rdev); extern void evergreen_irq_suspend(struct radeon_device *rdev); extern int evergreen_mc_init(struct radeon_device *rdev); extern void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev); +extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev); #define EVERGREEN_PFP_UCODE_SIZE 1120 #define EVERGREEN_PM4_UCODE_SIZE 1376 @@ -260,8 +262,11 @@ int ni_mc_load_microcode(struct radeon_device *rdev) WREG32(MC_SEQ_SUP_CNTL, 0x00000001); /* wait for training to complete */ - while (!(RREG32(MC_IO_PAD_CNTL_D0) & MEM_FALL_OUT_CMD)) - udelay(10); + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(MC_IO_PAD_CNTL_D0) & MEM_FALL_OUT_CMD) + break; + udelay(1); + } if (running) WREG32(MC_SHARED_BLACKOUT_CNTL, blackout); @@ -799,13 +804,24 @@ static void cayman_gpu_init(struct radeon_device *rdev) rdev->config.cayman.tile_config |= (3 << 0); break; } - rdev->config.cayman.tile_config |= - ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4; + switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) { + case 0: /* four banks */ + rdev->config.cayman.tile_config |= 0 << 4; + break; + case 1: /* eight banks */ + rdev->config.cayman.tile_config |= 1 << 4; + break; + case 2: /* sixteen banks */ + default: + rdev->config.cayman.tile_config |= 2 << 4; + break; + } rdev->config.cayman.tile_config |= ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8; rdev->config.cayman.tile_config |= ((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12; + rdev->config.cayman.backend_map = gb_backend_map; WREG32(GB_BACKEND_MAP, gb_backend_map); WREG32(GB_ADDR_CONFIG, gb_addr_config); WREG32(DMIF_ADDR_CONFIG, gb_addr_config); @@ -930,7 +946,7 @@ int cayman_pcie_gart_enable(struct radeon_device *rdev) { int r; - if (rdev->gart.table.vram.robj == NULL) { + if (rdev->gart.robj == NULL) { dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); return -EINVAL; } @@ -966,14 +982,15 @@ int cayman_pcie_gart_enable(struct radeon_device *rdev) WREG32(VM_CONTEXT1_CNTL, 0); cayman_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); rdev->gart.ready = true; return 0; } void cayman_pcie_gart_disable(struct radeon_device *rdev) { - int r; - /* Disable all tables */ WREG32(VM_CONTEXT0_CNTL, 0); WREG32(VM_CONTEXT1_CNTL, 0); @@ -989,14 +1006,7 @@ void cayman_pcie_gart_disable(struct radeon_device *rdev) WREG32(VM_L2_CNTL2, 0); WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY | L2_CACHE_BIGK_FRAGMENT_SIZE(6)); - if (rdev->gart.table.vram.robj) { - r = radeon_bo_reserve(rdev->gart.table.vram.robj, false); - if (likely(r == 0)) { - radeon_bo_kunmap(rdev->gart.table.vram.robj); - radeon_bo_unpin(rdev->gart.table.vram.robj); - radeon_bo_unreserve(rdev->gart.table.vram.robj); - } - } + radeon_gart_table_vram_unpin(rdev); } void cayman_pcie_gart_fini(struct radeon_device *rdev) @@ -1340,6 +1350,11 @@ static int cayman_startup(struct radeon_device *rdev) { int r; + /* enable pcie gen2 link */ + evergreen_pcie_gen2_enable(rdev); + + evergreen_mc_program(rdev); + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) { r = ni_init_microcode(rdev); if (r) { @@ -1353,7 +1368,10 @@ static int cayman_startup(struct radeon_device *rdev) return r; } - evergreen_mc_program(rdev); + r = r600_vram_scratch_init(rdev); + if (r) + return r; + r = cayman_pcie_gart_enable(rdev); if (r) return r; @@ -1361,7 +1379,7 @@ static int cayman_startup(struct radeon_device *rdev) r = evergreen_blit_init(rdev); if (r) { - evergreen_blit_fini(rdev); + r600_blit_fini(rdev); rdev->asic->copy = NULL; dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); } @@ -1372,6 +1390,12 @@ static int cayman_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + r = r600_irq_init(rdev); if (r) { DRM_ERROR("radeon: IH init failed (%d).\n", r); @@ -1422,21 +1446,13 @@ int cayman_resume(struct radeon_device *rdev) int cayman_suspend(struct radeon_device *rdev) { - int r; - /* FIXME: we should wait for ring to be empty */ cayman_cp_enable(rdev, false); rdev->cp.ready = false; evergreen_irq_suspend(rdev); radeon_wb_disable(rdev); cayman_pcie_gart_disable(rdev); - - /* unpin shaders bo */ - r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); - if (likely(r == 0)) { - radeon_bo_unpin(rdev->r600_blit.shader_obj); - radeon_bo_unreserve(rdev->r600_blit.shader_obj); - } + r600_blit_suspend(rdev); return 0; } @@ -1497,10 +1513,6 @@ int cayman_init(struct radeon_device *rdev) if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; - rdev->cp.ring_obj = NULL; r600_ring_init(rdev, 1024 * 1024); @@ -1549,13 +1561,14 @@ int cayman_init(struct radeon_device *rdev) void cayman_fini(struct radeon_device *rdev) { - evergreen_blit_fini(rdev); + r600_blit_fini(rdev); cayman_cp_fini(rdev); r600_irq_fini(rdev); radeon_wb_fini(rdev); radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); cayman_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); radeon_fence_driver_fini(rdev); radeon_bo_fini(rdev); diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index d94f440..dfd1267 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -41,6 +41,7 @@ #include <linux/firmware.h> #include <linux/platform_device.h> +#include <linux/module.h> #include "r100_reg_safe.h" #include "rn50_reg_safe.h" @@ -68,6 +69,108 @@ MODULE_FIRMWARE(FIRMWARE_R520); * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ +int r100_reloc_pitch_offset(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, + unsigned reg) +{ + int r; + u32 tile_flags = 0; + u32 tmp; + struct radeon_cs_reloc *reloc; + u32 value; + + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + value = radeon_get_ib_value(p, idx); + tmp = value & 0x003fffff; + tmp += (((u32)reloc->lobj.gpu_offset) >> 10); + + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= RADEON_DST_TILE_MACRO; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { + if (reg == RADEON_SRC_PITCH_OFFSET) { + DRM_ERROR("Cannot src blit from microtiled surface\n"); + r100_cs_dump_packet(p, pkt); + return -EINVAL; + } + tile_flags |= RADEON_DST_TILE_MICRO; + } + + tmp |= tile_flags; + p->ib->ptr[idx] = (value & 0x3fc00000) | tmp; + return 0; +} + +int r100_packet3_load_vbpntr(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + int idx) +{ + unsigned c, i; + struct radeon_cs_reloc *reloc; + struct r100_cs_track *track; + int r = 0; + volatile uint32_t *ib; + u32 idx_value; + + ib = p->ib->ptr; + track = (struct r100_cs_track *)p->track; + c = radeon_get_ib_value(p, idx++) & 0x1F; + if (c > 16) { + DRM_ERROR("Only 16 vertex buffers are allowed %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return -EINVAL; + } + track->num_arrays = c; + for (i = 0; i < (c - 1); i+=2, idx+=3) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + idx_value = radeon_get_ib_value(p, idx); + ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); + + track->arrays[i + 0].esize = idx_value >> 8; + track->arrays[i + 0].robj = reloc->robj; + track->arrays[i + 0].esize &= 0x7F; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx+2] = radeon_get_ib_value(p, idx + 2) + ((u32)reloc->lobj.gpu_offset); + track->arrays[i + 1].robj = reloc->robj; + track->arrays[i + 1].esize = idx_value >> 24; + track->arrays[i + 1].esize &= 0x7F; + } + if (c & 1) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + idx_value = radeon_get_ib_value(p, idx); + ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); + track->arrays[i + 0].robj = reloc->robj; + track->arrays[i + 0].esize = idx_value >> 8; + track->arrays[i + 0].esize &= 0x7F; + } + return r; +} + void r100_pre_page_flip(struct radeon_device *rdev, int crtc) { /* enable the pflip int */ @@ -480,7 +583,7 @@ int r100_pci_gart_init(struct radeon_device *rdev) { int r; - if (rdev->gart.table.ram.ptr) { + if (rdev->gart.ptr) { WARN(1, "R100 PCI GART already initialized\n"); return 0; } @@ -519,6 +622,9 @@ int r100_pci_gart_enable(struct radeon_device *rdev) tmp = RREG32(RADEON_AIC_CNTL) | RADEON_PCIGART_TRANSLATE_EN; WREG32(RADEON_AIC_CNTL, tmp); r100_pci_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); rdev->gart.ready = true; return 0; } @@ -536,10 +642,12 @@ void r100_pci_gart_disable(struct radeon_device *rdev) int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) { + u32 *gtt = rdev->gart.ptr; + if (i < 0 || i > rdev->gart.num_gpu_pages) { return -EINVAL; } - rdev->gart.table.ram.ptr[i] = cpu_to_le32(lower_32_bits(addr)); + gtt[i] = cpu_to_le32(lower_32_bits(addr)); return 0; } @@ -580,6 +688,10 @@ int r100_irq_set(struct radeon_device *rdev) tmp |= RADEON_FP2_DETECT_MASK; } WREG32(RADEON_GEN_INT_CNTL, tmp); + + /* read back to post the write */ + RREG32(RADEON_GEN_INT_CNTL); + return 0; } @@ -594,7 +706,7 @@ void r100_irq_disable(struct radeon_device *rdev) WREG32(R_000044_GEN_INT_STATUS, tmp); } -static inline uint32_t r100_irq_ack(struct radeon_device *rdev) +static uint32_t r100_irq_ack(struct radeon_device *rdev) { uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS); uint32_t irq_mask = RADEON_SW_INT_TEST | @@ -3152,7 +3264,7 @@ void r100_bandwidth_update(struct radeon_device *rdev) } } -static inline void r100_cs_track_texture_print(struct r100_cs_track_texture *t) +static void r100_cs_track_texture_print(struct r100_cs_track_texture *t) { DRM_ERROR("pitch %d\n", t->pitch); DRM_ERROR("use_pitch %d\n", t->use_pitch); @@ -3797,6 +3909,12 @@ static int r100_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + r100_irq_set(rdev); rdev->config.r100.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); /* 1M ring buffer */ @@ -3942,9 +4060,6 @@ int r100_init(struct radeon_device *rdev) r = radeon_fence_driver_init(rdev); if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; /* Memory manager */ r = radeon_bo_init(rdev); if (r) @@ -3970,3 +4085,43 @@ int r100_init(struct radeon_device *rdev) } return 0; } + +uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg) +{ + if (reg < rdev->rmmio_size) + return readl(((void __iomem *)rdev->rmmio) + reg); + else { + writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); + return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); + } +} + +void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + if (reg < rdev->rmmio_size) + writel(v, ((void __iomem *)rdev->rmmio) + reg); + else { + writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); + writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); + } +} + +u32 r100_io_rreg(struct radeon_device *rdev, u32 reg) +{ + if (reg < rdev->rio_mem_size) + return ioread32(rdev->rio_mem + reg); + else { + iowrite32(reg, rdev->rio_mem + RADEON_MM_INDEX); + return ioread32(rdev->rio_mem + RADEON_MM_DATA); + } +} + +void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v) +{ + if (reg < rdev->rio_mem_size) + iowrite32(v, rdev->rio_mem + reg); + else { + iowrite32(reg, rdev->rio_mem + RADEON_MM_INDEX); + iowrite32(v, rdev->rio_mem + RADEON_MM_DATA); + } +} diff --git a/drivers/gpu/drm/radeon/r100_track.h b/drivers/gpu/drm/radeon/r100_track.h index 686f9dc..6a603b3 100644 --- a/drivers/gpu/drm/radeon/r100_track.h +++ b/drivers/gpu/drm/radeon/r100_track.h @@ -92,106 +92,10 @@ int r200_packet0_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, unsigned idx, unsigned reg); - - -static inline int r100_reloc_pitch_offset(struct radeon_cs_parser *p, - struct radeon_cs_packet *pkt, - unsigned idx, - unsigned reg) -{ - int r; - u32 tile_flags = 0; - u32 tmp; - struct radeon_cs_reloc *reloc; - u32 value; - - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for ib[%d]=0x%04X\n", - idx, reg); - r100_cs_dump_packet(p, pkt); - return r; - } - value = radeon_get_ib_value(p, idx); - tmp = value & 0x003fffff; - tmp += (((u32)reloc->lobj.gpu_offset) >> 10); - - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) - tile_flags |= RADEON_DST_TILE_MACRO; - if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { - if (reg == RADEON_SRC_PITCH_OFFSET) { - DRM_ERROR("Cannot src blit from microtiled surface\n"); - r100_cs_dump_packet(p, pkt); - return -EINVAL; - } - tile_flags |= RADEON_DST_TILE_MICRO; - } - - tmp |= tile_flags; - p->ib->ptr[idx] = (value & 0x3fc00000) | tmp; - return 0; -} - -static inline int r100_packet3_load_vbpntr(struct radeon_cs_parser *p, - struct radeon_cs_packet *pkt, - int idx) -{ - unsigned c, i; - struct radeon_cs_reloc *reloc; - struct r100_cs_track *track; - int r = 0; - volatile uint32_t *ib; - u32 idx_value; - - ib = p->ib->ptr; - track = (struct r100_cs_track *)p->track; - c = radeon_get_ib_value(p, idx++) & 0x1F; - if (c > 16) { - DRM_ERROR("Only 16 vertex buffers are allowed %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return -EINVAL; - } - track->num_arrays = c; - for (i = 0; i < (c - 1); i+=2, idx+=3) { - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - idx_value = radeon_get_ib_value(p, idx); - ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); - - track->arrays[i + 0].esize = idx_value >> 8; - track->arrays[i + 0].robj = reloc->robj; - track->arrays[i + 0].esize &= 0x7F; - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - ib[idx+2] = radeon_get_ib_value(p, idx + 2) + ((u32)reloc->lobj.gpu_offset); - track->arrays[i + 1].robj = reloc->robj; - track->arrays[i + 1].esize = idx_value >> 24; - track->arrays[i + 1].esize &= 0x7F; - } - if (c & 1) { - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - idx_value = radeon_get_ib_value(p, idx); - ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); - track->arrays[i + 0].robj = reloc->robj; - track->arrays[i + 0].esize = idx_value >> 8; - track->arrays[i + 0].esize &= 0x7F; - } - return r; -} +int r100_reloc_pitch_offset(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, + unsigned reg); +int r100_packet3_load_vbpntr(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + int idx); diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 55a7f19..441570b 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -74,7 +74,7 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev) int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) { - void __iomem *ptr = (void *)rdev->gart.table.vram.ptr; + void __iomem *ptr = rdev->gart.ptr; if (i < 0 || i > rdev->gart.num_gpu_pages) { return -EINVAL; @@ -93,7 +93,7 @@ int rv370_pcie_gart_init(struct radeon_device *rdev) { int r; - if (rdev->gart.table.vram.robj) { + if (rdev->gart.robj) { WARN(1, "RV370 PCIE GART already initialized\n"); return 0; } @@ -116,7 +116,7 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev) uint32_t tmp; int r; - if (rdev->gart.table.vram.robj == NULL) { + if (rdev->gart.robj == NULL) { dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); return -EINVAL; } @@ -144,8 +144,9 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev) tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD; WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); rv370_pcie_gart_tlb_flush(rdev); - DRM_INFO("PCIE GART of %uM enabled (table at 0x%08X).\n", - (unsigned)(rdev->mc.gtt_size >> 20), table_addr); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)table_addr); rdev->gart.ready = true; return 0; } @@ -153,7 +154,6 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev) void rv370_pcie_gart_disable(struct radeon_device *rdev) { u32 tmp; - int r; WREG32_PCIE(RADEON_PCIE_TX_GART_START_LO, 0); WREG32_PCIE(RADEON_PCIE_TX_GART_END_LO, 0); @@ -162,14 +162,7 @@ void rv370_pcie_gart_disable(struct radeon_device *rdev) tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL); tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD; WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp & ~RADEON_PCIE_TX_GART_EN); - if (rdev->gart.table.vram.robj) { - r = radeon_bo_reserve(rdev->gart.table.vram.robj, false); - if (likely(r == 0)) { - radeon_bo_kunmap(rdev->gart.table.vram.robj); - radeon_bo_unpin(rdev->gart.table.vram.robj); - radeon_bo_unreserve(rdev->gart.table.vram.robj); - } - } + radeon_gart_table_vram_unpin(rdev); } void rv370_pcie_gart_fini(struct radeon_device *rdev) @@ -708,16 +701,21 @@ static int r300_packet0_check(struct radeon_cs_parser *p, return r; } - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) - tile_flags |= R300_TXO_MACRO_TILE; - if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) - tile_flags |= R300_TXO_MICRO_TILE; - else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) - tile_flags |= R300_TXO_MICRO_TILE_SQUARE; - - tmp = idx_value + ((u32)reloc->lobj.gpu_offset); - tmp |= tile_flags; - ib[idx] = tmp; + if (p->keep_tiling_flags) { + ib[idx] = (idx_value & 31) | /* keep the 1st 5 bits */ + ((idx_value & ~31) + (u32)reloc->lobj.gpu_offset); + } else { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R300_TXO_MACRO_TILE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R300_TXO_MICRO_TILE; + else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) + tile_flags |= R300_TXO_MICRO_TILE_SQUARE; + + tmp = idx_value + ((u32)reloc->lobj.gpu_offset); + tmp |= tile_flags; + ib[idx] = tmp; + } track->textures[i].robj = reloc->robj; track->tex_dirty = true; break; @@ -767,24 +765,26 @@ static int r300_packet0_check(struct radeon_cs_parser *p, /* RB3D_COLORPITCH1 */ /* RB3D_COLORPITCH2 */ /* RB3D_COLORPITCH3 */ - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for ib[%d]=0x%04X\n", - idx, reg); - r100_cs_dump_packet(p, pkt); - return r; - } + if (!p->keep_tiling_flags) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) - tile_flags |= R300_COLOR_TILE_ENABLE; - if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) - tile_flags |= R300_COLOR_MICROTILE_ENABLE; - else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) - tile_flags |= R300_COLOR_MICROTILE_SQUARE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R300_COLOR_TILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R300_COLOR_MICROTILE_ENABLE; + else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) + tile_flags |= R300_COLOR_MICROTILE_SQUARE_ENABLE; - tmp = idx_value & ~(0x7 << 16); - tmp |= tile_flags; - ib[idx] = tmp; + tmp = idx_value & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + } i = (reg - 0x4E38) >> 2; track->cb[i].pitch = idx_value & 0x3FFE; switch (((idx_value >> 21) & 0xF)) { @@ -850,25 +850,26 @@ static int r300_packet0_check(struct radeon_cs_parser *p, break; case 0x4F24: /* ZB_DEPTHPITCH */ - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for ib[%d]=0x%04X\n", - idx, reg); - r100_cs_dump_packet(p, pkt); - return r; - } - - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) - tile_flags |= R300_DEPTHMACROTILE_ENABLE; - if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) - tile_flags |= R300_DEPTHMICROTILE_TILED; - else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) - tile_flags |= R300_DEPTHMICROTILE_TILED_SQUARE; + if (!p->keep_tiling_flags) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } - tmp = idx_value & ~(0x7 << 16); - tmp |= tile_flags; - ib[idx] = tmp; + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R300_DEPTHMACROTILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R300_DEPTHMICROTILE_TILED; + else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) + tile_flags |= R300_DEPTHMICROTILE_TILED_SQUARE; + tmp = idx_value & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + } track->zb.pitch = idx_value & 0x3FFC; track->zb_dirty = true; break; @@ -1396,6 +1397,12 @@ static int r300_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + r100_irq_set(rdev); rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); /* 1M ring buffer */ @@ -1520,9 +1527,6 @@ int r300_init(struct radeon_device *rdev) r = radeon_fence_driver_init(rdev); if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; /* Memory manager */ r = radeon_bo_init(rdev); if (r) diff --git a/drivers/gpu/drm/radeon/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c index a12f373..9aa02be 100644 --- a/drivers/gpu/drm/radeon/r300_cmdbuf.c +++ b/drivers/gpu/drm/radeon/r300_cmdbuf.c @@ -791,7 +791,7 @@ static __inline__ int r300_emit_packet3(drm_radeon_private_t *dev_priv, /** * Emit the sequence to pacify R300. */ -static __inline__ void r300_pacify(drm_radeon_private_t *dev_priv) +static void r300_pacify(drm_radeon_private_t *dev_priv) { uint32_t cache_z, cache_3d, cache_2d; RING_LOCALS; diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c index 417fab8..5b219b8 100644 --- a/drivers/gpu/drm/radeon/r420.c +++ b/drivers/gpu/drm/radeon/r420.c @@ -255,6 +255,12 @@ static int r420_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + r100_irq_set(rdev); rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); /* 1M ring buffer */ @@ -391,10 +397,6 @@ int r420_init(struct radeon_device *rdev) if (r) { return r; } - r = radeon_irq_kms_init(rdev); - if (r) { - return r; - } /* Memory manager */ r = radeon_bo_init(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c index 3081d07..f36a5c9 100644 --- a/drivers/gpu/drm/radeon/r520.c +++ b/drivers/gpu/drm/radeon/r520.c @@ -188,6 +188,12 @@ static int r520_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + rs600_irq_set(rdev); rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); /* 1M ring buffer */ @@ -281,9 +287,6 @@ int r520_init(struct radeon_device *rdev) r = radeon_fence_driver_init(rdev); if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; /* Memory manager */ r = radeon_bo_init(rdev); if (r) diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 1a4ed43..e5299a0 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -29,6 +29,7 @@ #include <linux/seq_file.h> #include <linux/firmware.h> #include <linux/platform_device.h> +#include <linux/module.h> #include "drmP.h" #include "radeon_drm.h" #include "radeon.h" @@ -287,24 +288,6 @@ void r600_pm_get_dynpm_state(struct radeon_device *rdev) pcie_lanes); } -static int r600_pm_get_type_index(struct radeon_device *rdev, - enum radeon_pm_state_type ps_type, - int instance) -{ - int i; - int found_instance = -1; - - for (i = 0; i < rdev->pm.num_power_states; i++) { - if (rdev->pm.power_state[i].type == ps_type) { - found_instance++; - if (found_instance == instance) - return i; - } - } - /* return default if no match */ - return rdev->pm.default_power_state_index; -} - void rs780_pm_init_profile(struct radeon_device *rdev) { if (rdev->pm.num_power_states == 2) { @@ -420,6 +403,8 @@ void rs780_pm_init_profile(struct radeon_device *rdev) void r600_pm_init_profile(struct radeon_device *rdev) { + int idx; + if (rdev->family == CHIP_R600) { /* XXX */ /* default */ @@ -501,81 +486,43 @@ void r600_pm_init_profile(struct radeon_device *rdev) rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2; /* low sh */ - if (rdev->flags & RADEON_IS_MOBILITY) { - rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); - rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); - rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; - rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; - } else { - rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); - rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); - rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; - rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; - } + if (rdev->flags & RADEON_IS_MOBILITY) + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); + else + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; /* mid sh */ - if (rdev->flags & RADEON_IS_MOBILITY) { - rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); - rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); - rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; - rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1; - } else { - rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); - rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); - rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; - rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1; - } + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1; /* high sh */ - rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); - rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = idx; rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2; /* low mh */ - if (rdev->flags & RADEON_IS_MOBILITY) { - rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1); - rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1); - rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; - rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; - } else { - rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); - rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); - rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; - rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; - } + if (rdev->flags & RADEON_IS_MOBILITY) + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1); + else + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; /* mid mh */ - if (rdev->flags & RADEON_IS_MOBILITY) { - rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1); - rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1); - rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; - rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1; - } else { - rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); - rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); - rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; - rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1; - } + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1; /* high mh */ - rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); - rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = - r600_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = idx; rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2; } @@ -895,7 +842,7 @@ void r600_pcie_gart_tlb_flush(struct radeon_device *rdev) /* flush hdp cache so updates hit vram */ if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_RV740) && !(rdev->flags & RADEON_IS_AGP)) { - void __iomem *ptr = (void *)rdev->gart.table.vram.ptr; + void __iomem *ptr = (void *)rdev->gart.ptr; u32 tmp; /* r7xx hw bug. write to HDP_DEBUG1 followed by fb read @@ -930,7 +877,7 @@ int r600_pcie_gart_init(struct radeon_device *rdev) { int r; - if (rdev->gart.table.vram.robj) { + if (rdev->gart.robj) { WARN(1, "R600 PCIE GART already initialized\n"); return 0; } @@ -947,7 +894,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev) u32 tmp; int r, i; - if (rdev->gart.table.vram.robj == NULL) { + if (rdev->gart.robj == NULL) { dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); return -EINVAL; } @@ -992,6 +939,9 @@ int r600_pcie_gart_enable(struct radeon_device *rdev) WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); r600_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); rdev->gart.ready = true; return 0; } @@ -999,7 +949,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev) void r600_pcie_gart_disable(struct radeon_device *rdev) { u32 tmp; - int i, r; + int i; /* Disable all tables */ for (i = 0; i < 7; i++) @@ -1026,14 +976,7 @@ void r600_pcie_gart_disable(struct radeon_device *rdev) WREG32(MC_VM_L1_TLB_MCB_WR_SYS_CNTL, tmp); WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp); WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp); - if (rdev->gart.table.vram.robj) { - r = radeon_bo_reserve(rdev->gart.table.vram.robj, false); - if (likely(r == 0)) { - radeon_bo_kunmap(rdev->gart.table.vram.robj); - radeon_bo_unpin(rdev->gart.table.vram.robj); - radeon_bo_unreserve(rdev->gart.table.vram.robj); - } - } + radeon_gart_table_vram_unpin(rdev); } void r600_pcie_gart_fini(struct radeon_device *rdev) @@ -1133,7 +1076,7 @@ static void r600_mc_program(struct radeon_device *rdev) WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start >> 12); WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end >> 12); } - WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0); + WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12); tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); WREG32(MC_VM_FB_LOCATION, tmp); @@ -1272,6 +1215,53 @@ int r600_mc_init(struct radeon_device *rdev) return 0; } +int r600_vram_scratch_init(struct radeon_device *rdev) +{ + int r; + + if (rdev->vram_scratch.robj == NULL) { + r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, + PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, + &rdev->vram_scratch.robj); + if (r) { + return r; + } + } + + r = radeon_bo_reserve(rdev->vram_scratch.robj, false); + if (unlikely(r != 0)) + return r; + r = radeon_bo_pin(rdev->vram_scratch.robj, + RADEON_GEM_DOMAIN_VRAM, &rdev->vram_scratch.gpu_addr); + if (r) { + radeon_bo_unreserve(rdev->vram_scratch.robj); + return r; + } + r = radeon_bo_kmap(rdev->vram_scratch.robj, + (void **)&rdev->vram_scratch.ptr); + if (r) + radeon_bo_unpin(rdev->vram_scratch.robj); + radeon_bo_unreserve(rdev->vram_scratch.robj); + + return r; +} + +void r600_vram_scratch_fini(struct radeon_device *rdev) +{ + int r; + + if (rdev->vram_scratch.robj == NULL) { + return; + } + r = radeon_bo_reserve(rdev->vram_scratch.robj, false); + if (likely(r == 0)) { + radeon_bo_kunmap(rdev->vram_scratch.robj); + radeon_bo_unpin(rdev->vram_scratch.robj); + radeon_bo_unreserve(rdev->vram_scratch.robj); + } + radeon_bo_unref(&rdev->vram_scratch.robj); +} + /* We doesn't check that the GPU really needs a reset we simply do the * reset, it's up to the caller to determine if the GPU needs one. We * might add an helper function to check that. @@ -1661,6 +1651,7 @@ void r600_gpu_init(struct radeon_device *rdev) R6XX_MAX_BACKENDS_MASK) >> 16)), (cc_rb_backend_disable >> 16)); rdev->config.r600.tile_config = tiling_config; + rdev->config.r600.backend_map = backend_map; tiling_config |= BACKEND_MAP(backend_map); WREG32(GB_TILING_CONFIG, tiling_config); WREG32(DCP_TILING_CONFIG, tiling_config & 0xffff); @@ -1914,6 +1905,7 @@ void r600_gpu_init(struct radeon_device *rdev) WREG32(PA_CL_ENHANCE, (CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3))); WREG32(PA_SC_ENHANCE, FORCE_EOV_MAX_CLK_CNT(4095)); + WREG32(VC_ENHANCE, 0); } @@ -2212,9 +2204,6 @@ int r600_cp_resume(struct radeon_device *rdev) /* set the wb address whether it's enabled or not */ WREG32(CP_RB_RPTR_ADDR, -#ifdef __BIG_ENDIAN - RB_RPTR_SWAP(2) | -#endif ((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC)); WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF); WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF); @@ -2326,9 +2315,21 @@ int r600_ring_test(struct radeon_device *rdev) void r600_fence_ring_emit(struct radeon_device *rdev, struct radeon_fence *fence) { + u32 cp_coher_cntl = PACKET3_TC_ACTION_ENA | PACKET3_VC_ACTION_ENA | + PACKET3_SH_ACTION_ENA; + + if (rdev->family >= CHIP_RV770) + cp_coher_cntl |= PACKET3_FULL_CACHE_ENA; + if (rdev->wb.use_event) { u64 addr = rdev->wb.gpu_addr + R600_WB_EVENT_OFFSET + (u64)(rdev->fence_drv.scratch_reg - rdev->scratch.reg_base); + /* flush read cache over gart */ + radeon_ring_write(rdev, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(rdev, cp_coher_cntl); + radeon_ring_write(rdev, 0xFFFFFFFF); + radeon_ring_write(rdev, 0); + radeon_ring_write(rdev, 10); /* poll interval */ /* EVENT_WRITE_EOP - flush caches, send int */ radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); radeon_ring_write(rdev, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5)); @@ -2337,6 +2338,12 @@ void r600_fence_ring_emit(struct radeon_device *rdev, radeon_ring_write(rdev, fence->seq); radeon_ring_write(rdev, 0); } else { + /* flush read cache over gart */ + radeon_ring_write(rdev, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(rdev, cp_coher_cntl); + radeon_ring_write(rdev, 0xFFFFFFFF); + radeon_ring_write(rdev, 0); + radeon_ring_write(rdev, 10); /* poll interval */ radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0)); radeon_ring_write(rdev, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT) | EVENT_INDEX(0)); /* wait for 3D idle clean */ @@ -2363,19 +2370,33 @@ int r600_copy_blit(struct radeon_device *rdev, mutex_lock(&rdev->r600_blit.mutex); rdev->r600_blit.vb_ib = NULL; - r = r600_blit_prepare_copy(rdev, num_gpu_pages * RADEON_GPU_PAGE_SIZE); + r = r600_blit_prepare_copy(rdev, num_gpu_pages); if (r) { if (rdev->r600_blit.vb_ib) radeon_ib_free(rdev, &rdev->r600_blit.vb_ib); mutex_unlock(&rdev->r600_blit.mutex); return r; } - r600_kms_blit_copy(rdev, src_offset, dst_offset, num_gpu_pages * RADEON_GPU_PAGE_SIZE); + r600_kms_blit_copy(rdev, src_offset, dst_offset, num_gpu_pages); r600_blit_done_copy(rdev, fence); mutex_unlock(&rdev->r600_blit.mutex); return 0; } +void r600_blit_suspend(struct radeon_device *rdev) +{ + int r; + + /* unpin shaders bo */ + if (rdev->r600_blit.shader_obj) { + r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); + if (!r) { + radeon_bo_unpin(rdev->r600_blit.shader_obj); + radeon_bo_unreserve(rdev->r600_blit.shader_obj); + } + } +} + int r600_set_surface_reg(struct radeon_device *rdev, int reg, uint32_t tiling_flags, uint32_t pitch, uint32_t offset, uint32_t obj_size) @@ -2396,6 +2417,8 @@ int r600_startup(struct radeon_device *rdev) /* enable pcie gen2 link */ r600_pcie_gen2_enable(rdev); + r600_mc_program(rdev); + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) { r = r600_init_microcode(rdev); if (r) { @@ -2404,7 +2427,10 @@ int r600_startup(struct radeon_device *rdev) } } - r600_mc_program(rdev); + r = r600_vram_scratch_init(rdev); + if (r) + return r; + if (rdev->flags & RADEON_IS_AGP) { r600_agp_enable(rdev); } else { @@ -2426,6 +2452,12 @@ int r600_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + r = r600_irq_init(rdev); if (r) { DRM_ERROR("radeon: IH init failed (%d).\n", r); @@ -2495,8 +2527,6 @@ int r600_resume(struct radeon_device *rdev) int r600_suspend(struct radeon_device *rdev) { - int r; - r600_audio_fini(rdev); /* FIXME: we should wait for ring to be empty */ r600_cp_stop(rdev); @@ -2504,14 +2534,8 @@ int r600_suspend(struct radeon_device *rdev) r600_irq_suspend(rdev); radeon_wb_disable(rdev); r600_pcie_gart_disable(rdev); - /* unpin shaders bo */ - if (rdev->r600_blit.shader_obj) { - r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); - if (!r) { - radeon_bo_unpin(rdev->r600_blit.shader_obj); - radeon_bo_unreserve(rdev->r600_blit.shader_obj); - } - } + r600_blit_suspend(rdev); + return 0; } @@ -2577,10 +2601,6 @@ int r600_init(struct radeon_device *rdev) if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; - rdev->cp.ring_obj = NULL; r600_ring_init(rdev, 1024 * 1024); @@ -2632,6 +2652,7 @@ void r600_fini(struct radeon_device *rdev) radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); r600_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); radeon_agp_fini(rdev); radeon_gem_fini(rdev); radeon_fence_driver_fini(rdev); @@ -2995,10 +3016,6 @@ int r600_irq_init(struct radeon_device *rdev) /* RPTR_REARM only works if msi's are enabled */ if (rdev->msi_enabled) ih_cntl |= RPTR_REARM; - -#ifdef __BIG_ENDIAN - ih_cntl |= IH_MC_SWAP(IH_MC_SWAP_32BIT); -#endif WREG32(IH_CNTL, ih_cntl); /* force the active interrupt state to all disabled */ @@ -3139,10 +3156,13 @@ int r600_irq_set(struct radeon_device *rdev) WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3); } + /* posting read */ + RREG32(R_000E50_SRBM_STATUS); + return 0; } -static inline void r600_irq_ack(struct radeon_device *rdev) +static void r600_irq_ack(struct radeon_device *rdev) { u32 tmp; @@ -3243,7 +3263,7 @@ void r600_irq_disable(struct radeon_device *rdev) r600_disable_interrupt_state(rdev); } -static inline u32 r600_get_ih_wptr(struct radeon_device *rdev) +static u32 r600_get_ih_wptr(struct radeon_device *rdev) { u32 wptr, tmp; @@ -3263,6 +3283,7 @@ static inline u32 r600_get_ih_wptr(struct radeon_device *rdev) tmp = RREG32(IH_RB_CNTL); tmp |= IH_WPTR_OVERFLOW_CLEAR; WREG32(IH_RB_CNTL, tmp); + wptr &= ~RB_OVERFLOW; } return (wptr & rdev->ih.ptr_mask); } @@ -3309,6 +3330,10 @@ int r600_irq_process(struct radeon_device *rdev) if (!rdev->ih.enabled || rdev->shutdown) return IRQ_NONE; + /* No MSIs, need a dummy read to flush PCI DMAs */ + if (!rdev->msi_enabled) + RREG32(IH_RB_WPTR); + wptr = r600_get_ih_wptr(rdev); rptr = rdev->ih.rptr; DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); @@ -3321,6 +3346,9 @@ int r600_irq_process(struct radeon_device *rdev) } restart_ih: + /* Order reading of wptr vs. reading of IH ring data */ + rmb(); + /* display interrupts */ r600_irq_ack(rdev); diff --git a/drivers/gpu/drm/radeon/r600_blit.c b/drivers/gpu/drm/radeon/r600_blit.c index 7f10434..3c031a4 100644 --- a/drivers/gpu/drm/radeon/r600_blit.c +++ b/drivers/gpu/drm/radeon/r600_blit.c @@ -41,7 +41,7 @@ #define COLOR_5_6_5 0x8 #define COLOR_8_8_8_8 0x1a -static inline void +static void set_render_target(drm_radeon_private_t *dev_priv, int format, int w, int h, u64 gpu_addr) { u32 cb_color_info; @@ -99,7 +99,7 @@ set_render_target(drm_radeon_private_t *dev_priv, int format, int w, int h, u64 ADVANCE_RING(); } -static inline void +static void cp_set_surface_sync(drm_radeon_private_t *dev_priv, u32 sync_type, u32 size, u64 mc_addr) { @@ -121,7 +121,7 @@ cp_set_surface_sync(drm_radeon_private_t *dev_priv, ADVANCE_RING(); } -static inline void +static void set_shaders(struct drm_device *dev) { drm_radeon_private_t *dev_priv = dev->dev_private; @@ -184,7 +184,7 @@ set_shaders(struct drm_device *dev) R600_SH_ACTION_ENA, 512, gpu_addr); } -static inline void +static void set_vtx_resource(drm_radeon_private_t *dev_priv, u64 gpu_addr) { uint32_t sq_vtx_constant_word2; @@ -220,7 +220,7 @@ set_vtx_resource(drm_radeon_private_t *dev_priv, u64 gpu_addr) R600_VC_ACTION_ENA, 48, gpu_addr); } -static inline void +static void set_tex_resource(drm_radeon_private_t *dev_priv, int format, int w, int h, int pitch, u64 gpu_addr) { @@ -258,7 +258,7 @@ set_tex_resource(drm_radeon_private_t *dev_priv, } -static inline void +static void set_scissors(drm_radeon_private_t *dev_priv, int x1, int y1, int x2, int y2) { RING_LOCALS; @@ -282,7 +282,7 @@ set_scissors(drm_radeon_private_t *dev_priv, int x1, int y1, int x2, int y2) ADVANCE_RING(); } -static inline void +static void draw_auto(drm_radeon_private_t *dev_priv) { RING_LOCALS; @@ -311,7 +311,7 @@ draw_auto(drm_radeon_private_t *dev_priv) COMMIT_RING(); } -static inline void +static void set_default_state(drm_radeon_private_t *dev_priv) { int i; @@ -489,7 +489,7 @@ set_default_state(drm_radeon_private_t *dev_priv) ADVANCE_RING(); } -static inline uint32_t i2f(uint32_t input) +static uint32_t i2f(uint32_t input) { u32 result, i, exponent, fraction; @@ -515,7 +515,7 @@ static inline uint32_t i2f(uint32_t input) } -static inline int r600_nomm_get_vb(struct drm_device *dev) +static int r600_nomm_get_vb(struct drm_device *dev) { drm_radeon_private_t *dev_priv = dev->dev_private; dev_priv->blit_vb = radeon_freelist_get(dev); @@ -526,7 +526,7 @@ static inline int r600_nomm_get_vb(struct drm_device *dev) return 0; } -static inline void r600_nomm_put_vb(struct drm_device *dev) +static void r600_nomm_put_vb(struct drm_device *dev) { drm_radeon_private_t *dev_priv = dev->dev_private; @@ -534,7 +534,7 @@ static inline void r600_nomm_put_vb(struct drm_device *dev) radeon_cp_discard_buffer(dev, dev_priv->blit_vb->file_priv->master, dev_priv->blit_vb); } -static inline void *r600_nomm_get_vb_ptr(struct drm_device *dev) +static void *r600_nomm_get_vb_ptr(struct drm_device *dev) { drm_radeon_private_t *dev_priv = dev->dev_private; return (((char *)dev->agp_buffer_map->handle + diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c index 9aa74c3..e09d281 100644 --- a/drivers/gpu/drm/radeon/r600_blit_kms.c +++ b/drivers/gpu/drm/radeon/r600_blit_kms.c @@ -42,6 +42,9 @@ #define COLOR_5_6_5 0x8 #define COLOR_8_8_8_8 0x1a +#define RECT_UNIT_H 32 +#define RECT_UNIT_W (RADEON_GPU_PAGE_SIZE / 4 / RECT_UNIT_H) + /* emits 21 on rv770+, 23 on r600 */ static void set_render_target(struct radeon_device *rdev, int format, @@ -54,7 +57,9 @@ set_render_target(struct radeon_device *rdev, int format, if (h < 8) h = 8; - cb_color_info = ((format << 2) | (1 << 27) | (1 << 8)); + cb_color_info = CB_FORMAT(format) | + CB_SOURCE_FORMAT(CB_SF_EXPORT_NORM) | + CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1); pitch = (w / 8) - 1; slice = ((w * h) / 64) - 1; @@ -164,9 +169,10 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr) { u32 sq_vtx_constant_word2; - sq_vtx_constant_word2 = ((upper_32_bits(gpu_addr) & 0xff) | (16 << 8)); + sq_vtx_constant_word2 = SQ_VTXC_BASE_ADDR_HI(upper_32_bits(gpu_addr) & 0xff) | + SQ_VTXC_STRIDE(16); #ifdef __BIG_ENDIAN - sq_vtx_constant_word2 |= (2 << 30); + sq_vtx_constant_word2 |= SQ_VTXC_ENDIAN_SWAP(SQ_ENDIAN_8IN32); #endif radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 7)); @@ -195,25 +201,29 @@ set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr) static void set_tex_resource(struct radeon_device *rdev, int format, int w, int h, int pitch, - u64 gpu_addr) + u64 gpu_addr, u32 size) { uint32_t sq_tex_resource_word0, sq_tex_resource_word1, sq_tex_resource_word4; if (h < 1) h = 1; - sq_tex_resource_word0 = (1 << 0) | (1 << 3); - sq_tex_resource_word0 |= ((((pitch >> 3) - 1) << 8) | - ((w - 1) << 19)); + sq_tex_resource_word0 = S_038000_DIM(V_038000_SQ_TEX_DIM_2D) | + S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + sq_tex_resource_word0 |= S_038000_PITCH((pitch >> 3) - 1) | + S_038000_TEX_WIDTH(w - 1); + + sq_tex_resource_word1 = S_038004_DATA_FORMAT(format); + sq_tex_resource_word1 |= S_038004_TEX_HEIGHT(h - 1); - sq_tex_resource_word1 = (format << 26); - sq_tex_resource_word1 |= ((h - 1) << 0); + sq_tex_resource_word4 = S_038010_REQUEST_SIZE(1) | + S_038010_DST_SEL_X(SQ_SEL_X) | + S_038010_DST_SEL_Y(SQ_SEL_Y) | + S_038010_DST_SEL_Z(SQ_SEL_Z) | + S_038010_DST_SEL_W(SQ_SEL_W); - sq_tex_resource_word4 = ((1 << 14) | - (0 << 16) | - (1 << 19) | - (2 << 22) | - (3 << 25)); + cp_set_surface_sync(rdev, + PACKET3_TC_ACTION_ENA, size, gpu_addr); radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 7)); radeon_ring_write(rdev, 0); @@ -450,7 +460,7 @@ set_default_state(struct radeon_device *rdev) radeon_ring_write(rdev, sq_stack_resource_mgmt_2); } -static inline uint32_t i2f(uint32_t input) +static uint32_t i2f(uint32_t input) { u32 result, i, exponent, fraction; @@ -483,6 +493,27 @@ int r600_blit_init(struct radeon_device *rdev) u32 packet2s[16]; int num_packet2s = 0; + rdev->r600_blit.primitives.set_render_target = set_render_target; + rdev->r600_blit.primitives.cp_set_surface_sync = cp_set_surface_sync; + rdev->r600_blit.primitives.set_shaders = set_shaders; + rdev->r600_blit.primitives.set_vtx_resource = set_vtx_resource; + rdev->r600_blit.primitives.set_tex_resource = set_tex_resource; + rdev->r600_blit.primitives.set_scissors = set_scissors; + rdev->r600_blit.primitives.draw_auto = draw_auto; + rdev->r600_blit.primitives.set_default_state = set_default_state; + + rdev->r600_blit.ring_size_common = 40; /* shaders + def state */ + rdev->r600_blit.ring_size_common += 16; /* fence emit for VB IB */ + rdev->r600_blit.ring_size_common += 5; /* done copy */ + rdev->r600_blit.ring_size_common += 16; /* fence emit for done copy */ + + rdev->r600_blit.ring_size_per_loop = 76; + /* set_render_target emits 2 extra dwords on rv6xx */ + if (rdev->family > CHIP_R600 && rdev->family < CHIP_RV770) + rdev->r600_blit.ring_size_per_loop += 2; + + rdev->r600_blit.max_dim = 8192; + /* pin copy shader into vram if already initialized */ if (rdev->r600_blit.shader_obj) goto done; @@ -600,47 +631,80 @@ static void r600_vb_ib_put(struct radeon_device *rdev) radeon_ib_free(rdev, &rdev->r600_blit.vb_ib); } -int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes) +static unsigned r600_blit_create_rect(unsigned num_gpu_pages, + int *width, int *height, int max_dim) +{ + unsigned max_pages; + unsigned pages = num_gpu_pages; + int w, h; + + if (num_gpu_pages == 0) { + /* not supposed to be called with no pages, but just in case */ + h = 0; + w = 0; + pages = 0; + WARN_ON(1); + } else { + int rect_order = 2; + h = RECT_UNIT_H; + while (num_gpu_pages / rect_order) { + h *= 2; + rect_order *= 4; + if (h >= max_dim) { + h = max_dim; + break; + } + } + max_pages = (max_dim * h) / (RECT_UNIT_W * RECT_UNIT_H); + if (pages > max_pages) + pages = max_pages; + w = (pages * RECT_UNIT_W * RECT_UNIT_H) / h; + w = (w / RECT_UNIT_W) * RECT_UNIT_W; + pages = (w * h) / (RECT_UNIT_W * RECT_UNIT_H); + BUG_ON(pages == 0); + } + + + DRM_DEBUG("blit_rectangle: h=%d, w=%d, pages=%d\n", h, w, pages); + + /* return width and height only of the caller wants it */ + if (height) + *height = h; + if (width) + *width = w; + + return pages; +} + + +int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages) { int r; - int ring_size, line_size; - int max_size; - /* loops of emits 64 + fence emit possible */ - int dwords_per_loop = 76, num_loops; + int ring_size; + int num_loops = 0; + int dwords_per_loop = rdev->r600_blit.ring_size_per_loop; r = r600_vb_ib_get(rdev); if (r) return r; - /* set_render_target emits 2 extra dwords on rv6xx */ - if (rdev->family > CHIP_R600 && rdev->family < CHIP_RV770) - dwords_per_loop += 2; - - /* 8 bpp vs 32 bpp for xfer unit */ - if (size_bytes & 3) - line_size = 8192; - else - line_size = 8192*4; - - max_size = 8192 * line_size; + /* num loops */ + while (num_gpu_pages) { + num_gpu_pages -= + r600_blit_create_rect(num_gpu_pages, NULL, NULL, + rdev->r600_blit.max_dim); + num_loops++; + } - /* major loops cover the max size transfer */ - num_loops = ((size_bytes + max_size) / max_size); - /* minor loops cover the extra non aligned bits */ - num_loops += ((size_bytes % line_size) ? 1 : 0); /* calculate number of loops correctly */ ring_size = num_loops * dwords_per_loop; - /* set default + shaders */ - ring_size += 40; /* shaders + def state */ - ring_size += 10; /* fence emit for VB IB */ - ring_size += 5; /* done copy */ - ring_size += 10; /* fence emit for done copy */ + ring_size += rdev->r600_blit.ring_size_common; r = radeon_ring_lock(rdev, ring_size); if (r) return r; - set_default_state(rdev); /* 14 */ - set_shaders(rdev); /* 26 */ + rdev->r600_blit.primitives.set_default_state(rdev); + rdev->r600_blit.primitives.set_shaders(rdev); return 0; } @@ -659,182 +723,61 @@ void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence) void r600_kms_blit_copy(struct radeon_device *rdev, u64 src_gpu_addr, u64 dst_gpu_addr, - int size_bytes) + unsigned num_gpu_pages) { - int max_bytes; u64 vb_gpu_addr; u32 *vb; - DRM_DEBUG("emitting copy %16llx %16llx %d %d\n", src_gpu_addr, dst_gpu_addr, - size_bytes, rdev->r600_blit.vb_used); + DRM_DEBUG("emitting copy %16llx %16llx %d %d\n", + src_gpu_addr, dst_gpu_addr, + num_gpu_pages, rdev->r600_blit.vb_used); vb = (u32 *)(rdev->r600_blit.vb_ib->ptr + rdev->r600_blit.vb_used); - if ((size_bytes & 3) || (src_gpu_addr & 3) || (dst_gpu_addr & 3)) { - max_bytes = 8192; - - while (size_bytes) { - int cur_size = size_bytes; - int src_x = src_gpu_addr & 255; - int dst_x = dst_gpu_addr & 255; - int h = 1; - src_gpu_addr = src_gpu_addr & ~255ULL; - dst_gpu_addr = dst_gpu_addr & ~255ULL; - - if (!src_x && !dst_x) { - h = (cur_size / max_bytes); - if (h > 8192) - h = 8192; - if (h == 0) - h = 1; - else - cur_size = max_bytes; - } else { - if (cur_size > max_bytes) - cur_size = max_bytes; - if (cur_size > (max_bytes - dst_x)) - cur_size = (max_bytes - dst_x); - if (cur_size > (max_bytes - src_x)) - cur_size = (max_bytes - src_x); - } - - if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) { - WARN_ON(1); - } - - vb[0] = i2f(dst_x); - vb[1] = 0; - vb[2] = i2f(src_x); - vb[3] = 0; - - vb[4] = i2f(dst_x); - vb[5] = i2f(h); - vb[6] = i2f(src_x); - vb[7] = i2f(h); - - vb[8] = i2f(dst_x + cur_size); - vb[9] = i2f(h); - vb[10] = i2f(src_x + cur_size); - vb[11] = i2f(h); - - /* src 9 */ - set_tex_resource(rdev, FMT_8, - src_x + cur_size, h, src_x + cur_size, - src_gpu_addr); - - /* 5 */ - cp_set_surface_sync(rdev, - PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr); - /* dst 23 */ - set_render_target(rdev, COLOR_8, - dst_x + cur_size, h, - dst_gpu_addr); + while (num_gpu_pages) { + int w, h; + unsigned size_in_bytes; + unsigned pages_per_loop = + r600_blit_create_rect(num_gpu_pages, &w, &h, + rdev->r600_blit.max_dim); - /* scissors 12 */ - set_scissors(rdev, dst_x, 0, dst_x + cur_size, h); + size_in_bytes = pages_per_loop * RADEON_GPU_PAGE_SIZE; + DRM_DEBUG("rectangle w=%d h=%d\n", w, h); - /* 14 */ - vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used; - set_vtx_resource(rdev, vb_gpu_addr); - - /* draw 10 */ - draw_auto(rdev); - - /* 5 */ - cp_set_surface_sync(rdev, - PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA, - cur_size * h, dst_gpu_addr); - - vb += 12; - rdev->r600_blit.vb_used += 12 * 4; - - src_gpu_addr += cur_size * h; - dst_gpu_addr += cur_size * h; - size_bytes -= cur_size * h; + if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) { + WARN_ON(1); } - } else { - max_bytes = 8192 * 4; - - while (size_bytes) { - int cur_size = size_bytes; - int src_x = (src_gpu_addr & 255); - int dst_x = (dst_gpu_addr & 255); - int h = 1; - src_gpu_addr = src_gpu_addr & ~255ULL; - dst_gpu_addr = dst_gpu_addr & ~255ULL; - - if (!src_x && !dst_x) { - h = (cur_size / max_bytes); - if (h > 8192) - h = 8192; - if (h == 0) - h = 1; - else - cur_size = max_bytes; - } else { - if (cur_size > max_bytes) - cur_size = max_bytes; - if (cur_size > (max_bytes - dst_x)) - cur_size = (max_bytes - dst_x); - if (cur_size > (max_bytes - src_x)) - cur_size = (max_bytes - src_x); - } - - if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) { - WARN_ON(1); - } - vb[0] = i2f(dst_x / 4); - vb[1] = 0; - vb[2] = i2f(src_x / 4); - vb[3] = 0; - - vb[4] = i2f(dst_x / 4); - vb[5] = i2f(h); - vb[6] = i2f(src_x / 4); - vb[7] = i2f(h); - - vb[8] = i2f((dst_x + cur_size) / 4); - vb[9] = i2f(h); - vb[10] = i2f((src_x + cur_size) / 4); - vb[11] = i2f(h); - - /* src 9 */ - set_tex_resource(rdev, FMT_8_8_8_8, - (src_x + cur_size) / 4, - h, (src_x + cur_size) / 4, - src_gpu_addr); - /* 5 */ - cp_set_surface_sync(rdev, - PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr); - - /* dst 23 */ - set_render_target(rdev, COLOR_8_8_8_8, - (dst_x + cur_size) / 4, h, - dst_gpu_addr); - - /* scissors 12 */ - set_scissors(rdev, (dst_x / 4), 0, (dst_x + cur_size / 4), h); - - /* Vertex buffer setup 14 */ - vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used; - set_vtx_resource(rdev, vb_gpu_addr); - - /* draw 10 */ - draw_auto(rdev); - - /* 5 */ - cp_set_surface_sync(rdev, - PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA, - cur_size * h, dst_gpu_addr); - - /* 78 ring dwords per loop */ - vb += 12; - rdev->r600_blit.vb_used += 12 * 4; - - src_gpu_addr += cur_size * h; - dst_gpu_addr += cur_size * h; - size_bytes -= cur_size * h; - } + vb[0] = 0; + vb[1] = 0; + vb[2] = 0; + vb[3] = 0; + + vb[4] = 0; + vb[5] = i2f(h); + vb[6] = 0; + vb[7] = i2f(h); + + vb[8] = i2f(w); + vb[9] = i2f(h); + vb[10] = i2f(w); + vb[11] = i2f(h); + + rdev->r600_blit.primitives.set_tex_resource(rdev, FMT_8_8_8_8, + w, h, w, src_gpu_addr, size_in_bytes); + rdev->r600_blit.primitives.set_render_target(rdev, COLOR_8_8_8_8, + w, h, dst_gpu_addr); + rdev->r600_blit.primitives.set_scissors(rdev, 0, 0, w, h); + vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used; + rdev->r600_blit.primitives.set_vtx_resource(rdev, vb_gpu_addr); + rdev->r600_blit.primitives.draw_auto(rdev); + rdev->r600_blit.primitives.cp_set_surface_sync(rdev, + PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA, + size_in_bytes, dst_gpu_addr); + + vb += 12; + rdev->r600_blit.vb_used += 4*12; + src_gpu_addr += size_in_bytes; + dst_gpu_addr += size_in_bytes; + num_gpu_pages -= pages_per_loop; } } - diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c index c3ab959..c9db493 100644 --- a/drivers/gpu/drm/radeon/r600_cp.c +++ b/drivers/gpu/drm/radeon/r600_cp.c @@ -26,6 +26,8 @@ * Alex Deucher <alexander.deucher@amd.com> */ +#include <linux/module.h> + #include "drmP.h" #include "drm.h" #include "radeon_drm.h" @@ -1802,8 +1804,8 @@ static void r600_cp_init_ring_buffer(struct drm_device *dev, /* Set ring buffer size */ #ifdef __BIG_ENDIAN RADEON_WRITE(R600_CP_RB_CNTL, - RADEON_BUF_SWAP_32BIT | - RADEON_RB_NO_UPDATE | + R600_BUF_SWAP_32BIT | + R600_RB_NO_UPDATE | (dev_priv->ring.rptr_update_l2qw << 8) | dev_priv->ring.size_l2qw); #else @@ -1820,15 +1822,15 @@ static void r600_cp_init_ring_buffer(struct drm_device *dev, #ifdef __BIG_ENDIAN RADEON_WRITE(R600_CP_RB_CNTL, - RADEON_BUF_SWAP_32BIT | - RADEON_RB_NO_UPDATE | - RADEON_RB_RPTR_WR_ENA | + R600_BUF_SWAP_32BIT | + R600_RB_NO_UPDATE | + R600_RB_RPTR_WR_ENA | (dev_priv->ring.rptr_update_l2qw << 8) | dev_priv->ring.size_l2qw); #else RADEON_WRITE(R600_CP_RB_CNTL, - RADEON_RB_NO_UPDATE | - RADEON_RB_RPTR_WR_ENA | + R600_RB_NO_UPDATE | + R600_RB_RPTR_WR_ENA | (dev_priv->ring.rptr_update_l2qw << 8) | dev_priv->ring.size_l2qw); #endif @@ -1851,13 +1853,8 @@ static void r600_cp_init_ring_buffer(struct drm_device *dev, - ((unsigned long) dev->sg->virtual) + dev_priv->gart_vm_start; } - RADEON_WRITE(R600_CP_RB_RPTR_ADDR, -#ifdef __BIG_ENDIAN - (2 << 0) | -#endif - (rptr_addr & 0xfffffffc)); - RADEON_WRITE(R600_CP_RB_RPTR_ADDR_HI, - upper_32_bits(rptr_addr)); + RADEON_WRITE(R600_CP_RB_RPTR_ADDR, (rptr_addr & 0xfffffffc)); + RADEON_WRITE(R600_CP_RB_RPTR_ADDR_HI, upper_32_bits(rptr_addr)); #ifdef __BIG_ENDIAN RADEON_WRITE(R600_CP_RB_CNTL, diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 909bda8..cb1acff 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -162,7 +162,7 @@ static const struct gpu_formats color_formats_table[] = { [V_038004_FMT_32_AS_32_32_32_32] = { 1, 1, 4, 0, CHIP_CEDAR}, }; -static inline bool fmt_is_valid_color(u32 format) +static bool fmt_is_valid_color(u32 format) { if (format >= ARRAY_SIZE(color_formats_table)) return false; @@ -173,7 +173,7 @@ static inline bool fmt_is_valid_color(u32 format) return false; } -static inline bool fmt_is_valid_texture(u32 format, enum radeon_family family) +static bool fmt_is_valid_texture(u32 format, enum radeon_family family) { if (format >= ARRAY_SIZE(color_formats_table)) return false; @@ -187,7 +187,7 @@ static inline bool fmt_is_valid_texture(u32 format, enum radeon_family family) return false; } -static inline int fmt_get_blocksize(u32 format) +static int fmt_get_blocksize(u32 format) { if (format >= ARRAY_SIZE(color_formats_table)) return 0; @@ -195,7 +195,7 @@ static inline int fmt_get_blocksize(u32 format) return color_formats_table[format].blocksize; } -static inline int fmt_get_nblocksx(u32 format, u32 w) +static int fmt_get_nblocksx(u32 format, u32 w) { unsigned bw; @@ -209,7 +209,7 @@ static inline int fmt_get_nblocksx(u32 format, u32 w) return (w + bw - 1) / bw; } -static inline int fmt_get_nblocksy(u32 format, u32 h) +static int fmt_get_nblocksy(u32 format, u32 h) { unsigned bh; @@ -223,25 +223,6 @@ static inline int fmt_get_nblocksy(u32 format, u32 h) return (h + bh - 1) / bh; } -static inline int r600_bpe_from_format(u32 *bpe, u32 format) -{ - unsigned res; - - if (format >= ARRAY_SIZE(color_formats_table)) - goto fail; - - res = color_formats_table[format].blocksize; - if (res == 0) - goto fail; - - *bpe = res; - return 0; - -fail: - *bpe = 16; - return -EINVAL; -} - struct array_mode_checker { int array_mode; u32 group_size; @@ -252,7 +233,7 @@ struct array_mode_checker { }; /* returns alignment in pixels for pitch/height/depth and bytes for base */ -static inline int r600_get_array_mode_alignment(struct array_mode_checker *values, +static int r600_get_array_mode_alignment(struct array_mode_checker *values, u32 *pitch_align, u32 *height_align, u32 *depth_align, @@ -331,7 +312,7 @@ static void r600_cs_track_init(struct r600_cs_track *track) track->db_depth_control = 0xFFFFFFFF; } -static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i) +static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i) { struct r600_cs_track *track = p->track; u32 slice_tile_max, size, tmp; @@ -737,7 +718,7 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p, * Check next packet is relocation packet3, do bo validation and compute * GPU offset using the provided start. **/ -static inline int r600_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p) +static int r600_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p) { struct radeon_cs_packet p3reloc; int r; @@ -911,16 +892,15 @@ static int r600_cs_parse_packet0(struct radeon_cs_parser *p, * if register is safe. If register is not flag as safe this function * will test it against a list of register needind special handling. */ -static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) +static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) { struct r600_cs_track *track = (struct r600_cs_track *)p->track; struct radeon_cs_reloc *reloc; - u32 last_reg = ARRAY_SIZE(r600_reg_safe_bm); u32 m, i, tmp, *ib; int r; i = (reg >> 7); - if (i > last_reg) { + if (i >= ARRAY_SIZE(r600_reg_safe_bm)) { dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); return -EINVAL; } @@ -961,7 +941,8 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx track->db_depth_control = radeon_get_ib_value(p, idx); break; case R_028010_DB_DEPTH_INFO: - if (r600_cs_packet_next_is_pkt3_nop(p)) { + if (!p->keep_tiling_flags && + r600_cs_packet_next_is_pkt3_nop(p)) { r = r600_cs_packet_next_reloc(p, &reloc); if (r) { dev_warn(p->dev, "bad SET_CONTEXT_REG " @@ -1012,7 +993,8 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx case R_0280B4_CB_COLOR5_INFO: case R_0280B8_CB_COLOR6_INFO: case R_0280BC_CB_COLOR7_INFO: - if (r600_cs_packet_next_is_pkt3_nop(p)) { + if (!p->keep_tiling_flags && + r600_cs_packet_next_is_pkt3_nop(p)) { r = r600_cs_packet_next_reloc(p, &reloc); if (r) { dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg); @@ -1200,6 +1182,15 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx } ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); break; + case SX_MEMORY_EXPORT_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONFIG_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; default: dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); return -EINVAL; @@ -1207,7 +1198,7 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx return 0; } -static inline unsigned mip_minify(unsigned size, unsigned level) +static unsigned mip_minify(unsigned size, unsigned level) { unsigned val; @@ -1277,7 +1268,7 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned llevel, * This function will check that the resource has valid field and that * the texture and mipmap bo object are big enough to cover this resource. */ -static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx, +static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx, struct radeon_bo *texture, struct radeon_bo *mipmap, u64 base_offset, @@ -1302,10 +1293,12 @@ static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 i mip_offset <<= 8; word0 = radeon_get_ib_value(p, idx + 0); - if (tiling_flags & RADEON_TILING_MACRO) - word0 |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); - else if (tiling_flags & RADEON_TILING_MICRO) - word0 |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + if (!p->keep_tiling_flags) { + if (tiling_flags & RADEON_TILING_MACRO) + word0 |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); + else if (tiling_flags & RADEON_TILING_MICRO) + word0 |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + } word1 = radeon_get_ib_value(p, idx + 1); w0 = G_038000_TEX_WIDTH(word0) + 1; h0 = G_038004_TEX_HEIGHT(word1) + 1; @@ -1632,10 +1625,12 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } base_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); - if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) - ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); - else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) - ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + if (!p->keep_tiling_flags) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); + else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + } texture = reloc->robj; /* tex mip base */ r = r600_cs_packet_next_reloc(p, &reloc); diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index c45d921..57a825d 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -506,7 +506,7 @@ void r600_hdmi_enable(struct drm_encoder *encoder) offset = radeon_encoder->hdmi_offset; if (ASIC_IS_DCE32(rdev) && !ASIC_IS_DCE4(rdev)) { WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0x1, ~0x1); - } else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { + } else if (ASIC_IS_DCE2(rdev) && !ASIC_IS_DCE3(rdev)) { switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: WREG32_P(AVIVO_TMDSA_CNTL, 0x4, ~0x4); @@ -572,7 +572,7 @@ void r600_hdmi_disable(struct drm_encoder *encoder) if (ASIC_IS_DCE32(rdev) && !ASIC_IS_DCE4(rdev)) { WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0, ~0x1); - } else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { + } else if (ASIC_IS_DCE2(rdev) && !ASIC_IS_DCE3(rdev)) { switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: WREG32_P(AVIVO_TMDSA_CNTL, 0, ~0x4); diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 0245ae6..cb29480 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -79,6 +79,11 @@ #define CB_COLOR0_SIZE 0x28060 #define CB_COLOR0_VIEW 0x28080 #define CB_COLOR0_INFO 0x280a0 +# define CB_FORMAT(x) ((x) << 2) +# define CB_ARRAY_MODE(x) ((x) << 8) +# define CB_SOURCE_FORMAT(x) ((x) << 27) +# define CB_SF_EXPORT_FULL 0 +# define CB_SF_EXPORT_NORM 1 #define CB_COLOR0_TILE 0x280c0 #define CB_COLOR0_FRAG 0x280e0 #define CB_COLOR0_MASK 0x28100 @@ -417,6 +422,17 @@ #define SQ_PGM_START_VS 0x28858 #define SQ_PGM_RESOURCES_VS 0x28868 #define SQ_PGM_CF_OFFSET_VS 0x288d0 + +#define SQ_VTX_CONSTANT_WORD0_0 0x30000 +#define SQ_VTX_CONSTANT_WORD1_0 0x30004 +#define SQ_VTX_CONSTANT_WORD2_0 0x30008 +# define SQ_VTXC_BASE_ADDR_HI(x) ((x) << 0) +# define SQ_VTXC_STRIDE(x) ((x) << 8) +# define SQ_VTXC_ENDIAN_SWAP(x) ((x) << 30) +# define SQ_ENDIAN_NONE 0 +# define SQ_ENDIAN_8IN16 1 +# define SQ_ENDIAN_8IN32 2 +#define SQ_VTX_CONSTANT_WORD3_0 0x3000c #define SQ_VTX_CONSTANT_WORD6_0 0x38018 #define S__SQ_VTX_CONSTANT_TYPE(x) (((x) & 3) << 30) #define G__SQ_VTX_CONSTANT_TYPE(x) (((x) >> 30) & 3) @@ -445,6 +461,7 @@ #define TC_L2_SIZE(x) ((x)<<5) #define L2_DISABLE_LATE_HIT (1<<9) +#define VC_ENHANCE 0x9714 #define VGT_CACHE_INVALIDATION 0x88C4 #define CACHE_INVALIDATION(x) ((x)<<0) @@ -821,6 +838,7 @@ #define PACKET3_INDIRECT_BUFFER 0x32 #define PACKET3_SURFACE_SYNC 0x43 # define PACKET3_CB0_DEST_BASE_ENA (1 << 6) +# define PACKET3_FULL_CACHE_ENA (1 << 20) /* r7xx+ only */ # define PACKET3_TC_ACTION_ENA (1 << 23) # define PACKET3_VC_ACTION_ENA (1 << 24) # define PACKET3_CB_ACTION_ENA (1 << 25) @@ -1352,6 +1370,12 @@ #define S_038010_DST_SEL_W(x) (((x) & 0x7) << 25) #define G_038010_DST_SEL_W(x) (((x) >> 25) & 0x7) #define C_038010_DST_SEL_W 0xF1FFFFFF +# define SQ_SEL_X 0 +# define SQ_SEL_Y 1 +# define SQ_SEL_Z 2 +# define SQ_SEL_W 3 +# define SQ_SEL_0 4 +# define SQ_SEL_1 5 #define S_038010_BASE_LEVEL(x) (((x) & 0xF) << 28) #define G_038010_BASE_LEVEL(x) (((x) >> 28) & 0xF) #define C_038010_BASE_LEVEL 0x0FFFFFFF diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 59d72d0..28e69e9 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -60,7 +60,7 @@ * are considered as fatal) */ -#include <asm/atomic.h> +#include <linux/atomic.h> #include <linux/wait.h> #include <linux/list.h> #include <linux/kref.h> @@ -103,7 +103,7 @@ extern int radeon_msi; #define RADEON_FENCE_JIFFIES_TIMEOUT (HZ / 2) /* RADEON_IB_POOL_SIZE must be a power of 2 */ #define RADEON_IB_POOL_SIZE 16 -#define RADEON_DEBUGFS_MAX_NUM_FILES 32 +#define RADEON_DEBUGFS_MAX_COMPONENTS 32 #define RADEONFB_CONN_LIMIT 4 #define RADEON_BIOS_NUM_SCRATCH 8 @@ -123,21 +123,6 @@ struct radeon_device; /* * BIOS. */ -#define ATRM_BIOS_PAGE 4096 - -#if defined(CONFIG_VGA_SWITCHEROO) -bool radeon_atrm_supported(struct pci_dev *pdev); -int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len); -#else -static inline bool radeon_atrm_supported(struct pci_dev *pdev) -{ - return false; -} - -static inline int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len){ - return -EINVAL; -} -#endif bool radeon_get_bios(struct radeon_device *rdev); @@ -307,30 +292,17 @@ int radeon_mode_dumb_destroy(struct drm_file *file_priv, */ struct radeon_mc; -struct radeon_gart_table_ram { - volatile uint32_t *ptr; -}; - -struct radeon_gart_table_vram { - struct radeon_bo *robj; - volatile uint32_t *ptr; -}; - -union radeon_gart_table { - struct radeon_gart_table_ram ram; - struct radeon_gart_table_vram vram; -}; - #define RADEON_GPU_PAGE_SIZE 4096 #define RADEON_GPU_PAGE_MASK (RADEON_GPU_PAGE_SIZE - 1) #define RADEON_GPU_PAGE_SHIFT 12 struct radeon_gart { dma_addr_t table_addr; + struct radeon_bo *robj; + void *ptr; unsigned num_gpu_pages; unsigned num_cpu_pages; unsigned table_size; - union radeon_gart_table table; struct page **pages; dma_addr_t *pages_addr; bool *ttm_alloced; @@ -341,6 +313,8 @@ int radeon_gart_table_ram_alloc(struct radeon_device *rdev); void radeon_gart_table_ram_free(struct radeon_device *rdev); int radeon_gart_table_vram_alloc(struct radeon_device *rdev); void radeon_gart_table_vram_free(struct radeon_device *rdev); +int radeon_gart_table_vram_pin(struct radeon_device *rdev); +void radeon_gart_table_vram_unpin(struct radeon_device *rdev); int radeon_gart_init(struct radeon_device *rdev); void radeon_gart_fini(struct radeon_device *rdev); void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, @@ -348,6 +322,7 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, int pages, struct page **pagelist, dma_addr_t *dma_addr); +void radeon_gart_restore(struct radeon_device *rdev); /* @@ -438,25 +413,26 @@ union radeon_irq_stat_regs { struct evergreen_irq_stat_regs evergreen; }; +#define RADEON_MAX_HPD_PINS 6 +#define RADEON_MAX_CRTCS 6 +#define RADEON_MAX_HDMI_BLOCKS 2 + struct radeon_irq { bool installed; bool sw_int; - /* FIXME: use a define max crtc rather than hardcode it */ - bool crtc_vblank_int[6]; - bool pflip[6]; + bool crtc_vblank_int[RADEON_MAX_CRTCS]; + bool pflip[RADEON_MAX_CRTCS]; wait_queue_head_t vblank_queue; - /* FIXME: use defines for max hpd/dacs */ - bool hpd[6]; + bool hpd[RADEON_MAX_HPD_PINS]; bool gui_idle; bool gui_idle_acked; wait_queue_head_t idle_queue; - /* FIXME: use defines for max HDMI blocks */ - bool hdmi[2]; + bool hdmi[RADEON_MAX_HDMI_BLOCKS]; spinlock_t sw_lock; int sw_refcount; union radeon_irq_stat_regs stat_regs; - spinlock_t pflip_lock[6]; - int pflip_refcount[6]; + spinlock_t pflip_lock[RADEON_MAX_CRTCS]; + int pflip_refcount[RADEON_MAX_CRTCS]; }; int radeon_irq_kms_init(struct radeon_device *rdev); @@ -524,9 +500,30 @@ struct r600_ih { bool enabled; }; +struct r600_blit_cp_primitives { + void (*set_render_target)(struct radeon_device *rdev, int format, + int w, int h, u64 gpu_addr); + void (*cp_set_surface_sync)(struct radeon_device *rdev, + u32 sync_type, u32 size, + u64 mc_addr); + void (*set_shaders)(struct radeon_device *rdev); + void (*set_vtx_resource)(struct radeon_device *rdev, u64 gpu_addr); + void (*set_tex_resource)(struct radeon_device *rdev, + int format, int w, int h, int pitch, + u64 gpu_addr, u32 size); + void (*set_scissors)(struct radeon_device *rdev, int x1, int y1, + int x2, int y2); + void (*draw_auto)(struct radeon_device *rdev); + void (*set_default_state)(struct radeon_device *rdev); +}; + struct r600_blit { struct mutex mutex; struct radeon_bo *shader_obj; + struct r600_blit_cp_primitives primitives; + int max_dim; + int ring_size_common; + int ring_size_per_loop; u64 shader_gpu_addr; u32 vs_offset, ps_offset; u32 state_offset; @@ -535,6 +532,8 @@ struct r600_blit { struct radeon_ib *vb_ib; }; +void r600_blit_suspend(struct radeon_device *rdev); + int radeon_ib_get(struct radeon_device *rdev, struct radeon_ib **ib); void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib); int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib); @@ -597,37 +596,13 @@ struct radeon_cs_parser { struct radeon_ib *ib; void *track; unsigned family; - int parser_error; + int parser_error; + bool keep_tiling_flags; }; extern int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx); extern int radeon_cs_finish_pages(struct radeon_cs_parser *p); - - -static inline u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) -{ - struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; - u32 pg_idx, pg_offset; - u32 idx_value = 0; - int new_page; - - pg_idx = (idx * 4) / PAGE_SIZE; - pg_offset = (idx * 4) % PAGE_SIZE; - - if (ibc->kpage_idx[0] == pg_idx) - return ibc->kpage[0][pg_offset/4]; - if (ibc->kpage_idx[1] == pg_idx) - return ibc->kpage[1][pg_offset/4]; - - new_page = radeon_cs_update_pages(p, pg_idx); - if (new_page < 0) { - p->parser_error = new_page; - return 0; - } - - idx_value = ibc->kpage[new_page][pg_offset/4]; - return idx_value; -} +extern u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx); struct radeon_cs_packet { unsigned idx; @@ -795,8 +770,7 @@ struct radeon_pm_clock_info { struct radeon_power_state { enum radeon_pm_state_type type; - /* XXX: use a define for num clock modes */ - struct radeon_pm_clock_info clock_info[8]; + struct radeon_pm_clock_info *clock_info; /* number of valid clock modes in this power state */ int num_clock_modes; struct radeon_pm_clock_info *default_clock_mode; @@ -866,11 +840,14 @@ struct radeon_pm { struct device *int_hwmon_dev; }; +int radeon_pm_get_type_index(struct radeon_device *rdev, + enum radeon_pm_state_type ps_type, + int instance); /* * Benchmarking */ -void radeon_benchmark(struct radeon_device *rdev); +void radeon_benchmark(struct radeon_device *rdev, int test_number); /* @@ -1005,6 +982,7 @@ struct r600_asic { unsigned tiling_npipes; unsigned tiling_group_size; unsigned tile_config; + unsigned backend_map; struct r100_gpu_lockup lockup; }; @@ -1030,6 +1008,7 @@ struct rv770_asic { unsigned tiling_npipes; unsigned tiling_group_size; unsigned tile_config; + unsigned backend_map; struct r100_gpu_lockup lockup; }; @@ -1056,6 +1035,7 @@ struct evergreen_asic { unsigned tiling_npipes; unsigned tiling_group_size; unsigned tile_config; + unsigned backend_map; struct r100_gpu_lockup lockup; }; @@ -1143,12 +1123,55 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data, int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); -/* VRAM scratch page for HDP bug */ -struct r700_vram_scratch { +/* VRAM scratch page for HDP bug, default vram page */ +struct r600_vram_scratch { struct radeon_bo *robj; volatile uint32_t *ptr; + u64 gpu_addr; }; + +/* + * Mutex which allows recursive locking from the same process. + */ +struct radeon_mutex { + struct mutex mutex; + struct task_struct *owner; + int level; +}; + +static inline void radeon_mutex_init(struct radeon_mutex *mutex) +{ + mutex_init(&mutex->mutex); + mutex->owner = NULL; + mutex->level = 0; +} + +static inline void radeon_mutex_lock(struct radeon_mutex *mutex) +{ + if (mutex_trylock(&mutex->mutex)) { + /* The mutex was unlocked before, so it's ours now */ + mutex->owner = current; + } else if (mutex->owner != current) { + /* Another process locked the mutex, take it */ + mutex_lock(&mutex->mutex); + mutex->owner = current; + } + /* Otherwise the mutex was already locked by this process */ + + mutex->level++; +} + +static inline void radeon_mutex_unlock(struct radeon_mutex *mutex) +{ + if (--mutex->level > 0) + return; + + mutex->owner = NULL; + mutex_unlock(&mutex->mutex); +} + + /* * Core structure, functions and helpers. */ @@ -1176,7 +1199,7 @@ struct radeon_device { /* Register mmio */ resource_size_t rmmio_base; resource_size_t rmmio_size; - void *rmmio; + void __iomem *rmmio; radeon_rreg_t mc_rreg; radeon_wreg_t mc_wreg; radeon_rreg_t pll_rreg; @@ -1204,7 +1227,7 @@ struct radeon_device { struct radeon_gem gem; struct radeon_pm pm; uint32_t bios_scratch[RADEON_BIOS_NUM_SCRATCH]; - struct mutex cs_mutex; + struct radeon_mutex cs_mutex; struct radeon_wb wb; struct radeon_dummy_page dummy_page; bool gpu_lockup; @@ -1218,7 +1241,7 @@ struct radeon_device { const struct firmware *rlc_fw; /* r6/700 RLC firmware */ const struct firmware *mc_fw; /* NI MC firmware */ struct r600_blit r600_blit; - struct r700_vram_scratch vram_scratch; + struct r600_vram_scratch vram_scratch; int msi_enabled; /* msi enabled */ struct r600_ih ih; /* r6/700 interrupt ring */ struct work_struct hotplug_work; @@ -1250,45 +1273,10 @@ int radeon_device_init(struct radeon_device *rdev, void radeon_device_fini(struct radeon_device *rdev); int radeon_gpu_wait_for_idle(struct radeon_device *rdev); -static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg) -{ - if (reg < rdev->rmmio_size) - return readl(((void __iomem *)rdev->rmmio) + reg); - else { - writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); - return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); - } -} - -static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) -{ - if (reg < rdev->rmmio_size) - writel(v, ((void __iomem *)rdev->rmmio) + reg); - else { - writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); - writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); - } -} - -static inline u32 r100_io_rreg(struct radeon_device *rdev, u32 reg) -{ - if (reg < rdev->rio_mem_size) - return ioread32(rdev->rio_mem + reg); - else { - iowrite32(reg, rdev->rio_mem + RADEON_MM_INDEX); - return ioread32(rdev->rio_mem + RADEON_MM_DATA); - } -} - -static inline void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v) -{ - if (reg < rdev->rio_mem_size) - iowrite32(v, rdev->rio_mem + reg); - else { - iowrite32(reg, rdev->rio_mem + RADEON_MM_INDEX); - iowrite32(v, rdev->rio_mem + RADEON_MM_DATA); - } -} +uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg); +void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +u32 r100_io_rreg(struct radeon_device *rdev, u32 reg); +void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); /* * Cast helper @@ -1298,10 +1286,10 @@ static inline void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v) /* * Registers read & write functions. */ -#define RREG8(reg) readb(((void __iomem *)rdev->rmmio) + (reg)) -#define WREG8(reg, v) writeb(v, ((void __iomem *)rdev->rmmio) + (reg)) -#define RREG16(reg) readw(((void __iomem *)rdev->rmmio) + (reg)) -#define WREG16(reg, v) writew(v, ((void __iomem *)rdev->rmmio) + (reg)) +#define RREG8(reg) readb((rdev->rmmio) + (reg)) +#define WREG8(reg, v) writeb(v, (rdev->rmmio) + (reg)) +#define RREG16(reg) readw((rdev->rmmio) + (reg)) +#define WREG16(reg, v) writew(v, (rdev->rmmio) + (reg)) #define RREG32(reg) r100_mm_rreg(rdev, (reg)) #define DREG32(reg) printk(KERN_INFO "REGISTER: " #reg " : 0x%08X\n", r100_mm_rreg(rdev, (reg))) #define WREG32(reg, v) r100_mm_wreg(rdev, (reg), (v)) @@ -1411,19 +1399,19 @@ void radeon_atombios_fini(struct radeon_device *rdev); /* * RING helpers. */ + +#if DRM_DEBUG_CODE == 0 static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v) { -#if DRM_DEBUG_CODE - if (rdev->cp.count_dw <= 0) { - DRM_ERROR("radeon: writting more dword to ring than expected !\n"); - } -#endif rdev->cp.ring[rdev->cp.wptr++] = v; rdev->cp.wptr &= rdev->cp.ptr_mask; rdev->cp.count_dw--; rdev->cp.ring_free_dw--; } - +#else +/* With debugging this is just too big to inline */ +void radeon_ring_write(struct radeon_device *rdev, uint32_t v); +#endif /* * ASICs macro. @@ -1477,8 +1465,6 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v) /* AGP */ extern int radeon_gpu_reset(struct radeon_device *rdev); extern void radeon_agp_disable(struct radeon_device *rdev); -extern int radeon_gart_table_vram_pin(struct radeon_device *rdev); -extern void radeon_gart_restore(struct radeon_device *rdev); extern int radeon_modeset_init(struct radeon_device *rdev); extern void radeon_modeset_fini(struct radeon_device *rdev); extern bool radeon_card_posted(struct radeon_device *rdev); @@ -1502,6 +1488,12 @@ extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state); extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size); /* + * R600 vram scratch functions + */ +int r600_vram_scratch_init(struct radeon_device *rdev); +void r600_vram_scratch_fini(struct radeon_device *rdev); + +/* * r600 functions used by radeon_encoder.c */ extern void r600_hdmi_enable(struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/radeon/radeon_acpi.c b/drivers/gpu/drm/radeon/radeon_acpi.c index 3f6636b..3516a60 100644 --- a/drivers/gpu/drm/radeon/radeon_acpi.c +++ b/drivers/gpu/drm/radeon/radeon_acpi.c @@ -35,7 +35,8 @@ static int radeon_atif_call(acpi_handle handle) /* Fail only if calling the method fails and ATIF is supported */ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { - printk(KERN_DEBUG "failed to evaluate ATIF got %s\n", acpi_format_exception(status)); + DRM_DEBUG_DRIVER("failed to evaluate ATIF got %s\n", + acpi_format_exception(status)); kfree(buffer.pointer); return 1; } @@ -50,13 +51,13 @@ int radeon_acpi_init(struct radeon_device *rdev) acpi_handle handle; int ret; - /* No need to proceed if we're sure that ATIF is not supported */ - if (!ASIC_IS_AVIVO(rdev) || !rdev->bios) - return 0; - /* Get the device handle */ handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev); + /* No need to proceed if we're sure that ATIF is not supported */ + if (!ASIC_IS_AVIVO(rdev) || !rdev->bios || !handle) + return 0; + /* Call the ATIF method */ ret = radeon_atif_call(handle); if (ret) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index b244962..a2e1eae 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -625,7 +625,7 @@ static struct radeon_asic r600_asic = { .fence_ring_emit = &r600_fence_ring_emit, .cs_parse = &r600_cs_parse, .copy_blit = &r600_copy_blit, - .copy_dma = &r600_copy_blit, + .copy_dma = NULL, .copy = &r600_copy_blit, .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, @@ -672,7 +672,7 @@ static struct radeon_asic rs780_asic = { .fence_ring_emit = &r600_fence_ring_emit, .cs_parse = &r600_cs_parse, .copy_blit = &r600_copy_blit, - .copy_dma = &r600_copy_blit, + .copy_dma = NULL, .copy = &r600_copy_blit, .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, @@ -719,7 +719,7 @@ static struct radeon_asic rv770_asic = { .fence_ring_emit = &r600_fence_ring_emit, .cs_parse = &r600_cs_parse, .copy_blit = &r600_copy_blit, - .copy_dma = &r600_copy_blit, + .copy_dma = NULL, .copy = &r600_copy_blit, .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, @@ -765,9 +765,9 @@ static struct radeon_asic evergreen_asic = { .get_vblank_counter = &evergreen_get_vblank_counter, .fence_ring_emit = &r600_fence_ring_emit, .cs_parse = &evergreen_cs_parse, - .copy_blit = &evergreen_copy_blit, - .copy_dma = &evergreen_copy_blit, - .copy = &evergreen_copy_blit, + .copy_blit = &r600_copy_blit, + .copy_dma = NULL, + .copy = &r600_copy_blit, .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, .get_memory_clock = &radeon_atom_get_memory_clock, @@ -812,9 +812,9 @@ static struct radeon_asic sumo_asic = { .get_vblank_counter = &evergreen_get_vblank_counter, .fence_ring_emit = &r600_fence_ring_emit, .cs_parse = &evergreen_cs_parse, - .copy_blit = &evergreen_copy_blit, - .copy_dma = &evergreen_copy_blit, - .copy = &evergreen_copy_blit, + .copy_blit = &r600_copy_blit, + .copy_dma = NULL, + .copy = &r600_copy_blit, .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, .get_memory_clock = NULL, @@ -834,7 +834,7 @@ static struct radeon_asic sumo_asic = { .pm_misc = &evergreen_pm_misc, .pm_prepare = &evergreen_pm_prepare, .pm_finish = &evergreen_pm_finish, - .pm_init_profile = &rs780_pm_init_profile, + .pm_init_profile = &sumo_pm_init_profile, .pm_get_dynpm_state = &r600_pm_get_dynpm_state, .pre_page_flip = &evergreen_pre_page_flip, .page_flip = &evergreen_page_flip, @@ -859,9 +859,9 @@ static struct radeon_asic btc_asic = { .get_vblank_counter = &evergreen_get_vblank_counter, .fence_ring_emit = &r600_fence_ring_emit, .cs_parse = &evergreen_cs_parse, - .copy_blit = &evergreen_copy_blit, - .copy_dma = &evergreen_copy_blit, - .copy = &evergreen_copy_blit, + .copy_blit = &r600_copy_blit, + .copy_dma = NULL, + .copy = &r600_copy_blit, .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, .get_memory_clock = &radeon_atom_get_memory_clock, @@ -906,9 +906,9 @@ static struct radeon_asic cayman_asic = { .get_vblank_counter = &evergreen_get_vblank_counter, .fence_ring_emit = &r600_fence_ring_emit, .cs_parse = &evergreen_cs_parse, - .copy_blit = &evergreen_copy_blit, - .copy_dma = &evergreen_copy_blit, - .copy = &evergreen_copy_blit, + .copy_blit = &r600_copy_blit, + .copy_dma = NULL, + .copy = &r600_copy_blit, .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, .get_memory_clock = &radeon_atom_get_memory_clock, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 4d81e96..5aa6670 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -361,11 +361,11 @@ void r600_hdmi_init(struct drm_encoder *encoder); int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder); void r600_hdmi_update_audio_settings(struct drm_encoder *encoder); /* r600 blit */ -int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes); +int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages); void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence); void r600_kms_blit_copy(struct radeon_device *rdev, u64 src_gpu_addr, u64 dst_gpu_addr, - int size_bytes); + unsigned num_gpu_pages); /* * rv770,rv730,rv710,rv740 @@ -386,6 +386,7 @@ void r700_cp_fini(struct radeon_device *rdev); struct evergreen_mc_save { u32 vga_render_control; u32 vga_hdp_control; + bool crtc_enabled[RADEON_MAX_CRTCS]; }; void evergreen_pcie_gart_tlb_flush(struct radeon_device *rdev); @@ -397,9 +398,6 @@ bool evergreen_gpu_is_lockup(struct radeon_device *rdev); int evergreen_asic_reset(struct radeon_device *rdev); void evergreen_bandwidth_update(struct radeon_device *rdev); void evergreen_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); -int evergreen_copy_blit(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, struct radeon_fence *fence); void evergreen_hpd_init(struct radeon_device *rdev); void evergreen_hpd_fini(struct radeon_device *rdev); bool evergreen_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd); @@ -412,18 +410,12 @@ extern int evergreen_cs_parse(struct radeon_cs_parser *p); extern void evergreen_pm_misc(struct radeon_device *rdev); extern void evergreen_pm_prepare(struct radeon_device *rdev); extern void evergreen_pm_finish(struct radeon_device *rdev); +extern void sumo_pm_init_profile(struct radeon_device *rdev); extern void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc); extern u32 evergreen_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base); extern void evergreen_post_page_flip(struct radeon_device *rdev, int crtc); void evergreen_disable_interrupt_state(struct radeon_device *rdev); int evergreen_blit_init(struct radeon_device *rdev); -void evergreen_blit_fini(struct radeon_device *rdev); -/* evergreen blit */ -int evergreen_blit_prepare_copy(struct radeon_device *rdev, int size_bytes); -void evergreen_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence); -void evergreen_kms_blit_copy(struct radeon_device *rdev, - u64 src_gpu_addr, u64 dst_gpu_addr, - int size_bytes); /* * cayman diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index f9d49e3..24e9756 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -62,7 +62,88 @@ union atom_supported_devices { struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1; }; -static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_device *rdev, +static void radeon_lookup_i2c_gpio_quirks(struct radeon_device *rdev, + ATOM_GPIO_I2C_ASSIGMENT *gpio, + u8 index) +{ + /* r4xx mask is technically not used by the hw, so patch in the legacy mask bits */ + if ((rdev->family == CHIP_R420) || + (rdev->family == CHIP_R423) || + (rdev->family == CHIP_RV410)) { + if ((le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0018) || + (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0019) || + (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x001a)) { + gpio->ucClkMaskShift = 0x19; + gpio->ucDataMaskShift = 0x18; + } + } + + /* some evergreen boards have bad data for this entry */ + if (ASIC_IS_DCE4(rdev)) { + if ((index == 7) && + (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1936) && + (gpio->sucI2cId.ucAccess == 0)) { + gpio->sucI2cId.ucAccess = 0x97; + gpio->ucDataMaskShift = 8; + gpio->ucDataEnShift = 8; + gpio->ucDataY_Shift = 8; + gpio->ucDataA_Shift = 8; + } + } + + /* some DCE3 boards have bad data for this entry */ + if (ASIC_IS_DCE3(rdev)) { + if ((index == 4) && + (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1fda) && + (gpio->sucI2cId.ucAccess == 0x94)) + gpio->sucI2cId.ucAccess = 0x14; + } +} + +static struct radeon_i2c_bus_rec radeon_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio) +{ + struct radeon_i2c_bus_rec i2c; + + memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec)); + + i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4; + i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4; + i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4; + i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4; + i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4; + i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4; + i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4; + i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4; + i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); + i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); + i2c.en_clk_mask = (1 << gpio->ucClkEnShift); + i2c.en_data_mask = (1 << gpio->ucDataEnShift); + i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); + i2c.y_data_mask = (1 << gpio->ucDataY_Shift); + i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); + i2c.a_data_mask = (1 << gpio->ucDataA_Shift); + + if (gpio->sucI2cId.sbfAccess.bfHW_Capable) + i2c.hw_capable = true; + else + i2c.hw_capable = false; + + if (gpio->sucI2cId.ucAccess == 0xa0) + i2c.mm_i2c = true; + else + i2c.mm_i2c = false; + + i2c.i2c_id = gpio->sucI2cId.ucAccess; + + if (i2c.mask_clk_reg) + i2c.valid = true; + else + i2c.valid = false; + + return i2c; +} + +static struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_device *rdev, uint8_t id) { struct atom_context *ctx = rdev->mode_info.atom_context; @@ -85,71 +166,10 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_dev for (i = 0; i < num_indices; i++) { gpio = &i2c_info->asGPIO_Info[i]; - /* r4xx mask is technically not used by the hw, so patch in the legacy mask bits */ - if ((rdev->family == CHIP_R420) || - (rdev->family == CHIP_R423) || - (rdev->family == CHIP_RV410)) { - if ((le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0018) || - (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0019) || - (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x001a)) { - gpio->ucClkMaskShift = 0x19; - gpio->ucDataMaskShift = 0x18; - } - } - - /* some evergreen boards have bad data for this entry */ - if (ASIC_IS_DCE4(rdev)) { - if ((i == 7) && - (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1936) && - (gpio->sucI2cId.ucAccess == 0)) { - gpio->sucI2cId.ucAccess = 0x97; - gpio->ucDataMaskShift = 8; - gpio->ucDataEnShift = 8; - gpio->ucDataY_Shift = 8; - gpio->ucDataA_Shift = 8; - } - } - - /* some DCE3 boards have bad data for this entry */ - if (ASIC_IS_DCE3(rdev)) { - if ((i == 4) && - (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1fda) && - (gpio->sucI2cId.ucAccess == 0x94)) - gpio->sucI2cId.ucAccess = 0x14; - } + radeon_lookup_i2c_gpio_quirks(rdev, gpio, i); if (gpio->sucI2cId.ucAccess == id) { - i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4; - i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4; - i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4; - i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4; - i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4; - i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4; - i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4; - i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4; - i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); - i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); - i2c.en_clk_mask = (1 << gpio->ucClkEnShift); - i2c.en_data_mask = (1 << gpio->ucDataEnShift); - i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); - i2c.y_data_mask = (1 << gpio->ucDataY_Shift); - i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); - i2c.a_data_mask = (1 << gpio->ucDataA_Shift); - - if (gpio->sucI2cId.sbfAccess.bfHW_Capable) - i2c.hw_capable = true; - else - i2c.hw_capable = false; - - if (gpio->sucI2cId.ucAccess == 0xa0) - i2c.mm_i2c = true; - else - i2c.mm_i2c = false; - - i2c.i2c_id = gpio->sucI2cId.ucAccess; - - if (i2c.mask_clk_reg) - i2c.valid = true; + i2c = radeon_get_bus_rec_for_i2c_gpio(gpio); break; } } @@ -169,8 +189,6 @@ void radeon_atombios_i2c_init(struct radeon_device *rdev) int i, num_indices; char stmp[32]; - memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec)); - if (atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); @@ -179,72 +197,12 @@ void radeon_atombios_i2c_init(struct radeon_device *rdev) for (i = 0; i < num_indices; i++) { gpio = &i2c_info->asGPIO_Info[i]; - i2c.valid = false; - - /* r4xx mask is technically not used by the hw, so patch in the legacy mask bits */ - if ((rdev->family == CHIP_R420) || - (rdev->family == CHIP_R423) || - (rdev->family == CHIP_RV410)) { - if ((le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0018) || - (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0019) || - (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x001a)) { - gpio->ucClkMaskShift = 0x19; - gpio->ucDataMaskShift = 0x18; - } - } - /* some evergreen boards have bad data for this entry */ - if (ASIC_IS_DCE4(rdev)) { - if ((i == 7) && - (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1936) && - (gpio->sucI2cId.ucAccess == 0)) { - gpio->sucI2cId.ucAccess = 0x97; - gpio->ucDataMaskShift = 8; - gpio->ucDataEnShift = 8; - gpio->ucDataY_Shift = 8; - gpio->ucDataA_Shift = 8; - } - } + radeon_lookup_i2c_gpio_quirks(rdev, gpio, i); - /* some DCE3 boards have bad data for this entry */ - if (ASIC_IS_DCE3(rdev)) { - if ((i == 4) && - (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1fda) && - (gpio->sucI2cId.ucAccess == 0x94)) - gpio->sucI2cId.ucAccess = 0x14; - } - - i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4; - i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4; - i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4; - i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4; - i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4; - i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4; - i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4; - i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4; - i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); - i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); - i2c.en_clk_mask = (1 << gpio->ucClkEnShift); - i2c.en_data_mask = (1 << gpio->ucDataEnShift); - i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); - i2c.y_data_mask = (1 << gpio->ucDataY_Shift); - i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); - i2c.a_data_mask = (1 << gpio->ucDataA_Shift); - - if (gpio->sucI2cId.sbfAccess.bfHW_Capable) - i2c.hw_capable = true; - else - i2c.hw_capable = false; - - if (gpio->sucI2cId.ucAccess == 0xa0) - i2c.mm_i2c = true; - else - i2c.mm_i2c = false; - - i2c.i2c_id = gpio->sucI2cId.ucAccess; + i2c = radeon_get_bus_rec_for_i2c_gpio(gpio); - if (i2c.mask_clk_reg) { - i2c.valid = true; + if (i2c.valid) { sprintf(stmp, "0x%x", i2c.i2c_id); rdev->i2c_bus[i] = radeon_i2c_create(rdev->ddev, &i2c, stmp); } @@ -252,7 +210,7 @@ void radeon_atombios_i2c_init(struct radeon_device *rdev) } } -static inline struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev, +static struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev, u8 id) { struct atom_context *ctx = rdev->mode_info.atom_context; @@ -499,6 +457,13 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, } } + /* Fujitsu D3003-S2 board lists DVI-I as DVI-I and VGA */ + if ((dev->pdev->device == 0x9805) && + (dev->pdev->subsystem_vendor == 0x1734) && + (dev->pdev->subsystem_device == 0x11bd)) { + if (*connector_type == DRM_MODE_CONNECTOR_VGA) + return false; + } return true; } @@ -1915,7 +1880,7 @@ static const char *thermal_controller_names[] = { "adm1032", "adm1030", "max6649", - "lm64", + "lm63", /* lm64 */ "f75375", "asc7xxx", }; @@ -1926,7 +1891,7 @@ static const char *pp_lib_thermal_controller_names[] = { "adm1032", "adm1030", "max6649", - "lm64", + "lm63", /* lm64 */ "f75375", "RV6xx", "RV770", @@ -2043,10 +2008,14 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev) return state_index; /* last mode is usually default, array is low to high */ for (i = 0; i < num_modes; i++) { + rdev->pm.power_state[state_index].clock_info = + kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL); + if (!rdev->pm.power_state[state_index].clock_info) + return state_index; + rdev->pm.power_state[state_index].num_clock_modes = 1; rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; switch (frev) { case 1: - rdev->pm.power_state[state_index].num_clock_modes = 1; rdev->pm.power_state[state_index].clock_info[0].mclk = le16_to_cpu(power_info->info.asPowerPlayInfo[i].usMemoryClock); rdev->pm.power_state[state_index].clock_info[0].sclk = @@ -2082,7 +2051,6 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev) state_index++; break; case 2: - rdev->pm.power_state[state_index].num_clock_modes = 1; rdev->pm.power_state[state_index].clock_info[0].mclk = le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMemoryClock); rdev->pm.power_state[state_index].clock_info[0].sclk = @@ -2119,7 +2087,6 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev) state_index++; break; case 3: - rdev->pm.power_state[state_index].num_clock_modes = 1; rdev->pm.power_state[state_index].clock_info[0].mclk = le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMemoryClock); rdev->pm.power_state[state_index].clock_info[0].sclk = @@ -2304,7 +2271,7 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde rdev->pm.default_power_state_index = state_index; rdev->pm.power_state[state_index].default_clock_mode = &rdev->pm.power_state[state_index].clock_info[mode_index - 1]; - if (ASIC_IS_DCE5(rdev)) { + if (ASIC_IS_DCE5(rdev) && !(rdev->flags & RADEON_IS_IGP)) { /* NI chips post without MC ucode, so default clocks are strobe mode only */ rdev->pm.default_sclk = rdev->pm.power_state[state_index].clock_info[0].sclk; rdev->pm.default_mclk = rdev->pm.power_state[state_index].clock_info[0].mclk; @@ -2426,17 +2393,31 @@ static int radeon_atombios_parse_power_table_4_5(struct radeon_device *rdev) le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + (power_state->v1.ucNonClockStateIndex * power_info->pplib.ucNonClockSize)); - for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) { - clock_info = (union pplib_clock_info *) - (mode_info->atom_context->bios + data_offset + - le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + - (power_state->v1.ucClockStateIndices[j] * - power_info->pplib.ucClockInfoSize)); - valid = radeon_atombios_parse_pplib_clock_info(rdev, - state_index, mode_index, - clock_info); - if (valid) - mode_index++; + rdev->pm.power_state[i].clock_info = kzalloc(sizeof(struct radeon_pm_clock_info) * + ((power_info->pplib.ucStateEntrySize - 1) ? + (power_info->pplib.ucStateEntrySize - 1) : 1), + GFP_KERNEL); + if (!rdev->pm.power_state[i].clock_info) + return state_index; + if (power_info->pplib.ucStateEntrySize - 1) { + for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) { + clock_info = (union pplib_clock_info *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + + (power_state->v1.ucClockStateIndices[j] * + power_info->pplib.ucClockInfoSize)); + valid = radeon_atombios_parse_pplib_clock_info(rdev, + state_index, mode_index, + clock_info); + if (valid) + mode_index++; + } + } else { + rdev->pm.power_state[state_index].clock_info[0].mclk = + rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[0].sclk = + rdev->clock.default_sclk; + mode_index++; } rdev->pm.power_state[state_index].num_clock_modes = mode_index; if (mode_index) { @@ -2478,6 +2459,7 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev) int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); u16 data_offset; u8 frev, crev; + u8 *power_state_offset; if (!atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset)) @@ -2500,25 +2482,36 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev) state_array->ucNumEntries, GFP_KERNEL); if (!rdev->pm.power_state) return state_index; + power_state_offset = (u8 *)state_array->states; for (i = 0; i < state_array->ucNumEntries; i++) { mode_index = 0; - power_state = (union pplib_power_state *)&state_array->states[i]; - /* XXX this might be an inagua bug... */ - non_clock_array_index = i; /* power_state->v2.nonClockInfoIndex */ + power_state = (union pplib_power_state *)power_state_offset; + non_clock_array_index = power_state->v2.nonClockInfoIndex; non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) &non_clock_info_array->nonClockInfo[non_clock_array_index]; - for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) { - clock_array_index = power_state->v2.clockInfoIndex[j]; - /* XXX this might be an inagua bug... */ - if (clock_array_index >= clock_info_array->ucNumEntries) - continue; - clock_info = (union pplib_clock_info *) - &clock_info_array->clockInfo[clock_array_index]; - valid = radeon_atombios_parse_pplib_clock_info(rdev, - state_index, mode_index, - clock_info); - if (valid) - mode_index++; + rdev->pm.power_state[i].clock_info = kzalloc(sizeof(struct radeon_pm_clock_info) * + (power_state->v2.ucNumDPMLevels ? + power_state->v2.ucNumDPMLevels : 1), + GFP_KERNEL); + if (!rdev->pm.power_state[i].clock_info) + return state_index; + if (power_state->v2.ucNumDPMLevels) { + for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) { + clock_array_index = power_state->v2.clockInfoIndex[j]; + clock_info = (union pplib_clock_info *) + &clock_info_array->clockInfo[clock_array_index]; + valid = radeon_atombios_parse_pplib_clock_info(rdev, + state_index, mode_index, + clock_info); + if (valid) + mode_index++; + } + } else { + rdev->pm.power_state[state_index].clock_info[0].mclk = + rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[0].sclk = + rdev->clock.default_sclk; + mode_index++; } rdev->pm.power_state[state_index].num_clock_modes = mode_index; if (mode_index) { @@ -2526,6 +2519,7 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev) non_clock_info); state_index++; } + power_state_offset += 2 + power_state->v2.ucNumDPMLevels; } /* if multiple clock modes, mark the lowest as no display */ for (i = 0; i < state_index; i++) { @@ -2577,19 +2571,23 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev) if (state_index == 0) { rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state), GFP_KERNEL); if (rdev->pm.power_state) { - /* add the default mode */ - rdev->pm.power_state[state_index].type = - POWER_STATE_TYPE_DEFAULT; - rdev->pm.power_state[state_index].num_clock_modes = 1; - rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk; - rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk; - rdev->pm.power_state[state_index].default_clock_mode = - &rdev->pm.power_state[state_index].clock_info[0]; - rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; - rdev->pm.power_state[state_index].pcie_lanes = 16; - rdev->pm.default_power_state_index = state_index; - rdev->pm.power_state[state_index].flags = 0; - state_index++; + rdev->pm.power_state[0].clock_info = + kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL); + if (rdev->pm.power_state[0].clock_info) { + /* add the default mode */ + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.power_state[state_index].num_clock_modes = 1; + rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk; + rdev->pm.power_state[state_index].default_clock_mode = + &rdev->pm.power_state[state_index].clock_info[0]; + rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; + rdev->pm.power_state[state_index].pcie_lanes = 16; + rdev->pm.default_power_state_index = state_index; + rdev->pm.power_state[state_index].flags = 0; + state_index++; + } } } @@ -2744,6 +2742,10 @@ void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev) /* tell the bios not to handle mode switching */ bios_6_scratch |= ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH; + /* clear the vbios dpms state */ + if (ASIC_IS_DCE4(rdev)) + bios_2_scratch &= ~ATOM_S2_DEVICE_DPMS_STATE; + if (rdev->family >= CHIP_R600) { WREG32(R600_BIOS_2_SCRATCH, bios_2_scratch); WREG32(R600_BIOS_6_SCRATCH, bios_6_scratch); diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c index 9d95792..2a2cf0b 100644 --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -30,56 +30,8 @@ static struct radeon_atpx_priv { /* handle for device - and atpx */ acpi_handle dhandle; acpi_handle atpx_handle; - acpi_handle atrm_handle; } radeon_atpx_priv; -/* retrieve the ROM in 4k blocks */ -static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, - int offset, int len) -{ - acpi_status status; - union acpi_object atrm_arg_elements[2], *obj; - struct acpi_object_list atrm_arg; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; - - atrm_arg.count = 2; - atrm_arg.pointer = &atrm_arg_elements[0]; - - atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; - atrm_arg_elements[0].integer.value = offset; - - atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; - atrm_arg_elements[1].integer.value = len; - - status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); - if (ACPI_FAILURE(status)) { - printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); - return -ENODEV; - } - - obj = (union acpi_object *)buffer.pointer; - memcpy(bios+offset, obj->buffer.pointer, len); - kfree(buffer.pointer); - return len; -} - -bool radeon_atrm_supported(struct pci_dev *pdev) -{ - /* get the discrete ROM only via ATRM */ - if (!radeon_atpx_priv.atpx_detected) - return false; - - if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) - return false; - return true; -} - - -int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len) -{ - return radeon_atrm_call(radeon_atpx_priv.atrm_handle, bios, offset, len); -} - static int radeon_atpx_get_version(acpi_handle handle) { acpi_status status; @@ -197,7 +149,7 @@ static int radeon_atpx_power_state(enum vga_switcheroo_client_id id, static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev) { - acpi_handle dhandle, atpx_handle, atrm_handle; + acpi_handle dhandle, atpx_handle; acpi_status status; dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); @@ -208,13 +160,8 @@ static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev) if (ACPI_FAILURE(status)) return false; - status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); - if (ACPI_FAILURE(status)) - return false; - radeon_atpx_priv.dhandle = dhandle; radeon_atpx_priv.atpx_handle = atpx_handle; - radeon_atpx_priv.atrm_handle = atrm_handle; return true; } diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index 10191d9..441de38 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -26,21 +26,81 @@ #include "radeon_reg.h" #include "radeon.h" -void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize, - unsigned sdomain, unsigned ddomain) +#define RADEON_BENCHMARK_COPY_BLIT 1 +#define RADEON_BENCHMARK_COPY_DMA 0 + +#define RADEON_BENCHMARK_ITERATIONS 1024 +#define RADEON_BENCHMARK_COMMON_MODES_N 17 + +static int radeon_benchmark_do_move(struct radeon_device *rdev, unsigned size, + uint64_t saddr, uint64_t daddr, + int flag, int n) +{ + unsigned long start_jiffies; + unsigned long end_jiffies; + struct radeon_fence *fence = NULL; + int i, r; + + start_jiffies = jiffies; + for (i = 0; i < n; i++) { + r = radeon_fence_create(rdev, &fence); + if (r) + return r; + + switch (flag) { + case RADEON_BENCHMARK_COPY_DMA: + r = radeon_copy_dma(rdev, saddr, daddr, + size / RADEON_GPU_PAGE_SIZE, + fence); + break; + case RADEON_BENCHMARK_COPY_BLIT: + r = radeon_copy_blit(rdev, saddr, daddr, + size / RADEON_GPU_PAGE_SIZE, + fence); + break; + default: + DRM_ERROR("Unknown copy method\n"); + r = -EINVAL; + } + if (r) + goto exit_do_move; + r = radeon_fence_wait(fence, false); + if (r) + goto exit_do_move; + radeon_fence_unref(&fence); + } + end_jiffies = jiffies; + r = jiffies_to_msecs(end_jiffies - start_jiffies); + +exit_do_move: + if (fence) + radeon_fence_unref(&fence); + return r; +} + + +static void radeon_benchmark_log_results(int n, unsigned size, + unsigned int time, + unsigned sdomain, unsigned ddomain, + char *kind) +{ + unsigned int throughput = (n * (size >> 10)) / time; + DRM_INFO("radeon: %s %u bo moves of %u kB from" + " %d to %d in %u ms, throughput: %u Mb/s or %u MB/s\n", + kind, n, size >> 10, sdomain, ddomain, time, + throughput * 8, throughput); +} + +static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, + unsigned sdomain, unsigned ddomain) { struct radeon_bo *dobj = NULL; struct radeon_bo *sobj = NULL; - struct radeon_fence *fence = NULL; uint64_t saddr, daddr; - unsigned long start_jiffies; - unsigned long end_jiffies; - unsigned long time; - unsigned i, n, size; - int r; + int r, n; + int time; - size = bsize; - n = 1024; + n = RADEON_BENCHMARK_ITERATIONS; r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, &sobj); if (r) { goto out_cleanup; @@ -67,65 +127,28 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize, } /* r100 doesn't have dma engine so skip the test */ - if (rdev->asic->copy_dma) { - - start_jiffies = jiffies; - for (i = 0; i < n; i++) { - r = radeon_fence_create(rdev, &fence); - if (r) { - goto out_cleanup; - } - - r = radeon_copy_dma(rdev, saddr, daddr, - size / RADEON_GPU_PAGE_SIZE, fence); - - if (r) { - goto out_cleanup; - } - r = radeon_fence_wait(fence, false); - if (r) { - goto out_cleanup; - } - radeon_fence_unref(&fence); - } - end_jiffies = jiffies; - time = end_jiffies - start_jiffies; - time = jiffies_to_msecs(time); - if (time > 0) { - i = ((n * size) >> 10) / time; - printk(KERN_INFO "radeon: dma %u bo moves of %ukb from" - " %d to %d in %lums (%ukb/ms %ukb/s %uM/s)\n", - n, size >> 10, - sdomain, ddomain, time, - i, i * 1000, (i * 1000) / 1024); - } + /* also, VRAM-to-VRAM test doesn't make much sense for DMA */ + /* skip it as well if domains are the same */ + if ((rdev->asic->copy_dma) && (sdomain != ddomain)) { + time = radeon_benchmark_do_move(rdev, size, saddr, daddr, + RADEON_BENCHMARK_COPY_DMA, n); + if (time < 0) + goto out_cleanup; + if (time > 0) + radeon_benchmark_log_results(n, size, time, + sdomain, ddomain, "dma"); } - start_jiffies = jiffies; - for (i = 0; i < n; i++) { - r = radeon_fence_create(rdev, &fence); - if (r) { - goto out_cleanup; - } - r = radeon_copy_blit(rdev, saddr, daddr, size / RADEON_GPU_PAGE_SIZE, fence); - if (r) { - goto out_cleanup; - } - r = radeon_fence_wait(fence, false); - if (r) { + if (rdev->asic->copy_blit) { + time = radeon_benchmark_do_move(rdev, size, saddr, daddr, + RADEON_BENCHMARK_COPY_BLIT, n); + if (time < 0) goto out_cleanup; - } - radeon_fence_unref(&fence); - } - end_jiffies = jiffies; - time = end_jiffies - start_jiffies; - time = jiffies_to_msecs(time); - if (time > 0) { - i = ((n * size) >> 10) / time; - printk(KERN_INFO "radeon: blit %u bo moves of %ukb from %d to %d" - " in %lums (%ukb/ms %ukb/s %uM/s)\n", n, size >> 10, - sdomain, ddomain, time, i, i * 1000, (i * 1000) / 1024); + if (time > 0) + radeon_benchmark_log_results(n, size, time, + sdomain, ddomain, "blit"); } + out_cleanup: if (sobj) { r = radeon_bo_reserve(sobj, false); @@ -143,18 +166,92 @@ out_cleanup: } radeon_bo_unref(&dobj); } - if (fence) { - radeon_fence_unref(&fence); - } + if (r) { - printk(KERN_WARNING "Error while benchmarking BO move.\n"); + DRM_ERROR("Error while benchmarking BO move.\n"); } } -void radeon_benchmark(struct radeon_device *rdev) +void radeon_benchmark(struct radeon_device *rdev, int test_number) { - radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_GTT, - RADEON_GEM_DOMAIN_VRAM); - radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_VRAM, - RADEON_GEM_DOMAIN_GTT); + int i; + int common_modes[RADEON_BENCHMARK_COMMON_MODES_N] = { + 640 * 480 * 4, + 720 * 480 * 4, + 800 * 600 * 4, + 848 * 480 * 4, + 1024 * 768 * 4, + 1152 * 768 * 4, + 1280 * 720 * 4, + 1280 * 800 * 4, + 1280 * 854 * 4, + 1280 * 960 * 4, + 1280 * 1024 * 4, + 1440 * 900 * 4, + 1400 * 1050 * 4, + 1680 * 1050 * 4, + 1600 * 1200 * 4, + 1920 * 1080 * 4, + 1920 * 1200 * 4 + }; + + switch (test_number) { + case 1: + /* simple test, VRAM to GTT and GTT to VRAM */ + radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_GTT, + RADEON_GEM_DOMAIN_VRAM); + radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_GTT); + break; + case 2: + /* simple test, VRAM to VRAM */ + radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_VRAM); + break; + case 3: + /* GTT to VRAM, buffer size sweep, powers of 2 */ + for (i = 1; i <= 65536; i <<= 1) + radeon_benchmark_move(rdev, i*1024, + RADEON_GEM_DOMAIN_GTT, + RADEON_GEM_DOMAIN_VRAM); + break; + case 4: + /* VRAM to GTT, buffer size sweep, powers of 2 */ + for (i = 1; i <= 65536; i <<= 1) + radeon_benchmark_move(rdev, i*1024, + RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_GTT); + break; + case 5: + /* VRAM to VRAM, buffer size sweep, powers of 2 */ + for (i = 1; i <= 65536; i <<= 1) + radeon_benchmark_move(rdev, i*1024, + RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_VRAM); + break; + case 6: + /* GTT to VRAM, buffer size sweep, common modes */ + for (i = 1; i < RADEON_BENCHMARK_COMMON_MODES_N; i++) + radeon_benchmark_move(rdev, common_modes[i], + RADEON_GEM_DOMAIN_GTT, + RADEON_GEM_DOMAIN_VRAM); + break; + case 7: + /* VRAM to GTT, buffer size sweep, common modes */ + for (i = 1; i < RADEON_BENCHMARK_COMMON_MODES_N; i++) + radeon_benchmark_move(rdev, common_modes[i], + RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_GTT); + break; + case 8: + /* VRAM to VRAM, buffer size sweep, common modes */ + for (i = 1; i < RADEON_BENCHMARK_COMMON_MODES_N; i++) + radeon_benchmark_move(rdev, common_modes[i], + RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_VRAM); + break; + + default: + DRM_ERROR("Unknown benchmark\n"); + } } diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 229a20f..6e98703 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -32,6 +32,7 @@ #include <linux/vga_switcheroo.h> #include <linux/slab.h> +#include <linux/acpi.h> /* * BIOS. */ @@ -75,7 +76,7 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev) static bool radeon_read_bios(struct radeon_device *rdev) { - uint8_t __iomem *bios; + uint8_t __iomem *bios, val1, val2; size_t size; rdev->bios = NULL; @@ -85,29 +86,112 @@ static bool radeon_read_bios(struct radeon_device *rdev) return false; } - if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { + val1 = readb(&bios[0]); + val2 = readb(&bios[1]); + + if (size == 0 || val1 != 0x55 || val2 != 0xaa) { pci_unmap_rom(rdev->pdev, bios); return false; } - rdev->bios = kmemdup(bios, size, GFP_KERNEL); + rdev->bios = kzalloc(size, GFP_KERNEL); if (rdev->bios == NULL) { pci_unmap_rom(rdev->pdev, bios); return false; } + memcpy_fromio(rdev->bios, bios, size); pci_unmap_rom(rdev->pdev, bios); return true; } +#ifdef CONFIG_ACPI /* ATRM is used to get the BIOS on the discrete cards in * dual-gpu systems. */ +/* retrieve the ROM in 4k blocks */ +#define ATRM_BIOS_PAGE 4096 +/** + * radeon_atrm_call - fetch a chunk of the vbios + * + * @atrm_handle: acpi ATRM handle + * @bios: vbios image pointer + * @offset: offset of vbios image data to fetch + * @len: length of vbios image data to fetch + * + * Executes ATRM to fetch a chunk of the discrete + * vbios image on PX systems (all asics). + * Returns the length of the buffer fetched. + */ +static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, + int offset, int len) +{ + acpi_status status; + union acpi_object atrm_arg_elements[2], *obj; + struct acpi_object_list atrm_arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; + + atrm_arg.count = 2; + atrm_arg.pointer = &atrm_arg_elements[0]; + + atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; + atrm_arg_elements[0].integer.value = offset; + + atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; + atrm_arg_elements[1].integer.value = len; + + status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); + if (ACPI_FAILURE(status)) { + printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); + return -ENODEV; + } + + obj = (union acpi_object *)buffer.pointer; + memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); + len = obj->buffer.length; + kfree(buffer.pointer); + return len; +} + static bool radeon_atrm_get_bios(struct radeon_device *rdev) { int ret; int size = 256 * 1024; int i; + struct pci_dev *pdev = NULL; + acpi_handle dhandle, atrm_handle; + acpi_status status; + bool found = false; + + /* ATRM is for the discrete card only */ + if (rdev->flags & RADEON_IS_IGP) + return false; + + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { + dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); + if (!dhandle) + continue; + + status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); + if (!ACPI_FAILURE(status)) { + found = true; + break; + } + } + + if (!found) { + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { + dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); + if (!dhandle) + continue; + + status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); + if (!ACPI_FAILURE(status)) { + found = true; + break; + } + } + } - if (!radeon_atrm_supported(rdev->pdev)) + if (!found) return false; rdev->bios = kmalloc(size, GFP_KERNEL); @@ -117,10 +201,11 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev) } for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { - ret = radeon_atrm_get_bios_chunk(rdev->bios, - (i * ATRM_BIOS_PAGE), - ATRM_BIOS_PAGE); - if (ret <= 0) + ret = radeon_atrm_call(atrm_handle, + rdev->bios, + (i * ATRM_BIOS_PAGE), + ATRM_BIOS_PAGE); + if (ret < ATRM_BIOS_PAGE) break; } @@ -130,6 +215,12 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev) } return true; } +#else +static inline bool radeon_atrm_get_bios(struct radeon_device *rdev) +{ + return false; +} +#endif static bool ni_read_disabled_bios(struct radeon_device *rdev) { @@ -476,6 +567,61 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev) return legacy_read_disabled_bios(rdev); } +#ifdef CONFIG_ACPI +static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) +{ + bool ret = false; + struct acpi_table_header *hdr; + acpi_size tbl_size; + UEFI_ACPI_VFCT *vfct; + GOP_VBIOS_CONTENT *vbios; + VFCT_IMAGE_HEADER *vhdr; + + if (!ACPI_SUCCESS(acpi_get_table_with_size("VFCT", 1, &hdr, &tbl_size))) + return false; + if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { + DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); + goto out_unmap; + } + + vfct = (UEFI_ACPI_VFCT *)hdr; + if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { + DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); + goto out_unmap; + } + + vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); + vhdr = &vbios->VbiosHeader; + DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", + vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction, + vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength); + + if (vhdr->PCIBus != rdev->pdev->bus->number || + vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) || + vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn) || + vhdr->VendorID != rdev->pdev->vendor || + vhdr->DeviceID != rdev->pdev->device) { + DRM_INFO("ACPI VFCT table is not for this card\n"); + goto out_unmap; + }; + + if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { + DRM_ERROR("ACPI VFCT image truncated\n"); + goto out_unmap; + } + + rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); + ret = !!rdev->bios; + +out_unmap: + return ret; +} +#else +static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev) +{ + return false; +} +#endif bool radeon_get_bios(struct radeon_device *rdev) { @@ -484,6 +630,8 @@ bool radeon_get_bios(struct radeon_device *rdev) r = radeon_atrm_get_bios(rdev); if (r == false) + r = radeon_acpi_vfct_bios(rdev); + if (r == false) r = igp_read_bios_from_vram(rdev); if (r == false) r = radeon_read_bios(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c index b956cf1..b6e18c8 100644 --- a/drivers/gpu/drm/radeon/radeon_clocks.c +++ b/drivers/gpu/drm/radeon/radeon_clocks.c @@ -96,7 +96,7 @@ uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev) * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device * tree. Hopefully, ATI OF driver is kind enough to fill these */ -static bool __devinit radeon_read_clocks_OF(struct drm_device *dev) +static bool radeon_read_clocks_OF(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; struct device_node *dp = rdev->pdev->dev.of_node; @@ -166,7 +166,7 @@ static bool __devinit radeon_read_clocks_OF(struct drm_device *dev) return true; } #else -static bool __devinit radeon_read_clocks_OF(struct drm_device *dev) +static bool radeon_read_clocks_OF(struct drm_device *dev) { return false; } diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index f616e40..87a677e 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -898,10 +898,14 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct } /* quirks */ + /* Radeon 7000 (RV100) */ + if (((dev->pdev->device == 0x5159) && + (dev->pdev->subsystem_vendor == 0x174B) && + (dev->pdev->subsystem_device == 0x7c28)) || /* Radeon 9100 (R200) */ - if ((dev->pdev->device == 0x514D) && + ((dev->pdev->device == 0x514D) && (dev->pdev->subsystem_vendor == 0x174B) && - (dev->pdev->subsystem_device == 0x7149)) { + (dev->pdev->subsystem_device == 0x7149))) { /* vbios value is bad, use the default */ found = 0; } @@ -1255,10 +1259,15 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder if ((RBIOS16(tmp) == lvds->native_mode.hdisplay) && (RBIOS16(tmp + 2) == lvds->native_mode.vdisplay)) { + u32 hss = (RBIOS16(tmp + 21) - RBIOS16(tmp + 19) - 1) * 8; + + if (hss > lvds->native_mode.hdisplay) + hss = (10 - 1) * 8; + lvds->native_mode.htotal = lvds->native_mode.hdisplay + (RBIOS16(tmp + 17) - RBIOS16(tmp + 19)) * 8; lvds->native_mode.hsync_start = lvds->native_mode.hdisplay + - (RBIOS16(tmp + 21) - RBIOS16(tmp + 19) - 1) * 8; + hss; lvds->native_mode.hsync_end = lvds->native_mode.hsync_start + (RBIOS8(tmp + 23) * 8); @@ -1484,6 +1493,9 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) of_machine_is_compatible("PowerBook6,7")) { /* ibook */ rdev->mode_info.connector_table = CT_IBOOK; + } else if (of_machine_is_compatible("PowerMac3,5")) { + /* PowerMac G4 Silver radeon 7500 */ + rdev->mode_info.connector_table = CT_MAC_G4_SILVER; } else if (of_machine_is_compatible("PowerMac4,4")) { /* emac */ rdev->mode_info.connector_table = CT_EMAC; @@ -1509,6 +1521,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) (rdev->pdev->subsystem_device == 0x4150)) { /* Mac G5 tower 9600 */ rdev->mode_info.connector_table = CT_MAC_G5_9600; + } else if ((rdev->pdev->device == 0x4c66) && + (rdev->pdev->subsystem_vendor == 0x1002) && + (rdev->pdev->subsystem_device == 0x4c66)) { + /* SAM440ep RV250 embedded board */ + rdev->mode_info.connector_table = CT_SAM440EP; } else #endif /* CONFIG_PPC_PMAC */ #ifdef CONFIG_PPC64 @@ -2082,6 +2099,115 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) CONNECTOR_OBJECT_ID_SVIDEO, &hpd); break; + case CT_SAM440EP: + DRM_INFO("Connector Table: %d (SAM440ep embedded board)\n", + rdev->mode_info.connector_table); + /* LVDS */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_NONE_DETECTED, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_LCD1_SUPPORT, + 0), + ATOM_DEVICE_LCD1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS, + &hpd); + /* DVI-I - secondary dac, int tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 1, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 2, + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 3, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_MAC_G4_SILVER: + DRM_INFO("Connector Table: %d (mac g4 silver)\n", + rdev->mode_info.connector_table); + /* DVI-I - tv dac, int tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; default: DRM_INFO("Connector table: %d (invalid)\n", rdev->mode_info.connector_table); @@ -2513,25 +2639,28 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev) u16 offset, misc, misc2 = 0; u8 rev, blocks, tmp; int state_index = 0; + struct radeon_i2c_bus_rec i2c_bus; rdev->pm.default_power_state_index = -1; /* allocate 2 power states */ rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * 2, GFP_KERNEL); - if (!rdev->pm.power_state) { - rdev->pm.default_power_state_index = state_index; - rdev->pm.num_power_states = 0; - - rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; - rdev->pm.current_clock_mode_index = 0; - return; - } + if (rdev->pm.power_state) { + /* allocate 1 clock mode per state */ + rdev->pm.power_state[0].clock_info = + kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL); + rdev->pm.power_state[1].clock_info = + kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL); + if (!rdev->pm.power_state[0].clock_info || + !rdev->pm.power_state[1].clock_info) + goto pm_failed; + } else + goto pm_failed; /* check for a thermal chip */ offset = combios_get_table_offset(dev, COMBIOS_OVERDRIVE_INFO_TABLE); if (offset) { u8 thermal_controller = 0, gpio = 0, i2c_addr = 0, clk_bit = 0, data_bit = 0; - struct radeon_i2c_bus_rec i2c_bus; rev = RBIOS8(offset); @@ -2573,6 +2702,25 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev) i2c_new_device(&rdev->pm.i2c_bus->adapter, &info); } } + } else { + /* boards with a thermal chip, but no overdrive table */ + + /* Asus 9600xt has an f75375 on the monid bus */ + if ((dev->pdev->device == 0x4152) && + (dev->pdev->subsystem_vendor == 0x1043) && + (dev->pdev->subsystem_device == 0xc002)) { + i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); + if (rdev->pm.i2c_bus) { + struct i2c_board_info info = { }; + const char *name = "f75375"; + info.addr = 0x28; + strlcpy(info.type, name, sizeof(info.type)); + i2c_new_device(&rdev->pm.i2c_bus->adapter, &info); + DRM_INFO("Possible %s thermal controller at 0x%02x\n", + name, info.addr); + } + } } if (rdev->flags & RADEON_IS_MOBILITY) { @@ -2672,6 +2820,14 @@ default_mode: rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; rdev->pm.current_clock_mode_index = 0; + return; + +pm_failed: + rdev->pm.default_power_state_index = state_index; + rdev->pm.num_power_states = 0; + + rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.current_clock_mode_index = 0; } void radeon_external_tmds_setup(struct drm_encoder *encoder) @@ -3243,6 +3399,14 @@ void radeon_combios_asic_init(struct drm_device *dev) rdev->pdev->subsystem_device == 0x30ae) return; + /* quirk for rs4xx HP Compaq dc5750 Small Form Factor to make it resume + * - it hangs on resume inside the dynclk 1 table. + */ + if (rdev->family == CHIP_RS480 && + rdev->pdev->subsystem_vendor == 0x103c && + rdev->pdev->subsystem_device == 0x280a) + return; + /* DYN CLK 1 */ table = combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE); if (table) diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index f1a1e8a..6303fc8 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -44,8 +44,6 @@ extern void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder, struct drm_connector *drm_connector); -bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector); - void radeon_connector_hotplug(struct drm_connector *connector) { struct drm_device *dev = connector->dev; @@ -84,6 +82,11 @@ void radeon_connector_hotplug(struct drm_connector *connector) if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); } else if (radeon_dp_needs_link_train(radeon_connector)) { + /* Don't try to start link training before we + * have the dpcd */ + if (!radeon_dp_getdpcd(radeon_connector)) + return; + /* set it to OFF so that drm_helper_connector_dpms() * won't return immediately since the current state * is ON at this point. @@ -451,65 +454,24 @@ int radeon_connector_set_property(struct drm_connector *connector, struct drm_pr return 0; } -/* - * Some integrated ATI Radeon chipset implementations (e. g. - * Asus M2A-VM HDMI) may indicate the availability of a DDC, - * even when there's no monitor connected. For these connectors - * following DDC probe extension will be applied: check also for the - * availability of EDID with at least a correct EDID header. Only then, - * DDC is assumed to be available. This prevents drm_get_edid() and - * drm_edid_block_valid() from periodically dumping data and kernel - * errors into the logs and onto the terminal. - */ -static bool radeon_connector_needs_extended_probe(struct radeon_device *dev, - uint32_t supported_device, - int connector_type) -{ - /* Asus M2A-VM HDMI board sends data to i2c bus even, - * if HDMI add-on card is not plugged in or HDMI is disabled in - * BIOS. Valid DDC can only be assumed, if also a valid EDID header - * can be retrieved via i2c bus during DDC probe */ - if ((dev->pdev->device == 0x791e) && - (dev->pdev->subsystem_vendor == 0x1043) && - (dev->pdev->subsystem_device == 0x826d)) { - if ((connector_type == DRM_MODE_CONNECTOR_HDMIA) && - (supported_device == ATOM_DEVICE_DFP2_SUPPORT)) - return true; - } - /* ECS A740GM-M with ATI RADEON 2100 sends data to i2c bus - * for a DVI connector that is not implemented */ - if ((dev->pdev->device == 0x796e) && - (dev->pdev->subsystem_vendor == 0x1019) && - (dev->pdev->subsystem_device == 0x2615)) { - if ((connector_type == DRM_MODE_CONNECTOR_DVID) && - (supported_device == ATOM_DEVICE_DFP2_SUPPORT)) - return true; - } - /* TOSHIBA Satellite L300D with ATI Mobility Radeon x1100 - * (RS690M) sends data to i2c bus for a HDMI connector that - * is not implemented */ - if ((dev->pdev->device == 0x791f) && - (dev->pdev->subsystem_vendor == 0x1179) && - (dev->pdev->subsystem_device == 0xff68)) { - if ((connector_type == DRM_MODE_CONNECTOR_HDMIA) && - (supported_device == ATOM_DEVICE_DFP2_SUPPORT)) - return true; - } - - /* Default: no EDID header probe required for DDC probing */ - return false; -} - static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder, struct drm_connector *connector) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + struct drm_display_mode *t, *mode; + + /* If the EDID preferred mode doesn't match the native mode, use it */ + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + if (mode->hdisplay != native_mode->hdisplay || + mode->vdisplay != native_mode->vdisplay) + memcpy(native_mode, mode, sizeof(*mode)); + } + } /* Try to get native mode details from EDID if necessary */ if (!native_mode->clock) { - struct drm_display_mode *t, *mode; - list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { if (mode->hdisplay == native_mode->hdisplay && mode->vdisplay == native_mode->vdisplay) { @@ -520,6 +482,7 @@ static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder, } } } + if (!native_mode->clock) { DRM_DEBUG_KMS("No LVDS native mode details, disabling RMX\n"); radeon_encoder->rmx_type = RMX_OFF; @@ -731,8 +694,7 @@ radeon_vga_detect(struct drm_connector *connector, bool force) ret = connector_status_disconnected; if (radeon_connector->ddc_bus) - dret = radeon_ddc_probe(radeon_connector, - radeon_connector->requires_extended_probe); + dret = radeon_ddc_probe(radeon_connector, false); if (dret) { radeon_connector->detected_by_load = false; if (radeon_connector->edid) { @@ -914,8 +876,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) bool dret = false; if (radeon_connector->ddc_bus) - dret = radeon_ddc_probe(radeon_connector, - radeon_connector->requires_extended_probe); + dret = radeon_ddc_probe(radeon_connector, false); if (dret) { radeon_connector->detected_by_load = false; if (radeon_connector->edid) { @@ -1218,7 +1179,8 @@ static int radeon_dp_get_modes(struct drm_connector *connector) } } else { /* need to setup ddc on the bridge */ - if (radeon_connector_encoder_is_dp_bridge(connector)) { + if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) != + ENCODER_OBJECT_ID_NONE) { if (encoder) radeon_atom_ext_encoder_setup_ddc(encoder); } @@ -1228,13 +1190,12 @@ static int radeon_dp_get_modes(struct drm_connector *connector) return ret; } -bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector) +u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector) { struct drm_mode_object *obj; struct drm_encoder *encoder; struct radeon_encoder *radeon_encoder; int i; - bool found = false; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] == 0) @@ -1250,14 +1211,13 @@ bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector) switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_TRAVIS: case ENCODER_OBJECT_ID_NUTMEG: - found = true; - break; + return radeon_encoder->encoder_id; default: break; } } - return found; + return ENCODER_OBJECT_ID_NONE; } bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector) @@ -1291,7 +1251,7 @@ bool radeon_connector_is_dp12_capable(struct drm_connector *connector) struct radeon_device *rdev = dev->dev_private; if (ASIC_IS_DCE5(rdev) && - (rdev->clock.dp_extclk >= 53900) && + (rdev->clock.default_dispclk >= 53900) && radeon_connector_encoder_is_hbr2(connector)) { return true; } @@ -1334,12 +1294,25 @@ radeon_dp_detect(struct drm_connector *connector, bool force) if (!radeon_dig_connector->edp_on) atombios_set_edp_panel_power(connector, ATOM_TRANSMITTER_ACTION_POWER_OFF); - } else { - /* need to setup ddc on the bridge */ - if (radeon_connector_encoder_is_dp_bridge(connector)) { - if (encoder) - radeon_atom_ext_encoder_setup_ddc(encoder); + } else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) != + ENCODER_OBJECT_ID_NONE) { + /* DP bridges are always DP */ + radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; + /* get the DPCD from the bridge */ + radeon_dp_getdpcd(radeon_connector); + + if (encoder) { + /* setup ddc on the bridge */ + radeon_atom_ext_encoder_setup_ddc(encoder); + /* bridge chips are always aux */ + if (radeon_ddc_probe(radeon_connector, true)) /* try DDC */ + ret = connector_status_connected; + else if (radeon_connector->dac_load_detect) { /* try load detection */ + struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + ret = encoder_funcs->detect(encoder, connector); + } } + } else { radeon_dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector); if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) { ret = connector_status_connected; @@ -1350,21 +1323,11 @@ radeon_dp_detect(struct drm_connector *connector, bool force) if (radeon_dp_getdpcd(radeon_connector)) ret = connector_status_connected; } else { - if (radeon_ddc_probe(radeon_connector, - radeon_connector->requires_extended_probe)) + /* try non-aux ddc (DP to DVI/HMDI/etc. adapter) */ + if (radeon_ddc_probe(radeon_connector, false)) ret = connector_status_connected; } } - - if ((ret == connector_status_disconnected) && - radeon_connector->dac_load_detect) { - struct drm_encoder *encoder = radeon_best_single_encoder(connector); - struct drm_encoder_helper_funcs *encoder_funcs; - if (encoder) { - encoder_funcs = encoder->helper_private; - ret = encoder_funcs->detect(encoder, connector); - } - } } radeon_connector_update_scratch_regs(connector, ret); @@ -1429,6 +1392,24 @@ struct drm_connector_funcs radeon_dp_connector_funcs = { .force = radeon_dvi_force, }; +static const struct drm_connector_funcs radeon_edp_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_dp_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = radeon_lvds_set_property, + .destroy = radeon_dp_connector_destroy, + .force = radeon_dvi_force, +}; + +static const struct drm_connector_funcs radeon_lvds_bridge_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_dp_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = radeon_lvds_set_property, + .destroy = radeon_dp_connector_destroy, + .force = radeon_dvi_force, +}; + void radeon_add_atom_connector(struct drm_device *dev, uint32_t connector_id, @@ -1506,9 +1487,7 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->shared_ddc = shared_ddc; radeon_connector->connector_object_id = connector_object_id; radeon_connector->hpd = *hpd; - radeon_connector->requires_extended_probe = - radeon_connector_needs_extended_probe(rdev, supported_device, - connector_type); + radeon_connector->router = *router; if (router->ddc_valid || router->cd_valid) { radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info); @@ -1522,8 +1501,6 @@ radeon_add_atom_connector(struct drm_device *dev, goto failed; radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; - drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); if (i2c_bus->valid) { /* add DP i2c bus */ if (connector_type == DRM_MODE_CONNECTOR_eDP) @@ -1540,6 +1517,10 @@ radeon_add_atom_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_VGA: case DRM_MODE_CONNECTOR_DVIA: default: + drm_connector_init(dev, &radeon_connector->base, + &radeon_dp_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, + &radeon_dp_connector_helper_funcs); connector->interlace_allowed = true; connector->doublescan_allowed = true; radeon_connector->dac_load_detect = true; @@ -1552,6 +1533,10 @@ radeon_add_atom_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_HDMIA: case DRM_MODE_CONNECTOR_HDMIB: case DRM_MODE_CONNECTOR_DisplayPort: + drm_connector_init(dev, &radeon_connector->base, + &radeon_dp_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, + &radeon_dp_connector_helper_funcs); drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.underscan_property, UNDERSCAN_OFF); @@ -1576,6 +1561,10 @@ radeon_add_atom_connector(struct drm_device *dev, break; case DRM_MODE_CONNECTOR_LVDS: case DRM_MODE_CONNECTOR_eDP: + drm_connector_init(dev, &radeon_connector->base, + &radeon_lvds_bridge_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, + &radeon_dp_connector_helper_funcs); drm_connector_attach_property(&radeon_connector->base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); @@ -1739,7 +1728,7 @@ radeon_add_atom_connector(struct drm_device *dev, goto failed; radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; - drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); + drm_connector_init(dev, &radeon_connector->base, &radeon_edp_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); if (i2c_bus->valid) { /* add DP i2c bus */ @@ -1855,9 +1844,7 @@ radeon_add_legacy_connector(struct drm_device *dev, radeon_connector->devices = supported_device; radeon_connector->connector_object_id = connector_object_id; radeon_connector->hpd = *hpd; - radeon_connector->requires_extended_probe = - radeon_connector_needs_extended_probe(rdev, supported_device, - connector_type); + switch (connector_type) { case DRM_MODE_CONNECTOR_VGA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index 7586779..72ae826 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -29,6 +29,8 @@ * Gareth Hughes <gareth@valinux.com> */ +#include <linux/module.h> + #include "drmP.h" #include "drm.h" #include "drm_sarea.h" @@ -2115,7 +2117,7 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) if (drm_pci_device_is_agp(dev)) dev_priv->flags |= RADEON_IS_AGP; - else if (drm_pci_device_is_pcie(dev)) + else if (pci_is_pcie(dev->pdev)) dev_priv->flags |= RADEON_IS_PCIE; else dev_priv->flags |= RADEON_IS_PCI; diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index fae00c0..0ddc08c 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -93,13 +93,15 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) { struct drm_radeon_cs *cs = data; uint64_t *chunk_array_ptr; - unsigned size, i; + unsigned size, i, flags = 0; + + INIT_LIST_HEAD(&p->validated); if (!cs->num_chunks) { return 0; } + /* get chunks */ - INIT_LIST_HEAD(&p->validated); p->idx = 0; p->chunk_ib_idx = -1; p->chunk_relocs_idx = -1; @@ -140,6 +142,10 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) if (p->chunks[i].length_dw == 0) return -EINVAL; } + if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS && + !p->chunks[i].length_dw) { + return -EINVAL; + } p->chunks[i].length_dw = user_chunk.length_dw; p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data; @@ -155,12 +161,17 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) p->chunks[i].user_ptr, size)) { return -EFAULT; } + if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) { + flags = p->chunks[i].kdata[0]; + } } else { p->chunks[i].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL); p->chunks[i].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL); if (p->chunks[i].kpage[0] == NULL || p->chunks[i].kpage[1] == NULL) { kfree(p->chunks[i].kpage[0]); kfree(p->chunks[i].kpage[1]); + p->chunks[i].kpage[0] = NULL; + p->chunks[i].kpage[1] = NULL; return -ENOMEM; } p->chunks[i].kpage_idx[0] = -1; @@ -174,6 +185,8 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) p->chunks[p->chunk_ib_idx].length_dw); return -EINVAL; } + + p->keep_tiling_flags = (flags & RADEON_CS_KEEP_TILING_FLAGS) != 0; return 0; } @@ -222,7 +235,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) struct radeon_cs_chunk *ib_chunk; int r; - mutex_lock(&rdev->cs_mutex); + radeon_mutex_lock(&rdev->cs_mutex); /* initialize parser */ memset(&parser, 0, sizeof(struct radeon_cs_parser)); parser.filp = filp; @@ -233,14 +246,14 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) if (r) { DRM_ERROR("Failed to initialize parser !\n"); radeon_cs_parser_fini(&parser, r); - mutex_unlock(&rdev->cs_mutex); + radeon_mutex_unlock(&rdev->cs_mutex); return r; } r = radeon_ib_get(rdev, &parser.ib); if (r) { DRM_ERROR("Failed to get ib !\n"); radeon_cs_parser_fini(&parser, r); - mutex_unlock(&rdev->cs_mutex); + radeon_mutex_unlock(&rdev->cs_mutex); return r; } r = radeon_cs_parser_relocs(&parser); @@ -248,7 +261,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) if (r != -ERESTARTSYS) DRM_ERROR("Failed to parse relocation %d!\n", r); radeon_cs_parser_fini(&parser, r); - mutex_unlock(&rdev->cs_mutex); + radeon_mutex_unlock(&rdev->cs_mutex); return r; } /* Copy the packet into the IB, the parser will read from the @@ -260,14 +273,14 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) if (r || parser.parser_error) { DRM_ERROR("Invalid command stream !\n"); radeon_cs_parser_fini(&parser, r); - mutex_unlock(&rdev->cs_mutex); + radeon_mutex_unlock(&rdev->cs_mutex); return r; } r = radeon_cs_finish_pages(&parser); if (r) { DRM_ERROR("Invalid command stream !\n"); radeon_cs_parser_fini(&parser, r); - mutex_unlock(&rdev->cs_mutex); + radeon_mutex_unlock(&rdev->cs_mutex); return r; } r = radeon_ib_schedule(rdev, parser.ib); @@ -275,7 +288,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) DRM_ERROR("Failed to schedule IB !\n"); } radeon_cs_parser_fini(&parser, r); - mutex_unlock(&rdev->cs_mutex); + radeon_mutex_unlock(&rdev->cs_mutex); return r; } diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index 72f749d..2132109 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -224,14 +224,14 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc, } DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); - if (x < 0) - xorigin = -x + 1; - if (y < 0) - yorigin = -y + 1; - if (xorigin >= CURSOR_WIDTH) - xorigin = CURSOR_WIDTH - 1; - if (yorigin >= CURSOR_HEIGHT) - yorigin = CURSOR_HEIGHT - 1; + if (x < 0) { + xorigin = min(-x, CURSOR_WIDTH - 1); + x = 0; + } + if (y < 0) { + yorigin = min(-y, CURSOR_HEIGHT - 1); + y = 0; + } if (ASIC_IS_AVIVO(rdev)) { int i = 0; @@ -270,16 +270,12 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc, radeon_lock_cursor(crtc, true); if (ASIC_IS_DCE4(rdev)) { - WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, - ((xorigin ? 0 : x) << 16) | - (yorigin ? 0 : y)); + WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); WREG32(EVERGREEN_CUR_SIZE + radeon_crtc->crtc_offset, ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); } else if (ASIC_IS_AVIVO(rdev)) { - WREG32(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset, - ((xorigin ? 0 : x) << 16) | - (yorigin ? 0 : y)); + WREG32(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); WREG32(AVIVO_D1CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset, ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); @@ -293,8 +289,8 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc, | yorigin)); WREG32(RADEON_CUR_HORZ_VERT_POSN + radeon_crtc->crtc_offset, (RADEON_CUR_LOCK - | ((xorigin ? 0 : x) << 16) - | (yorigin ? 0 : y))); + | (x << 16) + | y)); /* offset is from DISP(2)_BASE_ADDRESS */ WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, (radeon_crtc->legacy_cursor_offset + (yorigin * 256))); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index a275cf6..8cde84b 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -32,6 +32,7 @@ #include <drm/radeon_drm.h> #include <linux/vgaarb.h> #include <linux/vga_switcheroo.h> +#include <linux/efi.h> #include "radeon_reg.h" #include "radeon.h" #include "atom.h" @@ -303,6 +304,8 @@ void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 mc->mc_vram_size = mc->aper_size; } mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; + if (radeon_vram_limit && radeon_vram_limit < mc->real_vram_size) + mc->real_vram_size = radeon_vram_limit; dev_info(rdev->dev, "VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used)\n", mc->mc_vram_size >> 20, mc->vram_start, mc->vram_end, mc->real_vram_size >> 20); @@ -351,6 +354,10 @@ bool radeon_card_posted(struct radeon_device *rdev) { uint32_t reg; + if (efi_enabled(EFI_BOOT) && + rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) + return false; + /* first check CRTCs */ if (ASIC_IS_DCE4(rdev)) { reg = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) | @@ -712,7 +719,7 @@ int radeon_device_init(struct radeon_device *rdev, /* mutex initialization are all done here so we * can recall function without having locking issues */ - mutex_init(&rdev->cs_mutex); + radeon_mutex_init(&rdev->cs_mutex); mutex_init(&rdev->ib_pool.mutex); mutex_init(&rdev->cp.mutex); mutex_init(&rdev->dc_hw_i2c_mutex); @@ -746,14 +753,15 @@ int radeon_device_init(struct radeon_device *rdev, /* set DMA mask + need_dma32 flags. * PCIE - can handle 40-bits. - * IGP - can handle 40-bits (in theory) + * IGP - can handle 40-bits * AGP - generally dma32 is safest - * PCI - only dma32 + * PCI - dma32 for legacy pci gart, 40 bits on newer asics */ rdev->need_dma32 = false; if (rdev->flags & RADEON_IS_AGP) rdev->need_dma32 = true; - if (rdev->flags & RADEON_IS_PCI) + if ((rdev->flags & RADEON_IS_PCI) && + (rdev->family <= CHIP_RS740)) rdev->need_dma32 = true; dma_bits = rdev->need_dma32 ? 32 : 40; @@ -810,10 +818,16 @@ int radeon_device_init(struct radeon_device *rdev, return r; } if (radeon_testing) { - radeon_test_moves(rdev); + if (rdev->accel_working) + radeon_test_moves(rdev); + else + DRM_INFO("radeon: acceleration disabled, skipping move tests\n"); } if (radeon_benchmarking) { - radeon_benchmark(rdev); + if (rdev->accel_working) + radeon_benchmark(rdev, radeon_benchmarking); + else + DRM_INFO("radeon: acceleration disabled, skipping benchmarks\n"); } return 0; } @@ -954,6 +968,9 @@ int radeon_gpu_reset(struct radeon_device *rdev) int r; int resched; + /* Prevent CS ioctl from interfering */ + radeon_mutex_lock(&rdev->cs_mutex); + radeon_save_bios_scratch_regs(rdev); /* block TTM */ resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); @@ -966,10 +983,15 @@ int radeon_gpu_reset(struct radeon_device *rdev) radeon_restore_bios_scratch_regs(rdev); drm_helper_resume_force_mode(rdev->ddev); ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); - return 0; } - /* bad news, how to tell it to userspace ? */ - dev_info(rdev->dev, "GPU reset failed\n"); + + radeon_mutex_unlock(&rdev->cs_mutex); + + if (r) { + /* bad news, how to tell it to userspace ? */ + dev_info(rdev->dev, "GPU reset failed\n"); + } + return r; } @@ -981,7 +1003,7 @@ struct radeon_debugfs { struct drm_info_list *files; unsigned num_files; }; -static struct radeon_debugfs _radeon_debugfs[RADEON_DEBUGFS_MAX_NUM_FILES]; +static struct radeon_debugfs _radeon_debugfs[RADEON_DEBUGFS_MAX_COMPONENTS]; static unsigned _radeon_debugfs_count = 0; int radeon_debugfs_add_files(struct radeon_device *rdev, @@ -996,14 +1018,17 @@ int radeon_debugfs_add_files(struct radeon_device *rdev, return 0; } } - if ((_radeon_debugfs_count + nfiles) > RADEON_DEBUGFS_MAX_NUM_FILES) { - DRM_ERROR("Reached maximum number of debugfs files.\n"); - DRM_ERROR("Report so we increase RADEON_DEBUGFS_MAX_NUM_FILES.\n"); + + i = _radeon_debugfs_count + 1; + if (i > RADEON_DEBUGFS_MAX_COMPONENTS) { + DRM_ERROR("Reached maximum number of debugfs components.\n"); + DRM_ERROR("Report so we increase " + "RADEON_DEBUGFS_MAX_COMPONENTS.\n"); return -EINVAL; } _radeon_debugfs[_radeon_debugfs_count].files = files; _radeon_debugfs[_radeon_debugfs_count].num_files = nfiles; - _radeon_debugfs_count++; + _radeon_debugfs_count = i; #if defined(CONFIG_DEBUG_FS) drm_debugfs_create_files(files, nfiles, rdev->ddev->control->debugfs_root, diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 0896fae..ad5d774 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -33,8 +33,6 @@ #include "drm_crtc_helper.h" #include "drm_edid.h" -static int radeon_ddc_dump(struct drm_connector *connector); - static void avivo_crtc_load_lut(struct drm_crtc *crtc) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); @@ -282,7 +280,7 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) spin_lock_irqsave(&rdev->ddev->event_lock, flags); work = radeon_crtc->unpin_work; if (work == NULL || - !radeon_fence_signaled(work->fence)) { + (work->fence && !radeon_fence_signaled(work->fence))) { spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); return; } @@ -348,7 +346,6 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, struct radeon_framebuffer *new_radeon_fb; struct drm_gem_object *obj; struct radeon_bo *rbo; - struct radeon_fence *fence; struct radeon_unpin_work *work; unsigned long flags; u32 tiling_flags, pitch_pixels; @@ -359,16 +356,9 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, if (work == NULL) return -ENOMEM; - r = radeon_fence_create(rdev, &fence); - if (unlikely(r != 0)) { - kfree(work); - DRM_ERROR("flip queue: failed to create fence.\n"); - return -ENOMEM; - } work->event = event; work->rdev = rdev; work->crtc_id = radeon_crtc->crtc_id; - work->fence = radeon_fence_ref(fence); old_radeon_fb = to_radeon_framebuffer(crtc->fb); new_radeon_fb = to_radeon_framebuffer(fb); /* schedule unpin of the old buffer */ @@ -377,6 +367,10 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, drm_gem_object_reference(obj); rbo = gem_to_radeon_bo(obj); work->old_rbo = rbo; + obj = new_radeon_fb->obj; + rbo = gem_to_radeon_bo(obj); + if (rbo->tbo.sync_obj) + work->fence = radeon_fence_ref(rbo->tbo.sync_obj); INIT_WORK(&work->work, radeon_unpin_work_func); /* We borrow the event spin lock for protecting unpin_work */ @@ -391,9 +385,6 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, spin_unlock_irqrestore(&dev->event_lock, flags); /* pin the new buffer */ - obj = new_radeon_fb->obj; - rbo = gem_to_radeon_bo(obj); - DRM_DEBUG_DRIVER("flip-ioctl() cur_fbo = %p, cur_bbo = %p\n", work->old_rbo, rbo); @@ -461,37 +452,18 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, goto pflip_cleanup1; } - /* 32 ought to cover us */ - r = radeon_ring_lock(rdev, 32); - if (r) { - DRM_ERROR("failed to lock the ring before flip\n"); - goto pflip_cleanup2; - } - - /* emit the fence */ - radeon_fence_emit(rdev, fence); /* set the proper interrupt */ radeon_pre_page_flip(rdev, radeon_crtc->crtc_id); - /* fire the ring */ - radeon_ring_unlock_commit(rdev); return 0; -pflip_cleanup2: - drm_vblank_put(dev, radeon_crtc->crtc_id); - pflip_cleanup1: - r = radeon_bo_reserve(rbo, false); - if (unlikely(r != 0)) { + if (unlikely(radeon_bo_reserve(rbo, false) != 0)) { DRM_ERROR("failed to reserve new rbo in error path\n"); goto pflip_cleanup; } - r = radeon_bo_unpin(rbo); - if (unlikely(r != 0)) { - radeon_bo_unreserve(rbo); - r = -EINVAL; + if (unlikely(radeon_bo_unpin(rbo) != 0)) { DRM_ERROR("failed to unpin new rbo in error path\n"); - goto pflip_cleanup; } radeon_bo_unreserve(rbo); @@ -499,9 +471,9 @@ pflip_cleanup: spin_lock_irqsave(&dev->event_lock, flags); radeon_crtc->unpin_work = NULL; unlock_free: - drm_gem_object_unreference_unlocked(old_radeon_fb->obj); spin_unlock_irqrestore(&dev->event_lock, flags); - radeon_fence_unref(&fence); + drm_gem_object_unreference_unlocked(old_radeon_fb->obj); + radeon_fence_unref(&work->fence); kfree(work); return r; @@ -695,7 +667,6 @@ static void radeon_print_display_setup(struct drm_device *dev) static bool radeon_setup_enc_conn(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; - struct drm_connector *drm_connector; bool ret = false; if (rdev->bios) { @@ -715,8 +686,6 @@ static bool radeon_setup_enc_conn(struct drm_device *dev) if (ret) { radeon_setup_encoder_clones(dev); radeon_print_display_setup(dev); - list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) - radeon_ddc_dump(drm_connector); } return ret; @@ -728,21 +697,36 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) struct radeon_device *rdev = dev->dev_private; int ret = 0; + /* don't leak the edid if we already fetched it in detect() */ + if (radeon_connector->edid) + goto got_edid; + /* on hw with routers, select right port */ if (radeon_connector->router.ddc_valid) radeon_router_select_ddc_port(radeon_connector); - if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) || - (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) { + if (radeon_connector_encoder_get_dp_bridge_encoder_id(&radeon_connector->base) != + ENCODER_OBJECT_ID_NONE) { struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; + + if (dig->dp_i2c_bus) + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + &dig->dp_i2c_bus->adapter); + } else if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) || + (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) { + struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; + if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT || dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && dig->dp_i2c_bus) - radeon_connector->edid = drm_get_edid(&radeon_connector->base, &dig->dp_i2c_bus->adapter); - } - if (!radeon_connector->ddc_bus) - return -1; - if (!radeon_connector->edid) { - radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + &dig->dp_i2c_bus->adapter); + else if (radeon_connector->ddc_bus && !radeon_connector->edid) + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + &radeon_connector->ddc_bus->adapter); + } else { + if (radeon_connector->ddc_bus && !radeon_connector->edid) + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + &radeon_connector->ddc_bus->adapter); } if (!radeon_connector->edid) { @@ -756,42 +740,16 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev); } if (radeon_connector->edid) { +got_edid: drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid); ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid); + drm_edid_to_eld(&radeon_connector->base, radeon_connector->edid); return ret; } drm_mode_connector_update_edid_property(&radeon_connector->base, NULL); return 0; } -static int radeon_ddc_dump(struct drm_connector *connector) -{ - struct edid *edid; - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - int ret = 0; - - /* on hw with routers, select right port */ - if (radeon_connector->router.ddc_valid) - radeon_router_select_ddc_port(radeon_connector); - - if (!radeon_connector->ddc_bus) - return -1; - edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); - /* Log EDID retrieval status here. In particular with regard to - * connectors with requires_extended_probe flag set, that will prevent - * function radeon_dvi_detect() to fetch EDID on this connector, - * as long as there is no valid EDID header found */ - if (edid) { - DRM_INFO("Radeon display connector %s: Found valid EDID", - drm_get_connector_name(connector)); - kfree(edid); - } else { - DRM_INFO("Radeon display connector %s: No monitor connected or invalid EDID", - drm_get_connector_name(connector)); - } - return ret; -} - /* avivo */ static void avivo_get_fb_div(struct radeon_pll *pll, u32 target_clock, diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 60e1605..71499fc 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -36,6 +36,7 @@ #include "drm_pciids.h" #include <linux/console.h> +#include <linux/module.h> /* @@ -51,9 +52,11 @@ * 2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query * 2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query * 2.10.0 - fusion 2D tiling + * 2.11.0 - backend map, initial compute support for the CS checker + * 2.12.0 - RADEON_CS_KEEP_TILING_FLAGS */ #define KMS_DRIVER_MAJOR 2 -#define KMS_DRIVER_MINOR 10 +#define KMS_DRIVER_MINOR 12 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); int radeon_driver_unload_kms(struct drm_device *dev); diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 8a171b2..4b27efa 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -29,12 +29,6 @@ #include "radeon.h" #include "atom.h" -extern int atom_debug; - -/* evil but including atombios.h is much worse */ -bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, - struct drm_display_mode *mode); - static uint32_t radeon_encoder_clones(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; @@ -156,27 +150,6 @@ radeon_get_encoder_enum(struct drm_device *dev, uint32_t supported_device, uint8 return ret; } -static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder) -{ - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - case ENCODER_OBJECT_ID_INTERNAL_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_DDI: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - return true; - default: - return false; - } -} - void radeon_link_encoder_connector(struct drm_device *dev) { @@ -229,23 +202,7 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder) return NULL; } -static struct drm_connector * -radeon_get_connector_for_encoder_init(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_connector *connector; - struct radeon_connector *radeon_connector; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - radeon_connector = to_radeon_connector(connector); - if (radeon_encoder->devices & radeon_connector->devices) - return connector; - } - return NULL; -} - -struct drm_encoder *radeon_atom_get_external_encoder(struct drm_encoder *encoder) +struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); @@ -266,9 +223,9 @@ struct drm_encoder *radeon_atom_get_external_encoder(struct drm_encoder *encoder return NULL; } -bool radeon_encoder_is_dp_bridge(struct drm_encoder *encoder) +u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder) { - struct drm_encoder *other_encoder = radeon_atom_get_external_encoder(encoder); + struct drm_encoder *other_encoder = radeon_get_external_encoder(encoder); if (other_encoder) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(other_encoder); @@ -276,13 +233,12 @@ bool radeon_encoder_is_dp_bridge(struct drm_encoder *encoder) switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_TRAVIS: case ENCODER_OBJECT_ID_NUTMEG: - return true; + return radeon_encoder->encoder_id; default: - return false; + return ENCODER_OBJECT_ID_NONE; } } - - return false; + return ENCODER_OBJECT_ID_NONE; } void radeon_panel_mode_fixup(struct drm_encoder *encoder, @@ -332,2090 +288,3 @@ void radeon_panel_mode_fixup(struct drm_encoder *encoder, } -static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - - /* set the active encoder to connector routing */ - radeon_encoder_set_active_device(encoder); - drm_mode_set_crtcinfo(adjusted_mode, 0); - - /* hw bug */ - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) - && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2))) - adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; - - /* get the native mode for LVDS */ - if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) - radeon_panel_mode_fixup(encoder, adjusted_mode); - - /* get the native mode for TV */ - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) { - struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv; - if (tv_dac) { - if (tv_dac->tv_std == TV_STD_NTSC || - tv_dac->tv_std == TV_STD_NTSC_J || - tv_dac->tv_std == TV_STD_PAL_M) - radeon_atom_get_tv_timings(rdev, 0, adjusted_mode); - else - radeon_atom_get_tv_timings(rdev, 1, adjusted_mode); - } - } - - if (ASIC_IS_DCE3(rdev) && - ((radeon_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) || - radeon_encoder_is_dp_bridge(encoder))) { - struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); - radeon_dp_set_link_config(connector, mode); - } - - return true; -} - -static void -atombios_dac_setup(struct drm_encoder *encoder, int action) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - DAC_ENCODER_CONTROL_PS_ALLOCATION args; - int index = 0; - struct radeon_encoder_atom_dac *dac_info = radeon_encoder->enc_priv; - - memset(&args, 0, sizeof(args)); - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_DAC1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl); - break; - } - - args.ucAction = action; - - if (radeon_encoder->active_device & (ATOM_DEVICE_CRT_SUPPORT)) - args.ucDacStandard = ATOM_DAC1_PS2; - else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - args.ucDacStandard = ATOM_DAC1_CV; - else { - switch (dac_info->tv_std) { - case TV_STD_PAL: - case TV_STD_PAL_M: - case TV_STD_SCART_PAL: - case TV_STD_SECAM: - case TV_STD_PAL_CN: - args.ucDacStandard = ATOM_DAC1_PAL; - break; - case TV_STD_NTSC: - case TV_STD_NTSC_J: - case TV_STD_PAL_60: - default: - args.ucDacStandard = ATOM_DAC1_NTSC; - break; - } - } - args.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - -} - -static void -atombios_tv_setup(struct drm_encoder *encoder, int action) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - TV_ENCODER_CONTROL_PS_ALLOCATION args; - int index = 0; - struct radeon_encoder_atom_dac *dac_info = radeon_encoder->enc_priv; - - memset(&args, 0, sizeof(args)); - - index = GetIndexIntoMasterTable(COMMAND, TVEncoderControl); - - args.sTVEncoder.ucAction = action; - - if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - args.sTVEncoder.ucTvStandard = ATOM_TV_CV; - else { - switch (dac_info->tv_std) { - case TV_STD_NTSC: - args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC; - break; - case TV_STD_PAL: - args.sTVEncoder.ucTvStandard = ATOM_TV_PAL; - break; - case TV_STD_PAL_M: - args.sTVEncoder.ucTvStandard = ATOM_TV_PALM; - break; - case TV_STD_PAL_60: - args.sTVEncoder.ucTvStandard = ATOM_TV_PAL60; - break; - case TV_STD_NTSC_J: - args.sTVEncoder.ucTvStandard = ATOM_TV_NTSCJ; - break; - case TV_STD_SCART_PAL: - args.sTVEncoder.ucTvStandard = ATOM_TV_PAL; /* ??? */ - break; - case TV_STD_SECAM: - args.sTVEncoder.ucTvStandard = ATOM_TV_SECAM; - break; - case TV_STD_PAL_CN: - args.sTVEncoder.ucTvStandard = ATOM_TV_PALCN; - break; - default: - args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC; - break; - } - } - - args.sTVEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - -} - -union dvo_encoder_control { - ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION ext_tmds; - DVO_ENCODER_CONTROL_PS_ALLOCATION dvo; - DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 dvo_v3; -}; - -void -atombios_dvo_setup(struct drm_encoder *encoder, int action) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - union dvo_encoder_control args; - int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl); - - memset(&args, 0, sizeof(args)); - - if (ASIC_IS_DCE3(rdev)) { - /* DCE3+ */ - args.dvo_v3.ucAction = action; - args.dvo_v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - args.dvo_v3.ucDVOConfig = 0; /* XXX */ - } else if (ASIC_IS_DCE2(rdev)) { - /* DCE2 (pre-DCE3 R6xx, RS600/690/740 */ - args.dvo.sDVOEncoder.ucAction = action; - args.dvo.sDVOEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - /* DFP1, CRT1, TV1 depending on the type of port */ - args.dvo.sDVOEncoder.ucDeviceType = ATOM_DEVICE_DFP1_INDEX; - - if (radeon_encoder->pixel_clock > 165000) - args.dvo.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute |= PANEL_ENCODER_MISC_DUAL; - } else { - /* R4xx, R5xx */ - args.ext_tmds.sXTmdsEncoder.ucEnable = action; - - if (radeon_encoder->pixel_clock > 165000) - args.ext_tmds.sXTmdsEncoder.ucMisc |= PANEL_ENCODER_MISC_DUAL; - - /*if (pScrn->rgbBits == 8)*/ - args.ext_tmds.sXTmdsEncoder.ucMisc |= ATOM_PANEL_MISC_888RGB; - } - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); -} - -union lvds_encoder_control { - LVDS_ENCODER_CONTROL_PS_ALLOCATION v1; - LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 v2; -}; - -void -atombios_digital_setup(struct drm_encoder *encoder, int action) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - union lvds_encoder_control args; - int index = 0; - int hdmi_detected = 0; - uint8_t frev, crev; - - if (!dig) - return; - - if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) - hdmi_detected = 1; - - memset(&args, 0, sizeof(args)); - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - index = GetIndexIntoMasterTable(COMMAND, TMDS1EncoderControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl); - else - index = GetIndexIntoMasterTable(COMMAND, TMDS2EncoderControl); - break; - } - - if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) - return; - - switch (frev) { - case 1: - case 2: - switch (crev) { - case 1: - args.v1.ucMisc = 0; - args.v1.ucAction = action; - if (hdmi_detected) - args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; - args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { - if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL) - args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL; - if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB) - args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB; - } else { - if (dig->linkb) - args.v1.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; - if (radeon_encoder->pixel_clock > 165000) - args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL; - /*if (pScrn->rgbBits == 8) */ - args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB; - } - break; - case 2: - case 3: - args.v2.ucMisc = 0; - args.v2.ucAction = action; - if (crev == 3) { - if (dig->coherent_mode) - args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT; - } - if (hdmi_detected) - args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; - args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - args.v2.ucTruncate = 0; - args.v2.ucSpatial = 0; - args.v2.ucTemporal = 0; - args.v2.ucFRC = 0; - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { - if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL) - args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL; - if (dig->lcd_misc & ATOM_PANEL_MISC_SPATIAL) { - args.v2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN; - if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB) - args.v2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH; - } - if (dig->lcd_misc & ATOM_PANEL_MISC_TEMPORAL) { - args.v2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN; - if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB) - args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH; - if (((dig->lcd_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2) - args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4; - } - } else { - if (dig->linkb) - args.v2.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; - if (radeon_encoder->pixel_clock > 165000) - args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL; - } - break; - default: - DRM_ERROR("Unknown table version %d, %d\n", frev, crev); - break; - } - break; - default: - DRM_ERROR("Unknown table version %d, %d\n", frev, crev); - break; - } - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); -} - -int -atombios_get_encoder_mode(struct drm_encoder *encoder) -{ - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct drm_connector *connector; - struct radeon_connector *radeon_connector; - struct radeon_connector_atom_dig *dig_connector; - - /* dp bridges are always DP */ - if (radeon_encoder_is_dp_bridge(encoder)) - return ATOM_ENCODER_MODE_DP; - - /* DVO is always DVO */ - if (radeon_encoder->encoder_id == ATOM_ENCODER_MODE_DVO) - return ATOM_ENCODER_MODE_DVO; - - connector = radeon_get_connector_for_encoder(encoder); - /* if we don't have an active device yet, just use one of - * the connectors tied to the encoder. - */ - if (!connector) - connector = radeon_get_connector_for_encoder_init(encoder); - radeon_connector = to_radeon_connector(connector); - - switch (connector->connector_type) { - case DRM_MODE_CONNECTOR_DVII: - case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ - if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) { - /* fix me */ - if (ASIC_IS_DCE4(rdev)) - return ATOM_ENCODER_MODE_DVI; - else - return ATOM_ENCODER_MODE_HDMI; - } else if (radeon_connector->use_digital) - return ATOM_ENCODER_MODE_DVI; - else - return ATOM_ENCODER_MODE_CRT; - break; - case DRM_MODE_CONNECTOR_DVID: - case DRM_MODE_CONNECTOR_HDMIA: - default: - if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) { - /* fix me */ - if (ASIC_IS_DCE4(rdev)) - return ATOM_ENCODER_MODE_DVI; - else - return ATOM_ENCODER_MODE_HDMI; - } else - return ATOM_ENCODER_MODE_DVI; - break; - case DRM_MODE_CONNECTOR_LVDS: - return ATOM_ENCODER_MODE_LVDS; - break; - case DRM_MODE_CONNECTOR_DisplayPort: - dig_connector = radeon_connector->con_priv; - if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || - (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) - return ATOM_ENCODER_MODE_DP; - else if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) { - /* fix me */ - if (ASIC_IS_DCE4(rdev)) - return ATOM_ENCODER_MODE_DVI; - else - return ATOM_ENCODER_MODE_HDMI; - } else - return ATOM_ENCODER_MODE_DVI; - break; - case DRM_MODE_CONNECTOR_eDP: - return ATOM_ENCODER_MODE_DP; - case DRM_MODE_CONNECTOR_DVIA: - case DRM_MODE_CONNECTOR_VGA: - return ATOM_ENCODER_MODE_CRT; - break; - case DRM_MODE_CONNECTOR_Composite: - case DRM_MODE_CONNECTOR_SVIDEO: - case DRM_MODE_CONNECTOR_9PinDIN: - /* fix me */ - return ATOM_ENCODER_MODE_TV; - /*return ATOM_ENCODER_MODE_CV;*/ - break; - } -} - -/* - * DIG Encoder/Transmitter Setup - * - * DCE 3.0/3.1 - * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA. - * Supports up to 3 digital outputs - * - 2 DIG encoder blocks. - * DIG1 can drive UNIPHY link A or link B - * DIG2 can drive UNIPHY link B or LVTMA - * - * DCE 3.2 - * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B). - * Supports up to 5 digital outputs - * - 2 DIG encoder blocks. - * DIG1/2 can drive UNIPHY0/1/2 link A or link B - * - * DCE 4.0/5.0 - * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B). - * Supports up to 6 digital outputs - * - 6 DIG encoder blocks. - * - DIG to PHY mapping is hardcoded - * DIG1 drives UNIPHY0 link A, A+B - * DIG2 drives UNIPHY0 link B - * DIG3 drives UNIPHY1 link A, A+B - * DIG4 drives UNIPHY1 link B - * DIG5 drives UNIPHY2 link A, A+B - * DIG6 drives UNIPHY2 link B - * - * DCE 4.1 - * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B). - * Supports up to 6 digital outputs - * - 2 DIG encoder blocks. - * DIG1/2 can drive UNIPHY0/1/2 link A or link B - * - * Routing - * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links) - * Examples: - * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI - * crtc1 -> dig1 -> UNIPHY0 link B -> DP - * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS - * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI - */ - -union dig_encoder_control { - DIG_ENCODER_CONTROL_PS_ALLOCATION v1; - DIG_ENCODER_CONTROL_PARAMETERS_V2 v2; - DIG_ENCODER_CONTROL_PARAMETERS_V3 v3; - DIG_ENCODER_CONTROL_PARAMETERS_V4 v4; -}; - -void -atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); - union dig_encoder_control args; - int index = 0; - uint8_t frev, crev; - int dp_clock = 0; - int dp_lane_count = 0; - int hpd_id = RADEON_HPD_NONE; - int bpc = 8; - - if (connector) { - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct radeon_connector_atom_dig *dig_connector = - radeon_connector->con_priv; - - dp_clock = dig_connector->dp_clock; - dp_lane_count = dig_connector->dp_lane_count; - hpd_id = radeon_connector->hpd.hpd; - bpc = connector->display_info.bpc; - } - - /* no dig encoder assigned */ - if (dig->dig_encoder == -1) - return; - - memset(&args, 0, sizeof(args)); - - if (ASIC_IS_DCE4(rdev)) - index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl); - else { - if (dig->dig_encoder) - index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl); - else - index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl); - } - - if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) - return; - - args.v1.ucAction = action; - args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE) - args.v3.ucPanelMode = panel_mode; - else - args.v1.ucEncoderMode = atombios_get_encoder_mode(encoder); - - if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) || - (args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP_MST)) - args.v1.ucLaneNum = dp_lane_count; - else if (radeon_encoder->pixel_clock > 165000) - args.v1.ucLaneNum = 8; - else - args.v1.ucLaneNum = 4; - - if (ASIC_IS_DCE5(rdev)) { - if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) || - (args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP_MST)) { - if (dp_clock == 270000) - args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ; - else if (dp_clock == 540000) - args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ; - } - args.v4.acConfig.ucDigSel = dig->dig_encoder; - switch (bpc) { - case 0: - args.v4.ucBitPerColor = PANEL_BPC_UNDEFINE; - break; - case 6: - args.v4.ucBitPerColor = PANEL_6BIT_PER_COLOR; - break; - case 8: - default: - args.v4.ucBitPerColor = PANEL_8BIT_PER_COLOR; - break; - case 10: - args.v4.ucBitPerColor = PANEL_10BIT_PER_COLOR; - break; - case 12: - args.v4.ucBitPerColor = PANEL_12BIT_PER_COLOR; - break; - case 16: - args.v4.ucBitPerColor = PANEL_16BIT_PER_COLOR; - break; - } - if (hpd_id == RADEON_HPD_NONE) - args.v4.ucHPD_ID = 0; - else - args.v4.ucHPD_ID = hpd_id + 1; - } else if (ASIC_IS_DCE4(rdev)) { - if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) && (dp_clock == 270000)) - args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ; - args.v3.acConfig.ucDigSel = dig->dig_encoder; - switch (bpc) { - case 0: - args.v3.ucBitPerColor = PANEL_BPC_UNDEFINE; - break; - case 6: - args.v3.ucBitPerColor = PANEL_6BIT_PER_COLOR; - break; - case 8: - default: - args.v3.ucBitPerColor = PANEL_8BIT_PER_COLOR; - break; - case 10: - args.v3.ucBitPerColor = PANEL_10BIT_PER_COLOR; - break; - case 12: - args.v3.ucBitPerColor = PANEL_12BIT_PER_COLOR; - break; - case 16: - args.v3.ucBitPerColor = PANEL_16BIT_PER_COLOR; - break; - } - } else { - if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) && (dp_clock == 270000)) - args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1; - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2; - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3; - break; - } - if (dig->linkb) - args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB; - else - args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; - } - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - -} - -union dig_transmitter_control { - DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1; - DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2; - DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3; - DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 v4; -}; - -void -atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - struct drm_connector *connector; - union dig_transmitter_control args; - int index = 0; - uint8_t frev, crev; - bool is_dp = false; - int pll_id = 0; - int dp_clock = 0; - int dp_lane_count = 0; - int connector_object_id = 0; - int igp_lane_info = 0; - int dig_encoder = dig->dig_encoder; - - if (action == ATOM_TRANSMITTER_ACTION_INIT) { - connector = radeon_get_connector_for_encoder_init(encoder); - /* just needed to avoid bailing in the encoder check. the encoder - * isn't used for init - */ - dig_encoder = 0; - } else - connector = radeon_get_connector_for_encoder(encoder); - - if (connector) { - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct radeon_connector_atom_dig *dig_connector = - radeon_connector->con_priv; - - dp_clock = dig_connector->dp_clock; - dp_lane_count = dig_connector->dp_lane_count; - connector_object_id = - (radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; - igp_lane_info = dig_connector->igp_lane_info; - } - - /* no dig encoder assigned */ - if (dig_encoder == -1) - return; - - if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP) - is_dp = true; - - memset(&args, 0, sizeof(args)); - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - index = GetIndexIntoMasterTable(COMMAND, LVTMATransmitterControl); - break; - } - - if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) - return; - - args.v1.ucAction = action; - if (action == ATOM_TRANSMITTER_ACTION_INIT) { - args.v1.usInitInfo = cpu_to_le16(connector_object_id); - } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { - args.v1.asMode.ucLaneSel = lane_num; - args.v1.asMode.ucLaneSet = lane_set; - } else { - if (is_dp) - args.v1.usPixelClock = - cpu_to_le16(dp_clock / 10); - else if (radeon_encoder->pixel_clock > 165000) - args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); - else - args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - } - if (ASIC_IS_DCE4(rdev)) { - if (is_dp) - args.v3.ucLaneNum = dp_lane_count; - else if (radeon_encoder->pixel_clock > 165000) - args.v3.ucLaneNum = 8; - else - args.v3.ucLaneNum = 4; - - if (dig->linkb) - args.v3.acConfig.ucLinkSel = 1; - if (dig_encoder & 1) - args.v3.acConfig.ucEncoderSel = 1; - - /* Select the PLL for the PHY - * DP PHY should be clocked from external src if there is - * one. - */ - if (encoder->crtc) { - struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); - pll_id = radeon_crtc->pll_id; - } - - if (ASIC_IS_DCE5(rdev)) { - /* On DCE5 DCPLL usually generates the DP ref clock */ - if (is_dp) { - if (rdev->clock.dp_extclk) - args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_EXTCLK; - else - args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_DCPLL; - } else - args.v4.acConfig.ucRefClkSource = pll_id; - } else { - /* On DCE4, if there is an external clock, it generates the DP ref clock */ - if (is_dp && rdev->clock.dp_extclk) - args.v3.acConfig.ucRefClkSource = 2; /* external src */ - else - args.v3.acConfig.ucRefClkSource = pll_id; - } - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - args.v3.acConfig.ucTransmitterSel = 0; - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - args.v3.acConfig.ucTransmitterSel = 1; - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - args.v3.acConfig.ucTransmitterSel = 2; - break; - } - - if (is_dp) - args.v3.acConfig.fCoherentMode = 1; /* DP requires coherent */ - else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { - if (dig->coherent_mode) - args.v3.acConfig.fCoherentMode = 1; - if (radeon_encoder->pixel_clock > 165000) - args.v3.acConfig.fDualLinkConnector = 1; - } - } else if (ASIC_IS_DCE32(rdev)) { - args.v2.acConfig.ucEncoderSel = dig_encoder; - if (dig->linkb) - args.v2.acConfig.ucLinkSel = 1; - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - args.v2.acConfig.ucTransmitterSel = 0; - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - args.v2.acConfig.ucTransmitterSel = 1; - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - args.v2.acConfig.ucTransmitterSel = 2; - break; - } - - if (is_dp) { - args.v2.acConfig.fCoherentMode = 1; - args.v2.acConfig.fDPConnector = 1; - } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { - if (dig->coherent_mode) - args.v2.acConfig.fCoherentMode = 1; - if (radeon_encoder->pixel_clock > 165000) - args.v2.acConfig.fDualLinkConnector = 1; - } - } else { - args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL; - - if (dig_encoder) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER; - else - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER; - - if ((rdev->flags & RADEON_IS_IGP) && - (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY)) { - if (is_dp || (radeon_encoder->pixel_clock <= 165000)) { - if (igp_lane_info & 0x1) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3; - else if (igp_lane_info & 0x2) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_4_7; - else if (igp_lane_info & 0x4) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_11; - else if (igp_lane_info & 0x8) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15; - } else { - if (igp_lane_info & 0x3) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7; - else if (igp_lane_info & 0xc) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15; - } - } - - if (dig->linkb) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB; - else - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA; - - if (is_dp) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; - else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { - if (dig->coherent_mode) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; - if (radeon_encoder->pixel_clock > 165000) - args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK; - } - } - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); -} - -bool -atombios_set_edp_panel_power(struct drm_connector *connector, int action) -{ - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct drm_device *dev = radeon_connector->base.dev; - struct radeon_device *rdev = dev->dev_private; - union dig_transmitter_control args; - int index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); - uint8_t frev, crev; - - if (connector->connector_type != DRM_MODE_CONNECTOR_eDP) - goto done; - - if (!ASIC_IS_DCE4(rdev)) - goto done; - - if ((action != ATOM_TRANSMITTER_ACTION_POWER_ON) && - (action != ATOM_TRANSMITTER_ACTION_POWER_OFF)) - goto done; - - if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) - goto done; - - memset(&args, 0, sizeof(args)); - - args.v1.ucAction = action; - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - - /* wait for the panel to power up */ - if (action == ATOM_TRANSMITTER_ACTION_POWER_ON) { - int i; - - for (i = 0; i < 300; i++) { - if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) - return true; - mdelay(1); - } - return false; - } -done: - return true; -} - -union external_encoder_control { - EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION v1; - EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 v3; -}; - -static void -atombios_external_encoder_setup(struct drm_encoder *encoder, - struct drm_encoder *ext_encoder, - int action) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_encoder *ext_radeon_encoder = to_radeon_encoder(ext_encoder); - union external_encoder_control args; - struct drm_connector *connector; - int index = GetIndexIntoMasterTable(COMMAND, ExternalEncoderControl); - u8 frev, crev; - int dp_clock = 0; - int dp_lane_count = 0; - int connector_object_id = 0; - u32 ext_enum = (ext_radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT; - int bpc = 8; - - if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT) - connector = radeon_get_connector_for_encoder_init(encoder); - else - connector = radeon_get_connector_for_encoder(encoder); - - if (connector) { - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct radeon_connector_atom_dig *dig_connector = - radeon_connector->con_priv; - - dp_clock = dig_connector->dp_clock; - dp_lane_count = dig_connector->dp_lane_count; - connector_object_id = - (radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; - bpc = connector->display_info.bpc; - } - - memset(&args, 0, sizeof(args)); - - if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) - return; - - switch (frev) { - case 1: - /* no params on frev 1 */ - break; - case 2: - switch (crev) { - case 1: - case 2: - args.v1.sDigEncoder.ucAction = action; - args.v1.sDigEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - args.v1.sDigEncoder.ucEncoderMode = atombios_get_encoder_mode(encoder); - - if (args.v1.sDigEncoder.ucEncoderMode == ATOM_ENCODER_MODE_DP) { - if (dp_clock == 270000) - args.v1.sDigEncoder.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; - args.v1.sDigEncoder.ucLaneNum = dp_lane_count; - } else if (radeon_encoder->pixel_clock > 165000) - args.v1.sDigEncoder.ucLaneNum = 8; - else - args.v1.sDigEncoder.ucLaneNum = 4; - break; - case 3: - args.v3.sExtEncoder.ucAction = action; - if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT) - args.v3.sExtEncoder.usConnectorId = cpu_to_le16(connector_object_id); - else - args.v3.sExtEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); - args.v3.sExtEncoder.ucEncoderMode = atombios_get_encoder_mode(encoder); - - if (args.v3.sExtEncoder.ucEncoderMode == ATOM_ENCODER_MODE_DP) { - if (dp_clock == 270000) - args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ; - else if (dp_clock == 540000) - args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_5_40GHZ; - args.v3.sExtEncoder.ucLaneNum = dp_lane_count; - } else if (radeon_encoder->pixel_clock > 165000) - args.v3.sExtEncoder.ucLaneNum = 8; - else - args.v3.sExtEncoder.ucLaneNum = 4; - switch (ext_enum) { - case GRAPH_OBJECT_ENUM_ID1: - args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER1; - break; - case GRAPH_OBJECT_ENUM_ID2: - args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER2; - break; - case GRAPH_OBJECT_ENUM_ID3: - args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER3; - break; - } - switch (bpc) { - case 0: - args.v3.sExtEncoder.ucBitPerColor = PANEL_BPC_UNDEFINE; - break; - case 6: - args.v3.sExtEncoder.ucBitPerColor = PANEL_6BIT_PER_COLOR; - break; - case 8: - default: - args.v3.sExtEncoder.ucBitPerColor = PANEL_8BIT_PER_COLOR; - break; - case 10: - args.v3.sExtEncoder.ucBitPerColor = PANEL_10BIT_PER_COLOR; - break; - case 12: - args.v3.sExtEncoder.ucBitPerColor = PANEL_12BIT_PER_COLOR; - break; - case 16: - args.v3.sExtEncoder.ucBitPerColor = PANEL_16BIT_PER_COLOR; - break; - } - break; - default: - DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); - return; - } - break; - default: - DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); - return; - } - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); -} - -static void -atombios_yuv_setup(struct drm_encoder *encoder, bool enable) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); - ENABLE_YUV_PS_ALLOCATION args; - int index = GetIndexIntoMasterTable(COMMAND, EnableYUV); - uint32_t temp, reg; - - memset(&args, 0, sizeof(args)); - - if (rdev->family >= CHIP_R600) - reg = R600_BIOS_3_SCRATCH; - else - reg = RADEON_BIOS_3_SCRATCH; - - /* XXX: fix up scratch reg handling */ - temp = RREG32(reg); - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) - WREG32(reg, (ATOM_S3_TV1_ACTIVE | - (radeon_crtc->crtc_id << 18))); - else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - WREG32(reg, (ATOM_S3_CV_ACTIVE | (radeon_crtc->crtc_id << 24))); - else - WREG32(reg, 0); - - if (enable) - args.ucEnable = ATOM_ENABLE; - args.ucCRTC = radeon_crtc->crtc_id; - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - - WREG32(reg, temp); -} - -static void -radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder); - DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args; - int index = 0; - bool is_dig = false; - bool is_dce5_dac = false; - bool is_dce5_dvo = false; - - memset(&args, 0, sizeof(args)); - - DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n", - radeon_encoder->encoder_id, mode, radeon_encoder->devices, - radeon_encoder->active_device); - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - index = GetIndexIntoMasterTable(COMMAND, TMDSAOutputControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - is_dig = true; - break; - case ENCODER_OBJECT_ID_INTERNAL_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_DDI: - index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - if (ASIC_IS_DCE5(rdev)) - is_dce5_dvo = true; - else if (ASIC_IS_DCE3(rdev)) - is_dig = true; - else - index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); - else - index = GetIndexIntoMasterTable(COMMAND, LVTMAOutputControl); - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - if (ASIC_IS_DCE5(rdev)) - is_dce5_dac = true; - else { - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); - else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); - else - index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); - } - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); - else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); - else - index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); - break; - } - - if (is_dig) { - switch (mode) { - case DRM_MODE_DPMS_ON: - /* some early dce3.2 boards have a bug in their transmitter control table */ - if ((rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV730)) - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); - else - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0); - if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP) { - struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); - - if (connector && - (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) { - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct radeon_connector_atom_dig *radeon_dig_connector = - radeon_connector->con_priv; - atombios_set_edp_panel_power(connector, - ATOM_TRANSMITTER_ACTION_POWER_ON); - radeon_dig_connector->edp_on = true; - } - if (ASIC_IS_DCE4(rdev)) - atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); - radeon_dp_link_train(encoder, connector); - if (ASIC_IS_DCE4(rdev)) - atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); - } - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0); - if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP) { - struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); - - if (ASIC_IS_DCE4(rdev)) - atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); - if (connector && - (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) { - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct radeon_connector_atom_dig *radeon_dig_connector = - radeon_connector->con_priv; - atombios_set_edp_panel_power(connector, - ATOM_TRANSMITTER_ACTION_POWER_OFF); - radeon_dig_connector->edp_on = false; - } - } - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0); - break; - } - } else if (is_dce5_dac) { - switch (mode) { - case DRM_MODE_DPMS_ON: - atombios_dac_setup(encoder, ATOM_ENABLE); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - atombios_dac_setup(encoder, ATOM_DISABLE); - break; - } - } else if (is_dce5_dvo) { - switch (mode) { - case DRM_MODE_DPMS_ON: - atombios_dvo_setup(encoder, ATOM_ENABLE); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - atombios_dvo_setup(encoder, ATOM_DISABLE); - break; - } - } else { - switch (mode) { - case DRM_MODE_DPMS_ON: - args.ucAction = ATOM_ENABLE; - /* workaround for DVOOutputControl on some RS690 systems */ - if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DDI) { - u32 reg = RREG32(RADEON_BIOS_3_SCRATCH); - WREG32(RADEON_BIOS_3_SCRATCH, reg & ~ATOM_S3_DFP2I_ACTIVE); - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - WREG32(RADEON_BIOS_3_SCRATCH, reg); - } else - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { - args.ucAction = ATOM_LCD_BLON; - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - } - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - args.ucAction = ATOM_DISABLE; - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { - args.ucAction = ATOM_LCD_BLOFF; - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - } - break; - } - } - - if (ext_encoder) { - switch (mode) { - case DRM_MODE_DPMS_ON: - default: - if (ASIC_IS_DCE41(rdev)) { - atombios_external_encoder_setup(encoder, ext_encoder, - EXTERNAL_ENCODER_ACTION_V3_ENABLE_OUTPUT); - atombios_external_encoder_setup(encoder, ext_encoder, - EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING_OFF); - } else - atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - if (ASIC_IS_DCE41(rdev)) { - atombios_external_encoder_setup(encoder, ext_encoder, - EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING); - atombios_external_encoder_setup(encoder, ext_encoder, - EXTERNAL_ENCODER_ACTION_V3_DISABLE_OUTPUT); - } else - atombios_external_encoder_setup(encoder, ext_encoder, ATOM_DISABLE); - break; - } - } - - radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); - -} - -union crtc_source_param { - SELECT_CRTC_SOURCE_PS_ALLOCATION v1; - SELECT_CRTC_SOURCE_PARAMETERS_V2 v2; -}; - -static void -atombios_set_encoder_crtc_source(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); - union crtc_source_param args; - int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source); - uint8_t frev, crev; - struct radeon_encoder_atom_dig *dig; - - memset(&args, 0, sizeof(args)); - - if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) - return; - - switch (frev) { - case 1: - switch (crev) { - case 1: - default: - if (ASIC_IS_AVIVO(rdev)) - args.v1.ucCRTC = radeon_crtc->crtc_id; - else { - if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) { - args.v1.ucCRTC = radeon_crtc->crtc_id; - } else { - args.v1.ucCRTC = radeon_crtc->crtc_id << 2; - } - } - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - args.v1.ucDevice = ATOM_DEVICE_DFP1_INDEX; - break; - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) - args.v1.ucDevice = ATOM_DEVICE_LCD1_INDEX; - else - args.v1.ucDevice = ATOM_DEVICE_DFP3_INDEX; - break; - case ENCODER_OBJECT_ID_INTERNAL_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_DDI: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - args.v1.ucDevice = ATOM_DEVICE_DFP2_INDEX; - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) - args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; - else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; - else - args.v1.ucDevice = ATOM_DEVICE_CRT1_INDEX; - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) - args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; - else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; - else - args.v1.ucDevice = ATOM_DEVICE_CRT2_INDEX; - break; - } - break; - case 2: - args.v2.ucCRTC = radeon_crtc->crtc_id; - args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder); - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - dig = radeon_encoder->enc_priv; - switch (dig->dig_encoder) { - case 0: - args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID; - break; - case 1: - args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID; - break; - case 2: - args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID; - break; - case 3: - args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID; - break; - case 4: - args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID; - break; - case 5: - args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID; - break; - } - break; - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID; - break; - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) - args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; - else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; - else - args.v2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID; - break; - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) - args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; - else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) - args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; - else - args.v2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID; - break; - } - break; - } - break; - default: - DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); - return; - } - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - - /* update scratch regs with new routing */ - radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); -} - -static void -atombios_apply_encoder_quirks(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); - - /* Funky macbooks */ - if ((dev->pdev->device == 0x71C5) && - (dev->pdev->subsystem_vendor == 0x106b) && - (dev->pdev->subsystem_device == 0x0080)) { - if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) { - uint32_t lvtma_bit_depth_control = RREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL); - - lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_EN; - lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN; - - WREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL, lvtma_bit_depth_control); - } - } - - /* set scaler clears this on some chips */ - if (ASIC_IS_AVIVO(rdev) && - (!(radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)))) { - if (ASIC_IS_DCE4(rdev)) { - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, - EVERGREEN_INTERLEAVE_EN); - else - WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, 0); - } else { - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, - AVIVO_D1MODE_INTERLEAVE_EN); - else - WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, 0); - } - } -} - -static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_encoder *test_encoder; - struct radeon_encoder_atom_dig *dig; - uint32_t dig_enc_in_use = 0; - - /* DCE4/5 */ - if (ASIC_IS_DCE4(rdev)) { - dig = radeon_encoder->enc_priv; - if (ASIC_IS_DCE41(rdev)) { - if (dig->linkb) - return 1; - else - return 0; - } else { - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - if (dig->linkb) - return 1; - else - return 0; - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - if (dig->linkb) - return 3; - else - return 2; - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - if (dig->linkb) - return 5; - else - return 4; - break; - } - } - } - - /* on DCE32 and encoder can driver any block so just crtc id */ - if (ASIC_IS_DCE32(rdev)) { - return radeon_crtc->crtc_id; - } - - /* on DCE3 - LVTMA can only be driven by DIGB */ - list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) { - struct radeon_encoder *radeon_test_encoder; - - if (encoder == test_encoder) - continue; - - if (!radeon_encoder_is_digital(test_encoder)) - continue; - - radeon_test_encoder = to_radeon_encoder(test_encoder); - dig = radeon_test_encoder->enc_priv; - - if (dig->dig_encoder >= 0) - dig_enc_in_use |= (1 << dig->dig_encoder); - } - - if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) { - if (dig_enc_in_use & 0x2) - DRM_ERROR("LVDS required digital encoder 2 but it was in use - stealing\n"); - return 1; - } - if (!(dig_enc_in_use & 1)) - return 0; - return 1; -} - -/* This only needs to be called once at startup */ -void -radeon_atom_encoder_init(struct radeon_device *rdev) -{ - struct drm_device *dev = rdev->ddev; - struct drm_encoder *encoder; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder); - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0); - break; - default: - break; - } - - if (ext_encoder && ASIC_IS_DCE41(rdev)) - atombios_external_encoder_setup(encoder, ext_encoder, - EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT); - } -} - -static void -radeon_atom_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder); - - radeon_encoder->pixel_clock = adjusted_mode->clock; - - if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE4(rdev)) { - if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT)) - atombios_yuv_setup(encoder, true); - else - atombios_yuv_setup(encoder, false); - } - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE); - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - if (ASIC_IS_DCE4(rdev)) { - /* disable the transmitter */ - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); - /* setup and enable the encoder */ - atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0); - - /* enable the transmitter */ - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); - } else { - /* disable the encoder and transmitter */ - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); - atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0); - - /* setup and enable the encoder and transmitter */ - atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0); - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0); - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); - } - break; - case ENCODER_OBJECT_ID_INTERNAL_DDI: - case ENCODER_OBJECT_ID_INTERNAL_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - atombios_dvo_setup(encoder, ATOM_ENABLE); - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - case ENCODER_OBJECT_ID_INTERNAL_DAC2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - atombios_dac_setup(encoder, ATOM_ENABLE); - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) { - if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) - atombios_tv_setup(encoder, ATOM_ENABLE); - else - atombios_tv_setup(encoder, ATOM_DISABLE); - } - break; - } - - if (ext_encoder) { - if (ASIC_IS_DCE41(rdev)) - atombios_external_encoder_setup(encoder, ext_encoder, - EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP); - else - atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE); - } - - atombios_apply_encoder_quirks(encoder, adjusted_mode); - - if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) { - r600_hdmi_enable(encoder); - r600_hdmi_setmode(encoder, adjusted_mode); - } -} - -static bool -atombios_dac_load_detect(struct drm_encoder *encoder, struct drm_connector *connector) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | - ATOM_DEVICE_CV_SUPPORT | - ATOM_DEVICE_CRT_SUPPORT)) { - DAC_LOAD_DETECTION_PS_ALLOCATION args; - int index = GetIndexIntoMasterTable(COMMAND, DAC_LoadDetection); - uint8_t frev, crev; - - memset(&args, 0, sizeof(args)); - - if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) - return false; - - args.sDacload.ucMisc = 0; - - if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) || - (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1)) - args.sDacload.ucDacType = ATOM_DAC_A; - else - args.sDacload.ucDacType = ATOM_DAC_B; - - if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) - args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT); - else if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) - args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT); - else if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) { - args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT); - if (crev >= 3) - args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb; - } else if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { - args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT); - if (crev >= 3) - args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb; - } - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - - return true; - } else - return false; -} - -static enum drm_connector_status -radeon_atom_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - uint32_t bios_0_scratch; - - if (!atombios_dac_load_detect(encoder, connector)) { - DRM_DEBUG_KMS("detect returned false \n"); - return connector_status_unknown; - } - - if (rdev->family >= CHIP_R600) - bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH); - else - bios_0_scratch = RREG32(RADEON_BIOS_0_SCRATCH); - - DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices); - if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) { - if (bios_0_scratch & ATOM_S0_CRT1_MASK) - return connector_status_connected; - } - if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) { - if (bios_0_scratch & ATOM_S0_CRT2_MASK) - return connector_status_connected; - } - if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) { - if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A)) - return connector_status_connected; - } - if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { - if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) - return connector_status_connected; /* CTV */ - else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) - return connector_status_connected; /* STV */ - } - return connector_status_disconnected; -} - -static enum drm_connector_status -radeon_atom_dig_detect(struct drm_encoder *encoder, struct drm_connector *connector) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder); - u32 bios_0_scratch; - - if (!ASIC_IS_DCE4(rdev)) - return connector_status_unknown; - - if (!ext_encoder) - return connector_status_unknown; - - if ((radeon_connector->devices & ATOM_DEVICE_CRT_SUPPORT) == 0) - return connector_status_unknown; - - /* load detect on the dp bridge */ - atombios_external_encoder_setup(encoder, ext_encoder, - EXTERNAL_ENCODER_ACTION_V3_DACLOAD_DETECTION); - - bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH); - - DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices); - if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) { - if (bios_0_scratch & ATOM_S0_CRT1_MASK) - return connector_status_connected; - } - if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) { - if (bios_0_scratch & ATOM_S0_CRT2_MASK) - return connector_status_connected; - } - if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) { - if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A)) - return connector_status_connected; - } - if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { - if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) - return connector_status_connected; /* CTV */ - else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) - return connector_status_connected; /* STV */ - } - return connector_status_disconnected; -} - -void -radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder) -{ - struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder); - - if (ext_encoder) - /* ddc_setup on the dp bridge */ - atombios_external_encoder_setup(encoder, ext_encoder, - EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP); - -} - -static void radeon_atom_encoder_prepare(struct drm_encoder *encoder) -{ - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); - - if ((radeon_encoder->active_device & - (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) || - radeon_encoder_is_dp_bridge(encoder)) { - struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - if (dig) - dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder); - } - - radeon_atom_output_lock(encoder, true); - radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); - - if (connector) { - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - - /* select the clock/data port if it uses a router */ - if (radeon_connector->router.cd_valid) - radeon_router_select_cd_port(radeon_connector); - - /* turn eDP panel on for mode set */ - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) - atombios_set_edp_panel_power(connector, - ATOM_TRANSMITTER_ACTION_POWER_ON); - } - - /* this is needed for the pll/ss setup to work correctly in some cases */ - atombios_set_encoder_crtc_source(encoder); -} - -static void radeon_atom_encoder_commit(struct drm_encoder *encoder) -{ - radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_ON); - radeon_atom_output_lock(encoder, false); -} - -static void radeon_atom_encoder_disable(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_encoder_atom_dig *dig; - - /* check for pre-DCE3 cards with shared encoders; - * can't really use the links individually, so don't disable - * the encoder if it's in use by another connector - */ - if (!ASIC_IS_DCE3(rdev)) { - struct drm_encoder *other_encoder; - struct radeon_encoder *other_radeon_encoder; - - list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) { - other_radeon_encoder = to_radeon_encoder(other_encoder); - if ((radeon_encoder->encoder_id == other_radeon_encoder->encoder_id) && - drm_helper_encoder_in_use(other_encoder)) - goto disable_done; - } - } - - radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_DISABLE); - break; - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - if (ASIC_IS_DCE4(rdev)) - /* disable the transmitter */ - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); - else { - /* disable the encoder and transmitter */ - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); - atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0); - } - break; - case ENCODER_OBJECT_ID_INTERNAL_DDI: - case ENCODER_OBJECT_ID_INTERNAL_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - atombios_dvo_setup(encoder, ATOM_DISABLE); - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - case ENCODER_OBJECT_ID_INTERNAL_DAC2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - atombios_dac_setup(encoder, ATOM_DISABLE); - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) - atombios_tv_setup(encoder, ATOM_DISABLE); - break; - } - -disable_done: - if (radeon_encoder_is_digital(encoder)) { - if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) - r600_hdmi_disable(encoder); - dig = radeon_encoder->enc_priv; - dig->dig_encoder = -1; - } - radeon_encoder->active_device = 0; -} - -/* these are handled by the primary encoders */ -static void radeon_atom_ext_prepare(struct drm_encoder *encoder) -{ - -} - -static void radeon_atom_ext_commit(struct drm_encoder *encoder) -{ - -} - -static void -radeon_atom_ext_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - -} - -static void radeon_atom_ext_disable(struct drm_encoder *encoder) -{ - -} - -static void -radeon_atom_ext_dpms(struct drm_encoder *encoder, int mode) -{ - -} - -static bool radeon_atom_ext_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static const struct drm_encoder_helper_funcs radeon_atom_ext_helper_funcs = { - .dpms = radeon_atom_ext_dpms, - .mode_fixup = radeon_atom_ext_mode_fixup, - .prepare = radeon_atom_ext_prepare, - .mode_set = radeon_atom_ext_mode_set, - .commit = radeon_atom_ext_commit, - .disable = radeon_atom_ext_disable, - /* no detect for TMDS/LVDS yet */ -}; - -static const struct drm_encoder_helper_funcs radeon_atom_dig_helper_funcs = { - .dpms = radeon_atom_encoder_dpms, - .mode_fixup = radeon_atom_mode_fixup, - .prepare = radeon_atom_encoder_prepare, - .mode_set = radeon_atom_encoder_mode_set, - .commit = radeon_atom_encoder_commit, - .disable = radeon_atom_encoder_disable, - .detect = radeon_atom_dig_detect, -}; - -static const struct drm_encoder_helper_funcs radeon_atom_dac_helper_funcs = { - .dpms = radeon_atom_encoder_dpms, - .mode_fixup = radeon_atom_mode_fixup, - .prepare = radeon_atom_encoder_prepare, - .mode_set = radeon_atom_encoder_mode_set, - .commit = radeon_atom_encoder_commit, - .detect = radeon_atom_dac_detect, -}; - -void radeon_enc_destroy(struct drm_encoder *encoder) -{ - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - kfree(radeon_encoder->enc_priv); - drm_encoder_cleanup(encoder); - kfree(radeon_encoder); -} - -static const struct drm_encoder_funcs radeon_atom_enc_funcs = { - .destroy = radeon_enc_destroy, -}; - -struct radeon_encoder_atom_dac * -radeon_atombios_set_dac_info(struct radeon_encoder *radeon_encoder) -{ - struct drm_device *dev = radeon_encoder->base.dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder_atom_dac *dac = kzalloc(sizeof(struct radeon_encoder_atom_dac), GFP_KERNEL); - - if (!dac) - return NULL; - - dac->tv_std = radeon_atombios_get_tv_info(rdev); - return dac; -} - -struct radeon_encoder_atom_dig * -radeon_atombios_set_dig_info(struct radeon_encoder *radeon_encoder) -{ - int encoder_enum = (radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT; - struct radeon_encoder_atom_dig *dig = kzalloc(sizeof(struct radeon_encoder_atom_dig), GFP_KERNEL); - - if (!dig) - return NULL; - - /* coherent mode by default */ - dig->coherent_mode = true; - dig->dig_encoder = -1; - - if (encoder_enum == 2) - dig->linkb = true; - else - dig->linkb = false; - - return dig; -} - -void -radeon_add_atom_encoder(struct drm_device *dev, - uint32_t encoder_enum, - uint32_t supported_device, - u16 caps) -{ - struct radeon_device *rdev = dev->dev_private; - struct drm_encoder *encoder; - struct radeon_encoder *radeon_encoder; - - /* see if we already added it */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - radeon_encoder = to_radeon_encoder(encoder); - if (radeon_encoder->encoder_enum == encoder_enum) { - radeon_encoder->devices |= supported_device; - return; - } - - } - - /* add a new one */ - radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); - if (!radeon_encoder) - return; - - encoder = &radeon_encoder->base; - switch (rdev->num_crtc) { - case 1: - encoder->possible_crtcs = 0x1; - break; - case 2: - default: - encoder->possible_crtcs = 0x3; - break; - case 4: - encoder->possible_crtcs = 0xf; - break; - case 6: - encoder->possible_crtcs = 0x3f; - break; - } - - radeon_encoder->enc_priv = NULL; - - radeon_encoder->encoder_enum = encoder_enum; - radeon_encoder->encoder_id = (encoder_enum & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; - radeon_encoder->devices = supported_device; - radeon_encoder->rmx_type = RMX_OFF; - radeon_encoder->underscan_type = UNDERSCAN_OFF; - radeon_encoder->is_ext_encoder = false; - radeon_encoder->caps = caps; - - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { - radeon_encoder->rmx_type = RMX_FULL; - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS); - radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder); - } else { - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS); - radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder); - } - drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs); - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC1: - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC); - radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder); - drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs); - break; - case ENCODER_OBJECT_ID_INTERNAL_DAC2: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TVDAC); - radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder); - drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs); - break; - case ENCODER_OBJECT_ID_INTERNAL_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_DDI: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { - radeon_encoder->rmx_type = RMX_FULL; - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS); - radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder); - } else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) { - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC); - radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder); - } else { - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS); - radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder); - } - drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs); - break; - case ENCODER_OBJECT_ID_SI170B: - case ENCODER_OBJECT_ID_CH7303: - case ENCODER_OBJECT_ID_EXTERNAL_SDVOA: - case ENCODER_OBJECT_ID_EXTERNAL_SDVOB: - case ENCODER_OBJECT_ID_TITFP513: - case ENCODER_OBJECT_ID_VT1623: - case ENCODER_OBJECT_ID_HDMI_SI1930: - case ENCODER_OBJECT_ID_TRAVIS: - case ENCODER_OBJECT_ID_NUTMEG: - /* these are handled by the primary encoders */ - radeon_encoder->is_ext_encoder = true; - if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS); - else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC); - else - drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS); - drm_encoder_helper_add(encoder, &radeon_atom_ext_helper_funcs); - break; - } -} diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 021d2b6..76ec0e9 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -29,7 +29,7 @@ * Dave Airlie */ #include <linux/seq_file.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <linux/wait.h> #include <linux/list.h> #include <linux/kref.h> @@ -48,7 +48,7 @@ static void radeon_fence_write(struct radeon_device *rdev, u32 seq) scratch_index = R600_WB_EVENT_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base; else scratch_index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base; - rdev->wb.wb[scratch_index/4] = cpu_to_le32(seq);; + rdev->wb.wb[scratch_index/4] = cpu_to_le32(seq); } else WREG32(rdev->fence_drv.scratch_reg, seq); } @@ -263,7 +263,7 @@ retry: */ if (seq == rdev->fence_drv.last_seq && radeon_gpu_is_lockup(rdev)) { /* good news we believe it's a lockup */ - WARN(1, "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n", + printk(KERN_WARNING "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n", fence->seq, seq); /* FIXME: what should we do ? marking everyone * as signaled for now diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index a533f52..d257210 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -49,27 +49,27 @@ int radeon_gart_table_ram_alloc(struct radeon_device *rdev) rdev->gart.table_size >> PAGE_SHIFT); } #endif - rdev->gart.table.ram.ptr = ptr; - memset((void *)rdev->gart.table.ram.ptr, 0, rdev->gart.table_size); + rdev->gart.ptr = ptr; + memset((void *)rdev->gart.ptr, 0, rdev->gart.table_size); return 0; } void radeon_gart_table_ram_free(struct radeon_device *rdev) { - if (rdev->gart.table.ram.ptr == NULL) { + if (rdev->gart.ptr == NULL) { return; } #ifdef CONFIG_X86 if (rdev->family == CHIP_RS400 || rdev->family == CHIP_RS480 || rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) { - set_memory_wb((unsigned long)rdev->gart.table.ram.ptr, + set_memory_wb((unsigned long)rdev->gart.ptr, rdev->gart.table_size >> PAGE_SHIFT); } #endif pci_free_consistent(rdev->pdev, rdev->gart.table_size, - (void *)rdev->gart.table.ram.ptr, + (void *)rdev->gart.ptr, rdev->gart.table_addr); - rdev->gart.table.ram.ptr = NULL; + rdev->gart.ptr = NULL; rdev->gart.table_addr = 0; } @@ -77,10 +77,10 @@ int radeon_gart_table_vram_alloc(struct radeon_device *rdev) { int r; - if (rdev->gart.table.vram.robj == NULL) { + if (rdev->gart.robj == NULL) { r = radeon_bo_create(rdev, rdev->gart.table_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - &rdev->gart.table.vram.robj); + &rdev->gart.robj); if (r) { return r; } @@ -93,38 +93,46 @@ int radeon_gart_table_vram_pin(struct radeon_device *rdev) uint64_t gpu_addr; int r; - r = radeon_bo_reserve(rdev->gart.table.vram.robj, false); + r = radeon_bo_reserve(rdev->gart.robj, false); if (unlikely(r != 0)) return r; - r = radeon_bo_pin(rdev->gart.table.vram.robj, + r = radeon_bo_pin(rdev->gart.robj, RADEON_GEM_DOMAIN_VRAM, &gpu_addr); if (r) { - radeon_bo_unreserve(rdev->gart.table.vram.robj); + radeon_bo_unreserve(rdev->gart.robj); return r; } - r = radeon_bo_kmap(rdev->gart.table.vram.robj, - (void **)&rdev->gart.table.vram.ptr); + r = radeon_bo_kmap(rdev->gart.robj, &rdev->gart.ptr); if (r) - radeon_bo_unpin(rdev->gart.table.vram.robj); - radeon_bo_unreserve(rdev->gart.table.vram.robj); + radeon_bo_unpin(rdev->gart.robj); + radeon_bo_unreserve(rdev->gart.robj); rdev->gart.table_addr = gpu_addr; return r; } -void radeon_gart_table_vram_free(struct radeon_device *rdev) +void radeon_gart_table_vram_unpin(struct radeon_device *rdev) { int r; - if (rdev->gart.table.vram.robj == NULL) { + if (rdev->gart.robj == NULL) { return; } - r = radeon_bo_reserve(rdev->gart.table.vram.robj, false); + r = radeon_bo_reserve(rdev->gart.robj, false); if (likely(r == 0)) { - radeon_bo_kunmap(rdev->gart.table.vram.robj); - radeon_bo_unpin(rdev->gart.table.vram.robj); - radeon_bo_unreserve(rdev->gart.table.vram.robj); + radeon_bo_kunmap(rdev->gart.robj); + radeon_bo_unpin(rdev->gart.robj); + radeon_bo_unreserve(rdev->gart.robj); + rdev->gart.ptr = NULL; + } +} + +void radeon_gart_table_vram_free(struct radeon_device *rdev) +{ + if (rdev->gart.robj == NULL) { + return; } - radeon_bo_unref(&rdev->gart.table.vram.robj); + radeon_gart_table_vram_unpin(rdev); + radeon_bo_unref(&rdev->gart.robj); } @@ -142,7 +150,7 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, u64 page_base; if (!rdev->gart.ready) { - WARN(1, "trying to unbind memory to unitialized GART !\n"); + WARN(1, "trying to unbind memory from uninitialized GART !\n"); return; } t = offset / RADEON_GPU_PAGE_SIZE; @@ -151,18 +159,22 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, if (rdev->gart.pages[p]) { if (!rdev->gart.ttm_alloced[p]) pci_unmap_page(rdev->pdev, rdev->gart.pages_addr[p], - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); rdev->gart.pages[p] = NULL; rdev->gart.pages_addr[p] = rdev->dummy_page.addr; page_base = rdev->gart.pages_addr[p]; for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { - radeon_gart_set_page(rdev, t, page_base); + if (rdev->gart.ptr) { + radeon_gart_set_page(rdev, t, page_base); + } page_base += RADEON_GPU_PAGE_SIZE; } } } - mb(); - radeon_gart_tlb_flush(rdev); + if (rdev->gart.ptr) { + mb(); + radeon_gart_tlb_flush(rdev); + } } int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, @@ -174,7 +186,7 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, int i, j; if (!rdev->gart.ready) { - WARN(1, "trying to bind memory to unitialized GART !\n"); + WARN(1, "trying to bind memory to uninitialized GART !\n"); return -EINVAL; } t = offset / RADEON_GPU_PAGE_SIZE; @@ -199,14 +211,18 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, } } rdev->gart.pages[p] = pagelist[i]; - page_base = rdev->gart.pages_addr[p]; - for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { - radeon_gart_set_page(rdev, t, page_base); - page_base += RADEON_GPU_PAGE_SIZE; + if (rdev->gart.ptr) { + page_base = rdev->gart.pages_addr[p]; + for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { + radeon_gart_set_page(rdev, t, page_base); + page_base += RADEON_GPU_PAGE_SIZE; + } } } - mb(); - radeon_gart_tlb_flush(rdev); + if (rdev->gart.ptr) { + mb(); + radeon_gart_tlb_flush(rdev); + } return 0; } @@ -215,6 +231,9 @@ void radeon_gart_restore(struct radeon_device *rdev) int i, j, t; u64 page_base; + if (!rdev->gart.ptr) { + return; + } for (i = 0, t = 0; i < rdev->gart.num_cpu_pages; i++) { page_base = rdev->gart.pages_addr[i]; for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index c90425c..894b5f0 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -23,6 +23,8 @@ * Authors: Dave Airlie * Alex Deucher */ +#include <linux/export.h> + #include "drmP.h" #include "radeon_drm.h" #include "radeon.h" @@ -32,7 +34,7 @@ * radeon_ddc_probe * */ -bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool requires_extended_probe) +bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool use_aux) { u8 out = 0x0; u8 buf[8]; @@ -47,42 +49,43 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool requires_e { .addr = 0x50, .flags = I2C_M_RD, - .len = 1, + .len = 8, .buf = buf, } }; - /* Read 8 bytes from i2c for extended probe of EDID header */ - if (requires_extended_probe) - msgs[1].len = 8; - /* on hw with routers, select right port */ if (radeon_connector->router.ddc_valid) radeon_router_select_ddc_port(radeon_connector); - ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2); + if (use_aux) { + struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; + ret = i2c_transfer(&dig->dp_i2c_bus->adapter, msgs, 2); + } else { + ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2); + } + if (ret != 2) /* Couldn't find an accessible DDC on this connector */ return false; - if (requires_extended_probe) { - /* Probe also for valid EDID header - * EDID header starts with: - * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00. - * Only the first 6 bytes must be valid as - * drm_edid_block_valid() can fix the last 2 bytes */ - if (drm_edid_header_is_valid(buf) < 6) { - /* Couldn't find an accessible EDID on this - * connector */ - return false; - } + /* Probe also for valid EDID header + * EDID header starts with: + * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00. + * Only the first 6 bytes must be valid as + * drm_edid_block_valid() can fix the last 2 bytes */ + if (drm_edid_header_is_valid(buf) < 6) { + /* Couldn't find an accessible EDID on this + * connector */ + return false; } return true; } /* bit banging i2c */ -static void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state) +static int pre_xfer(struct i2c_adapter *i2c_adap) { + struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); struct radeon_device *rdev = i2c->dev->dev_private; struct radeon_i2c_bus_rec *rec = &i2c->rec; uint32_t temp; @@ -137,19 +140,30 @@ static void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state) WREG32(rec->en_data_reg, temp); /* mask the gpio pins for software use */ - temp = RREG32(rec->mask_clk_reg); - if (lock_state) - temp |= rec->mask_clk_mask; - else - temp &= ~rec->mask_clk_mask; + temp = RREG32(rec->mask_clk_reg) | rec->mask_clk_mask; WREG32(rec->mask_clk_reg, temp); temp = RREG32(rec->mask_clk_reg); + temp = RREG32(rec->mask_data_reg) | rec->mask_data_mask; + WREG32(rec->mask_data_reg, temp); temp = RREG32(rec->mask_data_reg); - if (lock_state) - temp |= rec->mask_data_mask; - else - temp &= ~rec->mask_data_mask; + + return 0; +} + +static void post_xfer(struct i2c_adapter *i2c_adap) +{ + struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t temp; + + /* unmask the gpio pins for software use */ + temp = RREG32(rec->mask_clk_reg) & ~rec->mask_clk_mask; + WREG32(rec->mask_clk_reg, temp); + temp = RREG32(rec->mask_clk_reg); + + temp = RREG32(rec->mask_data_reg) & ~rec->mask_data_mask; WREG32(rec->mask_data_reg, temp); temp = RREG32(rec->mask_data_reg); } @@ -209,22 +223,6 @@ static void set_data(void *i2c_priv, int data) WREG32(rec->en_data_reg, val); } -static int pre_xfer(struct i2c_adapter *i2c_adap) -{ - struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); - - radeon_i2c_do_lock(i2c, 1); - - return 0; -} - -static void post_xfer(struct i2c_adapter *i2c_adap) -{ - struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); - - radeon_i2c_do_lock(i2c, 0); -} - /* hw i2c */ static u32 radeon_get_i2c_prescale(struct radeon_device *rdev) @@ -1000,6 +998,9 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c) /* Add the default buses */ void radeon_i2c_init(struct radeon_device *rdev) { + if (radeon_hw_i2c) + DRM_INFO("hw_i2c forced on, you may experience display detection problems!\n"); + if (rdev->is_atom_bios) radeon_atombios_i2c_init(rdev); else diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c index 465746b..00da384 100644 --- a/drivers/gpu/drm/radeon/radeon_irq.c +++ b/drivers/gpu/drm/radeon/radeon_irq.c @@ -129,7 +129,7 @@ void radeon_disable_vblank(struct drm_device *dev, int crtc) } } -static inline u32 radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv, u32 *r500_disp_int) +static u32 radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv, u32 *r500_disp_int) { u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS); u32 irq_mask = RADEON_SW_INT_TEST; diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 1cfe753..42f5a2b 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -51,10 +51,12 @@ static void radeon_hotplug_work_func(struct work_struct *work) struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; + mutex_lock(&mode_config->mutex); if (mode_config->num_connector) { list_for_each_entry(connector, &mode_config->connector_list, head) radeon_connector_hotplug(connector); } + mutex_unlock(&mode_config->mutex); /* Just fire off a uevent and let userspace tell us what to do */ drm_helper_hpd_irq_event(dev); } @@ -67,10 +69,10 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev) /* Disable *all* interrupts */ rdev->irq.sw_int = false; rdev->irq.gui_idle = false; - for (i = 0; i < rdev->num_crtc; i++) - rdev->irq.crtc_vblank_int[i] = false; - for (i = 0; i < 6; i++) { + for (i = 0; i < RADEON_MAX_HPD_PINS; i++) rdev->irq.hpd[i] = false; + for (i = 0; i < RADEON_MAX_CRTCS; i++) { + rdev->irq.crtc_vblank_int[i] = false; rdev->irq.pflip[i] = false; } radeon_irq_set(rdev); @@ -99,10 +101,10 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev) /* Disable *all* interrupts */ rdev->irq.sw_int = false; rdev->irq.gui_idle = false; - for (i = 0; i < rdev->num_crtc; i++) - rdev->irq.crtc_vblank_int[i] = false; - for (i = 0; i < 6; i++) { + for (i = 0; i < RADEON_MAX_HPD_PINS; i++) rdev->irq.hpd[i] = false; + for (i = 0; i < RADEON_MAX_CRTCS; i++) { + rdev->irq.crtc_vblank_int[i] = false; rdev->irq.pflip[i] = false; } radeon_irq_set(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index bd58af6..4bb9e27 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -39,8 +39,12 @@ int radeon_driver_unload_kms(struct drm_device *dev) if (rdev == NULL) return 0; + if (rdev->rmmio == NULL) + goto done_free; radeon_modeset_fini(rdev); radeon_device_fini(rdev); + +done_free: kfree(rdev); dev->dev_private = NULL; return 0; @@ -60,7 +64,7 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags) /* update BUS flag */ if (drm_pci_device_is_agp(dev)) { flags |= RADEON_IS_AGP; - } else if (drm_pci_device_is_pcie(dev)) { + } else if (pci_is_pcie(dev->pdev)) { flags |= RADEON_IS_PCIE; } else { flags |= RADEON_IS_PCI; @@ -237,6 +241,19 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) case RADEON_INFO_FUSION_GART_WORKING: value = 1; break; + case RADEON_INFO_BACKEND_MAP: + if (rdev->family >= CHIP_CAYMAN) + value = rdev->config.cayman.backend_map; + else if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.backend_map; + else if (rdev->family >= CHIP_RV770) + value = rdev->config.rv770.backend_map; + else if (rdev->family >= CHIP_R600) + value = rdev->config.r600.backend_map; + else { + return -EINVAL; + } + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 41a5d48..a9238b0 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -416,12 +416,40 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc, /* Pin framebuffer & get tilling informations */ obj = radeon_fb->obj; rbo = gem_to_radeon_bo(obj); +retry: r = radeon_bo_reserve(rbo, false); if (unlikely(r != 0)) return r; r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base); if (unlikely(r != 0)) { radeon_bo_unreserve(rbo); + + /* On old GPU like RN50 with little vram pining can fails because + * current fb is taking all space needed. So instead of unpining + * the old buffer after pining the new one, first unpin old one + * and then retry pining new one. + * + * As only master can set mode only master can pin and it is + * unlikely the master client will race with itself especialy + * on those old gpu with single crtc. + * + * We don't shutdown the display controller because new buffer + * will end up in same spot. + */ + if (!atomic && fb && fb != crtc->fb) { + struct radeon_bo *old_rbo; + unsigned long nsize, osize; + + old_rbo = gem_to_radeon_bo(to_radeon_framebuffer(fb)->obj); + osize = radeon_bo_size(old_rbo); + nsize = radeon_bo_size(rbo); + if (nsize <= osize && !radeon_bo_reserve(old_rbo, false)) { + radeon_bo_unpin(old_rbo); + radeon_bo_unreserve(old_rbo); + fb = NULL; + goto retry; + } + } return -EINVAL; } radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); @@ -991,12 +1019,6 @@ static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct drm_device *dev = crtc->dev; - struct radeon_device *rdev = dev->dev_private; - - /* adjust pm to upcoming mode change */ - radeon_pm_compute_clocks(rdev); - if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) return false; return true; diff --git a/drivers/gpu/drm/radeon/radeon_legacy_tv.c b/drivers/gpu/drm/radeon/radeon_legacy_tv.c index c7b6cb4..b37ec0f 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_tv.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_tv.c @@ -864,7 +864,7 @@ void radeon_legacy_tv_adjust_crtc_reg(struct drm_encoder *encoder, *v_sync_strt_wid = tmp; } -static inline int get_post_div(int value) +static int get_post_div(int value) { int post_div; switch (value) { diff --git a/drivers/gpu/drm/radeon/radeon_mem.c b/drivers/gpu/drm/radeon/radeon_mem.c index ed95155..988548e 100644 --- a/drivers/gpu/drm/radeon/radeon_mem.c +++ b/drivers/gpu/drm/radeon/radeon_mem.c @@ -139,7 +139,7 @@ static int init_heap(struct mem_block **heap, int start, int size) if (!blocks) return -ENOMEM; - *heap = kmalloc(sizeof(**heap), GFP_KERNEL); + *heap = kzalloc(sizeof(**heap), GFP_KERNEL); if (!*heap) { kfree(blocks); return -ENOMEM; @@ -150,7 +150,6 @@ static int init_heap(struct mem_block **heap, int start, int size) blocks->file_priv = NULL; blocks->next = blocks->prev = *heap; - memset(*heap, 0, sizeof(**heap)); (*heap)->file_priv = (struct drm_file *) - 1; (*heap)->next = (*heap)->prev = blocks; return 0; diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index ed0178f..bb42df4 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -210,6 +210,8 @@ enum radeon_connector_table { CT_RN50_POWER, CT_MAC_X800, CT_MAC_G5_9600, + CT_SAM440EP, + CT_MAC_G4_SILVER }; enum radeon_dvo_chip { @@ -362,6 +364,7 @@ struct radeon_encoder_atom_dig { struct backlight_device *bl_dev; int dpms_mode; uint8_t backlight_level; + int panel_mode; }; struct radeon_encoder_atom_dac { @@ -438,9 +441,6 @@ struct radeon_connector { struct radeon_i2c_chan *ddc_bus; /* some systems have an hdmi and vga port with a shared ddc line */ bool shared_ddc; - /* for some Radeon chip families we apply an additional EDID header - check as part of the DDC probe */ - bool requires_extended_probe; bool use_digital; /* we need to mind the EDID between detect and get modes due to analog/digital/tvencoder */ @@ -459,6 +459,8 @@ struct radeon_framebuffer { struct drm_gem_object *obj; }; +#define ENCODER_MODE_IS_DP(em) (((em) == ATOM_ENCODER_MODE_DP) || \ + ((em) == ATOM_ENCODER_MODE_DP_MST)) extern enum radeon_tv_std radeon_combios_get_tv_info(struct radeon_device *rdev); @@ -468,8 +470,8 @@ radeon_atombios_get_tv_info(struct radeon_device *rdev); extern struct drm_connector * radeon_get_connector_for_encoder(struct drm_encoder *encoder); -extern bool radeon_encoder_is_dp_bridge(struct drm_encoder *encoder); -extern bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector); +extern u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder); +extern u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector); extern bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector); extern bool radeon_connector_is_dp12_capable(struct drm_connector *connector); @@ -483,13 +485,15 @@ extern void radeon_dp_link_train(struct drm_encoder *encoder, extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector); extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector); extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector); +extern int radeon_dp_get_panel_mode(struct drm_encoder *encoder, + struct drm_connector *connector); extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode); extern void radeon_atom_encoder_init(struct radeon_device *rdev); extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set); extern void radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder); -extern struct drm_encoder *radeon_atom_get_external_encoder(struct drm_encoder *encoder); +extern struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder); extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, u8 write_byte, u8 *read_byte); @@ -519,8 +523,7 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c, u8 val); extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector); extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector); -extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector, - bool requires_extended_probe); +extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool use_aux); extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 35da1b4..3e35bbe 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -513,21 +513,70 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo) rbo = container_of(bo, struct radeon_bo, tbo); radeon_bo_check_tiling(rbo, 0, 0); rdev = rbo->rdev; - if (bo->mem.mem_type == TTM_PL_VRAM) { - size = bo->mem.num_pages << PAGE_SHIFT; - offset = bo->mem.start << PAGE_SHIFT; - if ((offset + size) > rdev->mc.visible_vram_size) { - /* hurrah the memory is not visible ! */ - radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); - rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; - r = ttm_bo_validate(bo, &rbo->placement, false, true, false); - if (unlikely(r != 0)) - return r; - offset = bo->mem.start << PAGE_SHIFT; - /* this should not happen */ - if ((offset + size) > rdev->mc.visible_vram_size) - return -EINVAL; - } + if (bo->mem.mem_type != TTM_PL_VRAM) + return 0; + + size = bo->mem.num_pages << PAGE_SHIFT; + offset = bo->mem.start << PAGE_SHIFT; + if ((offset + size) <= rdev->mc.visible_vram_size) + return 0; + + /* hurrah the memory is not visible ! */ + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); + rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; + r = ttm_bo_validate(bo, &rbo->placement, false, true, false); + if (unlikely(r == -ENOMEM)) { + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT); + return ttm_bo_validate(bo, &rbo->placement, false, true, false); + } else if (unlikely(r != 0)) { + return r; + } + + offset = bo->mem.start << PAGE_SHIFT; + /* this should never happen */ + if ((offset + size) > rdev->mc.visible_vram_size) + return -EINVAL; + + return 0; +} + +int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); + if (unlikely(r != 0)) + return r; + spin_lock(&bo->tbo.bdev->fence_lock); + if (mem_type) + *mem_type = bo->tbo.mem.mem_type; + if (bo->tbo.sync_obj) + r = ttm_bo_wait(&bo->tbo, true, true, no_wait); + spin_unlock(&bo->tbo.bdev->fence_lock); + ttm_bo_unreserve(&bo->tbo); + return r; +} + + +/** + * radeon_bo_reserve - reserve bo + * @bo: bo structure + * @no_wait: don't sleep while trying to reserve (return -EBUSY) + * + * Returns: + * -EBUSY: buffer is busy and @no_wait is true + * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by + * a signal. Release all buffer reservations and return to user-space. + */ +int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); + if (unlikely(r != 0)) { + if (r != -ERESTARTSYS) + dev_err(bo->rdev->dev, "%p reserve failed\n", bo); + return r; } return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h index 7199c6a..fb3f433 100644 --- a/drivers/gpu/drm/radeon/radeon_object.h +++ b/drivers/gpu/drm/radeon/radeon_object.h @@ -52,28 +52,7 @@ static inline unsigned radeon_mem_type_to_domain(u32 mem_type) return 0; } -/** - * radeon_bo_reserve - reserve bo - * @bo: bo structure - * @no_wait: don't sleep while trying to reserve (return -EBUSY) - * - * Returns: - * -EBUSY: buffer is busy and @no_wait is true - * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by - * a signal. Release all buffer reservations and return to user-space. - */ -static inline int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait) -{ - int r; - - r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); - if (unlikely(r != 0)) { - if (r != -ERESTARTSYS) - dev_err(bo->rdev->dev, "%p reserve failed\n", bo); - return r; - } - return 0; -} +int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait); static inline void radeon_bo_unreserve(struct radeon_bo *bo) { @@ -118,23 +97,8 @@ static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo) return bo->tbo.addr_space_offset; } -static inline int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, - bool no_wait) -{ - int r; - - r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); - if (unlikely(r != 0)) - return r; - spin_lock(&bo->tbo.bdev->fence_lock); - if (mem_type) - *mem_type = bo->tbo.mem.mem_type; - if (bo->tbo.sync_obj) - r = ttm_bo_wait(&bo->tbo, true, true, no_wait); - spin_unlock(&bo->tbo.bdev->fence_lock); - ttm_bo_unreserve(&bo->tbo); - return r; -} +extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, + bool no_wait); extern int radeon_bo_create(struct radeon_device *rdev, unsigned long size, int byte_align, diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 8270a85..b0a0ee4 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -53,6 +53,24 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev); #define ACPI_AC_CLASS "ac_adapter" +int radeon_pm_get_type_index(struct radeon_device *rdev, + enum radeon_pm_state_type ps_type, + int instance) +{ + int i; + int found_instance = -1; + + for (i = 0; i < rdev->pm.num_power_states; i++) { + if (rdev->pm.power_state[i].type == ps_type) { + found_instance++; + if (found_instance == instance) + return i; + } + } + /* return default if no match */ + return rdev->pm.default_power_state_index; +} + #ifdef CONFIG_ACPI static int radeon_acpi_event(struct notifier_block *nb, unsigned long val, @@ -555,8 +573,10 @@ void radeon_pm_resume(struct radeon_device *rdev) rdev->pm.current_clock_mode_index = 0; rdev->pm.current_sclk = rdev->pm.default_sclk; rdev->pm.current_mclk = rdev->pm.default_mclk; - rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; - rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci; + if (rdev->pm.power_state) { + rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; + rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci; + } if (rdev->pm.pm_method == PM_METHOD_DYNPM && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) { rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h index bc44a3d..b4ce864 100644 --- a/drivers/gpu/drm/radeon/radeon_reg.h +++ b/drivers/gpu/drm/radeon/radeon_reg.h @@ -3295,7 +3295,7 @@ # define RADEON_RB_BUFSZ_MASK (0x3f << 0) # define RADEON_RB_BLKSZ_SHIFT 8 # define RADEON_RB_BLKSZ_MASK (0x3f << 8) -# define RADEON_BUF_SWAP_32BIT (1 << 17) +# define RADEON_BUF_SWAP_32BIT (2 << 16) # define RADEON_MAX_FETCH_SHIFT 18 # define RADEON_MAX_FETCH_MASK (0x3 << 18) # define RADEON_RB_NO_UPDATE (1 << 27) diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 08c0233..65be5e8 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -35,6 +35,44 @@ int radeon_debugfs_ib_init(struct radeon_device *rdev); +u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) +{ + struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; + u32 pg_idx, pg_offset; + u32 idx_value = 0; + int new_page; + + pg_idx = (idx * 4) / PAGE_SIZE; + pg_offset = (idx * 4) % PAGE_SIZE; + + if (ibc->kpage_idx[0] == pg_idx) + return ibc->kpage[0][pg_offset/4]; + if (ibc->kpage_idx[1] == pg_idx) + return ibc->kpage[1][pg_offset/4]; + + new_page = radeon_cs_update_pages(p, pg_idx); + if (new_page < 0) { + p->parser_error = new_page; + return 0; + } + + idx_value = ibc->kpage[new_page][pg_offset/4]; + return idx_value; +} + +void radeon_ring_write(struct radeon_device *rdev, uint32_t v) +{ +#if DRM_DEBUG_CODE + if (rdev->cp.count_dw <= 0) { + DRM_ERROR("radeon: writting more dword to ring than expected !\n"); + } +#endif + rdev->cp.ring[rdev->cp.wptr++] = v; + rdev->cp.wptr &= rdev->cp.ptr_mask; + rdev->cp.count_dw--; + rdev->cp.ring_free_dw--; +} + void radeon_ib_bogus_cleanup(struct radeon_device *rdev) { struct radeon_ib *ib, *n; @@ -268,6 +306,9 @@ int radeon_ring_alloc(struct radeon_device *rdev, unsigned ndw) { int r; + /* make sure we aren't trying to allocate more space than there is on the ring */ + if (ndw > (rdev->cp.ring_size / 4)) + return -ENOMEM; /* Align requested size with padding so unlock_commit can * pad safely */ ndw = (ndw + rdev->cp.align_mask) & ~rdev->cp.align_mask; diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c index 92e7ea7..e8422ae 100644 --- a/drivers/gpu/drm/radeon/radeon_state.c +++ b/drivers/gpu/drm/radeon/radeon_state.c @@ -272,12 +272,12 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t * return 0; } -static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * - dev_priv, - struct drm_file *file_priv, - drm_radeon_kcmd_buffer_t * - cmdbuf, - unsigned int *cmdsz) +static int radeon_check_and_fixup_packet3(drm_radeon_private_t * + dev_priv, + struct drm_file *file_priv, + drm_radeon_kcmd_buffer_t * + cmdbuf, + unsigned int *cmdsz) { u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); u32 offset, narrays; @@ -446,8 +446,8 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * * CP hardware state programming functions */ -static __inline__ void radeon_emit_clip_rect(drm_radeon_private_t * dev_priv, - struct drm_clip_rect * box) +static void radeon_emit_clip_rect(drm_radeon_private_t * dev_priv, + struct drm_clip_rect * box) { RING_LOCALS; diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index dee4a0c..602fa35 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -40,10 +40,14 @@ void radeon_test_moves(struct radeon_device *rdev) size = 1024 * 1024; /* Number of tests = - * (Total GTT - IB pool - writeback page - ring buffer) / test size + * (Total GTT - IB pool - writeback page - ring buffers) / test size */ - n = ((u32)(rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024 - RADEON_GPU_PAGE_SIZE - - rdev->cp.ring_size)) / size; + n = rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024 - rdev->cp.ring_size; + if (rdev->wb.wb_obj) + n -= RADEON_GPU_PAGE_SIZE; + if (rdev->ih.ring_obj) + n -= rdev->ih.ring_size; + n /= size; gtt_obj = kzalloc(n * sizeof(*gtt_obj), GFP_KERNEL); if (!gtt_obj) { @@ -132,9 +136,15 @@ void radeon_test_moves(struct radeon_device *rdev) gtt_start++, vram_start++) { if (*vram_start != gtt_start) { DRM_ERROR("Incorrect GTT->VRAM copy %d: Got 0x%p, " - "expected 0x%p (GTT map 0x%p-0x%p)\n", - i, *vram_start, gtt_start, gtt_map, - gtt_end); + "expected 0x%p (GTT/VRAM offset " + "0x%16llx/0x%16llx)\n", + i, *vram_start, gtt_start, + (unsigned long long) + (gtt_addr - rdev->mc.gtt_start + + (void*)gtt_start - gtt_map), + (unsigned long long) + (vram_addr - rdev->mc.vram_start + + (void*)gtt_start - gtt_map)); radeon_bo_kunmap(vram_obj); goto out_cleanup; } @@ -175,9 +185,15 @@ void radeon_test_moves(struct radeon_device *rdev) gtt_start++, vram_start++) { if (*gtt_start != vram_start) { DRM_ERROR("Incorrect VRAM->GTT copy %d: Got 0x%p, " - "expected 0x%p (VRAM map 0x%p-0x%p)\n", - i, *gtt_start, vram_start, vram_map, - vram_end); + "expected 0x%p (VRAM/GTT offset " + "0x%16llx/0x%16llx)\n", + i, *gtt_start, vram_start, + (unsigned long long) + (vram_addr - rdev->mc.vram_start + + (void*)vram_start - vram_map), + (unsigned long long) + (gtt_addr - rdev->mc.gtt_start + + (void*)vram_start - vram_map)); radeon_bo_kunmap(gtt_obj[i]); goto out_cleanup; } diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 3e9b41b..0b5468b 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -455,6 +455,29 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_ return -EINVAL; mem->bus.base = rdev->mc.aper_base; mem->bus.is_iomem = true; +#ifdef __alpha__ + /* + * Alpha: use bus.addr to hold the ioremap() return, + * so we can modify bus.base below. + */ + if (mem->placement & TTM_PL_FLAG_WC) + mem->bus.addr = + ioremap_wc(mem->bus.base + mem->bus.offset, + mem->bus.size); + else + mem->bus.addr = + ioremap_nocache(mem->bus.base + mem->bus.offset, + mem->bus.size); + + /* + * Alpha: Use just the bus offset plus + * the hose/domain memory base for bus.base. + * It then can be used to build PTEs for VRAM + * access, as done in ttm_bo_vm_fault(). + */ + mem->bus.base = (mem->bus.base & 0x0ffffffffUL) + + rdev->ddev->hose->dense_mem_base; +#endif break; default: return -EINVAL; diff --git a/drivers/gpu/drm/radeon/reg_srcs/cayman b/drivers/gpu/drm/radeon/reg_srcs/cayman index 0aa8e85..2316977 100644 --- a/drivers/gpu/drm/radeon/reg_srcs/cayman +++ b/drivers/gpu/drm/radeon/reg_srcs/cayman @@ -208,6 +208,7 @@ cayman 0x9400 0x0002834C PA_SC_VPORT_ZMAX_15 0x00028350 SX_MISC 0x00028354 SX_SURFACE_SYNC +0x0002835C SX_SCATTER_EXPORT_SIZE 0x00028380 SQ_VTX_SEMANTIC_0 0x00028384 SQ_VTX_SEMANTIC_1 0x00028388 SQ_VTX_SEMANTIC_2 @@ -432,6 +433,7 @@ cayman 0x9400 0x00028700 SPI_STACK_MGMT 0x00028704 SPI_WAVE_MGMT_1 0x00028708 SPI_WAVE_MGMT_2 +0x00028720 GDS_ADDR_BASE 0x00028724 GDS_ADDR_SIZE 0x00028780 CB_BLEND0_CONTROL 0x00028784 CB_BLEND1_CONTROL diff --git a/drivers/gpu/drm/radeon/reg_srcs/evergreen b/drivers/gpu/drm/radeon/reg_srcs/evergreen index 0e28cae..161737a 100644 --- a/drivers/gpu/drm/radeon/reg_srcs/evergreen +++ b/drivers/gpu/drm/radeon/reg_srcs/evergreen @@ -44,6 +44,7 @@ evergreen 0x9400 0x00008E28 SQ_STATIC_THREAD_MGMT_3 0x00008E2C SQ_LDS_RESOURCE_MGMT 0x00008E48 SQ_EX_ALLOC_TABLE_SLOTS +0x00009014 SX_MEMORY_EXPORT_SIZE 0x00009100 SPI_CONFIG_CNTL 0x0000913C SPI_CONFIG_CNTL_1 0x00009508 TA_CNTL_AUX @@ -442,7 +443,9 @@ evergreen 0x9400 0x000286EC SPI_COMPUTE_NUM_THREAD_X 0x000286F0 SPI_COMPUTE_NUM_THREAD_Y 0x000286F4 SPI_COMPUTE_NUM_THREAD_Z +0x00028720 GDS_ADDR_BASE 0x00028724 GDS_ADDR_SIZE +0x00028728 GDS_ORDERED_WAVE_PER_SE 0x00028780 CB_BLEND0_CONTROL 0x00028784 CB_BLEND1_CONTROL 0x00028788 CB_BLEND2_CONTROL diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600 index ea49752..0380c5c 100644 --- a/drivers/gpu/drm/radeon/reg_srcs/r600 +++ b/drivers/gpu/drm/radeon/reg_srcs/r600 @@ -429,6 +429,7 @@ r600 0x9400 0x00028438 SX_ALPHA_REF 0x00028410 SX_ALPHA_TEST_CONTROL 0x00028350 SX_MISC +0x00009014 SX_MEMORY_EXPORT_SIZE 0x00009604 TC_INVALIDATE 0x00009400 TD_FILTER4 0x00009404 TD_FILTER4_1 diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index 317eac1..c087434 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -77,7 +77,7 @@ int rs400_gart_init(struct radeon_device *rdev) { int r; - if (rdev->gart.table.ram.ptr) { + if (rdev->gart.ptr) { WARN(1, "RS400 GART already initialized\n"); return 0; } @@ -185,6 +185,9 @@ int rs400_gart_enable(struct radeon_device *rdev) /* Enable gart */ WREG32_MC(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | size_reg)); rs400_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); rdev->gart.ready = true; return 0; } @@ -212,6 +215,7 @@ void rs400_gart_fini(struct radeon_device *rdev) int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) { uint32_t entry; + u32 *gtt = rdev->gart.ptr; if (i < 0 || i > rdev->gart.num_gpu_pages) { return -EINVAL; @@ -221,7 +225,7 @@ int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) ((upper_32_bits(addr) & 0xff) << 4) | RS400_PTE_WRITEABLE | RS400_PTE_READABLE; entry = cpu_to_le32(entry); - rdev->gart.table.ram.ptr[i] = entry; + gtt[i] = entry; return 0; } @@ -410,6 +414,12 @@ static int rs400_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + r100_irq_set(rdev); rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); /* 1M ring buffer */ @@ -518,9 +528,6 @@ int rs400_init(struct radeon_device *rdev) r = radeon_fence_driver_init(rdev); if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; /* Memory manager */ r = radeon_bo_init(rdev); if (r) diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 2026c2d..fc37558 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -419,7 +419,7 @@ int rs600_gart_init(struct radeon_device *rdev) { int r; - if (rdev->gart.table.vram.robj) { + if (rdev->gart.robj) { WARN(1, "RS600 GART already initialized\n"); return 0; } @@ -437,7 +437,7 @@ static int rs600_gart_enable(struct radeon_device *rdev) u32 tmp; int r, i; - if (rdev->gart.table.vram.robj == NULL) { + if (rdev->gart.robj == NULL) { dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); return -EINVAL; } @@ -490,6 +490,9 @@ static int rs600_gart_enable(struct radeon_device *rdev) tmp = RREG32_MC(R_000009_MC_CNTL1); WREG32_MC(R_000009_MC_CNTL1, (tmp | S_000009_ENABLE_PAGE_TABLES(1))); rs600_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); rdev->gart.ready = true; return 0; } @@ -497,20 +500,12 @@ static int rs600_gart_enable(struct radeon_device *rdev) void rs600_gart_disable(struct radeon_device *rdev) { u32 tmp; - int r; /* FIXME: disable out of gart access */ WREG32_MC(R_000100_MC_PT0_CNTL, 0); tmp = RREG32_MC(R_000009_MC_CNTL1); WREG32_MC(R_000009_MC_CNTL1, tmp & C_000009_ENABLE_PAGE_TABLES); - if (rdev->gart.table.vram.robj) { - r = radeon_bo_reserve(rdev->gart.table.vram.robj, false); - if (r == 0) { - radeon_bo_kunmap(rdev->gart.table.vram.robj); - radeon_bo_unpin(rdev->gart.table.vram.robj); - radeon_bo_unreserve(rdev->gart.table.vram.robj); - } - } + radeon_gart_table_vram_unpin(rdev); } void rs600_gart_fini(struct radeon_device *rdev) @@ -528,15 +523,18 @@ void rs600_gart_fini(struct radeon_device *rdev) int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) { - void __iomem *ptr = (void *)rdev->gart.table.vram.ptr; + void __iomem *ptr = (void *)rdev->gart.ptr; if (i < 0 || i > rdev->gart.num_gpu_pages) { return -EINVAL; } addr = addr & 0xFFFFFFFFFFFFF000ULL; - addr |= R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED; - addr |= R600_PTE_READABLE | R600_PTE_WRITEABLE; - writeq(addr, ((void __iomem *)ptr) + (i * 8)); + if (addr == rdev->dummy_page.addr) + addr |= R600_PTE_SYSTEM | R600_PTE_SNOOPED; + else + addr |= (R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED | + R600_PTE_READABLE | R600_PTE_WRITEABLE); + writeq(addr, ptr + (i * 8)); return 0; } @@ -578,6 +576,10 @@ int rs600_irq_set(struct radeon_device *rdev) WREG32(R_006540_DxMODE_INT_MASK, mode_int); WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1); WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2); + + /* posting read */ + RREG32(R_000040_GEN_INT_CNTL); + return 0; } @@ -853,6 +855,12 @@ static int rs600_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + rs600_irq_set(rdev); rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); /* 1M ring buffer */ @@ -968,9 +976,6 @@ int rs600_init(struct radeon_device *rdev) r = radeon_fence_driver_init(rdev); if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; /* Memory manager */ r = radeon_bo_init(rdev); if (r) diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index a9049ed..414a681 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -160,6 +160,16 @@ void rs690_mc_init(struct radeon_device *rdev) base = RREG32_MC(R_000100_MCCFG_FB_LOCATION); base = G_000100_MC_FB_START(base) << 16; rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev); + /* Some boards seem to be configured for 128MB of sideport memory, + * but really only have 64MB. Just skip the sideport and use + * UMA memory. + */ + if (rdev->mc.igp_sideport_enabled && + (rdev->mc.real_vram_size == (384 * 1024 * 1024))) { + base += 128 * 1024 * 1024; + rdev->mc.real_vram_size -= 128 * 1024 * 1024; + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; + } rs690_pm_info(rdev); radeon_vram_location(rdev, &rdev->mc, base); rdev->mc.gtt_base_align = rdev->mc.gtt_size - 1; @@ -622,6 +632,12 @@ static int rs690_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + rs600_irq_set(rdev); rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); /* 1M ring buffer */ @@ -738,9 +754,6 @@ int rs690_init(struct radeon_device *rdev) r = radeon_fence_driver_init(rdev); if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; /* Memory manager */ r = radeon_bo_init(rdev); if (r) diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index d5f45b4..9103638 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -380,6 +380,12 @@ static int rv515_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + rs600_irq_set(rdev); rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); /* 1M ring buffer */ @@ -500,9 +506,6 @@ int rv515_init(struct radeon_device *rdev) r = radeon_fence_driver_init(rdev); if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; /* Memory manager */ r = radeon_bo_init(rdev); if (r) diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 51d20aa..3e72074 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -129,7 +129,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev) u32 tmp; int r, i; - if (rdev->gart.table.vram.robj == NULL) { + if (rdev->gart.robj == NULL) { dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); return -EINVAL; } @@ -168,6 +168,9 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev) WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); r600_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); rdev->gart.ready = true; return 0; } @@ -175,7 +178,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev) void rv770_pcie_gart_disable(struct radeon_device *rdev) { u32 tmp; - int i, r; + int i; /* Disable all tables */ for (i = 0; i < 7; i++) @@ -195,14 +198,7 @@ void rv770_pcie_gart_disable(struct radeon_device *rdev) WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); - if (rdev->gart.table.vram.robj) { - r = radeon_bo_reserve(rdev->gart.table.vram.robj, false); - if (likely(r == 0)) { - radeon_bo_kunmap(rdev->gart.table.vram.robj); - radeon_bo_unpin(rdev->gart.table.vram.robj); - radeon_bo_unreserve(rdev->gart.table.vram.robj); - } - } + radeon_gart_table_vram_unpin(rdev); } void rv770_pcie_gart_fini(struct radeon_device *rdev) @@ -286,7 +282,7 @@ static void rv770_mc_program(struct radeon_device *rdev) WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end >> 12); } - WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0); + WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12); tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); WREG32(MC_VM_FB_LOCATION, tmp); @@ -695,8 +691,12 @@ static void rv770_gpu_init(struct radeon_device *rdev) if (rdev->family == CHIP_RV770) gb_tiling_config |= BANK_TILING(1); - else - gb_tiling_config |= BANK_TILING((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); + else { + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + gb_tiling_config |= BANK_TILING(1); + else + gb_tiling_config |= BANK_TILING(0); + } rdev->config.rv770.tiling_nbanks = 4 << ((gb_tiling_config >> 4) & 0x3); gb_tiling_config |= GROUP_SIZE((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); if ((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) @@ -736,6 +736,7 @@ static void rv770_gpu_init(struct radeon_device *rdev) (cc_rb_backend_disable >> 16)); rdev->config.rv770.tile_config = gb_tiling_config; + rdev->config.rv770.backend_map = backend_map; gb_tiling_config |= BACKEND_MAP(backend_map); WREG32(GB_TILING_CONFIG, gb_tiling_config); @@ -781,6 +782,9 @@ static void rv770_gpu_init(struct radeon_device *rdev) ACK_FLUSH_CTL(3) | SYNC_FLUSH_CTL)); + if (rdev->family != CHIP_RV770) + WREG32(SMX_SAR_CTL0, 0x00003f3f); + db_debug3 = RREG32(DB_DEBUG3); db_debug3 &= ~DB_CLK_OFF_DELAY(0x1f); switch (rdev->family) { @@ -959,55 +963,7 @@ static void rv770_gpu_init(struct radeon_device *rdev) WREG32(PA_CL_ENHANCE, (CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3))); - -} - -static int rv770_vram_scratch_init(struct radeon_device *rdev) -{ - int r; - u64 gpu_addr; - - if (rdev->vram_scratch.robj == NULL) { - r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, - PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - &rdev->vram_scratch.robj); - if (r) { - return r; - } - } - - r = radeon_bo_reserve(rdev->vram_scratch.robj, false); - if (unlikely(r != 0)) - return r; - r = radeon_bo_pin(rdev->vram_scratch.robj, - RADEON_GEM_DOMAIN_VRAM, &gpu_addr); - if (r) { - radeon_bo_unreserve(rdev->vram_scratch.robj); - return r; - } - r = radeon_bo_kmap(rdev->vram_scratch.robj, - (void **)&rdev->vram_scratch.ptr); - if (r) - radeon_bo_unpin(rdev->vram_scratch.robj); - radeon_bo_unreserve(rdev->vram_scratch.robj); - - return r; -} - -static void rv770_vram_scratch_fini(struct radeon_device *rdev) -{ - int r; - - if (rdev->vram_scratch.robj == NULL) { - return; - } - r = radeon_bo_reserve(rdev->vram_scratch.robj, false); - if (likely(r == 0)) { - radeon_bo_kunmap(rdev->vram_scratch.robj); - radeon_bo_unpin(rdev->vram_scratch.robj); - radeon_bo_unreserve(rdev->vram_scratch.robj); - } - radeon_bo_unref(&rdev->vram_scratch.robj); + WREG32(VC_ENHANCE, 0); } void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) @@ -1101,6 +1057,8 @@ static int rv770_startup(struct radeon_device *rdev) /* enable pcie gen2 link */ rv770_pcie_gen2_enable(rdev); + rv770_mc_program(rdev); + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) { r = r600_init_microcode(rdev); if (r) { @@ -1109,7 +1067,10 @@ static int rv770_startup(struct radeon_device *rdev) } } - rv770_mc_program(rdev); + r = r600_vram_scratch_init(rdev); + if (r) + return r; + if (rdev->flags & RADEON_IS_AGP) { rv770_agp_enable(rdev); } else { @@ -1117,9 +1078,7 @@ static int rv770_startup(struct radeon_device *rdev) if (r) return r; } - r = rv770_vram_scratch_init(rdev); - if (r) - return r; + rv770_gpu_init(rdev); r = r600_blit_init(rdev); if (r) { @@ -1134,6 +1093,12 @@ static int rv770_startup(struct radeon_device *rdev) return r; /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + r = r600_irq_init(rdev); if (r) { DRM_ERROR("radeon: IH init failed (%d).\n", r); @@ -1190,8 +1155,6 @@ int rv770_resume(struct radeon_device *rdev) int rv770_suspend(struct radeon_device *rdev) { - int r; - r600_audio_fini(rdev); /* FIXME: we should wait for ring to be empty */ r700_cp_stop(rdev); @@ -1199,14 +1162,8 @@ int rv770_suspend(struct radeon_device *rdev) r600_irq_suspend(rdev); radeon_wb_disable(rdev); rv770_pcie_gart_disable(rdev); - /* unpin shaders bo */ - if (rdev->r600_blit.shader_obj) { - r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); - if (likely(r == 0)) { - radeon_bo_unpin(rdev->r600_blit.shader_obj); - radeon_bo_unreserve(rdev->r600_blit.shader_obj); - } - } + r600_blit_suspend(rdev); + return 0; } @@ -1270,10 +1227,6 @@ int rv770_init(struct radeon_device *rdev) if (r) return r; - r = radeon_irq_kms_init(rdev); - if (r) - return r; - rdev->cp.ring_obj = NULL; r600_ring_init(rdev, 1024 * 1024); @@ -1327,7 +1280,7 @@ void rv770_fini(struct radeon_device *rdev) radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); rv770_pcie_gart_fini(rdev); - rv770_vram_scratch_fini(rdev); + r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); radeon_fence_driver_fini(rdev); radeon_agp_fini(rdev); diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h index 7538092..7095a71 100644 --- a/drivers/gpu/drm/radeon/rv770d.h +++ b/drivers/gpu/drm/radeon/rv770d.h @@ -208,6 +208,7 @@ #define SCRATCH_UMSK 0x8540 #define SCRATCH_ADDR 0x8544 +#define SMX_SAR_CTL0 0xA008 #define SMX_DC_CTL0 0xA020 #define USE_HASH_FUNCTION (1 << 0) #define CACHE_DEPTH(x) ((x) << 1) @@ -307,6 +308,8 @@ #define TCP_CNTL 0x9610 #define TCP_CHAN_STEER 0x9614 +#define VC_ENHANCE 0x9714 + #define VGT_CACHE_INVALIDATION 0x88C4 #define CACHE_INVALIDATION(x) ((x)<<0) #define VC_ONLY 0 diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c index 6464490..5468d1c 100644 --- a/drivers/gpu/drm/savage/savage_drv.c +++ b/drivers/gpu/drm/savage/savage_drv.c @@ -23,6 +23,8 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <linux/module.h> + #include "drmP.h" #include "savage_drm.h" #include "savage_drv.h" diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index 46d5be6..a9c5716 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -25,6 +25,8 @@ * */ +#include <linux/module.h> + #include "drmP.h" #include "sis_drm.h" #include "sis_drv.h" diff --git a/drivers/gpu/drm/sis/sis_drv.h b/drivers/gpu/drm/sis/sis_drv.h index ef940ba..194303c 100644 --- a/drivers/gpu/drm/sis/sis_drv.h +++ b/drivers/gpu/drm/sis/sis_drv.h @@ -48,8 +48,8 @@ enum sis_family { #define SIS_BASE (dev_priv->mmio) -#define SIS_READ(reg) DRM_READ32(SIS_BASE, reg); -#define SIS_WRITE(reg, val) DRM_WRITE32(SIS_BASE, reg, val); +#define SIS_READ(reg) DRM_READ32(SIS_BASE, reg) +#define SIS_WRITE(reg, val) DRM_WRITE32(SIS_BASE, reg, val) typedef struct drm_sis_private { drm_local_map_t *mmio; diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c index 8bf9881..cda2991 100644 --- a/drivers/gpu/drm/tdfx/tdfx_drv.c +++ b/drivers/gpu/drm/tdfx/tdfx_drv.c @@ -30,6 +30,8 @@ * Gareth Hughes <gareth@valinux.com> */ +#include <linux/module.h> + #include "drmP.h" #include "tdfx_drv.h" diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile index b2b33dd..f3cf6f0 100644 --- a/drivers/gpu/drm/ttm/Makefile +++ b/drivers/gpu/drm/ttm/Makefile @@ -7,8 +7,4 @@ ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \ ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \ ttm_bo_manager.o -ifeq ($(CONFIG_SWIOTLB),y) -ttm-y += ttm_page_alloc_dma.o -endif - obj-$(CONFIG_DRM_TTM) += ttm.o diff --git a/drivers/gpu/drm/ttm/ttm_agp_backend.c b/drivers/gpu/drm/ttm/ttm_agp_backend.c index 4a87282..1c4a72f 100644 --- a/drivers/gpu/drm/ttm/ttm_agp_backend.c +++ b/drivers/gpu/drm/ttm/ttm_agp_backend.c @@ -29,11 +29,8 @@ * Keith Packard. */ -#define pr_fmt(fmt) "[TTM] " fmt - #include "ttm/ttm_module.h" #include "ttm/ttm_bo_driver.h" -#include "ttm/ttm_page_alloc.h" #ifdef TTM_HAS_AGP #include "ttm/ttm_placement.h" #include <linux/agp_backend.h> @@ -43,77 +40,100 @@ #include <asm/agp.h> struct ttm_agp_backend { - struct ttm_tt ttm; + struct ttm_backend backend; struct agp_memory *mem; struct agp_bridge_data *bridge; }; -static int ttm_agp_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) +static int ttm_agp_populate(struct ttm_backend *backend, + unsigned long num_pages, struct page **pages, + struct page *dummy_read_page, + dma_addr_t *dma_addrs) { - struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm); - struct drm_mm_node *node = bo_mem->mm_node; + struct ttm_agp_backend *agp_be = + container_of(backend, struct ttm_agp_backend, backend); + struct page **cur_page, **last_page = pages + num_pages; struct agp_memory *mem; - int ret, cached = (bo_mem->placement & TTM_PL_FLAG_CACHED); - unsigned i; - mem = agp_allocate_memory(agp_be->bridge, ttm->num_pages, AGP_USER_MEMORY); + mem = agp_allocate_memory(agp_be->bridge, num_pages, AGP_USER_MEMORY); if (unlikely(mem == NULL)) return -ENOMEM; mem->page_count = 0; - for (i = 0; i < ttm->num_pages; i++) { - struct page *page = ttm->pages[i]; - + for (cur_page = pages; cur_page < last_page; ++cur_page) { + struct page *page = *cur_page; if (!page) - page = ttm->dummy_read_page; + page = dummy_read_page; mem->pages[mem->page_count++] = page; } agp_be->mem = mem; + return 0; +} + +static int ttm_agp_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem) +{ + struct ttm_agp_backend *agp_be = + container_of(backend, struct ttm_agp_backend, backend); + struct drm_mm_node *node = bo_mem->mm_node; + struct agp_memory *mem = agp_be->mem; + int cached = (bo_mem->placement & TTM_PL_FLAG_CACHED); + int ret; mem->is_flushed = 1; mem->type = (cached) ? AGP_USER_CACHED_MEMORY : AGP_USER_MEMORY; ret = agp_bind_memory(mem, node->start); if (ret) - pr_err("AGP Bind memory failed\n"); + printk(KERN_ERR TTM_PFX "AGP Bind memory failed.\n"); return ret; } -static int ttm_agp_unbind(struct ttm_tt *ttm) +static int ttm_agp_unbind(struct ttm_backend *backend) { - struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm); + struct ttm_agp_backend *agp_be = + container_of(backend, struct ttm_agp_backend, backend); + + if (agp_be->mem->is_bound) + return agp_unbind_memory(agp_be->mem); + else + return 0; +} - if (agp_be->mem) { - if (agp_be->mem->is_bound) - return agp_unbind_memory(agp_be->mem); - agp_free_memory(agp_be->mem); - agp_be->mem = NULL; +static void ttm_agp_clear(struct ttm_backend *backend) +{ + struct ttm_agp_backend *agp_be = + container_of(backend, struct ttm_agp_backend, backend); + struct agp_memory *mem = agp_be->mem; + + if (mem) { + ttm_agp_unbind(backend); + agp_free_memory(mem); } - return 0; + agp_be->mem = NULL; } -static void ttm_agp_destroy(struct ttm_tt *ttm) +static void ttm_agp_destroy(struct ttm_backend *backend) { - struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm); + struct ttm_agp_backend *agp_be = + container_of(backend, struct ttm_agp_backend, backend); if (agp_be->mem) - ttm_agp_unbind(ttm); - ttm_tt_fini(ttm); + ttm_agp_clear(backend); kfree(agp_be); } static struct ttm_backend_func ttm_agp_func = { + .populate = ttm_agp_populate, + .clear = ttm_agp_clear, .bind = ttm_agp_bind, .unbind = ttm_agp_unbind, .destroy = ttm_agp_destroy, }; -struct ttm_tt *ttm_agp_tt_create(struct ttm_bo_device *bdev, - struct agp_bridge_data *bridge, - unsigned long size, uint32_t page_flags, - struct page *dummy_read_page) +struct ttm_backend *ttm_agp_backend_init(struct ttm_bo_device *bdev, + struct agp_bridge_data *bridge) { struct ttm_agp_backend *agp_be; @@ -123,29 +143,10 @@ struct ttm_tt *ttm_agp_tt_create(struct ttm_bo_device *bdev, agp_be->mem = NULL; agp_be->bridge = bridge; - agp_be->ttm.func = &ttm_agp_func; - - if (ttm_tt_init(&agp_be->ttm, bdev, size, page_flags, dummy_read_page)) { - return NULL; - } - - return &agp_be->ttm; -} -EXPORT_SYMBOL(ttm_agp_tt_create); - -int ttm_agp_tt_populate(struct ttm_tt *ttm) -{ - if (ttm->state != tt_unpopulated) - return 0; - - return ttm_pool_populate(ttm); -} -EXPORT_SYMBOL(ttm_agp_tt_populate); - -void ttm_agp_tt_unpopulate(struct ttm_tt *ttm) -{ - ttm_pool_unpopulate(ttm); + agp_be->backend.func = &ttm_agp_func; + agp_be->backend.bdev = bdev; + return &agp_be->backend; } -EXPORT_SYMBOL(ttm_agp_tt_unpopulate); +EXPORT_SYMBOL(ttm_agp_backend_init); #endif diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 1843418..22a89cd 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -28,8 +28,6 @@ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> */ -#define pr_fmt(fmt) "[TTM] " fmt - #include "ttm/ttm_module.h" #include "ttm/ttm_bo_driver.h" #include "ttm/ttm_placement.h" @@ -70,13 +68,15 @@ static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type) { struct ttm_mem_type_manager *man = &bdev->man[mem_type]; - pr_err(" has_type: %d\n", man->has_type); - pr_err(" use_type: %d\n", man->use_type); - pr_err(" flags: 0x%08X\n", man->flags); - pr_err(" gpu_offset: 0x%08lX\n", man->gpu_offset); - pr_err(" size: %llu\n", man->size); - pr_err(" available_caching: 0x%08X\n", man->available_caching); - pr_err(" default_caching: 0x%08X\n", man->default_caching); + printk(KERN_ERR TTM_PFX " has_type: %d\n", man->has_type); + printk(KERN_ERR TTM_PFX " use_type: %d\n", man->use_type); + printk(KERN_ERR TTM_PFX " flags: 0x%08X\n", man->flags); + printk(KERN_ERR TTM_PFX " gpu_offset: 0x%08lX\n", man->gpu_offset); + printk(KERN_ERR TTM_PFX " size: %llu\n", man->size); + printk(KERN_ERR TTM_PFX " available_caching: 0x%08X\n", + man->available_caching); + printk(KERN_ERR TTM_PFX " default_caching: 0x%08X\n", + man->default_caching); if (mem_type != TTM_PL_SYSTEM) (*man->func->debug)(man, TTM_PFX); } @@ -86,16 +86,16 @@ static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, { int i, ret, mem_type; - pr_err("No space for %p (%lu pages, %luK, %luM)\n", - bo, bo->mem.num_pages, bo->mem.size >> 10, - bo->mem.size >> 20); + printk(KERN_ERR TTM_PFX "No space for %p (%lu pages, %luK, %luM)\n", + bo, bo->mem.num_pages, bo->mem.size >> 10, + bo->mem.size >> 20); for (i = 0; i < placement->num_placement; i++) { ret = ttm_mem_type_from_flags(placement->placement[i], &mem_type); if (ret) return; - pr_err(" placement[%d]=0x%08X (%d)\n", - i, placement->placement[i], mem_type); + printk(KERN_ERR TTM_PFX " placement[%d]=0x%08X (%d)\n", + i, placement->placement[i], mem_type); ttm_mem_type_debug(bo->bdev, mem_type); } } @@ -137,7 +137,6 @@ static void ttm_bo_release_list(struct kref *list_kref) struct ttm_buffer_object *bo = container_of(list_kref, struct ttm_buffer_object, list_kref); struct ttm_bo_device *bdev = bo->bdev; - size_t acc_size = bo->acc_size; BUG_ON(atomic_read(&bo->list_kref.refcount)); BUG_ON(atomic_read(&bo->kref.refcount)); @@ -153,9 +152,9 @@ static void ttm_bo_release_list(struct kref *list_kref) if (bo->destroy) bo->destroy(bo); else { + ttm_mem_global_free(bdev->glob->mem_glob, bo->acc_size); kfree(bo); } - ttm_mem_global_free(bdev->glob->mem_glob, acc_size); } int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible) @@ -338,13 +337,29 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc) if (zero_alloc) page_flags |= TTM_PAGE_FLAG_ZERO_ALLOC; case ttm_bo_type_kernel: - bo->ttm = bdev->driver->ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT, - page_flags, glob->dummy_read_page); + bo->ttm = ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT, + page_flags, glob->dummy_read_page); if (unlikely(bo->ttm == NULL)) ret = -ENOMEM; break; + case ttm_bo_type_user: + bo->ttm = ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT, + page_flags | TTM_PAGE_FLAG_USER, + glob->dummy_read_page); + if (unlikely(bo->ttm == NULL)) { + ret = -ENOMEM; + break; + } + + ret = ttm_tt_set_user(bo->ttm, current, + bo->buffer_start, bo->num_pages); + if (unlikely(ret != 0)) { + ttm_tt_destroy(bo->ttm); + bo->ttm = NULL; + } + break; default: - pr_err("Illegal buffer object type\n"); + printk(KERN_ERR TTM_PFX "Illegal buffer object type\n"); ret = -EINVAL; break; } @@ -416,23 +431,14 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, else ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, mem); - if (ret) { - if (bdev->driver->move_notify) { - struct ttm_mem_reg tmp_mem = *mem; - *mem = bo->mem; - bo->mem = tmp_mem; - bdev->driver->move_notify(bo, mem); - bo->mem = *mem; - } - + if (ret) goto out_err; - } moved: if (bo->evicted) { ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement); if (ret) - pr_err("Can not flush read caches\n"); + printk(KERN_ERR TTM_PFX "Can not flush read caches\n"); bo->evicted = false; } @@ -466,9 +472,6 @@ out_err: static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo) { - if (bo->bdev->driver->move_notify) - bo->bdev->driver->move_notify(bo, NULL); - if (bo->ttm) { ttm_tt_unbind(bo->ttm); ttm_tt_destroy(bo->ttm); @@ -734,7 +737,9 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, if (unlikely(ret != 0)) { if (ret != -ERESTARTSYS) { - pr_err("Failed to expire sync object before buffer eviction\n"); + printk(KERN_ERR TTM_PFX + "Failed to expire sync object before " + "buffer eviction.\n"); } goto out; } @@ -755,8 +760,9 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, no_wait_reserve, no_wait_gpu); if (ret) { if (ret != -ERESTARTSYS) { - pr_err("Failed to find memory space for buffer 0x%p eviction\n", - bo); + printk(KERN_ERR TTM_PFX + "Failed to find memory space for " + "buffer 0x%p eviction.\n", bo); ttm_bo_mem_space_debug(bo, &placement); } goto out; @@ -766,7 +772,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, no_wait_reserve, no_wait_gpu); if (ret) { if (ret != -ERESTARTSYS) - pr_err("Buffer eviction failed\n"); + printk(KERN_ERR TTM_PFX "Buffer eviction failed\n"); ttm_bo_mem_put(bo, &evict_mem); goto out; } @@ -907,12 +913,16 @@ static uint32_t ttm_bo_select_caching(struct ttm_mem_type_manager *man, } static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man, + bool disallow_fixed, uint32_t mem_type, uint32_t proposed_placement, uint32_t *masked_placement) { uint32_t cur_flags = ttm_bo_type_flags(mem_type); + if ((man->flags & TTM_MEMTYPE_FLAG_FIXED) && disallow_fixed) + return false; + if ((cur_flags & proposed_placement & TTM_PL_MASK_MEM) == 0) return false; @@ -957,6 +967,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, man = &bdev->man[mem_type]; type_ok = ttm_bo_mt_compatible(man, + bo->type == ttm_bo_type_user, mem_type, placement->placement[i], &cur_flags); @@ -1004,6 +1015,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (!man->has_type) continue; if (!ttm_bo_mt_compatible(man, + bo->type == ttm_bo_type_user, mem_type, placement->busy_placement[i], &cur_flags)) @@ -1089,24 +1101,32 @@ out_unlock: return ret; } -static int ttm_bo_mem_compat(struct ttm_placement *placement, - struct ttm_mem_reg *mem) +static bool ttm_bo_mem_compat(struct ttm_placement *placement, + struct ttm_mem_reg *mem, + uint32_t *new_flags) { int i; if (mem->mm_node && placement->lpfn != 0 && (mem->start < placement->fpfn || mem->start + mem->num_pages > placement->lpfn)) - return -1; + return false; for (i = 0; i < placement->num_placement; i++) { - if ((placement->placement[i] & mem->placement & - TTM_PL_MASK_CACHING) && - (placement->placement[i] & mem->placement & - TTM_PL_MASK_MEM)) - return i; + *new_flags = placement->placement[i]; + if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) && + (*new_flags & mem->placement & TTM_PL_MASK_MEM)) + return true; + } + + for (i = 0; i < placement->num_busy_placement; i++) { + *new_flags = placement->busy_placement[i]; + if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) && + (*new_flags & mem->placement & TTM_PL_MASK_MEM)) + return true; } - return -1; + + return false; } int ttm_bo_validate(struct ttm_buffer_object *bo, @@ -1115,6 +1135,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, bool no_wait_gpu) { int ret; + uint32_t new_flags; BUG_ON(!atomic_read(&bo->reserved)); /* Check that range is valid */ @@ -1125,8 +1146,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, /* * Check whether we need to move buffer. */ - ret = ttm_bo_mem_compat(placement, &bo->mem); - if (ret < 0) { + if (!ttm_bo_mem_compat(placement, &bo->mem, &new_flags)) { ret = ttm_bo_move_buffer(bo, placement, interruptible, no_wait_reserve, no_wait_gpu); if (ret) return ret; @@ -1135,7 +1155,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, * Use the access and other non-mapping-related flag bits from * the compatible memory placement flags to the active flags */ - ttm_flag_masked(&bo->mem.placement, placement->placement[ret], + ttm_flag_masked(&bo->mem.placement, new_flags, ~TTM_PL_MASK_MEMTYPE); } /* @@ -1173,22 +1193,11 @@ int ttm_bo_init(struct ttm_bo_device *bdev, { int ret = 0; unsigned long num_pages; - struct ttm_mem_global *mem_glob = bdev->glob->mem_glob; - - ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false); - if (ret) { - pr_err("Out of kernel memory\n"); - if (destroy) - (*destroy)(bo); - else - kfree(bo); - return -ENOMEM; - } size += buffer_start & ~PAGE_MASK; num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; if (num_pages == 0) { - pr_err("Illegal buffer object size\n"); + printk(KERN_ERR TTM_PFX "Illegal buffer object size.\n"); if (destroy) (*destroy)(bo); else @@ -1254,34 +1263,14 @@ out_err: } EXPORT_SYMBOL(ttm_bo_init); -size_t ttm_bo_acc_size(struct ttm_bo_device *bdev, - unsigned long bo_size, - unsigned struct_size) +static inline size_t ttm_bo_size(struct ttm_bo_global *glob, + unsigned long num_pages) { - unsigned npages = (PAGE_ALIGN(bo_size)) >> PAGE_SHIFT; - size_t size = 0; + size_t page_array_size = (num_pages * sizeof(void *) + PAGE_SIZE - 1) & + PAGE_MASK; - size += ttm_round_pot(struct_size); - size += PAGE_ALIGN(npages * sizeof(void *)); - size += ttm_round_pot(sizeof(struct ttm_tt)); - return size; + return glob->ttm_bo_size + 2 * page_array_size; } -EXPORT_SYMBOL(ttm_bo_acc_size); - -size_t ttm_bo_dma_acc_size(struct ttm_bo_device *bdev, - unsigned long bo_size, - unsigned struct_size) -{ - unsigned npages = (PAGE_ALIGN(bo_size)) >> PAGE_SHIFT; - size_t size = 0; - - size += ttm_round_pot(struct_size); - size += PAGE_ALIGN(npages * sizeof(void *)); - size += PAGE_ALIGN(npages * sizeof(dma_addr_t)); - size += ttm_round_pot(sizeof(struct ttm_dma_tt)); - return size; -} -EXPORT_SYMBOL(ttm_bo_dma_acc_size); int ttm_bo_create(struct ttm_bo_device *bdev, unsigned long size, @@ -1295,10 +1284,10 @@ int ttm_bo_create(struct ttm_bo_device *bdev, { struct ttm_buffer_object *bo; struct ttm_mem_global *mem_glob = bdev->glob->mem_glob; - size_t acc_size; int ret; - acc_size = ttm_bo_acc_size(bdev, size, sizeof(struct ttm_buffer_object)); + size_t acc_size = + ttm_bo_size(bdev->glob, (size + PAGE_SIZE - 1) >> PAGE_SHIFT); ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false); if (unlikely(ret != 0)) return ret; @@ -1339,7 +1328,8 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, if (allow_errors) { return ret; } else { - pr_err("Cleanup eviction failed\n"); + printk(KERN_ERR TTM_PFX + "Cleanup eviction failed\n"); } } spin_lock(&glob->lru_lock); @@ -1354,14 +1344,14 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type) int ret = -EINVAL; if (mem_type >= TTM_NUM_MEM_TYPES) { - pr_err("Illegal memory type %d\n", mem_type); + printk(KERN_ERR TTM_PFX "Illegal memory type %d\n", mem_type); return ret; } man = &bdev->man[mem_type]; if (!man->has_type) { - pr_err("Trying to take down uninitialized memory manager type %u\n", - mem_type); + printk(KERN_ERR TTM_PFX "Trying to take down uninitialized " + "memory manager type %u\n", mem_type); return ret; } @@ -1384,12 +1374,16 @@ int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type) struct ttm_mem_type_manager *man = &bdev->man[mem_type]; if (mem_type == 0 || mem_type >= TTM_NUM_MEM_TYPES) { - pr_err("Illegal memory manager memory type %u\n", mem_type); + printk(KERN_ERR TTM_PFX + "Illegal memory manager memory type %u.\n", + mem_type); return -EINVAL; } if (!man->has_type) { - pr_err("Memory type %u has not been initialized\n", mem_type); + printk(KERN_ERR TTM_PFX + "Memory type %u has not been initialized.\n", + mem_type); return 0; } @@ -1474,10 +1468,18 @@ int ttm_bo_global_init(struct drm_global_reference *ref) ttm_mem_init_shrink(&glob->shrink, ttm_bo_swapout); ret = ttm_mem_register_shrink(glob->mem_glob, &glob->shrink); if (unlikely(ret != 0)) { - pr_err("Could not register buffer object swapout\n"); + printk(KERN_ERR TTM_PFX + "Could not register buffer object swapout.\n"); goto out_no_shrink; } + glob->ttm_bo_extra_size = + ttm_round_pot(sizeof(struct ttm_tt)) + + ttm_round_pot(sizeof(struct ttm_backend)); + + glob->ttm_bo_size = glob->ttm_bo_extra_size + + ttm_round_pot(sizeof(struct ttm_buffer_object)); + atomic_set(&glob->bo_count, 0); ret = kobject_init_and_add( @@ -1507,8 +1509,9 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev) man->use_type = false; if ((i != TTM_PL_SYSTEM) && ttm_bo_clean_mm(bdev, i)) { ret = -EBUSY; - pr_err("DRM memory manager type %d is not clean\n", - i); + printk(KERN_ERR TTM_PFX + "DRM memory manager type %d " + "is not clean.\n", i); } man->has_type = false; } diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index f8187ea..082fcae 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -244,7 +244,7 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src, unsigned long page, pgprot_t prot) { - struct page *d = ttm->pages[page]; + struct page *d = ttm_tt_get_page(ttm, page); void *dst; if (!d) @@ -281,7 +281,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst, unsigned long page, pgprot_t prot) { - struct page *s = ttm->pages[page]; + struct page *s = ttm_tt_get_page(ttm, page); void *src; if (!s) @@ -342,12 +342,6 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, if (old_iomap == NULL && ttm == NULL) goto out2; - if (ttm->state == tt_unpopulated) { - ret = ttm->bdev->driver->ttm_tt_populate(ttm); - if (ret) - goto out1; - } - add = 0; dir = 1; @@ -445,7 +439,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, kref_init(&fbo->list_kref); kref_init(&fbo->kref); fbo->destroy = &ttm_transfered_destroy; - fbo->acc_size = 0; *new_obj = fbo; return 0; @@ -509,16 +502,10 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, { struct ttm_mem_reg *mem = &bo->mem; pgprot_t prot; struct ttm_tt *ttm = bo->ttm; - int ret; + struct page *d; + int i; BUG_ON(!ttm); - - if (ttm->state == tt_unpopulated) { - ret = ttm->bdev->driver->ttm_tt_populate(ttm); - if (ret) - return ret; - } - if (num_pages == 1 && (mem->placement & TTM_PL_FLAG_CACHED)) { /* * We're mapping a single page, and the desired @@ -526,9 +513,18 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, */ map->bo_kmap_type = ttm_bo_map_kmap; - map->page = ttm->pages[start_page]; + map->page = ttm_tt_get_page(ttm, start_page); map->virtual = kmap(map->page); } else { + /* + * Populate the part we're mapping; + */ + for (i = start_page; i < start_page + num_pages; ++i) { + d = ttm_tt_get_page(ttm, i); + if (!d) + return -ENOMEM; + } + /* * We need to use vmap to get the desired page protection * or to make the buffer object look contiguous. diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index a877813..e223175 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -28,8 +28,6 @@ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> */ -#define pr_fmt(fmt) "[TTM] " fmt - #include <ttm/ttm_module.h> #include <ttm/ttm_bo_driver.h> #include <ttm/ttm_placement.h> @@ -146,9 +144,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) } page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) + - bo->vm_node->start - vma->vm_pgoff; - page_last = ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) + - bo->vm_node->start - vma->vm_pgoff; + vma->vm_pgoff - bo->vm_node->start; + page_last = vma_pages(vma) + vma->vm_pgoff - + bo->vm_node->start; if (unlikely(page_offset >= bo->num_pages)) { retval = VM_FAULT_SIGBUS; @@ -176,23 +174,18 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) vma->vm_page_prot = (bo->mem.placement & TTM_PL_FLAG_CACHED) ? vm_get_page_prot(vma->vm_flags) : ttm_io_prot(bo->mem.placement, vma->vm_page_prot); - - /* Allocate all page at once, most common usage */ - if (ttm->bdev->driver->ttm_tt_populate(ttm)) { - retval = VM_FAULT_OOM; - goto out_io_unlock; - } } /* * Speculatively prefault a number of pages. Only error on * first page. */ + for (i = 0; i < TTM_BO_VM_NUM_PREFAULT; ++i) { if (bo->mem.bus.is_iomem) pfn = ((bo->mem.bus.base + bo->mem.bus.offset) >> PAGE_SHIFT) + page_offset; else { - page = ttm->pages[page_offset]; + page = ttm_tt_get_page(ttm, page_offset); if (unlikely(!page && i == 0)) { retval = VM_FAULT_OOM; goto out_io_unlock; @@ -264,7 +257,8 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, read_unlock(&bdev->vm_lock); if (unlikely(bo == NULL)) { - pr_err("Could not find buffer object to map\n"); + printk(KERN_ERR TTM_PFX + "Could not find buffer object to map.\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c index 23d2ecb..e70ddd8 100644 --- a/drivers/gpu/drm/ttm/ttm_memory.c +++ b/drivers/gpu/drm/ttm/ttm_memory.c @@ -25,8 +25,6 @@ * **************************************************************************/ -#define pr_fmt(fmt) "[TTM] " fmt - #include "ttm/ttm_memory.h" #include "ttm/ttm_module.h" #include "ttm/ttm_page_alloc.h" @@ -76,8 +74,9 @@ static void ttm_mem_zone_kobj_release(struct kobject *kobj) struct ttm_mem_zone *zone = container_of(kobj, struct ttm_mem_zone, kobj); - pr_info("Zone %7s: Used memory at exit: %llu kiB\n", - zone->name, (unsigned long long)zone->used_mem >> 10); + printk(KERN_INFO TTM_PFX + "Zone %7s: Used memory at exit: %llu kiB.\n", + zone->name, (unsigned long long) zone->used_mem >> 10); kfree(zone); } @@ -391,11 +390,11 @@ int ttm_mem_global_init(struct ttm_mem_global *glob) #endif for (i = 0; i < glob->num_zones; ++i) { zone = glob->zones[i]; - pr_info("Zone %7s: Available graphics memory: %llu kiB\n", - zone->name, (unsigned long long)zone->max_mem >> 10); + printk(KERN_INFO TTM_PFX + "Zone %7s: Available graphics memory: %llu kiB.\n", + zone->name, (unsigned long long) zone->max_mem >> 10); } ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); - ttm_dma_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); return 0; out_no_zone: ttm_mem_global_release(glob); @@ -410,7 +409,6 @@ void ttm_mem_global_release(struct ttm_mem_global *glob) /* let the page allocator first stop the shrink work. */ ttm_page_alloc_fini(); - ttm_dma_page_alloc_fini(); flush_workqueue(glob->swap_queue); destroy_workqueue(glob->swap_queue); diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c index 68daca4..93577f2 100644 --- a/drivers/gpu/drm/ttm/ttm_object.c +++ b/drivers/gpu/drm/ttm/ttm_object.c @@ -49,8 +49,6 @@ * for fast lookup of ref objects given a base object. */ -#define pr_fmt(fmt) "[TTM] " fmt - #include "ttm/ttm_object.h" #include "ttm/ttm_module.h" #include <linux/list.h> @@ -234,7 +232,8 @@ struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile, return NULL; if (tfile != base->tfile && !base->shareable) { - pr_err("Attempted access of non-shareable object\n"); + printk(KERN_ERR TTM_PFX + "Attempted access of non-shareable object.\n"); ttm_base_object_unref(&base); return NULL; } diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index ebc6fac..508c64c 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -30,9 +30,6 @@ * - Use page->lru to keep a free list * - doesn't track currently in use pages */ - -#define pr_fmt(fmt) "[TTM] " fmt - #include <linux/list.h> #include <linux/spinlock.h> #include <linux/highmem.h> @@ -170,13 +167,18 @@ static ssize_t ttm_pool_store(struct kobject *kobj, m->options.small = val; else if (attr == &ttm_page_pool_alloc_size) { if (val > NUM_PAGES_TO_ALLOC*8) { - pr_err("Setting allocation size to %lu is not allowed. Recommended size is %lu\n", + printk(KERN_ERR TTM_PFX + "Setting allocation size to %lu " + "is not allowed. Recommended size is " + "%lu\n", NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 7), NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10)); return size; } else if (val > NUM_PAGES_TO_ALLOC) { - pr_warn("Setting allocation size to larger than %lu is not recommended\n", - NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10)); + printk(KERN_WARNING TTM_PFX + "Setting allocation size to " + "larger than %lu is not recommended.\n", + NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10)); } m->options.alloc_size = val; } @@ -277,7 +279,8 @@ static void ttm_pages_put(struct page *pages[], unsigned npages) { unsigned i; if (set_pages_array_wb(pages, npages)) - pr_err("Failed to set %d pages to wb!\n", npages); + printk(KERN_ERR TTM_PFX "Failed to set %d pages to wb!\n", + npages); for (i = 0; i < npages; ++i) __free_page(pages[i]); } @@ -312,7 +315,8 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free) pages_to_free = kmalloc(npages_to_free * sizeof(struct page *), GFP_KERNEL); if (!pages_to_free) { - pr_err("Failed to allocate memory for pool free operation\n"); + printk(KERN_ERR TTM_PFX + "Failed to allocate memory for pool free operation.\n"); return 0; } @@ -394,13 +398,18 @@ static int ttm_pool_get_num_unused_pages(void) static int ttm_pool_mm_shrink(struct shrinker *shrink, struct shrink_control *sc) { - static atomic_t start_pool = ATOMIC_INIT(0); + static DEFINE_MUTEX(lock); + static unsigned start_pool; unsigned i; - unsigned pool_offset = atomic_add_return(1, &start_pool); + unsigned pool_offset; struct ttm_page_pool *pool; int shrink_pages = sc->nr_to_scan; - pool_offset = pool_offset % NUM_POOLS; + if (shrink_pages == 0) + goto out; + if (!mutex_trylock(&lock)) + return -1; + pool_offset = ++start_pool % NUM_POOLS; /* select start pool in round robin fashion */ for (i = 0; i < NUM_POOLS; ++i) { unsigned nr_free = shrink_pages; @@ -409,6 +418,8 @@ static int ttm_pool_mm_shrink(struct shrinker *shrink, pool = &_manager->pools[(i + pool_offset)%NUM_POOLS]; shrink_pages = ttm_page_pool_free(pool, nr_free); } + mutex_unlock(&lock); +out: /* return estimated number of unused pages in pool */ return ttm_pool_get_num_unused_pages(); } @@ -434,12 +445,16 @@ static int ttm_set_pages_caching(struct page **pages, case tt_uncached: r = set_pages_array_uc(pages, cpages); if (r) - pr_err("Failed to set %d pages to uc!\n", cpages); + printk(KERN_ERR TTM_PFX + "Failed to set %d pages to uc!\n", + cpages); break; case tt_wc: r = set_pages_array_wc(pages, cpages); if (r) - pr_err("Failed to set %d pages to wc!\n", cpages); + printk(KERN_ERR TTM_PFX + "Failed to set %d pages to wc!\n", + cpages); break; default: break; @@ -484,7 +499,8 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags, caching_array = kmalloc(max_cpages*sizeof(struct page *), GFP_KERNEL); if (!caching_array) { - pr_err("Unable to allocate table for new pages\n"); + printk(KERN_ERR TTM_PFX + "Unable to allocate table for new pages."); return -ENOMEM; } @@ -492,7 +508,7 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags, p = alloc_page(gfp_flags); if (!p) { - pr_err("Unable to get page %u\n", i); + printk(KERN_ERR TTM_PFX "Unable to get page %u.\n", i); /* store already allocated pages in the pool after * setting the caching state */ @@ -590,7 +606,8 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, ++pool->nrefills; pool->npages += alloc_size; } else { - pr_err("Failed to fill pool (%p)\n", pool); + printk(KERN_ERR TTM_PFX + "Failed to fill pool (%p).", pool); /* If we have any pages left put them to the pool. */ list_for_each_entry(p, &pool->list, lru) { ++cpages; @@ -609,10 +626,8 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, * @return count of pages still required to fulfill the request. */ static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool, - struct list_head *pages, - int ttm_flags, - enum ttm_caching_state cstate, - unsigned count) + struct list_head *pages, int ttm_flags, + enum ttm_caching_state cstate, unsigned count) { unsigned long irq_flags; struct list_head *p; @@ -652,63 +667,17 @@ out: return count; } -/* Put all pages in pages list to correct pool to wait for reuse */ -static void ttm_put_pages(struct page **pages, unsigned npages, int flags, - enum ttm_caching_state cstate) -{ - unsigned long irq_flags; - struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); - unsigned i; - - if (pool == NULL) { - /* No pool for this memory type so free the pages */ - for (i = 0; i < npages; i++) { - if (pages[i]) { - if (page_count(pages[i]) != 1) - pr_err("Erroneous page count. Leaking pages.\n"); - __free_page(pages[i]); - pages[i] = NULL; - } - } - return; - } - - spin_lock_irqsave(&pool->lock, irq_flags); - for (i = 0; i < npages; i++) { - if (pages[i]) { - if (page_count(pages[i]) != 1) - pr_err("Erroneous page count. Leaking pages.\n"); - list_add_tail(&pages[i]->lru, &pool->list); - pages[i] = NULL; - pool->npages++; - } - } - /* Check that we don't go over the pool limit */ - npages = 0; - if (pool->npages > _manager->options.max_size) { - npages = pool->npages - _manager->options.max_size; - /* free at least NUM_PAGES_TO_ALLOC number of pages - * to reduce calls to set_memory_wb */ - if (npages < NUM_PAGES_TO_ALLOC) - npages = NUM_PAGES_TO_ALLOC; - } - spin_unlock_irqrestore(&pool->lock, irq_flags); - if (npages) - ttm_page_pool_free(pool, npages); -} - /* * On success pages list will hold count number of correctly * cached pages. */ -static int ttm_get_pages(struct page **pages, unsigned npages, int flags, - enum ttm_caching_state cstate) +int ttm_get_pages(struct list_head *pages, int flags, + enum ttm_caching_state cstate, unsigned count, + dma_addr_t *dma_address) { struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); - struct list_head plist; struct page *p = NULL; gfp_t gfp_flags = GFP_USER; - unsigned count; int r; /* set zero flag for page allocation if required */ @@ -722,59 +691,99 @@ static int ttm_get_pages(struct page **pages, unsigned npages, int flags, else gfp_flags |= GFP_HIGHUSER; - for (r = 0; r < npages; ++r) { + for (r = 0; r < count; ++r) { p = alloc_page(gfp_flags); if (!p) { - pr_err("Unable to allocate page\n"); + printk(KERN_ERR TTM_PFX + "Unable to allocate page."); return -ENOMEM; } - pages[r] = p; + list_add(&p->lru, pages); } return 0; } + /* combine zero flag to pool flags */ gfp_flags |= pool->gfp_flags; /* First we take pages from the pool */ - INIT_LIST_HEAD(&plist); - npages = ttm_page_pool_get_pages(pool, &plist, flags, cstate, npages); - count = 0; - list_for_each_entry(p, &plist, lru) { - pages[count++] = p; - } + count = ttm_page_pool_get_pages(pool, pages, flags, cstate, count); /* clear the pages coming from the pool if requested */ if (flags & TTM_PAGE_FLAG_ZERO_ALLOC) { - list_for_each_entry(p, &plist, lru) { - clear_page(page_address(p)); + list_for_each_entry(p, pages, lru) { + if (PageHighMem(p)) + clear_highpage(p); + else + clear_page(page_address(p)); } } /* If pool didn't have enough pages allocate new one. */ - if (npages > 0) { + if (count > 0) { /* ttm_alloc_new_pages doesn't reference pool so we can run * multiple requests in parallel. **/ - INIT_LIST_HEAD(&plist); - r = ttm_alloc_new_pages(&plist, gfp_flags, flags, cstate, npages); - list_for_each_entry(p, &plist, lru) { - pages[count++] = p; - } + r = ttm_alloc_new_pages(pages, gfp_flags, flags, cstate, count); if (r) { /* If there is any pages in the list put them back to * the pool. */ - pr_err("Failed to allocate extra pages for large request\n"); - ttm_put_pages(pages, count, flags, cstate); + printk(KERN_ERR TTM_PFX + "Failed to allocate extra pages " + "for large request."); + ttm_put_pages(pages, 0, flags, cstate, NULL); return r; } } + return 0; } +/* Put all pages in pages list to correct pool to wait for reuse */ +void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags, + enum ttm_caching_state cstate, dma_addr_t *dma_address) +{ + unsigned long irq_flags; + struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); + struct page *p, *tmp; + + if (pool == NULL) { + /* No pool for this memory type so free the pages */ + + list_for_each_entry_safe(p, tmp, pages, lru) { + __free_page(p); + } + /* Make the pages list empty */ + INIT_LIST_HEAD(pages); + return; + } + if (page_count == 0) { + list_for_each_entry_safe(p, tmp, pages, lru) { + ++page_count; + } + } + + spin_lock_irqsave(&pool->lock, irq_flags); + list_splice_init(pages, &pool->list); + pool->npages += page_count; + /* Check that we don't go over the pool limit */ + page_count = 0; + if (pool->npages > _manager->options.max_size) { + page_count = pool->npages - _manager->options.max_size; + /* free at least NUM_PAGES_TO_ALLOC number of pages + * to reduce calls to set_memory_wb */ + if (page_count < NUM_PAGES_TO_ALLOC) + page_count = NUM_PAGES_TO_ALLOC; + } + spin_unlock_irqrestore(&pool->lock, irq_flags); + if (page_count) + ttm_page_pool_free(pool, page_count); +} + static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, int flags, char *name) { @@ -792,7 +801,7 @@ int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) WARN_ON(_manager); - pr_info("Initializing pool allocator\n"); + printk(KERN_INFO TTM_PFX "Initializing pool allocator.\n"); _manager = kzalloc(sizeof(*_manager), GFP_KERNEL); @@ -827,7 +836,7 @@ void ttm_page_alloc_fini(void) { int i; - pr_info("Finalizing pool allocator\n"); + printk(KERN_INFO TTM_PFX "Finalizing pool allocator.\n"); ttm_pool_mm_shrink_fini(_manager); for (i = 0; i < NUM_POOLS; ++i) @@ -837,62 +846,6 @@ void ttm_page_alloc_fini(void) _manager = NULL; } -int ttm_pool_populate(struct ttm_tt *ttm) -{ - struct ttm_mem_global *mem_glob = ttm->glob->mem_glob; - unsigned i; - int ret; - - if (ttm->state != tt_unpopulated) - return 0; - - for (i = 0; i < ttm->num_pages; ++i) { - ret = ttm_get_pages(&ttm->pages[i], 1, - ttm->page_flags, - ttm->caching_state); - if (ret != 0) { - ttm_pool_unpopulate(ttm); - return -ENOMEM; - } - - ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], - false, false); - if (unlikely(ret != 0)) { - ttm_pool_unpopulate(ttm); - return -ENOMEM; - } - } - - if (unlikely(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)) { - ret = ttm_tt_swapin(ttm); - if (unlikely(ret != 0)) { - ttm_pool_unpopulate(ttm); - return ret; - } - } - - ttm->state = tt_unbound; - return 0; -} -EXPORT_SYMBOL(ttm_pool_populate); - -void ttm_pool_unpopulate(struct ttm_tt *ttm) -{ - unsigned i; - - for (i = 0; i < ttm->num_pages; ++i) { - if (ttm->pages[i]) { - ttm_mem_global_free_page(ttm->glob->mem_glob, - ttm->pages[i]); - ttm_put_pages(&ttm->pages[i], 1, - ttm->page_flags, - ttm->caching_state); - } - } - ttm->state = tt_unpopulated; -} -EXPORT_SYMBOL(ttm_pool_unpopulate); - int ttm_page_alloc_debugfs(struct seq_file *m, void *data) { struct ttm_page_pool *p; diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c deleted file mode 100644 index 4f9e548..0000000 --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +++ /dev/null @@ -1,1134 +0,0 @@ -/* - * Copyright 2011 (c) Oracle Corp. - - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> - */ - -/* - * A simple DMA pool losely based on dmapool.c. It has certain advantages - * over the DMA pools: - * - Pool collects resently freed pages for reuse (and hooks up to - * the shrinker). - * - Tracks currently in use pages - * - Tracks whether the page is UC, WB or cached (and reverts to WB - * when freed). - */ - -#define pr_fmt(fmt) "[TTM] " fmt - -#include <linux/dma-mapping.h> -#include <linux/list.h> -#include <linux/seq_file.h> /* for seq_printf */ -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/highmem.h> -#include <linux/mm_types.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/atomic.h> -#include <linux/device.h> -#include <linux/kthread.h> -#include "ttm/ttm_bo_driver.h" -#include "ttm/ttm_page_alloc.h" -#ifdef TTM_HAS_AGP -#include <asm/agp.h> -#endif - -#define NUM_PAGES_TO_ALLOC (PAGE_SIZE/sizeof(struct page *)) -#define SMALL_ALLOCATION 4 -#define FREE_ALL_PAGES (~0U) -/* times are in msecs */ -#define IS_UNDEFINED (0) -#define IS_WC (1<<1) -#define IS_UC (1<<2) -#define IS_CACHED (1<<3) -#define IS_DMA32 (1<<4) - -enum pool_type { - POOL_IS_UNDEFINED, - POOL_IS_WC = IS_WC, - POOL_IS_UC = IS_UC, - POOL_IS_CACHED = IS_CACHED, - POOL_IS_WC_DMA32 = IS_WC | IS_DMA32, - POOL_IS_UC_DMA32 = IS_UC | IS_DMA32, - POOL_IS_CACHED_DMA32 = IS_CACHED | IS_DMA32, -}; -/* - * The pool structure. There are usually six pools: - * - generic (not restricted to DMA32): - * - write combined, uncached, cached. - * - dma32 (up to 2^32 - so up 4GB): - * - write combined, uncached, cached. - * for each 'struct device'. The 'cached' is for pages that are actively used. - * The other ones can be shrunk by the shrinker API if neccessary. - * @pools: The 'struct device->dma_pools' link. - * @type: Type of the pool - * @lock: Protects the inuse_list and free_list from concurrnet access. Must be - * used with irqsave/irqrestore variants because pool allocator maybe called - * from delayed work. - * @inuse_list: Pool of pages that are in use. The order is very important and - * it is in the order that the TTM pages that are put back are in. - * @free_list: Pool of pages that are free to be used. No order requirements. - * @dev: The device that is associated with these pools. - * @size: Size used during DMA allocation. - * @npages_free: Count of available pages for re-use. - * @npages_in_use: Count of pages that are in use. - * @nfrees: Stats when pool is shrinking. - * @nrefills: Stats when the pool is grown. - * @gfp_flags: Flags to pass for alloc_page. - * @name: Name of the pool. - * @dev_name: Name derieved from dev - similar to how dev_info works. - * Used during shutdown as the dev_info during release is unavailable. - */ -struct dma_pool { - struct list_head pools; /* The 'struct device->dma_pools link */ - enum pool_type type; - spinlock_t lock; - struct list_head inuse_list; - struct list_head free_list; - struct device *dev; - unsigned size; - unsigned npages_free; - unsigned npages_in_use; - unsigned long nfrees; /* Stats when shrunk. */ - unsigned long nrefills; /* Stats when grown. */ - gfp_t gfp_flags; - char name[13]; /* "cached dma32" */ - char dev_name[64]; /* Constructed from dev */ -}; - -/* - * The accounting page keeping track of the allocated page along with - * the DMA address. - * @page_list: The link to the 'page_list' in 'struct dma_pool'. - * @vaddr: The virtual address of the page - * @dma: The bus address of the page. If the page is not allocated - * via the DMA API, it will be -1. - */ -struct dma_page { - struct list_head page_list; - void *vaddr; - struct page *p; - dma_addr_t dma; -}; - -/* - * Limits for the pool. They are handled without locks because only place where - * they may change is in sysfs store. They won't have immediate effect anyway - * so forcing serialization to access them is pointless. - */ - -struct ttm_pool_opts { - unsigned alloc_size; - unsigned max_size; - unsigned small; -}; - -/* - * Contains the list of all of the 'struct device' and their corresponding - * DMA pools. Guarded by _mutex->lock. - * @pools: The link to 'struct ttm_pool_manager->pools' - * @dev: The 'struct device' associated with the 'pool' - * @pool: The 'struct dma_pool' associated with the 'dev' - */ -struct device_pools { - struct list_head pools; - struct device *dev; - struct dma_pool *pool; -}; - -/* - * struct ttm_pool_manager - Holds memory pools for fast allocation - * - * @lock: Lock used when adding/removing from pools - * @pools: List of 'struct device' and 'struct dma_pool' tuples. - * @options: Limits for the pool. - * @npools: Total amount of pools in existence. - * @shrinker: The structure used by [un|]register_shrinker - */ -struct ttm_pool_manager { - struct mutex lock; - struct list_head pools; - struct ttm_pool_opts options; - unsigned npools; - struct shrinker mm_shrink; - struct kobject kobj; -}; - -static struct ttm_pool_manager *_manager; - -static struct attribute ttm_page_pool_max = { - .name = "pool_max_size", - .mode = S_IRUGO | S_IWUSR -}; -static struct attribute ttm_page_pool_small = { - .name = "pool_small_allocation", - .mode = S_IRUGO | S_IWUSR -}; -static struct attribute ttm_page_pool_alloc_size = { - .name = "pool_allocation_size", - .mode = S_IRUGO | S_IWUSR -}; - -static struct attribute *ttm_pool_attrs[] = { - &ttm_page_pool_max, - &ttm_page_pool_small, - &ttm_page_pool_alloc_size, - NULL -}; - -static void ttm_pool_kobj_release(struct kobject *kobj) -{ - struct ttm_pool_manager *m = - container_of(kobj, struct ttm_pool_manager, kobj); - kfree(m); -} - -static ssize_t ttm_pool_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t size) -{ - struct ttm_pool_manager *m = - container_of(kobj, struct ttm_pool_manager, kobj); - int chars; - unsigned val; - chars = sscanf(buffer, "%u", &val); - if (chars == 0) - return size; - - /* Convert kb to number of pages */ - val = val / (PAGE_SIZE >> 10); - - if (attr == &ttm_page_pool_max) - m->options.max_size = val; - else if (attr == &ttm_page_pool_small) - m->options.small = val; - else if (attr == &ttm_page_pool_alloc_size) { - if (val > NUM_PAGES_TO_ALLOC*8) { - pr_err("Setting allocation size to %lu is not allowed. Recommended size is %lu\n", - NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 7), - NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10)); - return size; - } else if (val > NUM_PAGES_TO_ALLOC) { - pr_warn("Setting allocation size to larger than %lu is not recommended\n", - NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10)); - } - m->options.alloc_size = val; - } - - return size; -} - -static ssize_t ttm_pool_show(struct kobject *kobj, struct attribute *attr, - char *buffer) -{ - struct ttm_pool_manager *m = - container_of(kobj, struct ttm_pool_manager, kobj); - unsigned val = 0; - - if (attr == &ttm_page_pool_max) - val = m->options.max_size; - else if (attr == &ttm_page_pool_small) - val = m->options.small; - else if (attr == &ttm_page_pool_alloc_size) - val = m->options.alloc_size; - - val = val * (PAGE_SIZE >> 10); - - return snprintf(buffer, PAGE_SIZE, "%u\n", val); -} - -static const struct sysfs_ops ttm_pool_sysfs_ops = { - .show = &ttm_pool_show, - .store = &ttm_pool_store, -}; - -static struct kobj_type ttm_pool_kobj_type = { - .release = &ttm_pool_kobj_release, - .sysfs_ops = &ttm_pool_sysfs_ops, - .default_attrs = ttm_pool_attrs, -}; - -#ifndef CONFIG_X86 -static int set_pages_array_wb(struct page **pages, int addrinarray) -{ -#ifdef TTM_HAS_AGP - int i; - - for (i = 0; i < addrinarray; i++) - unmap_page_from_agp(pages[i]); -#endif - return 0; -} - -static int set_pages_array_wc(struct page **pages, int addrinarray) -{ -#ifdef TTM_HAS_AGP - int i; - - for (i = 0; i < addrinarray; i++) - map_page_into_agp(pages[i]); -#endif - return 0; -} - -static int set_pages_array_uc(struct page **pages, int addrinarray) -{ -#ifdef TTM_HAS_AGP - int i; - - for (i = 0; i < addrinarray; i++) - map_page_into_agp(pages[i]); -#endif - return 0; -} -#endif /* for !CONFIG_X86 */ - -static int ttm_set_pages_caching(struct dma_pool *pool, - struct page **pages, unsigned cpages) -{ - int r = 0; - /* Set page caching */ - if (pool->type & IS_UC) { - r = set_pages_array_uc(pages, cpages); - if (r) - pr_err("%s: Failed to set %d pages to uc!\n", - pool->dev_name, cpages); - } - if (pool->type & IS_WC) { - r = set_pages_array_wc(pages, cpages); - if (r) - pr_err("%s: Failed to set %d pages to wc!\n", - pool->dev_name, cpages); - } - return r; -} - -static void __ttm_dma_free_page(struct dma_pool *pool, struct dma_page *d_page) -{ - dma_addr_t dma = d_page->dma; - dma_free_coherent(pool->dev, pool->size, d_page->vaddr, dma); - - kfree(d_page); - d_page = NULL; -} -static struct dma_page *__ttm_dma_alloc_page(struct dma_pool *pool) -{ - struct dma_page *d_page; - - d_page = kmalloc(sizeof(struct dma_page), GFP_KERNEL); - if (!d_page) - return NULL; - - d_page->vaddr = dma_alloc_coherent(pool->dev, pool->size, - &d_page->dma, - pool->gfp_flags); - if (d_page->vaddr) - d_page->p = virt_to_page(d_page->vaddr); - else { - kfree(d_page); - d_page = NULL; - } - return d_page; -} -static enum pool_type ttm_to_type(int flags, enum ttm_caching_state cstate) -{ - enum pool_type type = IS_UNDEFINED; - - if (flags & TTM_PAGE_FLAG_DMA32) - type |= IS_DMA32; - if (cstate == tt_cached) - type |= IS_CACHED; - else if (cstate == tt_uncached) - type |= IS_UC; - else - type |= IS_WC; - - return type; -} - -static void ttm_pool_update_free_locked(struct dma_pool *pool, - unsigned freed_pages) -{ - pool->npages_free -= freed_pages; - pool->nfrees += freed_pages; - -} - -/* set memory back to wb and free the pages. */ -static void ttm_dma_pages_put(struct dma_pool *pool, struct list_head *d_pages, - struct page *pages[], unsigned npages) -{ - struct dma_page *d_page, *tmp; - - /* Don't set WB on WB page pool. */ - if (npages && !(pool->type & IS_CACHED) && - set_pages_array_wb(pages, npages)) - pr_err("%s: Failed to set %d pages to wb!\n", - pool->dev_name, npages); - - list_for_each_entry_safe(d_page, tmp, d_pages, page_list) { - list_del(&d_page->page_list); - __ttm_dma_free_page(pool, d_page); - } -} - -static void ttm_dma_page_put(struct dma_pool *pool, struct dma_page *d_page) -{ - /* Don't set WB on WB page pool. */ - if (!(pool->type & IS_CACHED) && set_pages_array_wb(&d_page->p, 1)) - pr_err("%s: Failed to set %d pages to wb!\n", - pool->dev_name, 1); - - list_del(&d_page->page_list); - __ttm_dma_free_page(pool, d_page); -} - -/* - * Free pages from pool. - * - * To prevent hogging the ttm_swap process we only free NUM_PAGES_TO_ALLOC - * number of pages in one go. - * - * @pool: to free the pages from - * @nr_free: If set to true will free all pages in pool - **/ -static unsigned ttm_dma_page_pool_free(struct dma_pool *pool, unsigned nr_free) -{ - unsigned long irq_flags; - struct dma_page *dma_p, *tmp; - struct page **pages_to_free; - struct list_head d_pages; - unsigned freed_pages = 0, - npages_to_free = nr_free; - - if (NUM_PAGES_TO_ALLOC < nr_free) - npages_to_free = NUM_PAGES_TO_ALLOC; -#if 0 - if (nr_free > 1) { - pr_debug("%s: (%s:%d) Attempting to free %d (%d) pages\n", - pool->dev_name, pool->name, current->pid, - npages_to_free, nr_free); - } -#endif - pages_to_free = kmalloc(npages_to_free * sizeof(struct page *), - GFP_KERNEL); - - if (!pages_to_free) { - pr_err("%s: Failed to allocate memory for pool free operation\n", - pool->dev_name); - return 0; - } - INIT_LIST_HEAD(&d_pages); -restart: - spin_lock_irqsave(&pool->lock, irq_flags); - - /* We picking the oldest ones off the list */ - list_for_each_entry_safe_reverse(dma_p, tmp, &pool->free_list, - page_list) { - if (freed_pages >= npages_to_free) - break; - - /* Move the dma_page from one list to another. */ - list_move(&dma_p->page_list, &d_pages); - - pages_to_free[freed_pages++] = dma_p->p; - /* We can only remove NUM_PAGES_TO_ALLOC at a time. */ - if (freed_pages >= NUM_PAGES_TO_ALLOC) { - - ttm_pool_update_free_locked(pool, freed_pages); - /** - * Because changing page caching is costly - * we unlock the pool to prevent stalling. - */ - spin_unlock_irqrestore(&pool->lock, irq_flags); - - ttm_dma_pages_put(pool, &d_pages, pages_to_free, - freed_pages); - - INIT_LIST_HEAD(&d_pages); - - if (likely(nr_free != FREE_ALL_PAGES)) - nr_free -= freed_pages; - - if (NUM_PAGES_TO_ALLOC >= nr_free) - npages_to_free = nr_free; - else - npages_to_free = NUM_PAGES_TO_ALLOC; - - freed_pages = 0; - - /* free all so restart the processing */ - if (nr_free) - goto restart; - - /* Not allowed to fall through or break because - * following context is inside spinlock while we are - * outside here. - */ - goto out; - - } - } - - /* remove range of pages from the pool */ - if (freed_pages) { - ttm_pool_update_free_locked(pool, freed_pages); - nr_free -= freed_pages; - } - - spin_unlock_irqrestore(&pool->lock, irq_flags); - - if (freed_pages) - ttm_dma_pages_put(pool, &d_pages, pages_to_free, freed_pages); -out: - kfree(pages_to_free); - return nr_free; -} - -static void ttm_dma_free_pool(struct device *dev, enum pool_type type) -{ - struct device_pools *p; - struct dma_pool *pool; - - if (!dev) - return; - - mutex_lock(&_manager->lock); - list_for_each_entry_reverse(p, &_manager->pools, pools) { - if (p->dev != dev) - continue; - pool = p->pool; - if (pool->type != type) - continue; - - list_del(&p->pools); - kfree(p); - _manager->npools--; - break; - } - list_for_each_entry_reverse(pool, &dev->dma_pools, pools) { - if (pool->type != type) - continue; - /* Takes a spinlock.. */ - ttm_dma_page_pool_free(pool, FREE_ALL_PAGES); - WARN_ON(((pool->npages_in_use + pool->npages_free) != 0)); - /* This code path is called after _all_ references to the - * struct device has been dropped - so nobody should be - * touching it. In case somebody is trying to _add_ we are - * guarded by the mutex. */ - list_del(&pool->pools); - kfree(pool); - break; - } - mutex_unlock(&_manager->lock); -} - -/* - * On free-ing of the 'struct device' this deconstructor is run. - * Albeit the pool might have already been freed earlier. - */ -static void ttm_dma_pool_release(struct device *dev, void *res) -{ - struct dma_pool *pool = *(struct dma_pool **)res; - - if (pool) - ttm_dma_free_pool(dev, pool->type); -} - -static int ttm_dma_pool_match(struct device *dev, void *res, void *match_data) -{ - return *(struct dma_pool **)res == match_data; -} - -static struct dma_pool *ttm_dma_pool_init(struct device *dev, gfp_t flags, - enum pool_type type) -{ - char *n[] = {"wc", "uc", "cached", " dma32", "unknown",}; - enum pool_type t[] = {IS_WC, IS_UC, IS_CACHED, IS_DMA32, IS_UNDEFINED}; - struct device_pools *sec_pool = NULL; - struct dma_pool *pool = NULL, **ptr; - unsigned i; - int ret = -ENODEV; - char *p; - - if (!dev) - return NULL; - - ptr = devres_alloc(ttm_dma_pool_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - ret = -ENOMEM; - - pool = kmalloc_node(sizeof(struct dma_pool), GFP_KERNEL, - dev_to_node(dev)); - if (!pool) - goto err_mem; - - sec_pool = kmalloc_node(sizeof(struct device_pools), GFP_KERNEL, - dev_to_node(dev)); - if (!sec_pool) - goto err_mem; - - INIT_LIST_HEAD(&sec_pool->pools); - sec_pool->dev = dev; - sec_pool->pool = pool; - - INIT_LIST_HEAD(&pool->free_list); - INIT_LIST_HEAD(&pool->inuse_list); - INIT_LIST_HEAD(&pool->pools); - spin_lock_init(&pool->lock); - pool->dev = dev; - pool->npages_free = pool->npages_in_use = 0; - pool->nfrees = 0; - pool->gfp_flags = flags; - pool->size = PAGE_SIZE; - pool->type = type; - pool->nrefills = 0; - p = pool->name; - for (i = 0; i < 5; i++) { - if (type & t[i]) { - p += snprintf(p, sizeof(pool->name) - (p - pool->name), - "%s", n[i]); - } - } - *p = 0; - /* We copy the name for pr_ calls b/c when dma_pool_destroy is called - * - the kobj->name has already been deallocated.*/ - snprintf(pool->dev_name, sizeof(pool->dev_name), "%s %s", - dev_driver_string(dev), dev_name(dev)); - mutex_lock(&_manager->lock); - /* You can get the dma_pool from either the global: */ - list_add(&sec_pool->pools, &_manager->pools); - _manager->npools++; - /* or from 'struct device': */ - list_add(&pool->pools, &dev->dma_pools); - mutex_unlock(&_manager->lock); - - *ptr = pool; - devres_add(dev, ptr); - - return pool; -err_mem: - devres_free(ptr); - kfree(sec_pool); - kfree(pool); - return ERR_PTR(ret); -} - -static struct dma_pool *ttm_dma_find_pool(struct device *dev, - enum pool_type type) -{ - struct dma_pool *pool, *tmp, *found = NULL; - - if (type == IS_UNDEFINED) - return found; - - /* NB: We iterate on the 'struct dev' which has no spinlock, but - * it does have a kref which we have taken. The kref is taken during - * graphic driver loading - in the drm_pci_init it calls either - * pci_dev_get or pci_register_driver which both end up taking a kref - * on 'struct device'. - * - * On teardown, the graphic drivers end up quiescing the TTM (put_pages) - * and calls the dev_res deconstructors: ttm_dma_pool_release. The nice - * thing is at that point of time there are no pages associated with the - * driver so this function will not be called. - */ - list_for_each_entry_safe(pool, tmp, &dev->dma_pools, pools) { - if (pool->type != type) - continue; - found = pool; - break; - } - return found; -} - -/* - * Free pages the pages that failed to change the caching state. If there - * are pages that have changed their caching state already put them to the - * pool. - */ -static void ttm_dma_handle_caching_state_failure(struct dma_pool *pool, - struct list_head *d_pages, - struct page **failed_pages, - unsigned cpages) -{ - struct dma_page *d_page, *tmp; - struct page *p; - unsigned i = 0; - - p = failed_pages[0]; - if (!p) - return; - /* Find the failed page. */ - list_for_each_entry_safe(d_page, tmp, d_pages, page_list) { - if (d_page->p != p) - continue; - /* .. and then progress over the full list. */ - list_del(&d_page->page_list); - __ttm_dma_free_page(pool, d_page); - if (++i < cpages) - p = failed_pages[i]; - else - break; - } - -} - -/* - * Allocate 'count' pages, and put 'need' number of them on the - * 'pages' and as well on the 'dma_address' starting at 'dma_offset' offset. - * The full list of pages should also be on 'd_pages'. - * We return zero for success, and negative numbers as errors. - */ -static int ttm_dma_pool_alloc_new_pages(struct dma_pool *pool, - struct list_head *d_pages, - unsigned count) -{ - struct page **caching_array; - struct dma_page *dma_p; - struct page *p; - int r = 0; - unsigned i, cpages; - unsigned max_cpages = min(count, - (unsigned)(PAGE_SIZE/sizeof(struct page *))); - - /* allocate array for page caching change */ - caching_array = kmalloc(max_cpages*sizeof(struct page *), GFP_KERNEL); - - if (!caching_array) { - pr_err("%s: Unable to allocate table for new pages\n", - pool->dev_name); - return -ENOMEM; - } - - if (count > 1) { - pr_debug("%s: (%s:%d) Getting %d pages\n", - pool->dev_name, pool->name, current->pid, count); - } - - for (i = 0, cpages = 0; i < count; ++i) { - dma_p = __ttm_dma_alloc_page(pool); - if (!dma_p) { - pr_err("%s: Unable to get page %u\n", - pool->dev_name, i); - - /* store already allocated pages in the pool after - * setting the caching state */ - if (cpages) { - r = ttm_set_pages_caching(pool, caching_array, - cpages); - if (r) - ttm_dma_handle_caching_state_failure( - pool, d_pages, caching_array, - cpages); - } - r = -ENOMEM; - goto out; - } - p = dma_p->p; -#ifdef CONFIG_HIGHMEM - /* gfp flags of highmem page should never be dma32 so we - * we should be fine in such case - */ - if (!PageHighMem(p)) -#endif - { - caching_array[cpages++] = p; - if (cpages == max_cpages) { - /* Note: Cannot hold the spinlock */ - r = ttm_set_pages_caching(pool, caching_array, - cpages); - if (r) { - ttm_dma_handle_caching_state_failure( - pool, d_pages, caching_array, - cpages); - goto out; - } - cpages = 0; - } - } - list_add(&dma_p->page_list, d_pages); - } - - if (cpages) { - r = ttm_set_pages_caching(pool, caching_array, cpages); - if (r) - ttm_dma_handle_caching_state_failure(pool, d_pages, - caching_array, cpages); - } -out: - kfree(caching_array); - return r; -} - -/* - * @return count of pages still required to fulfill the request. - */ -static int ttm_dma_page_pool_fill_locked(struct dma_pool *pool, - unsigned long *irq_flags) -{ - unsigned count = _manager->options.small; - int r = pool->npages_free; - - if (count > pool->npages_free) { - struct list_head d_pages; - - INIT_LIST_HEAD(&d_pages); - - spin_unlock_irqrestore(&pool->lock, *irq_flags); - - /* Returns how many more are neccessary to fulfill the - * request. */ - r = ttm_dma_pool_alloc_new_pages(pool, &d_pages, count); - - spin_lock_irqsave(&pool->lock, *irq_flags); - if (!r) { - /* Add the fresh to the end.. */ - list_splice(&d_pages, &pool->free_list); - ++pool->nrefills; - pool->npages_free += count; - r = count; - } else { - struct dma_page *d_page; - unsigned cpages = 0; - - pr_err("%s: Failed to fill %s pool (r:%d)!\n", - pool->dev_name, pool->name, r); - - list_for_each_entry(d_page, &d_pages, page_list) { - cpages++; - } - list_splice_tail(&d_pages, &pool->free_list); - pool->npages_free += cpages; - r = cpages; - } - } - return r; -} - -/* - * @return count of pages still required to fulfill the request. - * The populate list is actually a stack (not that is matters as TTM - * allocates one page at a time. - */ -static int ttm_dma_pool_get_pages(struct dma_pool *pool, - struct ttm_dma_tt *ttm_dma, - unsigned index) -{ - struct dma_page *d_page; - struct ttm_tt *ttm = &ttm_dma->ttm; - unsigned long irq_flags; - int count, r = -ENOMEM; - - spin_lock_irqsave(&pool->lock, irq_flags); - count = ttm_dma_page_pool_fill_locked(pool, &irq_flags); - if (count) { - d_page = list_first_entry(&pool->free_list, struct dma_page, page_list); - ttm->pages[index] = d_page->p; - ttm_dma->dma_address[index] = d_page->dma; - list_move_tail(&d_page->page_list, &ttm_dma->pages_list); - r = 0; - pool->npages_in_use += 1; - pool->npages_free -= 1; - } - spin_unlock_irqrestore(&pool->lock, irq_flags); - return r; -} - -/* - * On success pages list will hold count number of correctly - * cached pages. On failure will hold the negative return value (-ENOMEM, etc). - */ -int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev) -{ - struct ttm_tt *ttm = &ttm_dma->ttm; - struct ttm_mem_global *mem_glob = ttm->glob->mem_glob; - struct dma_pool *pool; - enum pool_type type; - unsigned i; - gfp_t gfp_flags; - int ret; - - if (ttm->state != tt_unpopulated) - return 0; - - type = ttm_to_type(ttm->page_flags, ttm->caching_state); - if (ttm->page_flags & TTM_PAGE_FLAG_DMA32) - gfp_flags = GFP_USER | GFP_DMA32; - else - gfp_flags = GFP_HIGHUSER; - if (ttm->page_flags & TTM_PAGE_FLAG_ZERO_ALLOC) - gfp_flags |= __GFP_ZERO; - - pool = ttm_dma_find_pool(dev, type); - if (!pool) { - pool = ttm_dma_pool_init(dev, gfp_flags, type); - if (IS_ERR_OR_NULL(pool)) { - return -ENOMEM; - } - } - - INIT_LIST_HEAD(&ttm_dma->pages_list); - for (i = 0; i < ttm->num_pages; ++i) { - ret = ttm_dma_pool_get_pages(pool, ttm_dma, i); - if (ret != 0) { - ttm_dma_unpopulate(ttm_dma, dev); - return -ENOMEM; - } - - ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], - false, false); - if (unlikely(ret != 0)) { - ttm_dma_unpopulate(ttm_dma, dev); - return -ENOMEM; - } - } - - if (unlikely(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)) { - ret = ttm_tt_swapin(ttm); - if (unlikely(ret != 0)) { - ttm_dma_unpopulate(ttm_dma, dev); - return ret; - } - } - - ttm->state = tt_unbound; - return 0; -} -EXPORT_SYMBOL_GPL(ttm_dma_populate); - -/* Get good estimation how many pages are free in pools */ -static int ttm_dma_pool_get_num_unused_pages(void) -{ - struct device_pools *p; - unsigned total = 0; - - mutex_lock(&_manager->lock); - list_for_each_entry(p, &_manager->pools, pools) - total += p->pool->npages_free; - mutex_unlock(&_manager->lock); - return total; -} - -/* Put all pages in pages list to correct pool to wait for reuse */ -void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev) -{ - struct ttm_tt *ttm = &ttm_dma->ttm; - struct dma_pool *pool; - struct dma_page *d_page, *next; - enum pool_type type; - bool is_cached = false; - unsigned count = 0, i, npages = 0; - unsigned long irq_flags; - - type = ttm_to_type(ttm->page_flags, ttm->caching_state); - pool = ttm_dma_find_pool(dev, type); - if (!pool) - return; - - is_cached = (ttm_dma_find_pool(pool->dev, - ttm_to_type(ttm->page_flags, tt_cached)) == pool); - - /* make sure pages array match list and count number of pages */ - list_for_each_entry(d_page, &ttm_dma->pages_list, page_list) { - ttm->pages[count] = d_page->p; - count++; - } - - spin_lock_irqsave(&pool->lock, irq_flags); - pool->npages_in_use -= count; - if (is_cached) { - pool->nfrees += count; - } else { - pool->npages_free += count; - list_splice(&ttm_dma->pages_list, &pool->free_list); - npages = count; - if (pool->npages_free > _manager->options.max_size) { - npages = pool->npages_free - _manager->options.max_size; - /* free at least NUM_PAGES_TO_ALLOC number of pages - * to reduce calls to set_memory_wb */ - if (npages < NUM_PAGES_TO_ALLOC) - npages = NUM_PAGES_TO_ALLOC; - } - } - spin_unlock_irqrestore(&pool->lock, irq_flags); - - if (is_cached) { - list_for_each_entry_safe(d_page, next, &ttm_dma->pages_list, page_list) { - ttm_mem_global_free_page(ttm->glob->mem_glob, - d_page->p); - ttm_dma_page_put(pool, d_page); - } - } else { - for (i = 0; i < count; i++) { - ttm_mem_global_free_page(ttm->glob->mem_glob, - ttm->pages[i]); - } - } - - INIT_LIST_HEAD(&ttm_dma->pages_list); - for (i = 0; i < ttm->num_pages; i++) { - ttm->pages[i] = NULL; - ttm_dma->dma_address[i] = 0; - } - - /* shrink pool if necessary (only on !is_cached pools)*/ - if (npages) - ttm_dma_page_pool_free(pool, npages); - ttm->state = tt_unpopulated; -} -EXPORT_SYMBOL_GPL(ttm_dma_unpopulate); - -/** - * Callback for mm to request pool to reduce number of page held. - */ -static int ttm_dma_pool_mm_shrink(struct shrinker *shrink, - struct shrink_control *sc) -{ - static atomic_t start_pool = ATOMIC_INIT(0); - unsigned idx = 0; - unsigned pool_offset = atomic_add_return(1, &start_pool); - unsigned shrink_pages = sc->nr_to_scan; - struct device_pools *p; - - if (list_empty(&_manager->pools)) - return 0; - - mutex_lock(&_manager->lock); - pool_offset = pool_offset % _manager->npools; - list_for_each_entry(p, &_manager->pools, pools) { - unsigned nr_free; - - if (!p->dev) - continue; - if (shrink_pages == 0) - break; - /* Do it in round-robin fashion. */ - if (++idx < pool_offset) - continue; - nr_free = shrink_pages; - shrink_pages = ttm_dma_page_pool_free(p->pool, nr_free); - pr_debug("%s: (%s:%d) Asked to shrink %d, have %d more to go\n", - p->pool->dev_name, p->pool->name, current->pid, - nr_free, shrink_pages); - } - mutex_unlock(&_manager->lock); - /* return estimated number of unused pages in pool */ - return ttm_dma_pool_get_num_unused_pages(); -} - -static void ttm_dma_pool_mm_shrink_init(struct ttm_pool_manager *manager) -{ - manager->mm_shrink.shrink = &ttm_dma_pool_mm_shrink; - manager->mm_shrink.seeks = 1; - register_shrinker(&manager->mm_shrink); -} - -static void ttm_dma_pool_mm_shrink_fini(struct ttm_pool_manager *manager) -{ - unregister_shrinker(&manager->mm_shrink); -} - -int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) -{ - int ret = -ENOMEM; - - WARN_ON(_manager); - - pr_info("Initializing DMA pool allocator\n"); - - _manager = kzalloc(sizeof(*_manager), GFP_KERNEL); - if (!_manager) - goto err_manager; - - mutex_init(&_manager->lock); - INIT_LIST_HEAD(&_manager->pools); - - _manager->options.max_size = max_pages; - _manager->options.small = SMALL_ALLOCATION; - _manager->options.alloc_size = NUM_PAGES_TO_ALLOC; - - /* This takes care of auto-freeing the _manager */ - ret = kobject_init_and_add(&_manager->kobj, &ttm_pool_kobj_type, - &glob->kobj, "dma_pool"); - if (unlikely(ret != 0)) { - kobject_put(&_manager->kobj); - goto err; - } - ttm_dma_pool_mm_shrink_init(_manager); - return 0; -err_manager: - kfree(_manager); - _manager = NULL; -err: - return ret; -} - -void ttm_dma_page_alloc_fini(void) -{ - struct device_pools *p, *t; - - pr_info("Finalizing DMA pool allocator\n"); - ttm_dma_pool_mm_shrink_fini(_manager); - - list_for_each_entry_safe_reverse(p, t, &_manager->pools, pools) { - dev_dbg(p->dev, "(%s:%d) Freeing.\n", p->pool->name, - current->pid); - WARN_ON(devres_destroy(p->dev, ttm_dma_pool_release, - ttm_dma_pool_match, p->pool)); - ttm_dma_free_pool(p->dev, p->pool->type); - } - kobject_put(&_manager->kobj); - _manager = NULL; -} - -int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data) -{ - struct device_pools *p; - struct dma_pool *pool = NULL; - char *h[] = {"pool", "refills", "pages freed", "inuse", "available", - "name", "virt", "busaddr"}; - - if (!_manager) { - seq_printf(m, "No pool allocator running.\n"); - return 0; - } - seq_printf(m, "%13s %12s %13s %8s %8s %8s\n", - h[0], h[1], h[2], h[3], h[4], h[5]); - mutex_lock(&_manager->lock); - list_for_each_entry(p, &_manager->pools, pools) { - struct device *dev = p->dev; - if (!dev) - continue; - pool = p->pool; - seq_printf(m, "%13s %12ld %13ld %8d %8d %8s\n", - pool->name, pool->nrefills, - pool->nfrees, pool->npages_in_use, - pool->npages_free, - pool->dev_name); - } - mutex_unlock(&_manager->lock); - return 0; -} -EXPORT_SYMBOL_GPL(ttm_dma_page_alloc_debugfs); diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index f1fd0ee..f9cc548 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -28,8 +28,6 @@ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> */ -#define pr_fmt(fmt) "[TTM] " fmt - #include <linux/sched.h> #include <linux/highmem.h> #include <linux/pagemap.h> @@ -37,6 +35,7 @@ #include <linux/file.h> #include <linux/swap.h> #include <linux/slab.h> +#include <linux/export.h> #include "drm_cache.h" #include "drm_mem_util.h" #include "ttm/ttm_module.h" @@ -44,21 +43,140 @@ #include "ttm/ttm_placement.h" #include "ttm/ttm_page_alloc.h" +static int ttm_tt_swapin(struct ttm_tt *ttm); + /** * Allocates storage for pointers to the pages that back the ttm. */ static void ttm_tt_alloc_page_directory(struct ttm_tt *ttm) { - ttm->pages = drm_calloc_large(ttm->num_pages, sizeof(void*)); + ttm->pages = drm_calloc_large(ttm->num_pages, sizeof(*ttm->pages)); + ttm->dma_address = drm_calloc_large(ttm->num_pages, + sizeof(*ttm->dma_address)); } -static void ttm_dma_tt_alloc_page_directory(struct ttm_dma_tt *ttm) +static void ttm_tt_free_page_directory(struct ttm_tt *ttm) { - ttm->ttm.pages = drm_calloc_large(ttm->ttm.num_pages, sizeof(void*)); - ttm->dma_address = drm_calloc_large(ttm->ttm.num_pages, - sizeof(*ttm->dma_address)); + drm_free_large(ttm->pages); + ttm->pages = NULL; + drm_free_large(ttm->dma_address); + ttm->dma_address = NULL; +} + +static void ttm_tt_free_user_pages(struct ttm_tt *ttm) +{ + int write; + int dirty; + struct page *page; + int i; + struct ttm_backend *be = ttm->be; + + BUG_ON(!(ttm->page_flags & TTM_PAGE_FLAG_USER)); + write = ((ttm->page_flags & TTM_PAGE_FLAG_WRITE) != 0); + dirty = ((ttm->page_flags & TTM_PAGE_FLAG_USER_DIRTY) != 0); + + if (be) + be->func->clear(be); + + for (i = 0; i < ttm->num_pages; ++i) { + page = ttm->pages[i]; + if (page == NULL) + continue; + + if (page == ttm->dummy_read_page) { + BUG_ON(write); + continue; + } + + if (write && dirty && !PageReserved(page)) + set_page_dirty_lock(page); + + ttm->pages[i] = NULL; + ttm_mem_global_free(ttm->glob->mem_glob, PAGE_SIZE); + put_page(page); + } + ttm->state = tt_unpopulated; + ttm->first_himem_page = ttm->num_pages; + ttm->last_lomem_page = -1; } +static struct page *__ttm_tt_get_page(struct ttm_tt *ttm, int index) +{ + struct page *p; + struct list_head h; + struct ttm_mem_global *mem_glob = ttm->glob->mem_glob; + int ret; + + while (NULL == (p = ttm->pages[index])) { + + INIT_LIST_HEAD(&h); + + ret = ttm_get_pages(&h, ttm->page_flags, ttm->caching_state, 1, + &ttm->dma_address[index]); + + if (ret != 0) + return NULL; + + p = list_first_entry(&h, struct page, lru); + + ret = ttm_mem_global_alloc_page(mem_glob, p, false, false); + if (unlikely(ret != 0)) + goto out_err; + + if (PageHighMem(p)) + ttm->pages[--ttm->first_himem_page] = p; + else + ttm->pages[++ttm->last_lomem_page] = p; + } + return p; +out_err: + put_page(p); + return NULL; +} + +struct page *ttm_tt_get_page(struct ttm_tt *ttm, int index) +{ + int ret; + + if (unlikely(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)) { + ret = ttm_tt_swapin(ttm); + if (unlikely(ret != 0)) + return NULL; + } + return __ttm_tt_get_page(ttm, index); +} + +int ttm_tt_populate(struct ttm_tt *ttm) +{ + struct page *page; + unsigned long i; + struct ttm_backend *be; + int ret; + + if (ttm->state != tt_unpopulated) + return 0; + + if (unlikely(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)) { + ret = ttm_tt_swapin(ttm); + if (unlikely(ret != 0)) + return ret; + } + + be = ttm->be; + + for (i = 0; i < ttm->num_pages; ++i) { + page = __ttm_tt_get_page(ttm, i); + if (!page) + return -ENOMEM; + } + + be->func->populate(be, ttm->num_pages, ttm->pages, + ttm->dummy_read_page, ttm->dma_address); + ttm->state = tt_unbound; + return 0; +} +EXPORT_SYMBOL(ttm_tt_populate); + #ifdef CONFIG_X86 static inline int ttm_tt_set_page_caching(struct page *p, enum ttm_caching_state c_old, @@ -160,100 +278,153 @@ int ttm_tt_set_placement_caching(struct ttm_tt *ttm, uint32_t placement) } EXPORT_SYMBOL(ttm_tt_set_placement_caching); +static void ttm_tt_free_alloced_pages(struct ttm_tt *ttm) +{ + int i; + unsigned count = 0; + struct list_head h; + struct page *cur_page; + struct ttm_backend *be = ttm->be; + + INIT_LIST_HEAD(&h); + + if (be) + be->func->clear(be); + for (i = 0; i < ttm->num_pages; ++i) { + + cur_page = ttm->pages[i]; + ttm->pages[i] = NULL; + if (cur_page) { + if (page_count(cur_page) != 1) + printk(KERN_ERR TTM_PFX + "Erroneous page count. " + "Leaking pages.\n"); + ttm_mem_global_free_page(ttm->glob->mem_glob, + cur_page); + list_add(&cur_page->lru, &h); + count++; + } + } + ttm_put_pages(&h, count, ttm->page_flags, ttm->caching_state, + ttm->dma_address); + ttm->state = tt_unpopulated; + ttm->first_himem_page = ttm->num_pages; + ttm->last_lomem_page = -1; +} + void ttm_tt_destroy(struct ttm_tt *ttm) { + struct ttm_backend *be; + if (unlikely(ttm == NULL)) return; - if (ttm->state == tt_bound) { - ttm_tt_unbind(ttm); + be = ttm->be; + if (likely(be != NULL)) { + be->func->destroy(be); + ttm->be = NULL; } if (likely(ttm->pages != NULL)) { - ttm->bdev->driver->ttm_tt_unpopulate(ttm); + if (ttm->page_flags & TTM_PAGE_FLAG_USER) + ttm_tt_free_user_pages(ttm); + else + ttm_tt_free_alloced_pages(ttm); + + ttm_tt_free_page_directory(ttm); } if (!(ttm->page_flags & TTM_PAGE_FLAG_PERSISTENT_SWAP) && ttm->swap_storage) fput(ttm->swap_storage); - ttm->swap_storage = NULL; - ttm->func->destroy(ttm); + kfree(ttm); } -int ttm_tt_init(struct ttm_tt *ttm, struct ttm_bo_device *bdev, - unsigned long size, uint32_t page_flags, - struct page *dummy_read_page) +int ttm_tt_set_user(struct ttm_tt *ttm, + struct task_struct *tsk, + unsigned long start, unsigned long num_pages) { - ttm->bdev = bdev; - ttm->glob = bdev->glob; - ttm->num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - ttm->caching_state = tt_cached; - ttm->page_flags = page_flags; - ttm->dummy_read_page = dummy_read_page; - ttm->state = tt_unpopulated; - ttm->swap_storage = NULL; + struct mm_struct *mm = tsk->mm; + int ret; + int write = (ttm->page_flags & TTM_PAGE_FLAG_WRITE) != 0; + struct ttm_mem_global *mem_glob = ttm->glob->mem_glob; - ttm_tt_alloc_page_directory(ttm); - if (!ttm->pages) { - ttm_tt_destroy(ttm); - pr_err("Failed allocating page table\n"); + BUG_ON(num_pages != ttm->num_pages); + BUG_ON((ttm->page_flags & TTM_PAGE_FLAG_USER) == 0); + + /** + * Account user pages as lowmem pages for now. + */ + + ret = ttm_mem_global_alloc(mem_glob, num_pages * PAGE_SIZE, + false, false); + if (unlikely(ret != 0)) + return ret; + + down_read(&mm->mmap_sem); + ret = get_user_pages(tsk, mm, start, num_pages, + write, 0, ttm->pages, NULL); + up_read(&mm->mmap_sem); + + if (ret != num_pages && write) { + ttm_tt_free_user_pages(ttm); + ttm_mem_global_free(mem_glob, num_pages * PAGE_SIZE); return -ENOMEM; } + + ttm->tsk = tsk; + ttm->start = start; + ttm->state = tt_unbound; + return 0; } -EXPORT_SYMBOL(ttm_tt_init); -void ttm_tt_fini(struct ttm_tt *ttm) +struct ttm_tt *ttm_tt_create(struct ttm_bo_device *bdev, unsigned long size, + uint32_t page_flags, struct page *dummy_read_page) { - drm_free_large(ttm->pages); - ttm->pages = NULL; -} -EXPORT_SYMBOL(ttm_tt_fini); + struct ttm_bo_driver *bo_driver = bdev->driver; + struct ttm_tt *ttm; -int ttm_dma_tt_init(struct ttm_dma_tt *ttm_dma, struct ttm_bo_device *bdev, - unsigned long size, uint32_t page_flags, - struct page *dummy_read_page) -{ - struct ttm_tt *ttm = &ttm_dma->ttm; + if (!bo_driver) + return NULL; + + ttm = kzalloc(sizeof(*ttm), GFP_KERNEL); + if (!ttm) + return NULL; - ttm->bdev = bdev; ttm->glob = bdev->glob; ttm->num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + ttm->first_himem_page = ttm->num_pages; + ttm->last_lomem_page = -1; ttm->caching_state = tt_cached; ttm->page_flags = page_flags; + ttm->dummy_read_page = dummy_read_page; - ttm->state = tt_unpopulated; - ttm->swap_storage = NULL; - INIT_LIST_HEAD(&ttm_dma->pages_list); - ttm_dma_tt_alloc_page_directory(ttm_dma); - if (!ttm->pages || !ttm_dma->dma_address) { + ttm_tt_alloc_page_directory(ttm); + if (!ttm->pages) { ttm_tt_destroy(ttm); - pr_err("Failed allocating page table\n"); - return -ENOMEM; + printk(KERN_ERR TTM_PFX "Failed allocating page table\n"); + return NULL; } - return 0; -} -EXPORT_SYMBOL(ttm_dma_tt_init); - -void ttm_dma_tt_fini(struct ttm_dma_tt *ttm_dma) -{ - struct ttm_tt *ttm = &ttm_dma->ttm; - - drm_free_large(ttm->pages); - ttm->pages = NULL; - drm_free_large(ttm_dma->dma_address); - ttm_dma->dma_address = NULL; + ttm->be = bo_driver->create_ttm_backend_entry(bdev); + if (!ttm->be) { + ttm_tt_destroy(ttm); + printk(KERN_ERR TTM_PFX "Failed creating ttm backend entry\n"); + return NULL; + } + ttm->state = tt_unpopulated; + return ttm; } -EXPORT_SYMBOL(ttm_dma_tt_fini); void ttm_tt_unbind(struct ttm_tt *ttm) { int ret; + struct ttm_backend *be = ttm->be; if (ttm->state == tt_bound) { - ret = ttm->func->unbind(ttm); + ret = be->func->unbind(be); BUG_ON(ret); ttm->state = tt_unbound; } @@ -262,6 +433,7 @@ void ttm_tt_unbind(struct ttm_tt *ttm) int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) { int ret = 0; + struct ttm_backend *be; if (!ttm) return -EINVAL; @@ -269,21 +441,25 @@ int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) if (ttm->state == tt_bound) return 0; - ret = ttm->bdev->driver->ttm_tt_populate(ttm); + be = ttm->be; + + ret = ttm_tt_populate(ttm); if (ret) return ret; - ret = ttm->func->bind(ttm, bo_mem); + ret = be->func->bind(be, bo_mem); if (unlikely(ret != 0)) return ret; ttm->state = tt_bound; + if (ttm->page_flags & TTM_PAGE_FLAG_USER) + ttm->page_flags |= TTM_PAGE_FLAG_USER_DIRTY; return 0; } EXPORT_SYMBOL(ttm_tt_bind); -int ttm_tt_swapin(struct ttm_tt *ttm) +static int ttm_tt_swapin(struct ttm_tt *ttm) { struct address_space *swap_space; struct file *swap_storage; @@ -294,6 +470,16 @@ int ttm_tt_swapin(struct ttm_tt *ttm) int i; int ret = -ENOMEM; + if (ttm->page_flags & TTM_PAGE_FLAG_USER) { + ret = ttm_tt_set_user(ttm, ttm->tsk, ttm->start, + ttm->num_pages); + if (unlikely(ret != 0)) + return ret; + + ttm->page_flags &= ~TTM_PAGE_FLAG_SWAPPED; + return 0; + } + swap_storage = ttm->swap_storage; BUG_ON(swap_storage == NULL); @@ -305,7 +491,7 @@ int ttm_tt_swapin(struct ttm_tt *ttm) ret = PTR_ERR(from_page); goto out_err; } - to_page = ttm->pages[i]; + to_page = __ttm_tt_get_page(ttm, i); if (unlikely(to_page == NULL)) goto out_err; @@ -326,6 +512,7 @@ int ttm_tt_swapin(struct ttm_tt *ttm) return 0; out_err: + ttm_tt_free_alloced_pages(ttm); return ret; } @@ -343,12 +530,24 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage) BUG_ON(ttm->state != tt_unbound && ttm->state != tt_unpopulated); BUG_ON(ttm->caching_state != tt_cached); + /* + * For user buffers, just unpin the pages, as there should be + * vma references. + */ + + if (ttm->page_flags & TTM_PAGE_FLAG_USER) { + ttm_tt_free_user_pages(ttm); + ttm->page_flags |= TTM_PAGE_FLAG_SWAPPED; + ttm->swap_storage = NULL; + return 0; + } + if (!persistent_swap_storage) { swap_storage = shmem_file_setup("ttm swap", ttm->num_pages << PAGE_SHIFT, 0); if (unlikely(IS_ERR(swap_storage))) { - pr_err("Failed allocating swap storage\n"); + printk(KERN_ERR "Failed allocating swap storage.\n"); return PTR_ERR(swap_storage); } } else @@ -377,7 +576,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage) page_cache_release(to_page); } - ttm->bdev->driver->ttm_tt_unpopulate(ttm); + ttm_tt_free_alloced_pages(ttm); ttm->swap_storage = swap_storage; ttm->page_flags |= TTM_PAGE_FLAG_SWAPPED; if (persistent_swap_storage) diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c index 920a552..a83e86d 100644 --- a/drivers/gpu/drm/via/via_drv.c +++ b/drivers/gpu/drm/via/via_drv.c @@ -22,6 +22,8 @@ * DEALINGS IN THE SOFTWARE. */ +#include <linux/module.h> + #include "drmP.h" #include "via_drm.h" #include "via_drv.h" diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig index 30ad133..794ff67 100644 --- a/drivers/gpu/drm/vmwgfx/Kconfig +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -7,7 +7,8 @@ config DRM_VMWGFX select FB_CFB_IMAGEBLIT select DRM_TTM help - KMS enabled DRM driver for SVGA2 virtual hardware. - - If unsure say n. The compiled module will be - called vmwgfx.ko + Choose this option if you would like to run 3D acceleration + in a VMware virtual machine. + This is a KMS enabled DRM driver for the VMware SVGA2 + virtual hardware. + The compiled module will be called "vmwgfx.ko". diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index c9281a1..586869c 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -4,6 +4,7 @@ ccflags-y := -Iinclude/drm vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \ vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \ - vmwgfx_overlay.o vmwgfx_fence.o vmwgfx_gmrid_manager.o + vmwgfx_overlay.o vmwgfx_marker.o vmwgfx_gmrid_manager.o \ + vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/svga3d_reg.h b/drivers/gpu/drm/vmwgfx/svga3d_reg.h index 77cb453..d0e085e 100644 --- a/drivers/gpu/drm/vmwgfx/svga3d_reg.h +++ b/drivers/gpu/drm/vmwgfx/svga3d_reg.h @@ -57,7 +57,8 @@ typedef enum { SVGA3D_HWVERSION_WS6_B1 = SVGA3D_MAKE_HWVERSION(1, 1), SVGA3D_HWVERSION_FUSION_11 = SVGA3D_MAKE_HWVERSION(1, 4), SVGA3D_HWVERSION_WS65_B1 = SVGA3D_MAKE_HWVERSION(2, 0), - SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS65_B1, + SVGA3D_HWVERSION_WS8_B1 = SVGA3D_MAKE_HWVERSION(2, 1), + SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS8_B1, } SVGA3dHardwareVersion; /* @@ -67,7 +68,8 @@ typedef enum { typedef uint32 SVGA3dBool; /* 32-bit Bool definition */ #define SVGA3D_NUM_CLIPPLANES 6 #define SVGA3D_MAX_SIMULTANEOUS_RENDER_TARGETS 8 - +#define SVGA3D_MAX_CONTEXT_IDS 256 +#define SVGA3D_MAX_SURFACE_IDS (32 * 1024) /* * Surface formats. @@ -79,76 +81,91 @@ typedef uint32 SVGA3dBool; /* 32-bit Bool definition */ */ typedef enum SVGA3dSurfaceFormat { - SVGA3D_FORMAT_INVALID = 0, + SVGA3D_FORMAT_INVALID = 0, - SVGA3D_X8R8G8B8 = 1, - SVGA3D_A8R8G8B8 = 2, + SVGA3D_X8R8G8B8 = 1, + SVGA3D_A8R8G8B8 = 2, - SVGA3D_R5G6B5 = 3, - SVGA3D_X1R5G5B5 = 4, - SVGA3D_A1R5G5B5 = 5, - SVGA3D_A4R4G4B4 = 6, + SVGA3D_R5G6B5 = 3, + SVGA3D_X1R5G5B5 = 4, + SVGA3D_A1R5G5B5 = 5, + SVGA3D_A4R4G4B4 = 6, - SVGA3D_Z_D32 = 7, - SVGA3D_Z_D16 = 8, - SVGA3D_Z_D24S8 = 9, - SVGA3D_Z_D15S1 = 10, + SVGA3D_Z_D32 = 7, + SVGA3D_Z_D16 = 8, + SVGA3D_Z_D24S8 = 9, + SVGA3D_Z_D15S1 = 10, - SVGA3D_LUMINANCE8 = 11, - SVGA3D_LUMINANCE4_ALPHA4 = 12, - SVGA3D_LUMINANCE16 = 13, - SVGA3D_LUMINANCE8_ALPHA8 = 14, + SVGA3D_LUMINANCE8 = 11, + SVGA3D_LUMINANCE4_ALPHA4 = 12, + SVGA3D_LUMINANCE16 = 13, + SVGA3D_LUMINANCE8_ALPHA8 = 14, - SVGA3D_DXT1 = 15, - SVGA3D_DXT2 = 16, - SVGA3D_DXT3 = 17, - SVGA3D_DXT4 = 18, - SVGA3D_DXT5 = 19, + SVGA3D_DXT1 = 15, + SVGA3D_DXT2 = 16, + SVGA3D_DXT3 = 17, + SVGA3D_DXT4 = 18, + SVGA3D_DXT5 = 19, - SVGA3D_BUMPU8V8 = 20, - SVGA3D_BUMPL6V5U5 = 21, - SVGA3D_BUMPX8L8V8U8 = 22, - SVGA3D_BUMPL8V8U8 = 23, + SVGA3D_BUMPU8V8 = 20, + SVGA3D_BUMPL6V5U5 = 21, + SVGA3D_BUMPX8L8V8U8 = 22, + SVGA3D_BUMPL8V8U8 = 23, - SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */ - SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */ + SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */ + SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */ - SVGA3D_A2R10G10B10 = 26, + SVGA3D_A2R10G10B10 = 26, /* signed formats */ - SVGA3D_V8U8 = 27, - SVGA3D_Q8W8V8U8 = 28, - SVGA3D_CxV8U8 = 29, + SVGA3D_V8U8 = 27, + SVGA3D_Q8W8V8U8 = 28, + SVGA3D_CxV8U8 = 29, /* mixed formats */ - SVGA3D_X8L8V8U8 = 30, - SVGA3D_A2W10V10U10 = 31, + SVGA3D_X8L8V8U8 = 30, + SVGA3D_A2W10V10U10 = 31, - SVGA3D_ALPHA8 = 32, + SVGA3D_ALPHA8 = 32, /* Single- and dual-component floating point formats */ - SVGA3D_R_S10E5 = 33, - SVGA3D_R_S23E8 = 34, - SVGA3D_RG_S10E5 = 35, - SVGA3D_RG_S23E8 = 36, + SVGA3D_R_S10E5 = 33, + SVGA3D_R_S23E8 = 34, + SVGA3D_RG_S10E5 = 35, + SVGA3D_RG_S23E8 = 36, /* * Any surface can be used as a buffer object, but SVGA3D_BUFFER is * the most efficient format to use when creating new surfaces * expressly for index or vertex data. */ - SVGA3D_BUFFER = 37, - SVGA3D_Z_D24X8 = 38, + SVGA3D_BUFFER = 37, + + SVGA3D_Z_D24X8 = 38, - SVGA3D_V16U16 = 39, + SVGA3D_V16U16 = 39, - SVGA3D_G16R16 = 40, - SVGA3D_A16B16G16R16 = 41, + SVGA3D_G16R16 = 40, + SVGA3D_A16B16G16R16 = 41, /* Packed Video formats */ - SVGA3D_UYVY = 42, - SVGA3D_YUY2 = 43, + SVGA3D_UYVY = 42, + SVGA3D_YUY2 = 43, + + /* Planar video formats */ + SVGA3D_NV12 = 44, + + /* Video format with alpha */ + SVGA3D_AYUV = 45, + + SVGA3D_BC4_UNORM = 108, + SVGA3D_BC5_UNORM = 111, + + /* Advanced D3D9 depth formats. */ + SVGA3D_Z_DF16 = 118, + SVGA3D_Z_DF24 = 119, + SVGA3D_Z_D24S8_INT = 120, SVGA3D_FORMAT_MAX } SVGA3dSurfaceFormat; @@ -414,10 +431,20 @@ typedef enum { SVGA3D_RS_SRCBLENDALPHA = 94, /* SVGA3dBlendOp */ SVGA3D_RS_DSTBLENDALPHA = 95, /* SVGA3dBlendOp */ SVGA3D_RS_BLENDEQUATIONALPHA = 96, /* SVGA3dBlendEquation */ + SVGA3D_RS_TRANSPARENCYANTIALIAS = 97, /* SVGA3dTransparencyAntialiasType */ + SVGA3D_RS_LINEAA = 98, /* SVGA3dBool */ + SVGA3D_RS_LINEWIDTH = 99, /* float */ SVGA3D_RS_MAX } SVGA3dRenderStateName; typedef enum { + SVGA3D_TRANSPARENCYANTIALIAS_NORMAL = 0, + SVGA3D_TRANSPARENCYANTIALIAS_ALPHATOCOVERAGE = 1, + SVGA3D_TRANSPARENCYANTIALIAS_SUPERSAMPLE = 2, + SVGA3D_TRANSPARENCYANTIALIAS_MAX +} SVGA3dTransparencyAntialiasType; + +typedef enum { SVGA3D_VERTEXMATERIAL_NONE = 0, /* Use the value in the current material */ SVGA3D_VERTEXMATERIAL_DIFFUSE = 1, /* Use the value in the diffuse component */ SVGA3D_VERTEXMATERIAL_SPECULAR = 2, /* Use the value in the specular component */ @@ -728,10 +755,10 @@ typedef enum { SVGA3D_TEX_FILTER_NEAREST = 1, SVGA3D_TEX_FILTER_LINEAR = 2, SVGA3D_TEX_FILTER_ANISOTROPIC = 3, - SVGA3D_TEX_FILTER_FLATCUBIC = 4, // Deprecated, not implemented - SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, // Deprecated, not implemented - SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, // Not currently implemented - SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, // Not currently implemented + SVGA3D_TEX_FILTER_FLATCUBIC = 4, /* Deprecated, not implemented */ + SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, /* Deprecated, not implemented */ + SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, /* Not currently implemented */ + SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, /* Not currently implemented */ SVGA3D_TEX_FILTER_MAX } SVGA3dTextureFilter; @@ -799,19 +826,19 @@ typedef enum { typedef enum { SVGA3D_DECLUSAGE_POSITION = 0, - SVGA3D_DECLUSAGE_BLENDWEIGHT, // 1 - SVGA3D_DECLUSAGE_BLENDINDICES, // 2 - SVGA3D_DECLUSAGE_NORMAL, // 3 - SVGA3D_DECLUSAGE_PSIZE, // 4 - SVGA3D_DECLUSAGE_TEXCOORD, // 5 - SVGA3D_DECLUSAGE_TANGENT, // 6 - SVGA3D_DECLUSAGE_BINORMAL, // 7 - SVGA3D_DECLUSAGE_TESSFACTOR, // 8 - SVGA3D_DECLUSAGE_POSITIONT, // 9 - SVGA3D_DECLUSAGE_COLOR, // 10 - SVGA3D_DECLUSAGE_FOG, // 11 - SVGA3D_DECLUSAGE_DEPTH, // 12 - SVGA3D_DECLUSAGE_SAMPLE, // 13 + SVGA3D_DECLUSAGE_BLENDWEIGHT, /* 1 */ + SVGA3D_DECLUSAGE_BLENDINDICES, /* 2 */ + SVGA3D_DECLUSAGE_NORMAL, /* 3 */ + SVGA3D_DECLUSAGE_PSIZE, /* 4 */ + SVGA3D_DECLUSAGE_TEXCOORD, /* 5 */ + SVGA3D_DECLUSAGE_TANGENT, /* 6 */ + SVGA3D_DECLUSAGE_BINORMAL, /* 7 */ + SVGA3D_DECLUSAGE_TESSFACTOR, /* 8 */ + SVGA3D_DECLUSAGE_POSITIONT, /* 9 */ + SVGA3D_DECLUSAGE_COLOR, /* 10 */ + SVGA3D_DECLUSAGE_FOG, /* 11 */ + SVGA3D_DECLUSAGE_DEPTH, /* 12 */ + SVGA3D_DECLUSAGE_SAMPLE, /* 13 */ SVGA3D_DECLUSAGE_MAX } SVGA3dDeclUsage; @@ -819,10 +846,10 @@ typedef enum { SVGA3D_DECLMETHOD_DEFAULT = 0, SVGA3D_DECLMETHOD_PARTIALU, SVGA3D_DECLMETHOD_PARTIALV, - SVGA3D_DECLMETHOD_CROSSUV, // Normal + SVGA3D_DECLMETHOD_CROSSUV, /* Normal */ SVGA3D_DECLMETHOD_UV, - SVGA3D_DECLMETHOD_LOOKUP, // Lookup a displacement map - SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, // Lookup a pre-sampled displacement map + SVGA3D_DECLMETHOD_LOOKUP, /* Lookup a displacement map */ + SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, /* Lookup a pre-sampled displacement map */ } SVGA3dDeclMethod; typedef enum { @@ -930,7 +957,6 @@ typedef enum { } SVGA3dCubeFace; typedef enum { - SVGA3D_SHADERTYPE_COMPILED_DX8 = 0, SVGA3D_SHADERTYPE_VS = 1, SVGA3D_SHADERTYPE_PS = 2, SVGA3D_SHADERTYPE_MAX @@ -968,12 +994,18 @@ typedef enum { } SVGA3dTransferType; /* - * The maximum number vertex arrays we're guaranteed to support in + * The maximum number of vertex arrays we're guaranteed to support in * SVGA_3D_CMD_DRAWPRIMITIVES. */ #define SVGA3D_MAX_VERTEX_ARRAYS 32 /* + * The maximum number of primitive ranges we're guaranteed to support + * in SVGA_3D_CMD_DRAWPRIMITIVES. + */ +#define SVGA3D_MAX_DRAW_PRIMITIVE_RANGES 32 + +/* * Identifiers for commands in the command FIFO. * * IDs between 1000 and 1039 (inclusive) were used by obsolete versions of @@ -990,7 +1022,7 @@ typedef enum { #define SVGA_3D_CMD_LEGACY_BASE 1000 #define SVGA_3D_CMD_BASE 1040 -#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0 +#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0 /* Deprecated */ #define SVGA_3D_CMD_SURFACE_DESTROY SVGA_3D_CMD_BASE + 1 #define SVGA_3D_CMD_SURFACE_COPY SVGA_3D_CMD_BASE + 2 #define SVGA_3D_CMD_SURFACE_STRETCHBLT SVGA_3D_CMD_BASE + 3 @@ -1008,7 +1040,7 @@ typedef enum { #define SVGA_3D_CMD_SETVIEWPORT SVGA_3D_CMD_BASE + 15 #define SVGA_3D_CMD_SETCLIPPLANE SVGA_3D_CMD_BASE + 16 #define SVGA_3D_CMD_CLEAR SVGA_3D_CMD_BASE + 17 -#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 // Deprecated +#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 /* Deprecated */ #define SVGA_3D_CMD_SHADER_DEFINE SVGA_3D_CMD_BASE + 19 #define SVGA_3D_CMD_SHADER_DESTROY SVGA_3D_CMD_BASE + 20 #define SVGA_3D_CMD_SET_SHADER SVGA_3D_CMD_BASE + 21 @@ -1018,9 +1050,13 @@ typedef enum { #define SVGA_3D_CMD_BEGIN_QUERY SVGA_3D_CMD_BASE + 25 #define SVGA_3D_CMD_END_QUERY SVGA_3D_CMD_BASE + 26 #define SVGA_3D_CMD_WAIT_FOR_QUERY SVGA_3D_CMD_BASE + 27 -#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 // Deprecated +#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 /* Deprecated */ #define SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN SVGA_3D_CMD_BASE + 29 -#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 30 +#define SVGA_3D_CMD_SURFACE_DEFINE_V2 SVGA_3D_CMD_BASE + 30 +#define SVGA_3D_CMD_GENERATE_MIPMAPS SVGA_3D_CMD_BASE + 31 +#define SVGA_3D_CMD_ACTIVATE_SURFACE SVGA_3D_CMD_BASE + 40 +#define SVGA_3D_CMD_DEACTIVATE_SURFACE SVGA_3D_CMD_BASE + 41 +#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 42 #define SVGA_3D_CMD_FUTURE_MAX 2000 @@ -1031,9 +1067,9 @@ typedef enum { typedef struct { union { struct { - uint16 function; // SVGA3dFogFunction - uint8 type; // SVGA3dFogType - uint8 base; // SVGA3dFogBase + uint16 function; /* SVGA3dFogFunction */ + uint8 type; /* SVGA3dFogType */ + uint8 base; /* SVGA3dFogBase */ }; uint32 uintValue; }; @@ -1109,6 +1145,8 @@ typedef enum { SVGA3D_SURFACE_HINT_RENDERTARGET = (1 << 6), SVGA3D_SURFACE_HINT_DEPTHSTENCIL = (1 << 7), SVGA3D_SURFACE_HINT_WRITEONLY = (1 << 8), + SVGA3D_SURFACE_MASKABLE_ANTIALIAS = (1 << 9), + SVGA3D_SURFACE_AUTOGENMIPMAPS = (1 << 10), } SVGA3dSurfaceFlags; typedef @@ -1121,6 +1159,12 @@ struct { uint32 sid; SVGA3dSurfaceFlags surfaceFlags; SVGA3dSurfaceFormat format; + /* + * If surfaceFlags has SVGA3D_SURFACE_CUBEMAP bit set, all SVGA3dSurfaceFace + * structures must have the same value of numMipLevels field. + * Otherwise, all but the first SVGA3dSurfaceFace structures must have the + * numMipLevels set to 0. + */ SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES]; /* * Followed by an SVGA3dSize structure for each mip level in each face. @@ -1135,6 +1179,31 @@ struct { typedef struct { + uint32 sid; + SVGA3dSurfaceFlags surfaceFlags; + SVGA3dSurfaceFormat format; + /* + * If surfaceFlags has SVGA3D_SURFACE_CUBEMAP bit set, all SVGA3dSurfaceFace + * structures must have the same value of numMipLevels field. + * Otherwise, all but the first SVGA3dSurfaceFace structures must have the + * numMipLevels set to 0. + */ + SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES]; + uint32 multisampleCount; + SVGA3dTextureFilter autogenFilter; + /* + * Followed by an SVGA3dSize structure for each mip level in each face. + * + * A note on surface sizes: Sizes are always specified in pixels, + * even if the true surface size is not a multiple of the minimum + * block size of the surface's format. For example, a 3x3x1 DXT1 + * compressed texture would actually be stored as a 4x4x1 image in + * memory. + */ +} SVGA3dCmdDefineSurface_v2; /* SVGA_3D_CMD_SURFACE_DEFINE_V2 */ + +typedef +struct { uint32 sid; } SVGA3dCmdDestroySurface; /* SVGA_3D_CMD_SURFACE_DESTROY */ @@ -1474,10 +1543,12 @@ struct { * SVGA3dCmdDrawPrimitives structure. In order, * they are: * - * 1. SVGA3dVertexDecl, quantity 'numVertexDecls' - * 2. SVGA3dPrimitiveRange, quantity 'numRanges' + * 1. SVGA3dVertexDecl, quantity 'numVertexDecls', but no more than + * SVGA3D_MAX_VERTEX_ARRAYS; + * 2. SVGA3dPrimitiveRange, quantity 'numRanges', but no more than + * SVGA3D_MAX_DRAW_PRIMITIVE_RANGES; * 3. Optionally, SVGA3dVertexDivisor, quantity 'numVertexDecls' (contains - * the frequency divisor for this the corresponding vertex decl) + * the frequency divisor for the corresponding vertex decl). */ } SVGA3dCmdDrawPrimitives; /* SVGA_3D_CMD_DRAWPRIMITIVES */ @@ -1671,6 +1742,12 @@ struct { /* Clipping: zero or more SVGASignedRects follow */ } SVGA3dCmdBlitSurfaceToScreen; /* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN */ +typedef +struct { + uint32 sid; + SVGA3dTextureFilter filter; +} SVGA3dCmdGenerateMipmaps; /* SVGA_3D_CMD_GENERATE_MIPMAPS */ + /* * Capability query index. @@ -1774,6 +1851,32 @@ typedef enum { SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16 = 67, SVGA3D_DEVCAP_SURFACEFMT_UYVY = 68, SVGA3D_DEVCAP_SURFACEFMT_YUY2 = 69, + SVGA3D_DEVCAP_MULTISAMPLE_NONMASKABLESAMPLES = 70, + SVGA3D_DEVCAP_MULTISAMPLE_MASKABLESAMPLES = 71, + SVGA3D_DEVCAP_ALPHATOCOVERAGE = 72, + SVGA3D_DEVCAP_SUPERSAMPLE = 73, + SVGA3D_DEVCAP_AUTOGENMIPMAPS = 74, + SVGA3D_DEVCAP_SURFACEFMT_NV12 = 75, + SVGA3D_DEVCAP_SURFACEFMT_AYUV = 76, + + /* + * This is the maximum number of SVGA context IDs that the guest + * can define using SVGA_3D_CMD_CONTEXT_DEFINE. + */ + SVGA3D_DEVCAP_MAX_CONTEXT_IDS = 77, + + /* + * This is the maximum number of SVGA surface IDs that the guest + * can define using SVGA_3D_CMD_SURFACE_DEFINE*. + */ + SVGA3D_DEVCAP_MAX_SURFACE_IDS = 78, + + SVGA3D_DEVCAP_SURFACEFMT_Z_DF16 = 79, + SVGA3D_DEVCAP_SURFACEFMT_Z_DF24 = 80, + SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8_INT = 81, + + SVGA3D_DEVCAP_SURFACEFMT_BC4_UNORM = 82, + SVGA3D_DEVCAP_SURFACEFMT_BC5_UNORM = 83, /* * Don't add new caps into the previous section; the values in this diff --git a/drivers/gpu/drm/vmwgfx/svga_escape.h b/drivers/gpu/drm/vmwgfx/svga_escape.h index 7b85e9b..8e8d968 100644 --- a/drivers/gpu/drm/vmwgfx/svga_escape.h +++ b/drivers/gpu/drm/vmwgfx/svga_escape.h @@ -75,7 +75,7 @@ */ #define SVGA_ESCAPE_VMWARE_HINT 0x00030000 -#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 // Deprecated +#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 /* Deprecated */ typedef struct { diff --git a/drivers/gpu/drm/vmwgfx/svga_overlay.h b/drivers/gpu/drm/vmwgfx/svga_overlay.h index f753d73..f38416f 100644 --- a/drivers/gpu/drm/vmwgfx/svga_overlay.h +++ b/drivers/gpu/drm/vmwgfx/svga_overlay.h @@ -38,9 +38,9 @@ * Video formats we support */ -#define VMWARE_FOURCC_YV12 0x32315659 // 'Y' 'V' '1' '2' -#define VMWARE_FOURCC_YUY2 0x32595559 // 'Y' 'U' 'Y' '2' -#define VMWARE_FOURCC_UYVY 0x59565955 // 'U' 'Y' 'V' 'Y' +#define VMWARE_FOURCC_YV12 0x32315659 /* 'Y' 'V' '1' '2' */ +#define VMWARE_FOURCC_YUY2 0x32595559 /* 'Y' 'U' 'Y' '2' */ +#define VMWARE_FOURCC_UYVY 0x59565955 /* 'U' 'Y' 'V' 'Y' */ typedef enum { SVGA_OVERLAY_FORMAT_INVALID = 0, @@ -68,7 +68,7 @@ struct SVGAEscapeVideoSetRegs { uint32 streamId; } header; - // May include zero or more items. + /* May include zero or more items. */ struct { uint32 registerId; uint32 value; @@ -134,12 +134,12 @@ struct { */ static inline bool -VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN - uint32 *width, // IN / OUT - uint32 *height, // IN / OUT - uint32 *size, // OUT - uint32 *pitches, // OUT (optional) - uint32 *offsets) // OUT (optional) +VMwareVideoGetAttributes(const SVGAOverlayFormat format, /* IN */ + uint32 *width, /* IN / OUT */ + uint32 *height, /* IN / OUT */ + uint32 *size, /* OUT */ + uint32 *pitches, /* OUT (optional) */ + uint32 *offsets) /* OUT (optional) */ { int tmp; @@ -198,4 +198,4 @@ VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN return true; } -#endif // _SVGA_OVERLAY_H_ +#endif /* _SVGA_OVERLAY_H_ */ diff --git a/drivers/gpu/drm/vmwgfx/svga_reg.h b/drivers/gpu/drm/vmwgfx/svga_reg.h index 1b96c2e..01f63cb 100644 --- a/drivers/gpu/drm/vmwgfx/svga_reg.h +++ b/drivers/gpu/drm/vmwgfx/svga_reg.h @@ -39,6 +39,15 @@ #define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405 /* + * SVGA_REG_ENABLE bit definitions. + */ +#define SVGA_REG_ENABLE_DISABLE 0 +#define SVGA_REG_ENABLE_ENABLE 1 +#define SVGA_REG_ENABLE_HIDE 2 +#define SVGA_REG_ENABLE_ENABLE_HIDE (SVGA_REG_ENABLE_ENABLE |\ + SVGA_REG_ENABLE_HIDE) + +/* * Legal values for the SVGA_REG_CURSOR_ON register in old-fashioned * cursor bypass mode. This is still supported, but no new guest * drivers should use it. @@ -158,7 +167,9 @@ enum { SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH = 44, SVGA_REG_TRACES = 45, /* Enable trace-based updates even when FIFO is on */ - SVGA_REG_TOP = 46, /* Must be 1 more than the last register */ + SVGA_REG_GMRS_MAX_PAGES = 46, /* Maximum number of 4KB pages for all GMRs */ + SVGA_REG_MEMORY_SIZE = 47, /* Total dedicated device memory excluding FIFO */ + SVGA_REG_TOP = 48, /* Must be 1 more than the last register */ SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */ /* Next 768 (== 256*3) registers exist for colormap */ @@ -265,7 +276,7 @@ enum { * possible. */ #define SVGA_GMR_NULL ((uint32) -1) -#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) // Guest Framebuffer (GFB) +#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) /* Guest Framebuffer (GFB) */ typedef struct SVGAGuestMemDescriptor { @@ -306,13 +317,35 @@ struct SVGAGMRImageFormat { struct { uint32 bitsPerPixel : 8; uint32 colorDepth : 8; - uint32 reserved : 16; // Must be zero + uint32 reserved : 16; /* Must be zero */ }; uint32 value; }; } SVGAGMRImageFormat; +typedef +struct SVGAGuestImage { + SVGAGuestPtr ptr; + + /* + * A note on interpretation of pitch: This value of pitch is the + * number of bytes between vertically adjacent image + * blocks. Normally this is the number of bytes between the first + * pixel of two adjacent scanlines. With compressed textures, + * however, this may represent the number of bytes between + * compression blocks rather than between rows of pixels. + * + * XXX: Compressed textures currently must be tightly packed in guest memory. + * + * If the image is 1-dimensional, pitch is ignored. + * + * If 'pitch' is zero, the SVGA3D device calculates a pitch value + * assuming each row of blocks is tightly packed. + */ + uint32 pitch; +} SVGAGuestImage; + /* * SVGAColorBGRX -- * @@ -328,7 +361,7 @@ struct SVGAColorBGRX { uint32 b : 8; uint32 g : 8; uint32 r : 8; - uint32 x : 8; // Unused + uint32 x : 8; /* Unused */ }; uint32 value; @@ -370,23 +403,34 @@ struct SVGASignedPoint { * Note the holes in the bitfield. Missing bits have been deprecated, * and must not be reused. Those capabilities will never be reported * by new versions of the SVGA device. + * + * SVGA_CAP_GMR2 -- + * Provides asynchronous commands to define and remap guest memory + * regions. Adds device registers SVGA_REG_GMRS_MAX_PAGES and + * SVGA_REG_MEMORY_SIZE. + * + * SVGA_CAP_SCREEN_OBJECT_2 -- + * Allow screen object support, and require backing stores from the + * guest for each screen object. */ #define SVGA_CAP_NONE 0x00000000 #define SVGA_CAP_RECT_COPY 0x00000002 #define SVGA_CAP_CURSOR 0x00000020 -#define SVGA_CAP_CURSOR_BYPASS 0x00000040 // Legacy (Use Cursor Bypass 3 instead) -#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 // Legacy (Use Cursor Bypass 3 instead) +#define SVGA_CAP_CURSOR_BYPASS 0x00000040 /* Legacy (Use Cursor Bypass 3 instead) */ +#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 /* Legacy (Use Cursor Bypass 3 instead) */ #define SVGA_CAP_8BIT_EMULATION 0x00000100 #define SVGA_CAP_ALPHA_CURSOR 0x00000200 #define SVGA_CAP_3D 0x00004000 #define SVGA_CAP_EXTENDED_FIFO 0x00008000 -#define SVGA_CAP_MULTIMON 0x00010000 // Legacy multi-monitor support +#define SVGA_CAP_MULTIMON 0x00010000 /* Legacy multi-monitor support */ #define SVGA_CAP_PITCHLOCK 0x00020000 #define SVGA_CAP_IRQMASK 0x00040000 -#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 // Legacy multi-monitor support +#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 /* Legacy multi-monitor support */ #define SVGA_CAP_GMR 0x00100000 #define SVGA_CAP_TRACES 0x00200000 +#define SVGA_CAP_GMR2 0x00400000 +#define SVGA_CAP_SCREEN_OBJECT_2 0x00800000 /* @@ -431,7 +475,7 @@ enum { SVGA_FIFO_CAPABILITIES = 4, SVGA_FIFO_FLAGS, - // Valid with SVGA_FIFO_CAP_FENCE: + /* Valid with SVGA_FIFO_CAP_FENCE: */ SVGA_FIFO_FENCE, /* @@ -444,33 +488,47 @@ enum { * extended FIFO. */ - // Valid if exists (i.e. if extended FIFO enabled): + /* Valid if exists (i.e. if extended FIFO enabled): */ SVGA_FIFO_3D_HWVERSION, /* See SVGA3dHardwareVersion in svga3d_reg.h */ - // Valid with SVGA_FIFO_CAP_PITCHLOCK: + /* Valid with SVGA_FIFO_CAP_PITCHLOCK: */ SVGA_FIFO_PITCHLOCK, - // Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3: + /* Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3: */ SVGA_FIFO_CURSOR_ON, /* Cursor bypass 3 show/hide register */ SVGA_FIFO_CURSOR_X, /* Cursor bypass 3 x register */ SVGA_FIFO_CURSOR_Y, /* Cursor bypass 3 y register */ SVGA_FIFO_CURSOR_COUNT, /* Incremented when any of the other 3 change */ SVGA_FIFO_CURSOR_LAST_UPDATED,/* Last time the host updated the cursor */ - // Valid with SVGA_FIFO_CAP_RESERVE: + /* Valid with SVGA_FIFO_CAP_RESERVE: */ SVGA_FIFO_RESERVED, /* Bytes past NEXT_CMD with real contents */ /* - * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT: + * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2: * * By default this is SVGA_ID_INVALID, to indicate that the cursor * coordinates are specified relative to the virtual root. If this * is set to a specific screen ID, cursor position is reinterpreted - * as a signed offset relative to that screen's origin. This is the - * only way to place the cursor on a non-rooted screen. + * as a signed offset relative to that screen's origin. */ SVGA_FIFO_CURSOR_SCREEN_ID, /* + * Valid with SVGA_FIFO_CAP_DEAD + * + * An arbitrary value written by the host, drivers should not use it. + */ + SVGA_FIFO_DEAD, + + /* + * Valid with SVGA_FIFO_CAP_3D_HWVERSION_REVISED: + * + * Contains 3D HWVERSION (see SVGA3dHardwareVersion in svga3d_reg.h) + * on platforms that can enforce graphics resource limits. + */ + SVGA_FIFO_3D_HWVERSION_REVISED, + + /* * XXX: The gap here, up until SVGA_FIFO_3D_CAPS, can be used for new * registers, but this must be done carefully and with judicious use of * capability bits, since comparisons based on SVGA_FIFO_MIN aren't @@ -508,7 +566,7 @@ enum { * sets SVGA_FIFO_MIN high enough to leave room for them. */ - // Valid if register exists: + /* Valid if register exists: */ SVGA_FIFO_GUEST_3D_HWVERSION, /* Guest driver's 3D version */ SVGA_FIFO_FENCE_GOAL, /* Matching target for SVGA_IRQFLAG_FENCE_GOAL */ SVGA_FIFO_BUSY, /* See "FIFO Synchronization Registers" */ @@ -709,6 +767,37 @@ enum { * * - When a screen is resized, either using Screen Object commands or * legacy multimon registers, its contents are preserved. + * + * SVGA_FIFO_CAP_GMR2 -- + * + * Provides new commands to define and remap guest memory regions (GMR). + * + * New 2D commands: + * DEFINE_GMR2, REMAP_GMR2. + * + * SVGA_FIFO_CAP_3D_HWVERSION_REVISED -- + * + * Indicates new register SVGA_FIFO_3D_HWVERSION_REVISED exists. + * This register may replace SVGA_FIFO_3D_HWVERSION on platforms + * that enforce graphics resource limits. This allows the platform + * to clear SVGA_FIFO_3D_HWVERSION and disable 3D in legacy guest + * drivers that do not limit their resources. + * + * Note this is an alias to SVGA_FIFO_CAP_GMR2 because these indicators + * are codependent (and thus we use a single capability bit). + * + * SVGA_FIFO_CAP_SCREEN_OBJECT_2 -- + * + * Modifies the DEFINE_SCREEN command to include a guest provided + * backing store in GMR memory and the bytesPerLine for the backing + * store. This capability requires the use of a backing store when + * creating screen objects. However if SVGA_FIFO_CAP_SCREEN_OBJECT + * is present then backing stores are optional. + * + * SVGA_FIFO_CAP_DEAD -- + * + * Drivers should not use this cap bit. This cap bit can not be + * reused since some hosts already expose it. */ #define SVGA_FIFO_CAP_NONE 0 @@ -720,6 +809,10 @@ enum { #define SVGA_FIFO_CAP_ESCAPE (1<<5) #define SVGA_FIFO_CAP_RESERVE (1<<6) #define SVGA_FIFO_CAP_SCREEN_OBJECT (1<<7) +#define SVGA_FIFO_CAP_GMR2 (1<<8) +#define SVGA_FIFO_CAP_3D_HWVERSION_REVISED SVGA_FIFO_CAP_GMR2 +#define SVGA_FIFO_CAP_SCREEN_OBJECT_2 (1<<9) +#define SVGA_FIFO_CAP_DEAD (1<<10) /* @@ -730,7 +823,7 @@ enum { #define SVGA_FIFO_FLAG_NONE 0 #define SVGA_FIFO_FLAG_ACCELFRONT (1<<0) -#define SVGA_FIFO_FLAG_RESERVED (1<<31) // Internal use only +#define SVGA_FIFO_FLAG_RESERVED (1<<31) /* Internal use only */ /* * FIFO reservation sentinel value @@ -763,22 +856,22 @@ enum { SVGA_VIDEO_DATA_OFFSET, SVGA_VIDEO_FORMAT, SVGA_VIDEO_COLORKEY, - SVGA_VIDEO_SIZE, // Deprecated + SVGA_VIDEO_SIZE, /* Deprecated */ SVGA_VIDEO_WIDTH, SVGA_VIDEO_HEIGHT, SVGA_VIDEO_SRC_X, SVGA_VIDEO_SRC_Y, SVGA_VIDEO_SRC_WIDTH, SVGA_VIDEO_SRC_HEIGHT, - SVGA_VIDEO_DST_X, // Signed int32 - SVGA_VIDEO_DST_Y, // Signed int32 + SVGA_VIDEO_DST_X, /* Signed int32 */ + SVGA_VIDEO_DST_Y, /* Signed int32 */ SVGA_VIDEO_DST_WIDTH, SVGA_VIDEO_DST_HEIGHT, SVGA_VIDEO_PITCH_1, SVGA_VIDEO_PITCH_2, SVGA_VIDEO_PITCH_3, - SVGA_VIDEO_DATA_GMRID, // Optional, defaults to SVGA_GMR_FRAMEBUFFER - SVGA_VIDEO_DST_SCREEN_ID, // Optional, defaults to virtual coords (SVGA_ID_INVALID) + SVGA_VIDEO_DATA_GMRID, /* Optional, defaults to SVGA_GMR_FRAMEBUFFER */ + SVGA_VIDEO_DST_SCREEN_ID, /* Optional, defaults to virtual coords (SVGA_ID_INVALID) */ SVGA_VIDEO_NUM_REGS }; @@ -829,15 +922,51 @@ typedef struct SVGAOverlayUnit { * compatibility. New flags can be added, and the struct may grow, * but existing fields must retain their meaning. * + * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2 are required fields of + * a SVGAGuestPtr that is used to back the screen contents. This + * memory must come from the GFB. The guest is not allowed to + * access the memory and doing so will have undefined results. The + * backing store is required to be page aligned and the size is + * padded to the next page boundry. The number of pages is: + * (bytesPerLine * size.width * 4 + PAGE_SIZE - 1) / PAGE_SIZE + * + * The pitch in the backingStore is required to be at least large + * enough to hold a 32bbp scanline. It is recommended that the + * driver pad bytesPerLine for a potential performance win. + * + * The cloneCount field is treated as a hint from the guest that + * the user wants this display to be cloned, countCount times. A + * value of zero means no cloning should happen. + */ + +#define SVGA_SCREEN_MUST_BE_SET (1 << 0) /* Must be set or results undefined */ +#define SVGA_SCREEN_HAS_ROOT SVGA_SCREEN_MUST_BE_SET /* Deprecated */ +#define SVGA_SCREEN_IS_PRIMARY (1 << 1) /* Guest considers this screen to be 'primary' */ +#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) /* Guest is running a fullscreen app here */ + +/* + * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2. When the screen is + * deactivated the base layer is defined to lose all contents and + * become black. When a screen is deactivated the backing store is + * optional. When set backingPtr and bytesPerLine will be ignored. */ +#define SVGA_SCREEN_DEACTIVATE (1 << 3) -#define SVGA_SCREEN_HAS_ROOT (1 << 0) // Screen is present in the virtual coord space -#define SVGA_SCREEN_IS_PRIMARY (1 << 1) // Guest considers this screen to be 'primary' -#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) // Guest is running a fullscreen app here +/* + * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2. When this flag is set + * the screen contents will be outputted as all black to the user + * though the base layer contents is preserved. The screen base layer + * can still be read and written to like normal though the no visible + * effect will be seen by the user. When the flag is changed the + * screen will be blanked or redrawn to the current contents as needed + * without any extra commands from the driver. This flag only has an + * effect when the screen is not deactivated. + */ +#define SVGA_SCREEN_BLANKING (1 << 4) typedef struct SVGAScreenObject { - uint32 structSize; // sizeof(SVGAScreenObject) + uint32 structSize; /* sizeof(SVGAScreenObject) */ uint32 id; uint32 flags; struct { @@ -847,7 +976,14 @@ struct SVGAScreenObject { struct { int32 x; int32 y; - } root; // Only used if SVGA_SCREEN_HAS_ROOT is set. + } root; + + /* + * Added and required by SVGA_FIFO_CAP_SCREEN_OBJECT_2, optional + * with SVGA_FIFO_CAP_SCREEN_OBJECT. + */ + SVGAGuestImage backingStore; + uint32 cloneCount; } SVGAScreenObject; @@ -885,6 +1021,8 @@ typedef enum { SVGA_CMD_BLIT_SCREEN_TO_GMRFB = 38, SVGA_CMD_ANNOTATION_FILL = 39, SVGA_CMD_ANNOTATION_COPY = 40, + SVGA_CMD_DEFINE_GMR2 = 41, + SVGA_CMD_REMAP_GMR2 = 42, SVGA_CMD_MAX } SVGAFifoCmdId; @@ -920,7 +1058,7 @@ typedef enum { */ typedef -struct { +struct SVGAFifoCmdUpdate { uint32 x; uint32 y; uint32 width; @@ -939,7 +1077,7 @@ struct { */ typedef -struct { +struct SVGAFifoCmdRectCopy { uint32 srcX; uint32 srcY; uint32 destX; @@ -963,14 +1101,14 @@ struct { */ typedef -struct { - uint32 id; // Reserved, must be zero. +struct SVGAFifoCmdDefineCursor { + uint32 id; /* Reserved, must be zero. */ uint32 hotspotX; uint32 hotspotY; uint32 width; uint32 height; - uint32 andMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL - uint32 xorMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL + uint32 andMaskDepth; /* Value must be 1 or equal to BITS_PER_PIXEL */ + uint32 xorMaskDepth; /* Value must be 1 or equal to BITS_PER_PIXEL */ /* * Followed by scanline data for AND mask, then XOR mask. * Each scanline is padded to a 32-bit boundary. @@ -992,8 +1130,8 @@ struct { */ typedef -struct { - uint32 id; // Reserved, must be zero. +struct SVGAFifoCmdDefineAlphaCursor { + uint32 id; /* Reserved, must be zero. */ uint32 hotspotX; uint32 hotspotY; uint32 width; @@ -1015,7 +1153,7 @@ struct { */ typedef -struct { +struct SVGAFifoCmdUpdateVerbose { uint32 x; uint32 y; uint32 width; @@ -1040,13 +1178,13 @@ struct { #define SVGA_ROP_COPY 0x03 typedef -struct { - uint32 color; // In the same format as the GFB +struct SVGAFifoCmdFrontRopFill { + uint32 color; /* In the same format as the GFB */ uint32 x; uint32 y; uint32 width; uint32 height; - uint32 rop; // Must be SVGA_ROP_COPY + uint32 rop; /* Must be SVGA_ROP_COPY */ } SVGAFifoCmdFrontRopFill; @@ -1083,7 +1221,7 @@ struct { */ typedef -struct { +struct SVGAFifoCmdEscape { uint32 nsid; uint32 size; /* followed by 'size' bytes of data */ @@ -1113,12 +1251,12 @@ struct { * registers (SVGA_REG_NUM_GUEST_DISPLAYS, SVGA_REG_DISPLAY_*). * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef struct { - SVGAScreenObject screen; // Variable-length according to version + SVGAScreenObject screen; /* Variable-length according to version */ } SVGAFifoCmdDefineScreen; @@ -1129,7 +1267,7 @@ struct { * re-use. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1182,7 +1320,7 @@ struct { * GMRFB. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1219,7 +1357,7 @@ struct { * SVGA_CMD_ANNOTATION_* commands for details. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1267,7 +1405,7 @@ struct { * the time any subsequent FENCE commands are reached. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1302,7 +1440,7 @@ struct { * user's display is being remoted over a network connection. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1334,7 +1472,7 @@ struct { * undefined. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1343,4 +1481,72 @@ struct { uint32 srcScreenId; } SVGAFifoCmdAnnotationCopy; + +/* + * SVGA_CMD_DEFINE_GMR2 -- + * + * Define guest memory region v2. See the description of GMRs above. + * + * Availability: + * SVGA_CAP_GMR2 + */ + +typedef +struct { + uint32 gmrId; + uint32 numPages; +} SVGAFifoCmdDefineGMR2; + + +/* + * SVGA_CMD_REMAP_GMR2 -- + * + * Remap guest memory region v2. See the description of GMRs above. + * + * This command allows guest to modify a portion of an existing GMR by + * invalidating it or reassigning it to different guest physical pages. + * The pages are identified by physical page number (PPN). The pages + * are assumed to be pinned and valid for DMA operations. + * + * Description of command flags: + * + * SVGA_REMAP_GMR2_VIA_GMR: If enabled, references a PPN list in a GMR. + * The PPN list must not overlap with the remap region (this can be + * handled trivially by referencing a separate GMR). If flag is + * disabled, PPN list is appended to SVGARemapGMR command. + * + * SVGA_REMAP_GMR2_PPN64: If set, PPN list is in PPN64 format, otherwise + * it is in PPN32 format. + * + * SVGA_REMAP_GMR2_SINGLE_PPN: If set, PPN list contains a single entry. + * A single PPN can be used to invalidate a portion of a GMR or + * map it to to a single guest scratch page. + * + * Availability: + * SVGA_CAP_GMR2 + */ + +typedef enum { + SVGA_REMAP_GMR2_PPN32 = 0, + SVGA_REMAP_GMR2_VIA_GMR = (1 << 0), + SVGA_REMAP_GMR2_PPN64 = (1 << 1), + SVGA_REMAP_GMR2_SINGLE_PPN = (1 << 2), +} SVGARemapGMR2Flags; + +typedef +struct { + uint32 gmrId; + SVGARemapGMR2Flags flags; + uint32 offsetPages; /* offset in pages to begin remap */ + uint32 numPages; /* number of pages to remap */ + /* + * Followed by additional data depending on SVGARemapGMR2Flags. + * + * If flag SVGA_REMAP_GMR2_VIA_GMR is set, single SVGAGuestPtr follows. + * Otherwise an array of page descriptors in PPN32 or PPN64 format + * (according to flag SVGA_REMAP_GMR2_PPN64) follows. If flag + * SVGA_REMAP_GMR2_SINGLE_PPN is set, array contains a single entry. + */ +} SVGAFifoCmdRemapGMR2; + #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index 87e43e0..5a72ed9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -42,6 +42,10 @@ static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM | static uint32_t gmr_placement_flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED; +static uint32_t gmr_ne_placement_flags = VMW_PL_FLAG_GMR | + TTM_PL_FLAG_CACHED | + TTM_PL_FLAG_NO_EVICT; + struct ttm_placement vmw_vram_placement = { .fpfn = 0, .lpfn = 0, @@ -56,6 +60,11 @@ static uint32_t vram_gmr_placement_flags[] = { VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED }; +static uint32_t gmr_vram_placement_flags[] = { + VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED, + TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED +}; + struct ttm_placement vmw_vram_gmr_placement = { .fpfn = 0, .lpfn = 0, @@ -65,6 +74,20 @@ struct ttm_placement vmw_vram_gmr_placement = { .busy_placement = &gmr_placement_flags }; +static uint32_t vram_gmr_ne_placement_flags[] = { + TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT, + VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT +}; + +struct ttm_placement vmw_vram_gmr_ne_placement = { + .fpfn = 0, + .lpfn = 0, + .num_placement = 2, + .placement = vram_gmr_ne_placement_flags, + .num_busy_placement = 1, + .busy_placement = &gmr_ne_placement_flags +}; + struct ttm_placement vmw_vram_sys_placement = { .fpfn = 0, .lpfn = 0, @@ -92,6 +115,30 @@ struct ttm_placement vmw_sys_placement = { .busy_placement = &sys_placement_flags }; +static uint32_t evictable_placement_flags[] = { + TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED, + TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED, + VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED +}; + +struct ttm_placement vmw_evictable_placement = { + .fpfn = 0, + .lpfn = 0, + .num_placement = 3, + .placement = evictable_placement_flags, + .num_busy_placement = 1, + .busy_placement = &sys_placement_flags +}; + +struct ttm_placement vmw_srf_placement = { + .fpfn = 0, + .lpfn = 0, + .num_placement = 1, + .num_busy_placement = 2, + .placement = &gmr_placement_flags, + .busy_placement = gmr_vram_placement_flags +}; + struct vmw_ttm_backend { struct ttm_backend backend; struct page **pages; @@ -274,39 +321,39 @@ static int vmw_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) static void *vmw_sync_obj_ref(void *sync_obj) { - return sync_obj; + + return (void *) + vmw_fence_obj_reference((struct vmw_fence_obj *) sync_obj); } static void vmw_sync_obj_unref(void **sync_obj) { - *sync_obj = NULL; + vmw_fence_obj_unreference((struct vmw_fence_obj **) sync_obj); } static int vmw_sync_obj_flush(void *sync_obj, void *sync_arg) { - struct vmw_private *dev_priv = (struct vmw_private *)sync_arg; - - mutex_lock(&dev_priv->hw_mutex); - vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC); - mutex_unlock(&dev_priv->hw_mutex); + vmw_fence_obj_flush((struct vmw_fence_obj *) sync_obj); return 0; } static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg) { - struct vmw_private *dev_priv = (struct vmw_private *)sync_arg; - uint32_t sequence = (unsigned long) sync_obj; + unsigned long flags = (unsigned long) sync_arg; + return vmw_fence_obj_signaled((struct vmw_fence_obj *) sync_obj, + (uint32_t) flags); - return vmw_fence_signaled(dev_priv, sequence); } static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg, bool lazy, bool interruptible) { - struct vmw_private *dev_priv = (struct vmw_private *)sync_arg; - uint32_t sequence = (unsigned long) sync_obj; + unsigned long flags = (unsigned long) sync_arg; - return vmw_wait_fence(dev_priv, false, sequence, false, 3*HZ); + return vmw_fence_obj_wait((struct vmw_fence_obj *) sync_obj, + (uint32_t) flags, + lazy, interruptible, + VMW_FENCE_WAIT_TIMEOUT); } struct ttm_bo_driver vmw_bo_driver = { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 55d4b29..f739fcf 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -24,6 +24,7 @@ * USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ +#include <linux/module.h> #include "drmP.h" #include "vmwgfx_drv.h" @@ -82,17 +83,31 @@ #define DRM_IOCTL_VMW_EXECBUF \ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, \ struct drm_vmw_execbuf_arg) -#define DRM_IOCTL_VMW_FIFO_DEBUG \ - DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FIFO_DEBUG, \ - struct drm_vmw_fifo_debug_arg) +#define DRM_IOCTL_VMW_GET_3D_CAP \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_GET_3D_CAP, \ + struct drm_vmw_get_3d_cap_arg) #define DRM_IOCTL_VMW_FENCE_WAIT \ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_WAIT, \ struct drm_vmw_fence_wait_arg) +#define DRM_IOCTL_VMW_FENCE_SIGNALED \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_SIGNALED, \ + struct drm_vmw_fence_signaled_arg) +#define DRM_IOCTL_VMW_FENCE_UNREF \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_UNREF, \ + struct drm_vmw_fence_arg) +#define DRM_IOCTL_VMW_FENCE_EVENT \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_EVENT, \ + struct drm_vmw_fence_event_arg) +#define DRM_IOCTL_VMW_PRESENT \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT, \ + struct drm_vmw_present_arg) +#define DRM_IOCTL_VMW_PRESENT_READBACK \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT_READBACK, \ + struct drm_vmw_present_readback_arg) #define DRM_IOCTL_VMW_UPDATE_LAYOUT \ - DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT, \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT, \ struct drm_vmw_update_layout_arg) - /** * The core DRM version of this macro doesn't account for * DRM_COMMAND_BASE. @@ -135,12 +150,28 @@ static struct drm_ioctl_desc vmw_ioctls[] = { DRM_AUTH | DRM_UNLOCKED), VMW_IOCTL_DEF(VMW_EXECBUF, vmw_execbuf_ioctl, DRM_AUTH | DRM_UNLOCKED), - VMW_IOCTL_DEF(VMW_FIFO_DEBUG, vmw_fifo_debug_ioctl, - DRM_AUTH | DRM_ROOT_ONLY | DRM_MASTER | DRM_UNLOCKED), - VMW_IOCTL_DEF(VMW_FENCE_WAIT, vmw_fence_wait_ioctl, + VMW_IOCTL_DEF(VMW_FENCE_WAIT, vmw_fence_obj_wait_ioctl, + DRM_AUTH | DRM_UNLOCKED), + VMW_IOCTL_DEF(VMW_FENCE_SIGNALED, + vmw_fence_obj_signaled_ioctl, + DRM_AUTH | DRM_UNLOCKED), + VMW_IOCTL_DEF(VMW_FENCE_UNREF, vmw_fence_obj_unref_ioctl, + DRM_AUTH | DRM_UNLOCKED), + VMW_IOCTL_DEF(VMW_FENCE_EVENT, + vmw_fence_event_ioctl, DRM_AUTH | DRM_UNLOCKED), - VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT, vmw_kms_update_layout_ioctl, - DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED) + VMW_IOCTL_DEF(VMW_GET_3D_CAP, vmw_get_cap_3d_ioctl, + DRM_AUTH | DRM_UNLOCKED), + + /* these allow direct access to the framebuffers mark as master only */ + VMW_IOCTL_DEF(VMW_PRESENT, vmw_present_ioctl, + DRM_MASTER | DRM_AUTH | DRM_UNLOCKED), + VMW_IOCTL_DEF(VMW_PRESENT_READBACK, + vmw_present_readback_ioctl, + DRM_MASTER | DRM_AUTH | DRM_UNLOCKED), + VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT, + vmw_kms_update_layout_ioctl, + DRM_MASTER | DRM_UNLOCKED), }; static struct pci_device_id vmw_pci_id_list[] = { @@ -190,8 +221,78 @@ static void vmw_print_capabilities(uint32_t capabilities) DRM_INFO(" GMR.\n"); if (capabilities & SVGA_CAP_TRACES) DRM_INFO(" Traces.\n"); + if (capabilities & SVGA_CAP_GMR2) + DRM_INFO(" GMR2.\n"); + if (capabilities & SVGA_CAP_SCREEN_OBJECT_2) + DRM_INFO(" Screen Object 2.\n"); } + +/** + * vmw_execbuf_prepare_dummy_query - Initialize a query result structure at + * the start of a buffer object. + * + * @dev_priv: The device private structure. + * + * This function will idle the buffer using an uninterruptible wait, then + * map the first page and initialize a pending occlusion query result structure, + * Finally it will unmap the buffer. + * + * TODO: Since we're only mapping a single page, we should optimize the map + * to use kmap_atomic / iomap_atomic. + */ +static void vmw_dummy_query_bo_prepare(struct vmw_private *dev_priv) +{ + struct ttm_bo_kmap_obj map; + volatile SVGA3dQueryResult *result; + bool dummy; + int ret; + struct ttm_bo_device *bdev = &dev_priv->bdev; + struct ttm_buffer_object *bo = dev_priv->dummy_query_bo; + + ttm_bo_reserve(bo, false, false, false, 0); + spin_lock(&bdev->fence_lock); + ret = ttm_bo_wait(bo, false, false, false); + spin_unlock(&bdev->fence_lock); + if (unlikely(ret != 0)) + (void) vmw_fallback_wait(dev_priv, false, true, 0, false, + 10*HZ); + + ret = ttm_bo_kmap(bo, 0, 1, &map); + if (likely(ret == 0)) { + result = ttm_kmap_obj_virtual(&map, &dummy); + result->totalSize = sizeof(*result); + result->state = SVGA3D_QUERYSTATE_PENDING; + result->result32 = 0xff; + ttm_bo_kunmap(&map); + } else + DRM_ERROR("Dummy query buffer map failed.\n"); + ttm_bo_unreserve(bo); +} + + +/** + * vmw_dummy_query_bo_create - create a bo to hold a dummy query result + * + * @dev_priv: A device private structure. + * + * This function creates a small buffer object that holds the query + * result for dummy queries emitted as query barriers. + * No interruptible waits are done within this function. + * + * Returns an error if bo creation fails. + */ +static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv) +{ + return ttm_bo_create(&dev_priv->bdev, + PAGE_SIZE, + ttm_bo_type_device, + &vmw_vram_sys_placement, + 0, 0, false, NULL, + &dev_priv->dummy_query_bo); +} + + static int vmw_request_device(struct vmw_private *dev_priv) { int ret; @@ -201,16 +302,42 @@ static int vmw_request_device(struct vmw_private *dev_priv) DRM_ERROR("Unable to initialize FIFO.\n"); return ret; } + vmw_fence_fifo_up(dev_priv->fman); + ret = vmw_dummy_query_bo_create(dev_priv); + if (unlikely(ret != 0)) + goto out_no_query_bo; + vmw_dummy_query_bo_prepare(dev_priv); return 0; + +out_no_query_bo: + vmw_fence_fifo_down(dev_priv->fman); + vmw_fifo_release(dev_priv, &dev_priv->fifo); + return ret; } static void vmw_release_device(struct vmw_private *dev_priv) { + /* + * Previous destructions should've released + * the pinned bo. + */ + + BUG_ON(dev_priv->pinned_bo != NULL); + + ttm_bo_unref(&dev_priv->dummy_query_bo); + vmw_fence_fifo_down(dev_priv->fman); vmw_fifo_release(dev_priv, &dev_priv->fifo); } -int vmw_3d_resource_inc(struct vmw_private *dev_priv) +/** + * Increase the 3d resource refcount. + * If the count was prevously zero, initialize the fifo, switching to svga + * mode. Note that the master holds a ref as well, and may request an + * explicit switch to svga mode if fb is not running, using @unhide_svga. + */ +int vmw_3d_resource_inc(struct vmw_private *dev_priv, + bool unhide_svga) { int ret = 0; @@ -219,19 +346,42 @@ int vmw_3d_resource_inc(struct vmw_private *dev_priv) ret = vmw_request_device(dev_priv); if (unlikely(ret != 0)) --dev_priv->num_3d_resources; + } else if (unhide_svga) { + mutex_lock(&dev_priv->hw_mutex); + vmw_write(dev_priv, SVGA_REG_ENABLE, + vmw_read(dev_priv, SVGA_REG_ENABLE) & + ~SVGA_REG_ENABLE_HIDE); + mutex_unlock(&dev_priv->hw_mutex); } + mutex_unlock(&dev_priv->release_mutex); return ret; } - -void vmw_3d_resource_dec(struct vmw_private *dev_priv) +/** + * Decrease the 3d resource refcount. + * If the count reaches zero, disable the fifo, switching to vga mode. + * Note that the master holds a refcount as well, and may request an + * explicit switch to vga mode when it releases its refcount to account + * for the situation of an X server vt switch to VGA with 3d resources + * active. + */ +void vmw_3d_resource_dec(struct vmw_private *dev_priv, + bool hide_svga) { int32_t n3d; mutex_lock(&dev_priv->release_mutex); if (unlikely(--dev_priv->num_3d_resources == 0)) vmw_release_device(dev_priv); + else if (hide_svga) { + mutex_lock(&dev_priv->hw_mutex); + vmw_write(dev_priv, SVGA_REG_ENABLE, + vmw_read(dev_priv, SVGA_REG_ENABLE) | + SVGA_REG_ENABLE_HIDE); + mutex_unlock(&dev_priv->hw_mutex); + } + n3d = (int32_t) dev_priv->num_3d_resources; mutex_unlock(&dev_priv->release_mutex); @@ -253,7 +403,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->dev = dev; dev_priv->vmw_chipset = chipset; - dev_priv->last_read_sequence = (uint32_t) -100; + dev_priv->last_read_seqno = (uint32_t) -100; mutex_init(&dev_priv->hw_mutex); mutex_init(&dev_priv->cmdbuf_mutex); mutex_init(&dev_priv->release_mutex); @@ -264,8 +414,10 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) mutex_init(&dev_priv->init_mutex); init_waitqueue_head(&dev_priv->fence_queue); init_waitqueue_head(&dev_priv->fifo_queue); - atomic_set(&dev_priv->fence_queue_waiters, 0); + dev_priv->fence_queue_waiters = 0; atomic_set(&dev_priv->fifo_queue_waiters, 0); + INIT_LIST_HEAD(&dev_priv->surface_lru); + dev_priv->used_memory_size = 0; dev_priv->io_start = pci_resource_start(dev->pdev, 0); dev_priv->vram_start = pci_resource_start(dev->pdev, 1); @@ -286,6 +438,10 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); + dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); + dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); + dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH); + dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT); if (dev_priv->capabilities & SVGA_CAP_GMR) { dev_priv->max_gmr_descriptors = vmw_read(dev_priv, @@ -293,11 +449,19 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->max_gmr_ids = vmw_read(dev_priv, SVGA_REG_GMR_MAX_IDS); } - - dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); - dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); - dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH); - dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT); + if (dev_priv->capabilities & SVGA_CAP_GMR2) { + dev_priv->max_gmr_pages = + vmw_read(dev_priv, SVGA_REG_GMRS_MAX_PAGES); + dev_priv->memory_size = + vmw_read(dev_priv, SVGA_REG_MEMORY_SIZE); + dev_priv->memory_size -= dev_priv->vram_size; + } else { + /* + * An arbitrary limit of 512MiB on surface + * memory. But all HWV8 hardware supports GMR2. + */ + dev_priv->memory_size = 512*1024*1024; + } mutex_unlock(&dev_priv->hw_mutex); @@ -309,6 +473,12 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) DRM_INFO("Max GMR descriptors is %u\n", (unsigned)dev_priv->max_gmr_descriptors); } + if (dev_priv->capabilities & SVGA_CAP_GMR2) { + DRM_INFO("Max number of GMR pages is %u\n", + (unsigned)dev_priv->max_gmr_pages); + DRM_INFO("Max dedicated hypervisor surface memory is %u kiB\n", + (unsigned)dev_priv->memory_size / 1024); + } DRM_INFO("VRAM at 0x%08x size is %u kiB\n", dev_priv->vram_start, dev_priv->vram_size / 1024); DRM_INFO("MMIO at 0x%08x size is %u kiB\n", @@ -333,21 +503,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_err1; } - ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, - (dev_priv->vram_size >> PAGE_SHIFT)); - if (unlikely(ret != 0)) { - DRM_ERROR("Failed initializing memory manager for VRAM.\n"); - goto out_err2; - } - - dev_priv->has_gmr = true; - if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, - dev_priv->max_gmr_ids) != 0) { - DRM_INFO("No GMR memory available. " - "Graphics memory resources are very limited.\n"); - dev_priv->has_gmr = false; - } - dev_priv->mmio_mtrr = drm_mtrr_add(dev_priv->mmio_start, dev_priv->mmio_size, DRM_MTRR_WC); @@ -395,22 +550,50 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_no_device; } } + + dev_priv->fman = vmw_fence_manager_init(dev_priv); + if (unlikely(dev_priv->fman == NULL)) + goto out_no_fman; + + + ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, + (dev_priv->vram_size >> PAGE_SHIFT)); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed initializing memory manager for VRAM.\n"); + goto out_no_vram; + } + + dev_priv->has_gmr = true; + if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, + dev_priv->max_gmr_ids) != 0) { + DRM_INFO("No GMR memory available. " + "Graphics memory resources are very limited.\n"); + dev_priv->has_gmr = false; + } + + /* Need to start the fifo to check if we can do screen objects */ + ret = vmw_3d_resource_inc(dev_priv, true); + if (unlikely(ret != 0)) + goto out_no_fifo; + vmw_kms_save_vga(dev_priv); + + /* Start kms and overlay systems, needs fifo. */ ret = vmw_kms_init(dev_priv); if (unlikely(ret != 0)) goto out_no_kms; vmw_overlay_init(dev_priv); + + /* 3D Depends on Screen Objects being used. */ + DRM_INFO("Detected %sdevice 3D availability.\n", + vmw_fifo_have_3d(dev_priv) ? + "" : "no "); + + /* We might be done with the fifo now */ if (dev_priv->enable_fb) { - ret = vmw_3d_resource_inc(dev_priv); - if (unlikely(ret != 0)) - goto out_no_fifo; - vmw_kms_save_vga(dev_priv); vmw_fb_init(dev_priv); - DRM_INFO("%s", vmw_fifo_have_3d(dev_priv) ? - "Detected device 3D availability.\n" : - "Detected no device 3D availability.\n"); } else { - DRM_INFO("Delayed 3D detection since we're not " - "running the device in SVGA mode yet.\n"); + vmw_kms_restore_vga(dev_priv); + vmw_3d_resource_dec(dev_priv, true); } if (dev_priv->capabilities & SVGA_CAP_IRQMASK) { @@ -427,15 +610,23 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) return 0; out_no_irq: - if (dev_priv->enable_fb) { + if (dev_priv->enable_fb) vmw_fb_close(dev_priv); - vmw_kms_restore_vga(dev_priv); - vmw_3d_resource_dec(dev_priv); - } -out_no_fifo: vmw_overlay_close(dev_priv); vmw_kms_close(dev_priv); out_no_kms: + /* We still have a 3D resource reference held */ + if (dev_priv->enable_fb) { + vmw_kms_restore_vga(dev_priv); + vmw_3d_resource_dec(dev_priv, false); + } +out_no_fifo: + if (dev_priv->has_gmr) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); +out_no_vram: + vmw_fence_manager_takedown(dev_priv->fman); +out_no_fman: if (dev_priv->stealth) pci_release_region(dev->pdev, 2); else @@ -447,10 +638,6 @@ out_err4: out_err3: drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start, dev_priv->mmio_size, DRM_MTRR_WC); - if (dev_priv->has_gmr) - (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); - (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); -out_err2: (void)ttm_bo_device_release(&dev_priv->bdev); out_err1: vmw_ttm_global_release(dev_priv); @@ -468,15 +655,23 @@ static int vmw_driver_unload(struct drm_device *dev) unregister_pm_notifier(&dev_priv->pm_nb); + if (dev_priv->ctx.cmd_bounce) + vfree(dev_priv->ctx.cmd_bounce); if (dev_priv->capabilities & SVGA_CAP_IRQMASK) drm_irq_uninstall(dev_priv->dev); if (dev_priv->enable_fb) { vmw_fb_close(dev_priv); vmw_kms_restore_vga(dev_priv); - vmw_3d_resource_dec(dev_priv); + vmw_3d_resource_dec(dev_priv, false); } vmw_kms_close(dev_priv); vmw_overlay_close(dev_priv); + + if (dev_priv->has_gmr) + (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + + vmw_fence_manager_takedown(dev_priv->fman); if (dev_priv->stealth) pci_release_region(dev->pdev, 2); else @@ -486,9 +681,6 @@ static int vmw_driver_unload(struct drm_device *dev) iounmap(dev_priv->mmio_virt); drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start, dev_priv->mmio_size, DRM_MTRR_WC); - if (dev_priv->has_gmr) - (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); - (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); (void)ttm_bo_device_release(&dev_priv->bdev); vmw_ttm_global_release(dev_priv); idr_destroy(&dev_priv->surface_idr); @@ -647,7 +839,7 @@ static int vmw_master_set(struct drm_device *dev, int ret = 0; if (!dev_priv->enable_fb) { - ret = vmw_3d_resource_inc(dev_priv); + ret = vmw_3d_resource_inc(dev_priv, true); if (unlikely(ret != 0)) return ret; vmw_kms_save_vga(dev_priv); @@ -689,7 +881,7 @@ out_no_active_lock: vmw_write(dev_priv, SVGA_REG_TRACES, 1); mutex_unlock(&dev_priv->hw_mutex); vmw_kms_restore_vga(dev_priv); - vmw_3d_resource_dec(dev_priv); + vmw_3d_resource_dec(dev_priv, true); } return ret; } @@ -710,7 +902,7 @@ static void vmw_master_drop(struct drm_device *dev, vmw_fp->locked_master = drm_master_get(file_priv->master); ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile); - vmw_kms_idle_workqueues(vmaster); + vmw_execbuf_release_pinned_bo(dev_priv, false, 0); if (unlikely((ret != 0))) { DRM_ERROR("Unable to lock TTM at VT switch.\n"); @@ -727,7 +919,7 @@ static void vmw_master_drop(struct drm_device *dev, vmw_write(dev_priv, SVGA_REG_TRACES, 1); mutex_unlock(&dev_priv->hw_mutex); vmw_kms_restore_vga(dev_priv); - vmw_3d_resource_dec(dev_priv); + vmw_3d_resource_dec(dev_priv, true); } dev_priv->active_master = &dev_priv->fbdev_master; @@ -762,6 +954,7 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, * This empties VRAM and unbinds all GMR bindings. * Buffer contents is moved to swappable memory. */ + vmw_execbuf_release_pinned_bo(dev_priv, false, 0); ttm_bo_swapout_all(&dev_priv->bdev); break; @@ -836,7 +1029,7 @@ static int vmw_pm_prepare(struct device *kdev) */ dev_priv->suspended = true; if (dev_priv->enable_fb) - vmw_3d_resource_dec(dev_priv); + vmw_3d_resource_dec(dev_priv, true); if (dev_priv->num_3d_resources != 0) { @@ -844,7 +1037,7 @@ static int vmw_pm_prepare(struct device *kdev) "while 3D resources are active.\n"); if (dev_priv->enable_fb) - vmw_3d_resource_inc(dev_priv); + vmw_3d_resource_inc(dev_priv, true); dev_priv->suspended = false; return -EBUSY; } @@ -868,7 +1061,7 @@ static void vmw_pm_complete(struct device *kdev) * start fifo. */ if (dev_priv->enable_fb) - vmw_3d_resource_inc(dev_priv); + vmw_3d_resource_inc(dev_priv, false); dev_priv->suspended = false; } @@ -892,6 +1085,8 @@ static struct drm_driver driver = { .irq_uninstall = vmw_irq_uninstall, .irq_handler = vmw_irq_handler, .get_vblank_counter = vmw_get_vblank_counter, + .enable_vblank = vmw_enable_vblank, + .disable_vblank = vmw_disable_vblank, .reclaim_buffers_locked = NULL, .ioctls = vmw_ioctls, .num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls), @@ -902,13 +1097,19 @@ static struct drm_driver driver = { .master_drop = vmw_master_drop, .open = vmw_driver_open, .postclose = vmw_postclose, + + .dumb_create = vmw_dumb_create, + .dumb_map_offset = vmw_dumb_map_offset, + .dumb_destroy = vmw_dumb_destroy, + .fops = { .owner = THIS_MODULE, .open = drm_open, .release = drm_release, .unlocked_ioctl = vmw_unlocked_ioctl, .mmap = vmw_mmap, - .poll = drm_poll, + .poll = vmw_fops_poll, + .read = vmw_fops_read, .fasync = drm_fasync, #if defined(CONFIG_COMPAT) .compat_ioctl = drm_compat_ioctl, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 10fc01f..0e3fa7d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -38,20 +38,27 @@ #include "ttm/ttm_lock.h" #include "ttm/ttm_execbuf_util.h" #include "ttm/ttm_module.h" +#include "vmwgfx_fence.h" -#define VMWGFX_DRIVER_DATE "20100927" -#define VMWGFX_DRIVER_MAJOR 1 -#define VMWGFX_DRIVER_MINOR 4 +#define VMWGFX_DRIVER_DATE "20111025" +#define VMWGFX_DRIVER_MAJOR 2 +#define VMWGFX_DRIVER_MINOR 3 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) #define VMWGFX_MAX_RELOCATIONS 2048 -#define VMWGFX_MAX_GMRS 2048 +#define VMWGFX_MAX_VALIDATIONS 2048 #define VMWGFX_MAX_DISPLAYS 16 +#define VMWGFX_CMD_BOUNCE_INIT_SIZE 32768 #define VMW_PL_GMR TTM_PL_PRIV0 #define VMW_PL_FLAG_GMR TTM_PL_FLAG_PRIV0 +#define VMW_RES_CONTEXT ttm_driver_type0 +#define VMW_RES_SURFACE ttm_driver_type1 +#define VMW_RES_STREAM ttm_driver_type2 +#define VMW_RES_FENCE ttm_driver_type3 + struct vmw_fpriv { struct drm_master *locked_master; struct ttm_object_file *tfile; @@ -72,9 +79,11 @@ struct vmw_resource { int id; enum ttm_object_type res_type; bool avail; + void (*remove_from_lists) (struct vmw_resource *res); void (*hw_destroy) (struct vmw_resource *res); void (*res_free) (struct vmw_resource *res); - + struct list_head validate_head; + struct list_head query_head; /* Protected by the cmdbuf mutex */ /* TODO is a generic snooper needed? */ #if 0 void (*snoop)(struct vmw_resource *res, @@ -90,8 +99,12 @@ struct vmw_cursor_snooper { uint32_t *image; }; +struct vmw_framebuffer; +struct vmw_surface_offset; + struct vmw_surface { struct vmw_resource res; + struct list_head lru_head; /* Protected by the resource lock */ uint32_t flags; uint32_t format; uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES]; @@ -102,9 +115,12 @@ struct vmw_surface { /* TODO so far just a extra pointer */ struct vmw_cursor_snooper snooper; + struct ttm_buffer_object *backup; + struct vmw_surface_offset *offsets; + uint32_t backup_size; }; -struct vmw_fence_queue { +struct vmw_marker_queue { struct list_head head; struct timespec lag; struct timespec lag_time; @@ -115,16 +131,12 @@ struct vmw_fifo_state { unsigned long reserved_size; __le32 *dynamic_buffer; __le32 *static_buffer; - __le32 *last_buffer; - uint32_t last_data_size; - uint32_t last_buffer_size; - bool last_buffer_add; unsigned long static_buffer_size; bool using_bounce_buffer; uint32_t capabilities; struct mutex fifo_mutex; struct rw_semaphore rwsem; - struct vmw_fence_queue fence_queue; + struct vmw_marker_queue marker_queue; }; struct vmw_relocation { @@ -136,6 +148,8 @@ struct vmw_sw_context{ struct ida bo_list; uint32_t last_cid; bool cid_valid; + bool kernel; /**< is the called made from the kernel */ + struct vmw_resource *cur_ctx; uint32_t last_sid; uint32_t sid_translation; bool sid_valid; @@ -143,8 +157,16 @@ struct vmw_sw_context{ struct list_head validate_nodes; struct vmw_relocation relocs[VMWGFX_MAX_RELOCATIONS]; uint32_t cur_reloc; - struct ttm_validate_buffer val_bufs[VMWGFX_MAX_GMRS]; + struct ttm_validate_buffer val_bufs[VMWGFX_MAX_VALIDATIONS]; uint32_t cur_val_buf; + uint32_t *cmd_bounce; + uint32_t cmd_bounce_size; + struct list_head resource_list; + uint32_t fence_flags; + struct list_head query_list; + struct ttm_buffer_object *cur_query_bo; + uint32_t cur_query_cid; + bool query_cid_valid; }; struct vmw_legacy_display; @@ -185,6 +207,8 @@ struct vmw_private { uint32_t capabilities; uint32_t max_gmr_descriptors; uint32_t max_gmr_ids; + uint32_t max_gmr_pages; + uint32_t memory_size; bool has_gmr; struct mutex hw_mutex; @@ -195,12 +219,7 @@ struct vmw_private { struct vmw_vga_topology_state vga_save[VMWGFX_MAX_DISPLAYS]; uint32_t vga_width; uint32_t vga_height; - uint32_t vga_depth; uint32_t vga_bpp; - uint32_t vga_pseudo; - uint32_t vga_red_mask; - uint32_t vga_green_mask; - uint32_t vga_blue_mask; uint32_t vga_bpl; uint32_t vga_pitchlock; @@ -212,6 +231,7 @@ struct vmw_private { void *fb_info; struct vmw_legacy_display *ldu_priv; + struct vmw_screen_object_display *sou_priv; struct vmw_overlay *overlay_priv; /* @@ -240,13 +260,16 @@ struct vmw_private { * Fencing and IRQs. */ - atomic_t fence_seq; + atomic_t marker_seq; wait_queue_head_t fence_queue; wait_queue_head_t fifo_queue; - atomic_t fence_queue_waiters; + int fence_queue_waiters; /* Protected by hw_mutex */ + int goal_queue_waiters; /* Protected by hw_mutex */ atomic_t fifo_queue_waiters; - uint32_t last_read_sequence; + uint32_t last_read_seqno; spinlock_t irq_lock; + struct vmw_fence_manager *fman; + uint32_t irq_mask; /* * Device state @@ -285,6 +308,26 @@ struct vmw_private { struct mutex release_mutex; uint32_t num_3d_resources; + + /* + * Query processing. These members + * are protected by the cmdbuf mutex. + */ + + struct ttm_buffer_object *dummy_query_bo; + struct ttm_buffer_object *pinned_bo; + uint32_t query_cid; + bool dummy_query_bo_pinned; + + /* + * Surface swapping. The "surface_lru" list is protected by the + * resource lock in order to be able to destroy a surface and take + * it off the lru atomically. "used_memory_size" is currently + * protected by the cmdbuf mutex for simplicity. + */ + + struct list_head surface_lru; + uint32_t used_memory_size; }; static inline struct vmw_private *vmw_priv(struct drm_device *dev) @@ -319,8 +362,8 @@ static inline uint32_t vmw_read(struct vmw_private *dev_priv, return val; } -int vmw_3d_resource_inc(struct vmw_private *dev_priv); -void vmw_3d_resource_dec(struct vmw_private *dev_priv); +int vmw_3d_resource_inc(struct vmw_private *dev_priv, bool unhide_svga); +void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga); /** * GMR utilities - vmwgfx_gmr.c @@ -345,7 +388,13 @@ extern int vmw_context_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_context_check(struct vmw_private *dev_priv, struct ttm_object_file *tfile, - int id); + int id, + struct vmw_resource **p_res); +extern int vmw_user_lookup_handle(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t handle, + struct vmw_surface **out_surf, + struct vmw_dma_buffer **out_buf); extern void vmw_surface_res_free(struct vmw_resource *res); extern int vmw_surface_init(struct vmw_private *dev_priv, struct vmw_surface *srf, @@ -363,6 +412,8 @@ extern int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, extern int vmw_surface_check(struct vmw_private *dev_priv, struct ttm_object_file *tfile, uint32_t handle, int *id); +extern int vmw_surface_validate(struct vmw_private *dev_priv, + struct vmw_surface *srf); extern void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo); extern int vmw_dmabuf_init(struct vmw_private *dev_priv, struct vmw_dma_buffer *vmw_bo, @@ -378,10 +429,6 @@ extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo, extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo); extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, uint32_t id, struct vmw_dma_buffer **out); -extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, - struct vmw_dma_buffer *bo); -extern int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv, - struct vmw_dma_buffer *bo); extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, @@ -390,7 +437,30 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv, struct ttm_object_file *tfile, uint32_t *inout_id, struct vmw_resource **out); +extern void vmw_resource_unreserve(struct list_head *list); +/** + * DMA buffer helper routines - vmwgfx_dmabuf.c + */ +extern int vmw_dmabuf_to_placement(struct vmw_private *vmw_priv, + struct vmw_dma_buffer *bo, + struct ttm_placement *placement, + bool interruptible); +extern int vmw_dmabuf_to_vram(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool interruptible); +extern int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool interruptible); +extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, + struct vmw_dma_buffer *bo, + bool pin, bool interruptible); +extern int vmw_dmabuf_unpin(struct vmw_private *vmw_priv, + struct vmw_dma_buffer *bo, + bool interruptible); +extern void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *buf, + SVGAGuestPtr *ptr); +extern void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin); /** * Misc Ioctl functionality - vmwgfx_ioctl.c @@ -398,8 +468,16 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv, extern int vmw_getparam_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data, +extern int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int vmw_present_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_present_readback_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern unsigned int vmw_fops_poll(struct file *filp, + struct poll_table_struct *wait); +extern ssize_t vmw_fops_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset); /** * Fifo utilities - vmwgfx_fifo.c @@ -412,11 +490,12 @@ extern void vmw_fifo_release(struct vmw_private *dev_priv, extern void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes); extern void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes); extern int vmw_fifo_send_fence(struct vmw_private *dev_priv, - uint32_t *sequence); + uint32_t *seqno); extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason); -extern int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma); extern bool vmw_fifo_have_3d(struct vmw_private *dev_priv); extern bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv); +extern int vmw_fifo_emit_dummy_query(struct vmw_private *dev_priv, + uint32_t cid); /** * TTM glue - vmwgfx_ttm_glue.c @@ -434,7 +513,10 @@ extern struct ttm_placement vmw_vram_placement; extern struct ttm_placement vmw_vram_ne_placement; extern struct ttm_placement vmw_vram_sys_placement; extern struct ttm_placement vmw_vram_gmr_placement; +extern struct ttm_placement vmw_vram_gmr_ne_placement; extern struct ttm_placement vmw_sys_placement; +extern struct ttm_placement vmw_evictable_placement; +extern struct ttm_placement vmw_srf_placement; extern struct ttm_bo_driver vmw_bo_driver; extern int vmw_dma_quiescent(struct drm_device *dev); @@ -444,45 +526,70 @@ extern int vmw_dma_quiescent(struct drm_device *dev); extern int vmw_execbuf_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int vmw_execbuf_process(struct drm_file *file_priv, + struct vmw_private *dev_priv, + void __user *user_commands, + void *kernel_commands, + uint32_t command_size, + uint64_t throttle_us, + struct drm_vmw_fence_rep __user + *user_fence_rep); + +extern void +vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, + bool only_on_cid_match, uint32_t cid); + +extern int vmw_execbuf_fence_commands(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_fence_obj **p_fence, + uint32_t *p_handle); +extern void vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, + struct vmw_fpriv *vmw_fp, + int ret, + struct drm_vmw_fence_rep __user + *user_fence_rep, + struct vmw_fence_obj *fence, + uint32_t fence_handle); /** * IRQs and wating - vmwgfx_irq.c */ extern irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS); -extern int vmw_wait_fence(struct vmw_private *dev_priv, bool lazy, - uint32_t sequence, bool interruptible, - unsigned long timeout); +extern int vmw_wait_seqno(struct vmw_private *dev_priv, bool lazy, + uint32_t seqno, bool interruptible, + unsigned long timeout); extern void vmw_irq_preinstall(struct drm_device *dev); extern int vmw_irq_postinstall(struct drm_device *dev); extern void vmw_irq_uninstall(struct drm_device *dev); -extern bool vmw_fence_signaled(struct vmw_private *dev_priv, - uint32_t sequence); -extern int vmw_fence_wait_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); +extern bool vmw_seqno_passed(struct vmw_private *dev_priv, + uint32_t seqno); extern int vmw_fallback_wait(struct vmw_private *dev_priv, bool lazy, bool fifo_idle, - uint32_t sequence, + uint32_t seqno, bool interruptible, unsigned long timeout); -extern void vmw_update_sequence(struct vmw_private *dev_priv, +extern void vmw_update_seqno(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo_state); - +extern void vmw_seqno_waiter_add(struct vmw_private *dev_priv); +extern void vmw_seqno_waiter_remove(struct vmw_private *dev_priv); +extern void vmw_goal_waiter_add(struct vmw_private *dev_priv); +extern void vmw_goal_waiter_remove(struct vmw_private *dev_priv); /** - * Rudimentary fence objects currently used only for throttling - - * vmwgfx_fence.c + * Rudimentary fence-like objects currently used only for throttling - + * vmwgfx_marker.c */ -extern void vmw_fence_queue_init(struct vmw_fence_queue *queue); -extern void vmw_fence_queue_takedown(struct vmw_fence_queue *queue); -extern int vmw_fence_push(struct vmw_fence_queue *queue, - uint32_t sequence); -extern int vmw_fence_pull(struct vmw_fence_queue *queue, - uint32_t signaled_sequence); +extern void vmw_marker_queue_init(struct vmw_marker_queue *queue); +extern void vmw_marker_queue_takedown(struct vmw_marker_queue *queue); +extern int vmw_marker_push(struct vmw_marker_queue *queue, + uint32_t seqno); +extern int vmw_marker_pull(struct vmw_marker_queue *queue, + uint32_t signaled_seqno); extern int vmw_wait_lag(struct vmw_private *dev_priv, - struct vmw_fence_queue *queue, uint32_t us); + struct vmw_marker_queue *queue, uint32_t us); /** * Kernel framebuffer - vmwgfx_fb.c @@ -508,17 +615,42 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, struct ttm_object_file *tfile, struct ttm_buffer_object *bo, SVGA3dCmdHeader *header); -void vmw_kms_write_svga(struct vmw_private *vmw_priv, - unsigned width, unsigned height, unsigned pitch, - unsigned bbp, unsigned depth); -int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); +int vmw_kms_write_svga(struct vmw_private *vmw_priv, + unsigned width, unsigned height, unsigned pitch, + unsigned bpp, unsigned depth); void vmw_kms_idle_workqueues(struct vmw_master *vmaster); bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, uint32_t pitch, uint32_t height); u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc); +int vmw_enable_vblank(struct drm_device *dev, int crtc); +void vmw_disable_vblank(struct drm_device *dev, int crtc); +int vmw_kms_present(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, + struct vmw_surface *surface, + uint32_t sid, int32_t destX, int32_t destY, + struct drm_vmw_rect *clips, + uint32_t num_clips); +int vmw_kms_readback(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, + struct drm_vmw_fence_rep __user *user_fence_rep, + struct drm_vmw_rect *clips, + uint32_t num_clips); +int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int vmw_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); + +int vmw_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, + uint64_t *offset); +int vmw_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); /** * Overlay control - vmwgfx_overlay.c */ @@ -576,4 +708,8 @@ static inline struct vmw_dma_buffer *vmw_dmabuf_reference(struct vmw_dma_buffer return NULL; } +static inline struct ttm_mem_global *vmw_mem_glob(struct vmw_private *dev_priv) +{ + return (struct ttm_mem_global *) dev_priv->mem_global_ref.object; +} #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 41b95ed..84ba033 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -44,10 +44,71 @@ static int vmw_cmd_ok(struct vmw_private *dev_priv, return 0; } +static void vmw_resource_to_validate_list(struct vmw_sw_context *sw_context, + struct vmw_resource **p_res) +{ + struct vmw_resource *res = *p_res; + + if (list_empty(&res->validate_head)) { + list_add_tail(&res->validate_head, &sw_context->resource_list); + *p_res = NULL; + } else + vmw_resource_unreference(p_res); +} + +/** + * vmw_bo_to_validate_list - add a bo to a validate list + * + * @sw_context: The software context used for this command submission batch. + * @bo: The buffer object to add. + * @fence_flags: Fence flags to be or'ed with any other fence flags for + * this buffer on this submission batch. + * @p_val_node: If non-NULL Will be updated with the validate node number + * on return. + * + * Returns -EINVAL if the limit of number of buffer objects per command + * submission is reached. + */ +static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context, + struct ttm_buffer_object *bo, + uint32_t fence_flags, + uint32_t *p_val_node) +{ + uint32_t val_node; + struct ttm_validate_buffer *val_buf; + + val_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf); + + if (unlikely(val_node >= VMWGFX_MAX_VALIDATIONS)) { + DRM_ERROR("Max number of DMA buffers per submission" + " exceeded.\n"); + return -EINVAL; + } + + val_buf = &sw_context->val_bufs[val_node]; + if (unlikely(val_node == sw_context->cur_val_buf)) { + val_buf->new_sync_obj_arg = NULL; + val_buf->bo = ttm_bo_reference(bo); + list_add_tail(&val_buf->head, &sw_context->validate_nodes); + ++sw_context->cur_val_buf; + } + + val_buf->new_sync_obj_arg = (void *) + ((unsigned long) val_buf->new_sync_obj_arg | fence_flags); + sw_context->fence_flags |= fence_flags; + + if (p_val_node) + *p_val_node = val_node; + + return 0; +} + static int vmw_cmd_cid_check(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGA3dCmdHeader *header) { + struct vmw_resource *ctx; + struct vmw_cid_cmd { SVGA3dCmdHeader header; __le32 cid; @@ -58,7 +119,8 @@ static int vmw_cmd_cid_check(struct vmw_private *dev_priv, if (likely(sw_context->cid_valid && cmd->cid == sw_context->last_cid)) return 0; - ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid); + ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid, + &ctx); if (unlikely(ret != 0)) { DRM_ERROR("Could not find or use context %u\n", (unsigned) cmd->cid); @@ -67,6 +129,8 @@ static int vmw_cmd_cid_check(struct vmw_private *dev_priv, sw_context->last_cid = cmd->cid; sw_context->cid_valid = true; + sw_context->cur_ctx = ctx; + vmw_resource_to_validate_list(sw_context, &ctx); return 0; } @@ -75,29 +139,45 @@ static int vmw_cmd_sid_check(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, uint32_t *sid) { + struct vmw_surface *srf; + int ret; + struct vmw_resource *res; + if (*sid == SVGA3D_INVALID_ID) return 0; - if (unlikely((!sw_context->sid_valid || - *sid != sw_context->last_sid))) { - int real_id; - int ret = vmw_surface_check(dev_priv, sw_context->tfile, - *sid, &real_id); + if (likely((sw_context->sid_valid && + *sid == sw_context->last_sid))) { + *sid = sw_context->sid_translation; + return 0; + } - if (unlikely(ret != 0)) { - DRM_ERROR("Could ot find or use surface 0x%08x " - "address 0x%08lx\n", - (unsigned int) *sid, - (unsigned long) sid); - return ret; - } + ret = vmw_user_surface_lookup_handle(dev_priv, + sw_context->tfile, + *sid, &srf); + if (unlikely(ret != 0)) { + DRM_ERROR("Could ot find or use surface 0x%08x " + "address 0x%08lx\n", + (unsigned int) *sid, + (unsigned long) sid); + return ret; + } - sw_context->last_sid = *sid; - sw_context->sid_valid = true; - *sid = real_id; - sw_context->sid_translation = real_id; - } else - *sid = sw_context->sid_translation; + ret = vmw_surface_validate(dev_priv, srf); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Could not validate surface.\n"); + vmw_surface_unreference(&srf); + return ret; + } + + sw_context->last_sid = *sid; + sw_context->sid_valid = true; + sw_context->sid_translation = srf->res.id; + *sid = sw_context->sid_translation; + + res = &srf->res; + vmw_resource_to_validate_list(sw_context, &res); return 0; } @@ -166,6 +246,12 @@ static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, } *cmd; cmd = container_of(header, struct vmw_sid_cmd, header); + + if (unlikely(!sw_context->kernel)) { + DRM_ERROR("Kernel only SVGA3d command: %u.\n", cmd->header.id); + return -EPERM; + } + return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid); } @@ -178,10 +264,179 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv, SVGA3dCmdPresent body; } *cmd; + cmd = container_of(header, struct vmw_sid_cmd, header); + + if (unlikely(!sw_context->kernel)) { + DRM_ERROR("Kernel only SVGA3d command: %u.\n", cmd->header.id); + return -EPERM; + } + return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid); } +/** + * vmw_query_bo_switch_prepare - Prepare to switch pinned buffer for queries. + * + * @dev_priv: The device private structure. + * @cid: The hardware context for the next query. + * @new_query_bo: The new buffer holding query results. + * @sw_context: The software context used for this command submission. + * + * This function checks whether @new_query_bo is suitable for holding + * query results, and if another buffer currently is pinned for query + * results. If so, the function prepares the state of @sw_context for + * switching pinned buffers after successful submission of the current + * command batch. It also checks whether we're using a new query context. + * In that case, it makes sure we emit a query barrier for the old + * context before the current query buffer is fenced. + */ +static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, + uint32_t cid, + struct ttm_buffer_object *new_query_bo, + struct vmw_sw_context *sw_context) +{ + int ret; + bool add_cid = false; + uint32_t cid_to_add; + + if (unlikely(new_query_bo != sw_context->cur_query_bo)) { + + if (unlikely(new_query_bo->num_pages > 4)) { + DRM_ERROR("Query buffer too large.\n"); + return -EINVAL; + } + + if (unlikely(sw_context->cur_query_bo != NULL)) { + BUG_ON(!sw_context->query_cid_valid); + add_cid = true; + cid_to_add = sw_context->cur_query_cid; + ret = vmw_bo_to_validate_list(sw_context, + sw_context->cur_query_bo, + DRM_VMW_FENCE_FLAG_EXEC, + NULL); + if (unlikely(ret != 0)) + return ret; + } + sw_context->cur_query_bo = new_query_bo; + + ret = vmw_bo_to_validate_list(sw_context, + dev_priv->dummy_query_bo, + DRM_VMW_FENCE_FLAG_EXEC, + NULL); + if (unlikely(ret != 0)) + return ret; + + } + + if (unlikely(cid != sw_context->cur_query_cid && + sw_context->query_cid_valid)) { + add_cid = true; + cid_to_add = sw_context->cur_query_cid; + } + + sw_context->cur_query_cid = cid; + sw_context->query_cid_valid = true; + + if (add_cid) { + struct vmw_resource *ctx = sw_context->cur_ctx; + + if (list_empty(&ctx->query_head)) + list_add_tail(&ctx->query_head, + &sw_context->query_list); + ret = vmw_bo_to_validate_list(sw_context, + dev_priv->dummy_query_bo, + DRM_VMW_FENCE_FLAG_EXEC, + NULL); + if (unlikely(ret != 0)) + return ret; + } + return 0; +} + + +/** + * vmw_query_bo_switch_commit - Finalize switching pinned query buffer + * + * @dev_priv: The device private structure. + * @sw_context: The software context used for this command submission batch. + * + * This function will check if we're switching query buffers, and will then, + * if no other query waits are issued this command submission batch, + * issue a dummy occlusion query wait used as a query barrier. When the fence + * object following that query wait has signaled, we are sure that all + * preseding queries have finished, and the old query buffer can be unpinned. + * However, since both the new query buffer and the old one are fenced with + * that fence, we can do an asynchronus unpin now, and be sure that the + * old query buffer won't be moved until the fence has signaled. + * + * As mentioned above, both the new - and old query buffers need to be fenced + * using a sequence emitted *after* calling this function. + */ +static void vmw_query_bo_switch_commit(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context) +{ + + struct vmw_resource *ctx, *next_ctx; + int ret; + + /* + * The validate list should still hold references to all + * contexts here. + */ + + list_for_each_entry_safe(ctx, next_ctx, &sw_context->query_list, + query_head) { + list_del_init(&ctx->query_head); + + BUG_ON(list_empty(&ctx->validate_head)); + + ret = vmw_fifo_emit_dummy_query(dev_priv, ctx->id); + + if (unlikely(ret != 0)) + DRM_ERROR("Out of fifo space for dummy query.\n"); + } + + if (dev_priv->pinned_bo != sw_context->cur_query_bo) { + if (dev_priv->pinned_bo) { + vmw_bo_pin(dev_priv->pinned_bo, false); + ttm_bo_unref(&dev_priv->pinned_bo); + } + + vmw_bo_pin(sw_context->cur_query_bo, true); + + /* + * We pin also the dummy_query_bo buffer so that we + * don't need to validate it when emitting + * dummy queries in context destroy paths. + */ + + vmw_bo_pin(dev_priv->dummy_query_bo, true); + dev_priv->dummy_query_bo_pinned = true; + + dev_priv->query_cid = sw_context->cur_query_cid; + dev_priv->pinned_bo = + ttm_bo_reference(sw_context->cur_query_bo); + } +} + +/** + * vmw_query_switch_backoff - clear query barrier list + * @sw_context: The sw context used for this submission batch. + * + * This function is used as part of an error path, where a previously + * set up list of query barriers needs to be cleared. + * + */ +static void vmw_query_switch_backoff(struct vmw_sw_context *sw_context) +{ + struct list_head *list, *next; + + list_for_each_safe(list, next, &sw_context->query_list) { + list_del_init(list); + } +} + static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGAGuestPtr *ptr, @@ -191,8 +446,6 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, struct ttm_buffer_object *bo; uint32_t handle = ptr->gmrId; struct vmw_relocation *reloc; - uint32_t cur_validate_node; - struct ttm_validate_buffer *val_buf; int ret; ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo); @@ -212,22 +465,11 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, reloc = &sw_context->relocs[sw_context->cur_reloc++]; reloc->location = ptr; - cur_validate_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf); - if (unlikely(cur_validate_node >= VMWGFX_MAX_GMRS)) { - DRM_ERROR("Max number of DMA buffers per submission" - " exceeded.\n"); - ret = -EINVAL; + ret = vmw_bo_to_validate_list(sw_context, bo, DRM_VMW_FENCE_FLAG_EXEC, + &reloc->index); + if (unlikely(ret != 0)) goto out_no_reloc; - } - reloc->index = cur_validate_node; - if (unlikely(cur_validate_node == sw_context->cur_val_buf)) { - val_buf = &sw_context->val_bufs[cur_validate_node]; - val_buf->bo = ttm_bo_reference(bo); - val_buf->new_sync_obj_arg = (void *) dev_priv; - list_add_tail(&val_buf->head, &sw_context->validate_nodes); - ++sw_context->cur_val_buf; - } *vmw_bo_p = vmw_bo; return 0; @@ -259,8 +501,11 @@ static int vmw_cmd_end_query(struct vmw_private *dev_priv, if (unlikely(ret != 0)) return ret; + ret = vmw_query_bo_switch_prepare(dev_priv, cmd->q.cid, + &vmw_bo->base, sw_context); + vmw_dmabuf_unreference(&vmw_bo); - return 0; + return ret; } static int vmw_cmd_wait_query(struct vmw_private *dev_priv, @@ -273,6 +518,7 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv, SVGA3dCmdWaitForQuery q; } *cmd; int ret; + struct vmw_resource *ctx; cmd = container_of(header, struct vmw_query_cmd, header); ret = vmw_cmd_cid_check(dev_priv, sw_context, header); @@ -286,10 +532,19 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv, return ret; vmw_dmabuf_unreference(&vmw_bo); + + /* + * This wait will act as a barrier for previous waits for this + * context. + */ + + ctx = sw_context->cur_ctx; + if (!list_empty(&ctx->query_head)) + list_del_init(&ctx->query_head); + return 0; } - static int vmw_cmd_dma(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGA3dCmdHeader *header) @@ -302,14 +557,37 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, SVGA3dCmdSurfaceDMA dma; } *cmd; int ret; + struct vmw_resource *res; + SVGA3dCmdSurfaceDMASuffix *suffix; + uint32_t bo_size; cmd = container_of(header, struct vmw_dma_cmd, header); + suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->dma + + header->size - sizeof(*suffix)); + + /* Make sure device and verifier stays in sync. */ + if (unlikely(suffix->suffixSize != sizeof(*suffix))) { + DRM_ERROR("Invalid DMA suffix size.\n"); + return -EINVAL; + } + ret = vmw_translate_guest_ptr(dev_priv, sw_context, &cmd->dma.guest.ptr, &vmw_bo); if (unlikely(ret != 0)) return ret; + /* Make sure DMA doesn't cross BO boundaries. */ + bo_size = vmw_bo->base.num_pages * PAGE_SIZE; + if (unlikely(cmd->dma.guest.ptr.offset > bo_size)) { + DRM_ERROR("Invalid DMA offset.\n"); + return -EINVAL; + } + + bo_size -= cmd->dma.guest.ptr.offset; + if (unlikely(suffix->maximumOffset > bo_size)) + suffix->maximumOffset = bo_size; + bo = &vmw_bo->base; ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile, cmd->dma.host.sid, &srf); @@ -318,18 +596,28 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, goto out_no_reloc; } - /** + ret = vmw_surface_validate(dev_priv, srf); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Culd not validate surface.\n"); + goto out_no_validate; + } + + /* * Patch command stream with device SID. */ - cmd->dma.host.sid = srf->res.id; vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header); - /** - * FIXME: May deadlock here when called from the - * command parsing code. - */ - vmw_surface_unreference(&srf); + vmw_dmabuf_unreference(&vmw_bo); + + res = &srf->res; + vmw_resource_to_validate_list(sw_context, &res); + + return 0; + +out_no_validate: + vmw_surface_unreference(&srf); out_no_reloc: vmw_dmabuf_unreference(&vmw_bo); return ret; @@ -419,6 +707,71 @@ static int vmw_cmd_tex_state(struct vmw_private *dev_priv, return 0; } +static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + void *buf) +{ + struct vmw_dma_buffer *vmw_bo; + int ret; + + struct { + uint32_t header; + SVGAFifoCmdDefineGMRFB body; + } *cmd = buf; + + ret = vmw_translate_guest_ptr(dev_priv, sw_context, + &cmd->body.ptr, + &vmw_bo); + if (unlikely(ret != 0)) + return ret; + + vmw_dmabuf_unreference(&vmw_bo); + + return ret; +} + +static int vmw_cmd_check_not_3d(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + void *buf, uint32_t *size) +{ + uint32_t size_remaining = *size; + uint32_t cmd_id; + + cmd_id = le32_to_cpu(((uint32_t *)buf)[0]); + switch (cmd_id) { + case SVGA_CMD_UPDATE: + *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdUpdate); + break; + case SVGA_CMD_DEFINE_GMRFB: + *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdDefineGMRFB); + break; + case SVGA_CMD_BLIT_GMRFB_TO_SCREEN: + *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); + break; + case SVGA_CMD_BLIT_SCREEN_TO_GMRFB: + *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); + break; + default: + DRM_ERROR("Unsupported SVGA command: %u.\n", cmd_id); + return -EINVAL; + } + + if (*size > size_remaining) { + DRM_ERROR("Invalid SVGA command (size mismatch):" + " %u.\n", cmd_id); + return -EINVAL; + } + + if (unlikely(!sw_context->kernel)) { + DRM_ERROR("Kernel only SVGA command: %u.\n", cmd_id); + return -EPERM; + } + + if (cmd_id == SVGA_CMD_DEFINE_GMRFB) + return vmw_cmd_check_define_gmrfb(dev_priv, sw_context, buf); + + return 0; +} typedef int (*vmw_cmd_func) (struct vmw_private *, struct vmw_sw_context *, @@ -471,11 +824,11 @@ static int vmw_cmd_check(struct vmw_private *dev_priv, SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf; int ret; - cmd_id = ((uint32_t *)buf)[0]; - if (cmd_id == SVGA_CMD_UPDATE) { - *size = 5 << 2; - return 0; - } + cmd_id = le32_to_cpu(((uint32_t *)buf)[0]); + /* Handle any none 3D commands */ + if (unlikely(cmd_id < SVGA_CMD_MAX)) + return vmw_cmd_check_not_3d(dev_priv, sw_context, buf, size); + cmd_id = le32_to_cpu(header->id); *size = le32_to_cpu(header->size) + sizeof(SVGA3dCmdHeader); @@ -500,7 +853,8 @@ out_err: static int vmw_cmd_check_all(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, - void *buf, uint32_t size) + void *buf, + uint32_t size) { int32_t cur_size = size; int ret; @@ -550,7 +904,11 @@ static void vmw_apply_relocations(struct vmw_sw_context *sw_context) static void vmw_clear_validations(struct vmw_sw_context *sw_context) { struct ttm_validate_buffer *entry, *next; + struct vmw_resource *res, *res_next; + /* + * Drop references to DMA buffers held during command submission. + */ list_for_each_entry_safe(entry, next, &sw_context->validate_nodes, head) { list_del(&entry->head); @@ -559,6 +917,16 @@ static void vmw_clear_validations(struct vmw_sw_context *sw_context) sw_context->cur_val_buf--; } BUG_ON(sw_context->cur_val_buf != 0); + + /* + * Drop references to resources held during command submission. + */ + vmw_resource_unreserve(&sw_context->resource_list); + list_for_each_entry_safe(res, res_next, &sw_context->resource_list, + validate_head) { + list_del_init(&res->validate_head); + vmw_resource_unreference(&res); + } } static int vmw_validate_single_buffer(struct vmw_private *dev_priv, @@ -566,6 +934,16 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv, { int ret; + + /* + * Don't validate pinned buffers. + */ + + if (bo == dev_priv->pinned_bo || + (bo == dev_priv->dummy_query_bo && + dev_priv->dummy_query_bo_pinned)) + return 0; + /** * Put BO in VRAM if there is space, otherwise as a GMR. * If there is no space in VRAM and GMR ids are all used up, @@ -602,57 +980,208 @@ static int vmw_validate_buffers(struct vmw_private *dev_priv, return 0; } -int vmw_execbuf_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +static int vmw_resize_cmd_bounce(struct vmw_sw_context *sw_context, + uint32_t size) +{ + if (likely(sw_context->cmd_bounce_size >= size)) + return 0; + + if (sw_context->cmd_bounce_size == 0) + sw_context->cmd_bounce_size = VMWGFX_CMD_BOUNCE_INIT_SIZE; + + while (sw_context->cmd_bounce_size < size) { + sw_context->cmd_bounce_size = + PAGE_ALIGN(sw_context->cmd_bounce_size + + (sw_context->cmd_bounce_size >> 1)); + } + + if (sw_context->cmd_bounce != NULL) + vfree(sw_context->cmd_bounce); + + sw_context->cmd_bounce = vmalloc(sw_context->cmd_bounce_size); + + if (sw_context->cmd_bounce == NULL) { + DRM_ERROR("Failed to allocate command bounce buffer.\n"); + sw_context->cmd_bounce_size = 0; + return -ENOMEM; + } + + return 0; +} + +/** + * vmw_execbuf_fence_commands - create and submit a command stream fence + * + * Creates a fence object and submits a command stream marker. + * If this fails for some reason, We sync the fifo and return NULL. + * It is then safe to fence buffers with a NULL pointer. + * + * If @p_handle is not NULL @file_priv must also not be NULL. Creates + * a userspace handle if @p_handle is not NULL, otherwise not. + */ + +int vmw_execbuf_fence_commands(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_fence_obj **p_fence, + uint32_t *p_handle) { - struct vmw_private *dev_priv = vmw_priv(dev); - struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data; - struct drm_vmw_fence_rep fence_rep; - struct drm_vmw_fence_rep __user *user_fence_rep; - int ret; - void *user_cmd; - void *cmd; uint32_t sequence; - struct vmw_sw_context *sw_context = &dev_priv->ctx; - struct vmw_master *vmaster = vmw_master(file_priv->master); + int ret; + bool synced = false; - ret = ttm_read_lock(&vmaster->lock, true); - if (unlikely(ret != 0)) - return ret; + /* p_handle implies file_priv. */ + BUG_ON(p_handle != NULL && file_priv == NULL); - ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); + ret = vmw_fifo_send_fence(dev_priv, &sequence); if (unlikely(ret != 0)) { - ret = -ERESTARTSYS; - goto out_no_cmd_mutex; + DRM_ERROR("Fence submission error. Syncing.\n"); + synced = true; } - cmd = vmw_fifo_reserve(dev_priv, arg->command_size); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Failed reserving fifo space for commands.\n"); - ret = -ENOMEM; - goto out_unlock; + if (p_handle != NULL) + ret = vmw_user_fence_create(file_priv, dev_priv->fman, + sequence, + DRM_VMW_FENCE_FLAG_EXEC, + p_fence, p_handle); + else + ret = vmw_fence_create(dev_priv->fman, sequence, + DRM_VMW_FENCE_FLAG_EXEC, + p_fence); + + if (unlikely(ret != 0 && !synced)) { + (void) vmw_fallback_wait(dev_priv, false, false, + sequence, false, + VMW_FENCE_WAIT_TIMEOUT); + *p_fence = NULL; } - user_cmd = (void __user *)(unsigned long)arg->commands; - ret = copy_from_user(cmd, user_cmd, arg->command_size); + return 0; +} + +/** + * vmw_execbuf_copy_fence_user - copy fence object information to + * user-space. + * + * @dev_priv: Pointer to a vmw_private struct. + * @vmw_fp: Pointer to the struct vmw_fpriv representing the calling file. + * @ret: Return value from fence object creation. + * @user_fence_rep: User space address of a struct drm_vmw_fence_rep to + * which the information should be copied. + * @fence: Pointer to the fenc object. + * @fence_handle: User-space fence handle. + * + * This function copies fence information to user-space. If copying fails, + * The user-space struct drm_vmw_fence_rep::error member is hopefully + * left untouched, and if it's preloaded with an -EFAULT by user-space, + * the error will hopefully be detected. + * Also if copying fails, user-space will be unable to signal the fence + * object so we wait for it immediately, and then unreference the + * user-space reference. + */ +void +vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, + struct vmw_fpriv *vmw_fp, + int ret, + struct drm_vmw_fence_rep __user *user_fence_rep, + struct vmw_fence_obj *fence, + uint32_t fence_handle) +{ + struct drm_vmw_fence_rep fence_rep; - if (unlikely(ret != 0)) { - ret = -EFAULT; - DRM_ERROR("Failed copying commands.\n"); - goto out_commit; + if (user_fence_rep == NULL) + return; + + memset(&fence_rep, 0, sizeof(fence_rep)); + + fence_rep.error = ret; + if (ret == 0) { + BUG_ON(fence == NULL); + + fence_rep.handle = fence_handle; + fence_rep.seqno = fence->seqno; + vmw_update_seqno(dev_priv, &dev_priv->fifo); + fence_rep.passed_seqno = dev_priv->last_read_seqno; + } + + /* + * copy_to_user errors will be detected by user space not + * seeing fence_rep::error filled in. Typically + * user-space would have pre-set that member to -EFAULT. + */ + ret = copy_to_user(user_fence_rep, &fence_rep, + sizeof(fence_rep)); + + /* + * User-space lost the fence object. We need to sync + * and unreference the handle. + */ + if (unlikely(ret != 0) && (fence_rep.error == 0)) { + ttm_ref_object_base_unref(vmw_fp->tfile, + fence_handle, TTM_REF_USAGE); + DRM_ERROR("Fence copy error. Syncing.\n"); + (void) vmw_fence_obj_wait(fence, fence->signal_mask, + false, false, + VMW_FENCE_WAIT_TIMEOUT); } +} + +int vmw_execbuf_process(struct drm_file *file_priv, + struct vmw_private *dev_priv, + void __user *user_commands, + void *kernel_commands, + uint32_t command_size, + uint64_t throttle_us, + struct drm_vmw_fence_rep __user *user_fence_rep) +{ + struct vmw_sw_context *sw_context = &dev_priv->ctx; + struct vmw_fence_obj *fence; + uint32_t handle; + void *cmd; + int ret; + + ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); + if (unlikely(ret != 0)) + return -ERESTARTSYS; + + if (kernel_commands == NULL) { + sw_context->kernel = false; + + ret = vmw_resize_cmd_bounce(sw_context, command_size); + if (unlikely(ret != 0)) + goto out_unlock; + + + ret = copy_from_user(sw_context->cmd_bounce, + user_commands, command_size); + + if (unlikely(ret != 0)) { + ret = -EFAULT; + DRM_ERROR("Failed copying commands.\n"); + goto out_unlock; + } + kernel_commands = sw_context->cmd_bounce; + } else + sw_context->kernel = true; sw_context->tfile = vmw_fpriv(file_priv)->tfile; sw_context->cid_valid = false; sw_context->sid_valid = false; sw_context->cur_reloc = 0; sw_context->cur_val_buf = 0; + sw_context->fence_flags = 0; + INIT_LIST_HEAD(&sw_context->query_list); + INIT_LIST_HEAD(&sw_context->resource_list); + sw_context->cur_query_bo = dev_priv->pinned_bo; + sw_context->cur_query_cid = dev_priv->query_cid; + sw_context->query_cid_valid = (dev_priv->pinned_bo != NULL); INIT_LIST_HEAD(&sw_context->validate_nodes); - ret = vmw_cmd_check_all(dev_priv, sw_context, cmd, arg->command_size); + ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands, + command_size); if (unlikely(ret != 0)) goto out_err; + ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes); if (unlikely(ret != 0)) goto out_err; @@ -663,57 +1192,206 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, vmw_apply_relocations(sw_context); - if (arg->throttle_us) { - ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.fence_queue, - arg->throttle_us); + if (throttle_us) { + ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue, + throttle_us); if (unlikely(ret != 0)) - goto out_err; + goto out_throttle; } - vmw_fifo_commit(dev_priv, arg->command_size); + cmd = vmw_fifo_reserve(dev_priv, command_size); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving fifo space for commands.\n"); + ret = -ENOMEM; + goto out_throttle; + } - ret = vmw_fifo_send_fence(dev_priv, &sequence); - - ttm_eu_fence_buffer_objects(&sw_context->validate_nodes, - (void *)(unsigned long) sequence); - vmw_clear_validations(sw_context); - mutex_unlock(&dev_priv->cmdbuf_mutex); + memcpy(cmd, kernel_commands, command_size); + vmw_fifo_commit(dev_priv, command_size); + vmw_query_bo_switch_commit(dev_priv, sw_context); + ret = vmw_execbuf_fence_commands(file_priv, dev_priv, + &fence, + (user_fence_rep) ? &handle : NULL); /* * This error is harmless, because if fence submission fails, - * vmw_fifo_send_fence will sync. + * vmw_fifo_send_fence will sync. The error will be propagated to + * user-space in @fence_rep */ if (ret != 0) DRM_ERROR("Fence submission error. Syncing.\n"); - fence_rep.error = ret; - fence_rep.fence_seq = (uint64_t) sequence; - fence_rep.pad64 = 0; - - user_fence_rep = (struct drm_vmw_fence_rep __user *) - (unsigned long)arg->fence_rep; + ttm_eu_fence_buffer_objects(&sw_context->validate_nodes, + (void *) fence); - /* - * copy_to_user errors will be detected by user space not - * seeing fence_rep::error filled in. - */ + vmw_clear_validations(sw_context); + vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret, + user_fence_rep, fence, handle); - ret = copy_to_user(user_fence_rep, &fence_rep, sizeof(fence_rep)); + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); - vmw_kms_cursor_post_execbuf(dev_priv); - ttm_read_unlock(&vmaster->lock); + mutex_unlock(&dev_priv->cmdbuf_mutex); return 0; + out_err: vmw_free_relocations(sw_context); +out_throttle: + vmw_query_switch_backoff(sw_context); ttm_eu_backoff_reservation(&sw_context->validate_nodes); vmw_clear_validations(sw_context); -out_commit: - vmw_fifo_commit(dev_priv, 0); out_unlock: mutex_unlock(&dev_priv->cmdbuf_mutex); -out_no_cmd_mutex: + return ret; +} + +/** + * vmw_execbuf_unpin_panic - Idle the fifo and unpin the query buffer. + * + * @dev_priv: The device private structure. + * + * This function is called to idle the fifo and unpin the query buffer + * if the normal way to do this hits an error, which should typically be + * extremely rare. + */ +static void vmw_execbuf_unpin_panic(struct vmw_private *dev_priv) +{ + DRM_ERROR("Can't unpin query buffer. Trying to recover.\n"); + + (void) vmw_fallback_wait(dev_priv, false, true, 0, false, 10*HZ); + vmw_bo_pin(dev_priv->pinned_bo, false); + vmw_bo_pin(dev_priv->dummy_query_bo, false); + dev_priv->dummy_query_bo_pinned = false; +} + + +/** + * vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned + * query bo. + * + * @dev_priv: The device private structure. + * @only_on_cid_match: Only flush and unpin if the current active query cid + * matches @cid. + * @cid: Optional context id to match. + * + * This function should be used to unpin the pinned query bo, or + * as a query barrier when we need to make sure that all queries have + * finished before the next fifo command. (For example on hardware + * context destructions where the hardware may otherwise leak unfinished + * queries). + * + * This function does not return any failure codes, but make attempts + * to do safe unpinning in case of errors. + * + * The function will synchronize on the previous query barrier, and will + * thus not finish until that barrier has executed. + */ +void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, + bool only_on_cid_match, uint32_t cid) +{ + int ret = 0; + struct list_head validate_list; + struct ttm_validate_buffer pinned_val, query_val; + struct vmw_fence_obj *fence; + + mutex_lock(&dev_priv->cmdbuf_mutex); + + if (dev_priv->pinned_bo == NULL) + goto out_unlock; + + if (only_on_cid_match && cid != dev_priv->query_cid) + goto out_unlock; + + INIT_LIST_HEAD(&validate_list); + + pinned_val.new_sync_obj_arg = (void *)(unsigned long) + DRM_VMW_FENCE_FLAG_EXEC; + pinned_val.bo = ttm_bo_reference(dev_priv->pinned_bo); + list_add_tail(&pinned_val.head, &validate_list); + + query_val.new_sync_obj_arg = pinned_val.new_sync_obj_arg; + query_val.bo = ttm_bo_reference(dev_priv->dummy_query_bo); + list_add_tail(&query_val.head, &validate_list); + + do { + ret = ttm_eu_reserve_buffers(&validate_list); + } while (ret == -ERESTARTSYS); + + if (unlikely(ret != 0)) { + vmw_execbuf_unpin_panic(dev_priv); + goto out_no_reserve; + } + + ret = vmw_fifo_emit_dummy_query(dev_priv, dev_priv->query_cid); + if (unlikely(ret != 0)) { + vmw_execbuf_unpin_panic(dev_priv); + goto out_no_emit; + } + + vmw_bo_pin(dev_priv->pinned_bo, false); + vmw_bo_pin(dev_priv->dummy_query_bo, false); + dev_priv->dummy_query_bo_pinned = false; + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + ttm_eu_fence_buffer_objects(&validate_list, (void *) fence); + + ttm_bo_unref(&query_val.bo); + ttm_bo_unref(&pinned_val.bo); + ttm_bo_unref(&dev_priv->pinned_bo); + +out_unlock: + mutex_unlock(&dev_priv->cmdbuf_mutex); + return; + +out_no_emit: + ttm_eu_backoff_reservation(&validate_list); +out_no_reserve: + ttm_bo_unref(&query_val.bo); + ttm_bo_unref(&pinned_val.bo); + ttm_bo_unref(&dev_priv->pinned_bo); + mutex_unlock(&dev_priv->cmdbuf_mutex); +} + + +int vmw_execbuf_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data; + struct vmw_master *vmaster = vmw_master(file_priv->master); + int ret; + + /* + * This will allow us to extend the ioctl argument while + * maintaining backwards compatibility: + * We take different code paths depending on the value of + * arg->version. + */ + + if (unlikely(arg->version != DRM_VMW_EXECBUF_VERSION)) { + DRM_ERROR("Incorrect execbuf version.\n"); + DRM_ERROR("You're running outdated experimental " + "vmwgfx user-space drivers."); + return -EINVAL; + } + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_execbuf_process(file_priv, dev_priv, + (void __user *)(unsigned long)arg->commands, + NULL, arg->command_size, arg->throttle_us, + (void __user *)(unsigned long)arg->fence_rep); + + if (unlikely(ret != 0)) + goto out_unlock; + + vmw_kms_cursor_post_execbuf(dev_priv); + +out_unlock: ttm_read_unlock(&vmaster->lock); return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index bfab60c..7f16ff2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -26,6 +26,8 @@ * **************************************************************************/ +#include <linux/export.h> + #include "drmP.h" #include "vmwgfx_drv.h" @@ -145,7 +147,7 @@ static int vmw_fb_check_var(struct fb_var_screeninfo *var, } if (!vmw_kms_validate_mode_vram(vmw_priv, - info->fix.line_length, + var->xres * var->bits_per_pixel/8, var->yoffset + var->yres)) { DRM_ERROR("Requested geom can not fit in framebuffer\n"); return -EINVAL; @@ -158,10 +160,16 @@ static int vmw_fb_set_par(struct fb_info *info) { struct vmw_fb_par *par = info->par; struct vmw_private *vmw_priv = par->vmw_priv; + int ret; + + info->fix.line_length = info->var.xres * info->var.bits_per_pixel/8; + + ret = vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres, + info->fix.line_length, + par->bpp, par->depth); + if (ret) + return ret; - vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres, - info->fix.line_length, - par->bpp, par->depth); if (vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY) { /* TODO check if pitch and offset changes */ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1); @@ -405,14 +413,14 @@ int vmw_fb_init(struct vmw_private *vmw_priv) struct fb_info *info; unsigned initial_width, initial_height; unsigned fb_width, fb_height; - unsigned fb_bbp, fb_depth, fb_offset, fb_pitch, fb_size; + unsigned fb_bpp, fb_depth, fb_offset, fb_pitch, fb_size; int ret; /* XXX These shouldn't be hardcoded. */ initial_width = 800; initial_height = 600; - fb_bbp = 32; + fb_bpp = 32; fb_depth = 24; /* XXX As shouldn't these be as well. */ @@ -422,7 +430,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv) initial_width = min(fb_width, initial_width); initial_height = min(fb_height, initial_height); - fb_pitch = fb_width * fb_bbp / 8; + fb_pitch = fb_width * fb_bpp / 8; fb_size = fb_pitch * fb_height; fb_offset = vmw_read(vmw_priv, SVGA_REG_FB_OFFSET); @@ -437,7 +445,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv) par = info->par; par->vmw_priv = vmw_priv; par->depth = fb_depth; - par->bpp = fb_bbp; + par->bpp = fb_bpp; par->vmalloc = NULL; par->max_width = fb_width; par->max_height = fb_height; @@ -588,58 +596,6 @@ int vmw_fb_close(struct vmw_private *vmw_priv) return 0; } -int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv, - struct vmw_dma_buffer *vmw_bo) -{ - struct ttm_buffer_object *bo = &vmw_bo->base; - int ret = 0; - - ret = ttm_bo_reserve(bo, false, false, false, 0); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_bo_validate(bo, &vmw_sys_placement, false, false, false); - ttm_bo_unreserve(bo); - - return ret; -} - -int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, - struct vmw_dma_buffer *vmw_bo) -{ - struct ttm_buffer_object *bo = &vmw_bo->base; - struct ttm_placement ne_placement = vmw_vram_ne_placement; - int ret = 0; - - ne_placement.lpfn = bo->num_pages; - - /* interuptable? */ - ret = ttm_write_lock(&vmw_priv->active_master->lock, false); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_bo_reserve(bo, false, false, false, 0); - if (unlikely(ret != 0)) - goto err_unlock; - - if (bo->mem.mem_type == TTM_PL_VRAM && - bo->mem.start < bo->num_pages && - bo->mem.start > 0) - (void) ttm_bo_validate(bo, &vmw_sys_placement, false, - false, false); - - ret = ttm_bo_validate(bo, &ne_placement, false, false, false); - - /* Could probably bug on */ - WARN_ON(bo->offset != 0); - - ttm_bo_unreserve(bo); -err_unlock: - ttm_write_unlock(&vmw_priv->active_master->lock); - - return ret; -} - int vmw_fb_off(struct vmw_private *vmw_priv) { struct fb_info *info; @@ -661,7 +617,7 @@ int vmw_fb_off(struct vmw_private *vmw_priv) par->bo_ptr = NULL; ttm_bo_kunmap(&par->map); - vmw_dmabuf_from_vram(vmw_priv, par->vmw_bo); + vmw_dmabuf_unpin(vmw_priv, par->vmw_bo, false); return 0; } @@ -687,7 +643,7 @@ int vmw_fb_on(struct vmw_private *vmw_priv) /* Make sure that all overlays are stoped when we take over */ vmw_overlay_stop_all(vmw_priv); - ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo); + ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo, true, false); if (unlikely(ret != 0)) { DRM_ERROR("could not move buffer to start of VRAM\n"); goto err_no_buffer; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 61eacc1..1ed5a1c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright (C) 2010 VMware, Inc., Palo Alto, CA., USA + * Copyright © 2011 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -25,149 +25,1083 @@ * **************************************************************************/ - +#include "drmP.h" #include "vmwgfx_drv.h" -struct vmw_fence { - struct list_head head; - uint32_t sequence; - struct timespec submitted; +#define VMW_FENCE_WRAP (1 << 31) + +struct vmw_fence_manager { + int num_fence_objects; + struct vmw_private *dev_priv; + spinlock_t lock; + struct list_head fence_list; + struct work_struct work; + u32 user_fence_size; + u32 fence_size; + u32 event_fence_action_size; + bool fifo_down; + struct list_head cleanup_list; + uint32_t pending_actions[VMW_ACTION_MAX]; + struct mutex goal_irq_mutex; + bool goal_irq_on; /* Protected by @goal_irq_mutex */ + bool seqno_valid; /* Protected by @lock, and may not be set to true + without the @goal_irq_mutex held. */ }; -void vmw_fence_queue_init(struct vmw_fence_queue *queue) +struct vmw_user_fence { + struct ttm_base_object base; + struct vmw_fence_obj fence; +}; + +/** + * struct vmw_event_fence_action - fence action that delivers a drm event. + * + * @e: A struct drm_pending_event that controls the event delivery. + * @action: A struct vmw_fence_action to hook up to a fence. + * @fence: A referenced pointer to the fence to keep it alive while @action + * hangs on it. + * @dev: Pointer to a struct drm_device so we can access the event stuff. + * @kref: Both @e and @action has destructors, so we need to refcount. + * @size: Size accounted for this object. + * @tv_sec: If non-null, the variable pointed to will be assigned + * current time tv_sec val when the fence signals. + * @tv_usec: Must be set if @tv_sec is set, and the variable pointed to will + * be assigned the current time tv_usec val when the fence signals. + */ +struct vmw_event_fence_action { + struct drm_pending_event e; + struct vmw_fence_action action; + struct vmw_fence_obj *fence; + struct drm_device *dev; + struct kref kref; + uint32_t size; + uint32_t *tv_sec; + uint32_t *tv_usec; +}; + +/** + * Note on fencing subsystem usage of irqs: + * Typically the vmw_fences_update function is called + * + * a) When a new fence seqno has been submitted by the fifo code. + * b) On-demand when we have waiters. Sleeping waiters will switch on the + * ANY_FENCE irq and call vmw_fences_update function each time an ANY_FENCE + * irq is received. When the last fence waiter is gone, that IRQ is masked + * away. + * + * In situations where there are no waiters and we don't submit any new fences, + * fence objects may not be signaled. This is perfectly OK, since there are + * no consumers of the signaled data, but that is NOT ok when there are fence + * actions attached to a fence. The fencing subsystem then makes use of the + * FENCE_GOAL irq and sets the fence goal seqno to that of the next fence + * which has an action attached, and each time vmw_fences_update is called, + * the subsystem makes sure the fence goal seqno is updated. + * + * The fence goal seqno irq is on as long as there are unsignaled fence + * objects with actions attached to them. + */ + +static void vmw_fence_obj_destroy_locked(struct kref *kref) { - INIT_LIST_HEAD(&queue->head); - queue->lag = ns_to_timespec(0); - getrawmonotonic(&queue->lag_time); - spin_lock_init(&queue->lock); + struct vmw_fence_obj *fence = + container_of(kref, struct vmw_fence_obj, kref); + + struct vmw_fence_manager *fman = fence->fman; + unsigned int num_fences; + + list_del_init(&fence->head); + num_fences = --fman->num_fence_objects; + spin_unlock_irq(&fman->lock); + if (fence->destroy) + fence->destroy(fence); + else + kfree(fence); + + spin_lock_irq(&fman->lock); } -void vmw_fence_queue_takedown(struct vmw_fence_queue *queue) + +/** + * Execute signal actions on fences recently signaled. + * This is done from a workqueue so we don't have to execute + * signal actions from atomic context. + */ + +static void vmw_fence_work_func(struct work_struct *work) { - struct vmw_fence *fence, *next; + struct vmw_fence_manager *fman = + container_of(work, struct vmw_fence_manager, work); + struct list_head list; + struct vmw_fence_action *action, *next_action; + bool seqno_valid; - spin_lock(&queue->lock); - list_for_each_entry_safe(fence, next, &queue->head, head) { - kfree(fence); + do { + INIT_LIST_HEAD(&list); + mutex_lock(&fman->goal_irq_mutex); + + spin_lock_irq(&fman->lock); + list_splice_init(&fman->cleanup_list, &list); + seqno_valid = fman->seqno_valid; + spin_unlock_irq(&fman->lock); + + if (!seqno_valid && fman->goal_irq_on) { + fman->goal_irq_on = false; + vmw_goal_waiter_remove(fman->dev_priv); + } + mutex_unlock(&fman->goal_irq_mutex); + + if (list_empty(&list)) + return; + + /* + * At this point, only we should be able to manipulate the + * list heads of the actions we have on the private list. + * hence fman::lock not held. + */ + + list_for_each_entry_safe(action, next_action, &list, head) { + list_del_init(&action->head); + if (action->cleanup) + action->cleanup(action); + } + } while (1); +} + +struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv) +{ + struct vmw_fence_manager *fman = kzalloc(sizeof(*fman), GFP_KERNEL); + + if (unlikely(fman == NULL)) + return NULL; + + fman->dev_priv = dev_priv; + spin_lock_init(&fman->lock); + INIT_LIST_HEAD(&fman->fence_list); + INIT_LIST_HEAD(&fman->cleanup_list); + INIT_WORK(&fman->work, &vmw_fence_work_func); + fman->fifo_down = true; + fman->user_fence_size = ttm_round_pot(sizeof(struct vmw_user_fence)); + fman->fence_size = ttm_round_pot(sizeof(struct vmw_fence_obj)); + fman->event_fence_action_size = + ttm_round_pot(sizeof(struct vmw_event_fence_action)); + mutex_init(&fman->goal_irq_mutex); + + return fman; +} + +void vmw_fence_manager_takedown(struct vmw_fence_manager *fman) +{ + unsigned long irq_flags; + bool lists_empty; + + (void) cancel_work_sync(&fman->work); + + spin_lock_irqsave(&fman->lock, irq_flags); + lists_empty = list_empty(&fman->fence_list) && + list_empty(&fman->cleanup_list); + spin_unlock_irqrestore(&fman->lock, irq_flags); + + BUG_ON(!lists_empty); + kfree(fman); +} + +static int vmw_fence_obj_init(struct vmw_fence_manager *fman, + struct vmw_fence_obj *fence, + u32 seqno, + uint32_t mask, + void (*destroy) (struct vmw_fence_obj *fence)) +{ + unsigned long irq_flags; + unsigned int num_fences; + int ret = 0; + + fence->seqno = seqno; + INIT_LIST_HEAD(&fence->seq_passed_actions); + fence->fman = fman; + fence->signaled = 0; + fence->signal_mask = mask; + kref_init(&fence->kref); + fence->destroy = destroy; + init_waitqueue_head(&fence->queue); + + spin_lock_irqsave(&fman->lock, irq_flags); + if (unlikely(fman->fifo_down)) { + ret = -EBUSY; + goto out_unlock; + } + list_add_tail(&fence->head, &fman->fence_list); + num_fences = ++fman->num_fence_objects; + +out_unlock: + spin_unlock_irqrestore(&fman->lock, irq_flags); + return ret; + +} + +struct vmw_fence_obj *vmw_fence_obj_reference(struct vmw_fence_obj *fence) +{ + if (unlikely(fence == NULL)) + return NULL; + + kref_get(&fence->kref); + return fence; +} + +/** + * vmw_fence_obj_unreference + * + * Note that this function may not be entered with disabled irqs since + * it may re-enable them in the destroy function. + * + */ +void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p) +{ + struct vmw_fence_obj *fence = *fence_p; + struct vmw_fence_manager *fman; + + if (unlikely(fence == NULL)) + return; + + fman = fence->fman; + *fence_p = NULL; + spin_lock_irq(&fman->lock); + BUG_ON(atomic_read(&fence->kref.refcount) == 0); + kref_put(&fence->kref, vmw_fence_obj_destroy_locked); + spin_unlock_irq(&fman->lock); +} + +void vmw_fences_perform_actions(struct vmw_fence_manager *fman, + struct list_head *list) +{ + struct vmw_fence_action *action, *next_action; + + list_for_each_entry_safe(action, next_action, list, head) { + list_del_init(&action->head); + fman->pending_actions[action->type]--; + if (action->seq_passed != NULL) + action->seq_passed(action); + + /* + * Add the cleanup action to the cleanup list so that + * it will be performed by a worker task. + */ + + list_add_tail(&action->head, &fman->cleanup_list); + } +} + +/** + * vmw_fence_goal_new_locked - Figure out a new device fence goal + * seqno if needed. + * + * @fman: Pointer to a fence manager. + * @passed_seqno: The seqno the device currently signals as passed. + * + * This function should be called with the fence manager lock held. + * It is typically called when we have a new passed_seqno, and + * we might need to update the fence goal. It checks to see whether + * the current fence goal has already passed, and, in that case, + * scans through all unsignaled fences to get the next fence object with an + * action attached, and sets the seqno of that fence as a new fence goal. + * + * returns true if the device goal seqno was updated. False otherwise. + */ +static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman, + u32 passed_seqno) +{ + u32 goal_seqno; + __le32 __iomem *fifo_mem; + struct vmw_fence_obj *fence; + + if (likely(!fman->seqno_valid)) + return false; + + fifo_mem = fman->dev_priv->mmio_virt; + goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL); + if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP)) + return false; + + fman->seqno_valid = false; + list_for_each_entry(fence, &fman->fence_list, head) { + if (!list_empty(&fence->seq_passed_actions)) { + fman->seqno_valid = true; + iowrite32(fence->seqno, + fifo_mem + SVGA_FIFO_FENCE_GOAL); + break; + } } - spin_unlock(&queue->lock); + + return true; +} + + +/** + * vmw_fence_goal_check_locked - Replace the device fence goal seqno if + * needed. + * + * @fence: Pointer to a struct vmw_fence_obj the seqno of which should be + * considered as a device fence goal. + * + * This function should be called with the fence manager lock held. + * It is typically called when an action has been attached to a fence to + * check whether the seqno of that fence should be used for a fence + * goal interrupt. This is typically needed if the current fence goal is + * invalid, or has a higher seqno than that of the current fence object. + * + * returns true if the device goal seqno was updated. False otherwise. + */ +static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence) +{ + u32 goal_seqno; + __le32 __iomem *fifo_mem; + + if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC) + return false; + + fifo_mem = fence->fman->dev_priv->mmio_virt; + goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL); + if (likely(fence->fman->seqno_valid && + goal_seqno - fence->seqno < VMW_FENCE_WRAP)) + return false; + + iowrite32(fence->seqno, fifo_mem + SVGA_FIFO_FENCE_GOAL); + fence->fman->seqno_valid = true; + + return true; +} + +void vmw_fences_update(struct vmw_fence_manager *fman) +{ + unsigned long flags; + struct vmw_fence_obj *fence, *next_fence; + struct list_head action_list; + bool needs_rerun; + uint32_t seqno, new_seqno; + __le32 __iomem *fifo_mem = fman->dev_priv->mmio_virt; + + seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); +rerun: + spin_lock_irqsave(&fman->lock, flags); + list_for_each_entry_safe(fence, next_fence, &fman->fence_list, head) { + if (seqno - fence->seqno < VMW_FENCE_WRAP) { + list_del_init(&fence->head); + fence->signaled |= DRM_VMW_FENCE_FLAG_EXEC; + INIT_LIST_HEAD(&action_list); + list_splice_init(&fence->seq_passed_actions, + &action_list); + vmw_fences_perform_actions(fman, &action_list); + wake_up_all(&fence->queue); + } else + break; + } + + needs_rerun = vmw_fence_goal_new_locked(fman, seqno); + + if (!list_empty(&fman->cleanup_list)) + (void) schedule_work(&fman->work); + spin_unlock_irqrestore(&fman->lock, flags); + + /* + * Rerun if the fence goal seqno was updated, and the + * hardware might have raced with that update, so that + * we missed a fence_goal irq. + */ + + if (unlikely(needs_rerun)) { + new_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); + if (new_seqno != seqno) { + seqno = new_seqno; + goto rerun; + } + } +} + +bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence, + uint32_t flags) +{ + struct vmw_fence_manager *fman = fence->fman; + unsigned long irq_flags; + uint32_t signaled; + + spin_lock_irqsave(&fman->lock, irq_flags); + signaled = fence->signaled; + spin_unlock_irqrestore(&fman->lock, irq_flags); + + flags &= fence->signal_mask; + if ((signaled & flags) == flags) + return 1; + + if ((signaled & DRM_VMW_FENCE_FLAG_EXEC) == 0) + vmw_fences_update(fman); + + spin_lock_irqsave(&fman->lock, irq_flags); + signaled = fence->signaled; + spin_unlock_irqrestore(&fman->lock, irq_flags); + + return ((signaled & flags) == flags); } -int vmw_fence_push(struct vmw_fence_queue *queue, - uint32_t sequence) +int vmw_fence_obj_wait(struct vmw_fence_obj *fence, + uint32_t flags, bool lazy, + bool interruptible, unsigned long timeout) { - struct vmw_fence *fence = kmalloc(sizeof(*fence), GFP_KERNEL); + struct vmw_private *dev_priv = fence->fman->dev_priv; + long ret; + + if (likely(vmw_fence_obj_signaled(fence, flags))) + return 0; + + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); + vmw_seqno_waiter_add(dev_priv); - if (unlikely(!fence)) + if (interruptible) + ret = wait_event_interruptible_timeout + (fence->queue, + vmw_fence_obj_signaled(fence, flags), + timeout); + else + ret = wait_event_timeout + (fence->queue, + vmw_fence_obj_signaled(fence, flags), + timeout); + + vmw_seqno_waiter_remove(dev_priv); + + if (unlikely(ret == 0)) + ret = -EBUSY; + else if (likely(ret > 0)) + ret = 0; + + return ret; +} + +void vmw_fence_obj_flush(struct vmw_fence_obj *fence) +{ + struct vmw_private *dev_priv = fence->fman->dev_priv; + + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); +} + +static void vmw_fence_destroy(struct vmw_fence_obj *fence) +{ + kfree(fence); +} + +int vmw_fence_create(struct vmw_fence_manager *fman, + uint32_t seqno, + uint32_t mask, + struct vmw_fence_obj **p_fence) +{ + struct vmw_fence_obj *fence; + int ret; + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (unlikely(fence == NULL)) return -ENOMEM; - fence->sequence = sequence; - getrawmonotonic(&fence->submitted); - spin_lock(&queue->lock); - list_add_tail(&fence->head, &queue->head); - spin_unlock(&queue->lock); + ret = vmw_fence_obj_init(fman, fence, seqno, mask, + vmw_fence_destroy); + if (unlikely(ret != 0)) + goto out_err_init; + *p_fence = fence; return 0; + +out_err_init: + kfree(fence); + return ret; } -int vmw_fence_pull(struct vmw_fence_queue *queue, - uint32_t signaled_sequence) + +static void vmw_user_fence_destroy(struct vmw_fence_obj *fence) { - struct vmw_fence *fence, *next; - struct timespec now; - bool updated = false; + struct vmw_user_fence *ufence = + container_of(fence, struct vmw_user_fence, fence); + struct vmw_fence_manager *fman = fence->fman; - spin_lock(&queue->lock); - getrawmonotonic(&now); + kfree(ufence); + /* + * Free kernel space accounting. + */ + ttm_mem_global_free(vmw_mem_glob(fman->dev_priv), + fman->user_fence_size); +} - if (list_empty(&queue->head)) { - queue->lag = ns_to_timespec(0); - queue->lag_time = now; - updated = true; - goto out_unlock; +static void vmw_user_fence_base_release(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct vmw_user_fence *ufence = + container_of(base, struct vmw_user_fence, base); + struct vmw_fence_obj *fence = &ufence->fence; + + *p_base = NULL; + vmw_fence_obj_unreference(&fence); +} + +int vmw_user_fence_create(struct drm_file *file_priv, + struct vmw_fence_manager *fman, + uint32_t seqno, + uint32_t mask, + struct vmw_fence_obj **p_fence, + uint32_t *p_handle) +{ + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_user_fence *ufence; + struct vmw_fence_obj *tmp; + struct ttm_mem_global *mem_glob = vmw_mem_glob(fman->dev_priv); + int ret; + + /* + * Kernel memory space accounting, since this object may + * be created by a user-space request. + */ + + ret = ttm_mem_global_alloc(mem_glob, fman->user_fence_size, + false, false); + if (unlikely(ret != 0)) + return ret; + + ufence = kzalloc(sizeof(*ufence), GFP_KERNEL); + if (unlikely(ufence == NULL)) { + ret = -ENOMEM; + goto out_no_object; } - list_for_each_entry_safe(fence, next, &queue->head, head) { - if (signaled_sequence - fence->sequence > (1 << 30)) - continue; + ret = vmw_fence_obj_init(fman, &ufence->fence, seqno, + mask, vmw_user_fence_destroy); + if (unlikely(ret != 0)) { + kfree(ufence); + goto out_no_object; + } - queue->lag = timespec_sub(now, fence->submitted); - queue->lag_time = now; - updated = true; - list_del(&fence->head); - kfree(fence); + /* + * The base object holds a reference which is freed in + * vmw_user_fence_base_release. + */ + tmp = vmw_fence_obj_reference(&ufence->fence); + ret = ttm_base_object_init(tfile, &ufence->base, false, + VMW_RES_FENCE, + &vmw_user_fence_base_release, NULL); + + + if (unlikely(ret != 0)) { + /* + * Free the base object's reference + */ + vmw_fence_obj_unreference(&tmp); + goto out_err; } -out_unlock: - spin_unlock(&queue->lock); + *p_fence = &ufence->fence; + *p_handle = ufence->base.hash.key; + + return 0; +out_err: + tmp = &ufence->fence; + vmw_fence_obj_unreference(&tmp); +out_no_object: + ttm_mem_global_free(mem_glob, fman->user_fence_size); + return ret; +} + + +/** + * vmw_fence_fifo_down - signal all unsignaled fence objects. + */ + +void vmw_fence_fifo_down(struct vmw_fence_manager *fman) +{ + unsigned long irq_flags; + struct list_head action_list; + int ret; + + /* + * The list may be altered while we traverse it, so always + * restart when we've released the fman->lock. + */ + + spin_lock_irqsave(&fman->lock, irq_flags); + fman->fifo_down = true; + while (!list_empty(&fman->fence_list)) { + struct vmw_fence_obj *fence = + list_entry(fman->fence_list.prev, struct vmw_fence_obj, + head); + kref_get(&fence->kref); + spin_unlock_irq(&fman->lock); + + ret = vmw_fence_obj_wait(fence, fence->signal_mask, + false, false, + VMW_FENCE_WAIT_TIMEOUT); + + if (unlikely(ret != 0)) { + list_del_init(&fence->head); + fence->signaled |= DRM_VMW_FENCE_FLAG_EXEC; + INIT_LIST_HEAD(&action_list); + list_splice_init(&fence->seq_passed_actions, + &action_list); + vmw_fences_perform_actions(fman, &action_list); + wake_up_all(&fence->queue); + } + + spin_lock_irq(&fman->lock); + + BUG_ON(!list_empty(&fence->head)); + kref_put(&fence->kref, vmw_fence_obj_destroy_locked); + } + spin_unlock_irqrestore(&fman->lock, irq_flags); +} + +void vmw_fence_fifo_up(struct vmw_fence_manager *fman) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&fman->lock, irq_flags); + fman->fifo_down = false; + spin_unlock_irqrestore(&fman->lock, irq_flags); +} + + +int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_fence_wait_arg *arg = + (struct drm_vmw_fence_wait_arg *)data; + unsigned long timeout; + struct ttm_base_object *base; + struct vmw_fence_obj *fence; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + int ret; + uint64_t wait_timeout = ((uint64_t)arg->timeout_us * HZ); + + /* + * 64-bit division not present on 32-bit systems, so do an + * approximation. (Divide by 1000000). + */ + + wait_timeout = (wait_timeout >> 20) + (wait_timeout >> 24) - + (wait_timeout >> 26); + + if (!arg->cookie_valid) { + arg->cookie_valid = 1; + arg->kernel_cookie = jiffies + wait_timeout; + } - return (updated) ? 0 : -EBUSY; + base = ttm_base_object_lookup(tfile, arg->handle); + if (unlikely(base == NULL)) { + printk(KERN_ERR "Wait invalid fence object handle " + "0x%08lx.\n", + (unsigned long)arg->handle); + return -EINVAL; + } + + fence = &(container_of(base, struct vmw_user_fence, base)->fence); + + timeout = jiffies; + if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) { + ret = ((vmw_fence_obj_signaled(fence, arg->flags)) ? + 0 : -EBUSY); + goto out; + } + + timeout = (unsigned long)arg->kernel_cookie - timeout; + + ret = vmw_fence_obj_wait(fence, arg->flags, arg->lazy, true, timeout); + +out: + ttm_base_object_unref(&base); + + /* + * Optionally unref the fence object. + */ + + if (ret == 0 && (arg->wait_options & DRM_VMW_WAIT_OPTION_UNREF)) + return ttm_ref_object_base_unref(tfile, arg->handle, + TTM_REF_USAGE); + return ret; } -static struct timespec vmw_timespec_add(struct timespec t1, - struct timespec t2) +int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) { - t1.tv_sec += t2.tv_sec; - t1.tv_nsec += t2.tv_nsec; - if (t1.tv_nsec >= 1000000000L) { - t1.tv_sec += 1; - t1.tv_nsec -= 1000000000L; + struct drm_vmw_fence_signaled_arg *arg = + (struct drm_vmw_fence_signaled_arg *) data; + struct ttm_base_object *base; + struct vmw_fence_obj *fence; + struct vmw_fence_manager *fman; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_private *dev_priv = vmw_priv(dev); + + base = ttm_base_object_lookup(tfile, arg->handle); + if (unlikely(base == NULL)) { + printk(KERN_ERR "Fence signaled invalid fence object handle " + "0x%08lx.\n", + (unsigned long)arg->handle); + return -EINVAL; } - return t1; + fence = &(container_of(base, struct vmw_user_fence, base)->fence); + fman = fence->fman; + + arg->signaled = vmw_fence_obj_signaled(fence, arg->flags); + spin_lock_irq(&fman->lock); + + arg->signaled_flags = fence->signaled; + arg->passed_seqno = dev_priv->last_read_seqno; + spin_unlock_irq(&fman->lock); + + ttm_base_object_unref(&base); + + return 0; +} + + +int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_fence_arg *arg = + (struct drm_vmw_fence_arg *) data; + + return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + arg->handle, + TTM_REF_USAGE); } -static struct timespec vmw_fifo_lag(struct vmw_fence_queue *queue) +/** + * vmw_event_fence_action_destroy + * + * @kref: The struct kref embedded in a struct vmw_event_fence_action. + * + * The vmw_event_fence_action destructor that may be called either after + * the fence action cleanup, or when the event is delivered. + * It frees both the vmw_event_fence_action struct and the actual + * event structure copied to user-space. + */ +static void vmw_event_fence_action_destroy(struct kref *kref) { - struct timespec now; + struct vmw_event_fence_action *eaction = + container_of(kref, struct vmw_event_fence_action, kref); + struct ttm_mem_global *mem_glob = + vmw_mem_glob(vmw_priv(eaction->dev)); + uint32_t size = eaction->size; - spin_lock(&queue->lock); - getrawmonotonic(&now); - queue->lag = vmw_timespec_add(queue->lag, - timespec_sub(now, queue->lag_time)); - queue->lag_time = now; - spin_unlock(&queue->lock); - return queue->lag; + kfree(eaction->e.event); + kfree(eaction); + ttm_mem_global_free(mem_glob, size); } -static bool vmw_lag_lt(struct vmw_fence_queue *queue, - uint32_t us) +/** + * vmw_event_fence_action_delivered + * + * @e: The struct drm_pending_event embedded in a struct + * vmw_event_fence_action. + * + * The struct drm_pending_event destructor that is called by drm + * once the event is delivered. Since we don't know whether this function + * will be called before or after the fence action destructor, we + * free a refcount and destroy if it becomes zero. + */ +static void vmw_event_fence_action_delivered(struct drm_pending_event *e) { - struct timespec lag, cond; + struct vmw_event_fence_action *eaction = + container_of(e, struct vmw_event_fence_action, e); - cond = ns_to_timespec((s64) us * 1000); - lag = vmw_fifo_lag(queue); - return (timespec_compare(&lag, &cond) < 1); + kref_put(&eaction->kref, vmw_event_fence_action_destroy); } -int vmw_wait_lag(struct vmw_private *dev_priv, - struct vmw_fence_queue *queue, uint32_t us) + +/** + * vmw_event_fence_action_seq_passed + * + * @action: The struct vmw_fence_action embedded in a struct + * vmw_event_fence_action. + * + * This function is called when the seqno of the fence where @action is + * attached has passed. It queues the event on the submitter's event list. + * This function is always called from atomic context, and may be called + * from irq context. It ups a refcount reflecting that we now have two + * destructors. + */ +static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action) { - struct vmw_fence *fence; - uint32_t sequence; - int ret; + struct vmw_event_fence_action *eaction = + container_of(action, struct vmw_event_fence_action, action); + struct drm_device *dev = eaction->dev; + struct drm_file *file_priv = eaction->e.file_priv; + unsigned long irq_flags; - while (!vmw_lag_lt(queue, us)) { - spin_lock(&queue->lock); - if (list_empty(&queue->head)) - sequence = atomic_read(&dev_priv->fence_seq); - else { - fence = list_first_entry(&queue->head, - struct vmw_fence, head); - sequence = fence->sequence; + kref_get(&eaction->kref); + spin_lock_irqsave(&dev->event_lock, irq_flags); + + if (likely(eaction->tv_sec != NULL)) { + struct timeval tv; + + do_gettimeofday(&tv); + *eaction->tv_sec = tv.tv_sec; + *eaction->tv_usec = tv.tv_usec; + } + + list_add_tail(&eaction->e.link, &file_priv->event_list); + wake_up_all(&file_priv->event_wait); + spin_unlock_irqrestore(&dev->event_lock, irq_flags); +} + +/** + * vmw_event_fence_action_cleanup + * + * @action: The struct vmw_fence_action embedded in a struct + * vmw_event_fence_action. + * + * This function is the struct vmw_fence_action destructor. It's typically + * called from a workqueue. + */ +static void vmw_event_fence_action_cleanup(struct vmw_fence_action *action) +{ + struct vmw_event_fence_action *eaction = + container_of(action, struct vmw_event_fence_action, action); + + vmw_fence_obj_unreference(&eaction->fence); + kref_put(&eaction->kref, vmw_event_fence_action_destroy); +} + + +/** + * vmw_fence_obj_add_action - Add an action to a fence object. + * + * @fence - The fence object. + * @action - The action to add. + * + * Note that the action callbacks may be executed before this function + * returns. + */ +void vmw_fence_obj_add_action(struct vmw_fence_obj *fence, + struct vmw_fence_action *action) +{ + struct vmw_fence_manager *fman = fence->fman; + unsigned long irq_flags; + bool run_update = false; + + mutex_lock(&fman->goal_irq_mutex); + spin_lock_irqsave(&fman->lock, irq_flags); + + fman->pending_actions[action->type]++; + if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC) { + struct list_head action_list; + + INIT_LIST_HEAD(&action_list); + list_add_tail(&action->head, &action_list); + vmw_fences_perform_actions(fman, &action_list); + } else { + list_add_tail(&action->head, &fence->seq_passed_actions); + + /* + * This function may set fman::seqno_valid, so it must + * be run with the goal_irq_mutex held. + */ + run_update = vmw_fence_goal_check_locked(fence); + } + + spin_unlock_irqrestore(&fman->lock, irq_flags); + + if (run_update) { + if (!fman->goal_irq_on) { + fman->goal_irq_on = true; + vmw_goal_waiter_add(fman->dev_priv); } - spin_unlock(&queue->lock); + vmw_fences_update(fman); + } + mutex_unlock(&fman->goal_irq_mutex); + +} - ret = vmw_wait_fence(dev_priv, false, sequence, true, - 3*HZ); +/** + * vmw_event_fence_action_create - Post an event for sending when a fence + * object seqno has passed. + * + * @file_priv: The file connection on which the event should be posted. + * @fence: The fence object on which to post the event. + * @event: Event to be posted. This event should've been alloced + * using k[mz]alloc, and should've been completely initialized. + * @interruptible: Interruptible waits if possible. + * + * As a side effect, the object pointed to by @event may have been + * freed when this function returns. If this function returns with + * an error code, the caller needs to free that object. + */ - if (unlikely(ret != 0)) - return ret; +int vmw_event_fence_action_create(struct drm_file *file_priv, + struct vmw_fence_obj *fence, + struct drm_event *event, + uint32_t *tv_sec, + uint32_t *tv_usec, + bool interruptible) +{ + struct vmw_event_fence_action *eaction; + struct ttm_mem_global *mem_glob = + vmw_mem_glob(fence->fman->dev_priv); + struct vmw_fence_manager *fman = fence->fman; + uint32_t size = fman->event_fence_action_size + + ttm_round_pot(event->length); + int ret; + + /* + * Account for internal structure size as well as the + * event size itself. + */ + + ret = ttm_mem_global_alloc(mem_glob, size, false, interruptible); + if (unlikely(ret != 0)) + return ret; - (void) vmw_fence_pull(queue, sequence); + eaction = kzalloc(sizeof(*eaction), GFP_KERNEL); + if (unlikely(eaction == NULL)) { + ttm_mem_global_free(mem_glob, size); + return -ENOMEM; } + + eaction->e.event = event; + eaction->e.file_priv = file_priv; + eaction->e.destroy = vmw_event_fence_action_delivered; + + eaction->action.seq_passed = vmw_event_fence_action_seq_passed; + eaction->action.cleanup = vmw_event_fence_action_cleanup; + eaction->action.type = VMW_ACTION_EVENT; + + eaction->fence = vmw_fence_obj_reference(fence); + eaction->dev = fman->dev_priv->dev; + eaction->size = size; + eaction->tv_sec = tv_sec; + eaction->tv_usec = tv_usec; + + kref_init(&eaction->kref); + vmw_fence_obj_add_action(fence, &eaction->action); + return 0; } +int vmw_fence_event_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_fence_event_arg *arg = + (struct drm_vmw_fence_event_arg *) data; + struct vmw_fence_obj *fence = NULL; + struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + struct drm_vmw_fence_rep __user *user_fence_rep = + (struct drm_vmw_fence_rep __user *)(unsigned long) + arg->fence_rep; + uint32_t handle; + unsigned long irq_flags; + struct drm_vmw_event_fence *event; + int ret; + + /* + * Look up an existing fence object, + * and if user-space wants a new reference, + * add one. + */ + if (arg->handle) { + struct ttm_base_object *base = + ttm_base_object_lookup(vmw_fp->tfile, arg->handle); + if (unlikely(base == NULL)) { + DRM_ERROR("Fence event invalid fence object handle " + "0x%08lx.\n", + (unsigned long)arg->handle); + return -EINVAL; + } + fence = &(container_of(base, struct vmw_user_fence, + base)->fence); + (void) vmw_fence_obj_reference(fence); + + if (user_fence_rep != NULL) { + bool existed; + + ret = ttm_ref_object_add(vmw_fp->tfile, base, + TTM_REF_USAGE, &existed); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to reference a fence " + "object.\n"); + goto out_no_ref_obj; + } + handle = base->hash.key; + } + ttm_base_object_unref(&base); + } + + /* + * Create a new fence object. + */ + if (!fence) { + ret = vmw_execbuf_fence_commands(file_priv, dev_priv, + &fence, + (user_fence_rep) ? + &handle : NULL); + if (unlikely(ret != 0)) { + DRM_ERROR("Fence event failed to create fence.\n"); + return ret; + } + } + + BUG_ON(fence == NULL); + + spin_lock_irqsave(&dev->event_lock, irq_flags); + + ret = (file_priv->event_space < sizeof(*event)) ? -EBUSY : 0; + if (likely(ret == 0)) + file_priv->event_space -= sizeof(*event); + + spin_unlock_irqrestore(&dev->event_lock, irq_flags); + + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate event space for this file.\n"); + goto out_no_event_space; + } + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (unlikely(event == NULL)) { + DRM_ERROR("Failed to allocate an event.\n"); + goto out_no_event; + } + + event->base.type = DRM_VMW_EVENT_FENCE_SIGNALED; + event->base.length = sizeof(*event); + event->user_data = arg->user_data; + + if (arg->flags & DRM_VMW_FE_FLAG_REQ_TIME) + ret = vmw_event_fence_action_create(file_priv, fence, + &event->base, + &event->tv_sec, + &event->tv_usec, + true); + else + ret = vmw_event_fence_action_create(file_priv, fence, + &event->base, + NULL, + NULL, + true); + + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Failed to attach event to fence.\n"); + goto out_no_attach; + } + + vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence, + handle); + vmw_fence_obj_unreference(&fence); + return 0; +out_no_attach: + kfree(event); +out_no_event: + spin_lock_irqsave(&dev->event_lock, irq_flags); + file_priv->event_space += sizeof(*event); + spin_unlock_irqrestore(&dev->event_lock, irq_flags); +out_no_event_space: + if (user_fence_rep != NULL) + ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + handle, TTM_REF_USAGE); +out_no_ref_obj: + vmw_fence_obj_unreference(&fence); + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c index 635c0ff..decca82 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -33,6 +33,7 @@ bool vmw_fifo_have_3d(struct vmw_private *dev_priv) { __le32 __iomem *fifo_mem = dev_priv->mmio_virt; uint32_t fifo_min, hwversion; + const struct vmw_fifo_state *fifo = &dev_priv->fifo; if (!(dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)) return false; @@ -41,11 +42,20 @@ bool vmw_fifo_have_3d(struct vmw_private *dev_priv) if (fifo_min <= SVGA_FIFO_3D_HWVERSION * sizeof(unsigned int)) return false; - hwversion = ioread32(fifo_mem + SVGA_FIFO_3D_HWVERSION); + hwversion = ioread32(fifo_mem + + ((fifo->capabilities & + SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ? + SVGA_FIFO_3D_HWVERSION_REVISED : + SVGA_FIFO_3D_HWVERSION)); + if (hwversion == 0) return false; - if (hwversion < SVGA3D_HWVERSION_WS65_B1) + if (hwversion < SVGA3D_HWVERSION_WS8_B1) + return false; + + /* Non-Screen Object path does not support surfaces */ + if (!dev_priv->sou_priv) return false; return true; @@ -72,22 +82,12 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) uint32_t max; uint32_t min; uint32_t dummy; - int ret; fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE; fifo->static_buffer = vmalloc(fifo->static_buffer_size); if (unlikely(fifo->static_buffer == NULL)) return -ENOMEM; - fifo->last_buffer_size = VMWGFX_FIFO_STATIC_SIZE; - fifo->last_data_size = 0; - fifo->last_buffer_add = false; - fifo->last_buffer = vmalloc(fifo->last_buffer_size); - if (unlikely(fifo->last_buffer == NULL)) { - ret = -ENOMEM; - goto out_err; - } - fifo->dynamic_buffer = NULL; fifo->reserved_size = 0; fifo->using_bounce_buffer = false; @@ -137,14 +137,10 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) (unsigned int) min, (unsigned int) fifo->capabilities); - atomic_set(&dev_priv->fence_seq, dev_priv->last_read_sequence); - iowrite32(dev_priv->last_read_sequence, fifo_mem + SVGA_FIFO_FENCE); - vmw_fence_queue_init(&fifo->fence_queue); + atomic_set(&dev_priv->marker_seq, dev_priv->last_read_seqno); + iowrite32(dev_priv->last_read_seqno, fifo_mem + SVGA_FIFO_FENCE); + vmw_marker_queue_init(&fifo->marker_queue); return vmw_fifo_send_fence(dev_priv, &dummy); -out_err: - vfree(fifo->static_buffer); - fifo->static_buffer = NULL; - return ret; } void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason) @@ -167,10 +163,11 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) mutex_lock(&dev_priv->hw_mutex); + vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC); while (vmw_read(dev_priv, SVGA_REG_BUSY) != 0) - vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC); + ; - dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE); + dev_priv->last_read_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); vmw_write(dev_priv, SVGA_REG_CONFIG_DONE, dev_priv->config_done_state); @@ -180,12 +177,7 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) dev_priv->traces_state); mutex_unlock(&dev_priv->hw_mutex); - vmw_fence_queue_takedown(&fifo->fence_queue); - - if (likely(fifo->last_buffer != NULL)) { - vfree(fifo->last_buffer); - fifo->last_buffer = NULL; - } + vmw_marker_queue_takedown(&fifo->marker_queue); if (likely(fifo->static_buffer != NULL)) { vfree(fifo->static_buffer); @@ -262,9 +254,8 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); outl(SVGA_IRQFLAG_FIFO_PROGRESS, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); - vmw_write(dev_priv, SVGA_REG_IRQMASK, - vmw_read(dev_priv, SVGA_REG_IRQMASK) | - SVGA_IRQFLAG_FIFO_PROGRESS); + dev_priv->irq_mask |= SVGA_IRQFLAG_FIFO_PROGRESS; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } mutex_unlock(&dev_priv->hw_mutex); @@ -286,9 +277,8 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, mutex_lock(&dev_priv->hw_mutex); if (atomic_dec_and_test(&dev_priv->fifo_queue_waiters)) { spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); - vmw_write(dev_priv, SVGA_REG_IRQMASK, - vmw_read(dev_priv, SVGA_REG_IRQMASK) & - ~SVGA_IRQFLAG_FIFO_PROGRESS); + dev_priv->irq_mask &= ~SVGA_IRQFLAG_FIFO_PROGRESS; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } mutex_unlock(&dev_priv->hw_mutex); @@ -296,6 +286,16 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, return ret; } +/** + * Reserve @bytes number of bytes in the fifo. + * + * This function will return NULL (error) on two conditions: + * If it timeouts waiting for fifo space, or if @bytes is larger than the + * available fifo space. + * + * Returns: + * Pointer to the fifo, or null on error (possible hardware hang). + */ void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes) { struct vmw_fifo_state *fifo_state = &dev_priv->fifo; @@ -466,7 +466,7 @@ void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes) mutex_unlock(&fifo_state->fifo_mutex); } -int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence) +int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *seqno) { struct vmw_fifo_state *fifo_state = &dev_priv->fifo; struct svga_fifo_cmd_fence *cmd_fence; @@ -476,16 +476,16 @@ int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence) fm = vmw_fifo_reserve(dev_priv, bytes); if (unlikely(fm == NULL)) { - *sequence = atomic_read(&dev_priv->fence_seq); + *seqno = atomic_read(&dev_priv->marker_seq); ret = -ENOMEM; - (void)vmw_fallback_wait(dev_priv, false, true, *sequence, + (void)vmw_fallback_wait(dev_priv, false, true, *seqno, false, 3*HZ); goto out_err; } do { - *sequence = atomic_add_return(1, &dev_priv->fence_seq); - } while (*sequence == 0); + *seqno = atomic_add_return(1, &dev_priv->marker_seq); + } while (*seqno == 0); if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE)) { @@ -502,61 +502,68 @@ int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence) cmd_fence = (struct svga_fifo_cmd_fence *) ((unsigned long)fm + sizeof(__le32)); - iowrite32(*sequence, &cmd_fence->fence); - fifo_state->last_buffer_add = true; + iowrite32(*seqno, &cmd_fence->fence); vmw_fifo_commit(dev_priv, bytes); - fifo_state->last_buffer_add = false; - (void) vmw_fence_push(&fifo_state->fence_queue, *sequence); - vmw_update_sequence(dev_priv, fifo_state); + (void) vmw_marker_push(&fifo_state->marker_queue, *seqno); + vmw_update_seqno(dev_priv, fifo_state); out_err: return ret; } /** - * Map the first page of the FIFO read-only to user-space. + * vmw_fifo_emit_dummy_query - emits a dummy query to the fifo. + * + * @dev_priv: The device private structure. + * @cid: The hardware context id used for the query. + * + * This function is used to emit a dummy occlusion query with + * no primitives rendered between query begin and query end. + * It's used to provide a query barrier, in order to know that when + * this query is finished, all preceding queries are also finished. + * + * A Query results structure should have been initialized at the start + * of the dev_priv->dummy_query_bo buffer object. And that buffer object + * must also be either reserved or pinned when this function is called. + * + * Returns -ENOMEM on failure to reserve fifo space. */ - -static int vmw_fifo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int vmw_fifo_emit_dummy_query(struct vmw_private *dev_priv, + uint32_t cid) { - int ret; - unsigned long address = (unsigned long)vmf->virtual_address; + /* + * A query wait without a preceding query end will + * actually finish all queries for this cid + * without writing to the query result structure. + */ - if (address != vma->vm_start) - return VM_FAULT_SIGBUS; + struct ttm_buffer_object *bo = dev_priv->dummy_query_bo; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdWaitForQuery body; + } *cmd; - ret = vm_insert_pfn(vma, address, vma->vm_pgoff); - if (likely(ret == -EBUSY || ret == 0)) - return VM_FAULT_NOPAGE; - else if (ret == -ENOMEM) - return VM_FAULT_OOM; + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); - return VM_FAULT_SIGBUS; -} + if (unlikely(cmd == NULL)) { + DRM_ERROR("Out of fifo space for dummy query.\n"); + return -ENOMEM; + } -static struct vm_operations_struct vmw_fifo_vm_ops = { - .fault = vmw_fifo_vm_fault, - .open = NULL, - .close = NULL -}; + cmd->header.id = SVGA_3D_CMD_WAIT_FOR_QUERY; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = cid; + cmd->body.type = SVGA3D_QUERYTYPE_OCCLUSION; + + if (bo->mem.mem_type == TTM_PL_VRAM) { + cmd->body.guestResult.gmrId = SVGA_GMR_FRAMEBUFFER; + cmd->body.guestResult.offset = bo->offset; + } else { + cmd->body.guestResult.gmrId = bo->mem.start; + cmd->body.guestResult.offset = 0; + } + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); -int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct drm_file *file_priv; - struct vmw_private *dev_priv; - - file_priv = filp->private_data; - dev_priv = vmw_priv(file_priv->minor->dev); - - if (vma->vm_pgoff != (dev_priv->mmio_start >> PAGE_SHIFT) || - (vma->vm_end - vma->vm_start) != PAGE_SIZE) - return -EINVAL; - - vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE); - vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_SHARED; - vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); - vma->vm_page_prot = ttm_io_prot(TTM_PL_FLAG_UNCACHED, - vma->vm_page_prot); - vma->vm_ops = &vmw_fifo_vm_ops; return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index de0c594..2952249 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA + * Copyright © 2009-2011 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -29,6 +29,97 @@ #include "drmP.h" #include "ttm/ttm_bo_driver.h" +#define VMW_PPN_SIZE (sizeof(unsigned long)) +/* A future safe maximum remap size. */ +#define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) + +static int vmw_gmr2_bind(struct vmw_private *dev_priv, + struct page *pages[], + unsigned long num_pages, + int gmr_id) +{ + SVGAFifoCmdDefineGMR2 define_cmd; + SVGAFifoCmdRemapGMR2 remap_cmd; + uint32_t *cmd; + uint32_t *cmd_orig; + uint32_t define_size = sizeof(define_cmd) + sizeof(*cmd); + uint32_t remap_num = num_pages / VMW_PPN_PER_REMAP + ((num_pages % VMW_PPN_PER_REMAP) > 0); + uint32_t remap_size = VMW_PPN_SIZE * num_pages + (sizeof(remap_cmd) + sizeof(*cmd)) * remap_num; + uint32_t remap_pos = 0; + uint32_t cmd_size = define_size + remap_size; + uint32_t i; + + cmd_orig = cmd = vmw_fifo_reserve(dev_priv, cmd_size); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + define_cmd.gmrId = gmr_id; + define_cmd.numPages = num_pages; + + *cmd++ = SVGA_CMD_DEFINE_GMR2; + memcpy(cmd, &define_cmd, sizeof(define_cmd)); + cmd += sizeof(define_cmd) / sizeof(*cmd); + + /* + * Need to split the command if there are too many + * pages that goes into the gmr. + */ + + remap_cmd.gmrId = gmr_id; + remap_cmd.flags = (VMW_PPN_SIZE > sizeof(*cmd)) ? + SVGA_REMAP_GMR2_PPN64 : SVGA_REMAP_GMR2_PPN32; + + while (num_pages > 0) { + unsigned long nr = min(num_pages, (unsigned long)VMW_PPN_PER_REMAP); + + remap_cmd.offsetPages = remap_pos; + remap_cmd.numPages = nr; + + *cmd++ = SVGA_CMD_REMAP_GMR2; + memcpy(cmd, &remap_cmd, sizeof(remap_cmd)); + cmd += sizeof(remap_cmd) / sizeof(*cmd); + + for (i = 0; i < nr; ++i) { + if (VMW_PPN_SIZE <= 4) + *cmd = page_to_pfn(*pages++); + else + *((uint64_t *)cmd) = page_to_pfn(*pages++); + + cmd += VMW_PPN_SIZE / sizeof(*cmd); + } + + num_pages -= nr; + remap_pos += nr; + } + + BUG_ON(cmd != cmd_orig + cmd_size / sizeof(*cmd)); + + vmw_fifo_commit(dev_priv, cmd_size); + + return 0; +} + +static void vmw_gmr2_unbind(struct vmw_private *dev_priv, + int gmr_id) +{ + SVGAFifoCmdDefineGMR2 define_cmd; + uint32_t define_size = sizeof(define_cmd) + 4; + uint32_t *cmd; + + cmd = vmw_fifo_reserve(dev_priv, define_size); + if (unlikely(cmd == NULL)) { + DRM_ERROR("GMR2 unbind failed.\n"); + return; + } + define_cmd.gmrId = gmr_id; + define_cmd.numPages = 0; + + *cmd++ = SVGA_CMD_DEFINE_GMR2; + memcpy(cmd, &define_cmd, sizeof(define_cmd)); + + vmw_fifo_commit(dev_priv, define_size); +} + /** * FIXME: Adjust to the ttm lowmem / highmem storage to minimize * the number of used descriptors. @@ -170,6 +261,9 @@ int vmw_gmr_bind(struct vmw_private *dev_priv, struct list_head desc_pages; int ret; + if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) + return vmw_gmr2_bind(dev_priv, pages, num_pages, gmr_id); + if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR))) return -EINVAL; @@ -192,6 +286,11 @@ int vmw_gmr_bind(struct vmw_private *dev_priv, void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id) { + if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) { + vmw_gmr2_unbind(dev_priv, gmr_id); + return; + } + mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id); wmb(); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index ac6e0d1..5f71715 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -40,6 +40,8 @@ struct vmwgfx_gmrid_man { spinlock_t lock; struct ida gmr_ida; uint32_t max_gmr_ids; + uint32_t max_gmr_pages; + uint32_t used_gmr_pages; }; static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man, @@ -49,33 +51,50 @@ static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man, { struct vmwgfx_gmrid_man *gman = (struct vmwgfx_gmrid_man *)man->priv; - int ret; + int ret = 0; int id; mem->mm_node = NULL; - do { - if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0)) - return -ENOMEM; + spin_lock(&gman->lock); + + if (gman->max_gmr_pages > 0) { + gman->used_gmr_pages += bo->num_pages; + if (unlikely(gman->used_gmr_pages > gman->max_gmr_pages)) + goto out_err_locked; + } + do { + spin_unlock(&gman->lock); + if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0)) { + ret = -ENOMEM; + goto out_err; + } spin_lock(&gman->lock); - ret = ida_get_new(&gman->gmr_ida, &id); + ret = ida_get_new(&gman->gmr_ida, &id); if (unlikely(ret == 0 && id >= gman->max_gmr_ids)) { ida_remove(&gman->gmr_ida, id); - spin_unlock(&gman->lock); - return 0; + ret = 0; + goto out_err_locked; } - - spin_unlock(&gman->lock); - } while (ret == -EAGAIN); if (likely(ret == 0)) { mem->mm_node = gman; mem->start = id; - } + mem->num_pages = bo->num_pages; + } else + goto out_err_locked; + + spin_unlock(&gman->lock); + return 0; +out_err: + spin_lock(&gman->lock); +out_err_locked: + gman->used_gmr_pages -= bo->num_pages; + spin_unlock(&gman->lock); return ret; } @@ -88,6 +107,7 @@ static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man, if (mem->mm_node) { spin_lock(&gman->lock); ida_remove(&gman->gmr_ida, mem->start); + gman->used_gmr_pages -= mem->num_pages; spin_unlock(&gman->lock); mem->mm_node = NULL; } @@ -96,6 +116,8 @@ static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man, static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man, unsigned long p_size) { + struct vmw_private *dev_priv = + container_of(man->bdev, struct vmw_private, bdev); struct vmwgfx_gmrid_man *gman = kzalloc(sizeof(*gman), GFP_KERNEL); @@ -103,6 +125,8 @@ static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man, return -ENOMEM; spin_lock_init(&gman->lock); + gman->max_gmr_pages = dev_priv->max_gmr_pages; + gman->used_gmr_pages = 0; ida_init(&gman->gmr_ida); gman->max_gmr_ids = p_size; man->priv = (void *) gman; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 570d577..66917c6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -27,6 +27,7 @@ #include "vmwgfx_drv.h" #include "vmwgfx_drm.h" +#include "vmwgfx_kms.h" int vmw_getparam_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -45,9 +46,6 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data, case DRM_VMW_PARAM_3D: param->value = vmw_fifo_have_3d(dev_priv) ? 1 : 0; break; - case DRM_VMW_PARAM_FIFO_OFFSET: - param->value = dev_priv->mmio_start; - break; case DRM_VMW_PARAM_HW_CAPS: param->value = dev_priv->capabilities; break; @@ -57,6 +55,19 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data, case DRM_VMW_PARAM_MAX_FB_SIZE: param->value = dev_priv->vram_size; break; + case DRM_VMW_PARAM_FIFO_HW_VERSION: + { + __le32 __iomem *fifo_mem = dev_priv->mmio_virt; + const struct vmw_fifo_state *fifo = &dev_priv->fifo; + + param->value = + ioread32(fifo_mem + + ((fifo->capabilities & + SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ? + SVGA_FIFO_3D_HWVERSION_REVISED : + SVGA_FIFO_3D_HWVERSION)); + break; + } default: DRM_ERROR("Illegal vmwgfx get param request: %d\n", param->param); @@ -66,25 +77,253 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data, return 0; } -int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data, + +int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct drm_vmw_get_3d_cap_arg *arg = + (struct drm_vmw_get_3d_cap_arg *) data; struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_fifo_state *fifo_state = &dev_priv->fifo; - struct drm_vmw_fifo_debug_arg *arg = - (struct drm_vmw_fifo_debug_arg *)data; - __le32 __user *buffer = (__le32 __user *) - (unsigned long)arg->debug_buffer; + uint32_t size; + __le32 __iomem *fifo_mem; + void __user *buffer = (void __user *)((unsigned long)(arg->buffer)); + void *bounce; + int ret; - if (unlikely(fifo_state->last_buffer == NULL)) + if (unlikely(arg->pad64 != 0)) { + DRM_ERROR("Illegal GET_3D_CAP argument.\n"); return -EINVAL; + } + + size = (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1) << 2; + + if (arg->max_size < size) + size = arg->max_size; + + bounce = vmalloc(size); + if (unlikely(bounce == NULL)) { + DRM_ERROR("Failed to allocate bounce buffer for 3D caps.\n"); + return -ENOMEM; + } + + fifo_mem = dev_priv->mmio_virt; + memcpy_fromio(bounce, &fifo_mem[SVGA_FIFO_3D_CAPS], size); + + ret = copy_to_user(buffer, bounce, size); + vfree(bounce); + + if (unlikely(ret != 0)) + DRM_ERROR("Failed to report 3D caps info.\n"); + + return ret; +} + +int vmw_present_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_present_arg *arg = + (struct drm_vmw_present_arg *)data; + struct vmw_surface *surface; + struct vmw_master *vmaster = vmw_master(file_priv->master); + struct drm_vmw_rect __user *clips_ptr; + struct drm_vmw_rect *clips = NULL; + struct drm_mode_object *obj; + struct vmw_framebuffer *vfb; + uint32_t num_clips; + int ret; + + num_clips = arg->num_clips; + clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; + + if (unlikely(num_clips == 0)) + return 0; + + if (clips_ptr == NULL) { + DRM_ERROR("Variable clips_ptr must be specified.\n"); + ret = -EINVAL; + goto out_clips; + } + + clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); + if (clips == NULL) { + DRM_ERROR("Failed to allocate clip rect list.\n"); + ret = -ENOMEM; + goto out_clips; + } + + ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); + if (ret) { + DRM_ERROR("Failed to copy clip rects from userspace.\n"); + ret = -EFAULT; + goto out_no_copy; + } + + ret = mutex_lock_interruptible(&dev->mode_config.mutex); + if (unlikely(ret != 0)) { + ret = -ERESTARTSYS; + goto out_no_mode_mutex; + } + + obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_ERROR("Invalid framebuffer id.\n"); + ret = -EINVAL; + goto out_no_fb; + } + vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + goto out_no_ttm_lock; + + ret = vmw_user_surface_lookup_handle(dev_priv, tfile, arg->sid, + &surface); + if (ret) + goto out_no_surface; + + ret = vmw_kms_present(dev_priv, file_priv, + vfb, surface, arg->sid, + arg->dest_x, arg->dest_y, + clips, num_clips); + + /* vmw_user_surface_lookup takes one ref so does new_fb */ + vmw_surface_unreference(&surface); - if (arg->debug_buffer_size < fifo_state->last_data_size) { - arg->used_size = arg->debug_buffer_size; - arg->did_not_fit = 1; - } else { - arg->used_size = fifo_state->last_data_size; - arg->did_not_fit = 0; +out_no_surface: + ttm_read_unlock(&vmaster->lock); +out_no_ttm_lock: +out_no_fb: + mutex_unlock(&dev->mode_config.mutex); +out_no_mode_mutex: +out_no_copy: + kfree(clips); +out_clips: + return ret; +} + +int vmw_present_readback_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_present_readback_arg *arg = + (struct drm_vmw_present_readback_arg *)data; + struct drm_vmw_fence_rep __user *user_fence_rep = + (struct drm_vmw_fence_rep __user *) + (unsigned long)arg->fence_rep; + struct vmw_master *vmaster = vmw_master(file_priv->master); + struct drm_vmw_rect __user *clips_ptr; + struct drm_vmw_rect *clips = NULL; + struct drm_mode_object *obj; + struct vmw_framebuffer *vfb; + uint32_t num_clips; + int ret; + + num_clips = arg->num_clips; + clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; + + if (unlikely(num_clips == 0)) + return 0; + + if (clips_ptr == NULL) { + DRM_ERROR("Argument clips_ptr must be specified.\n"); + ret = -EINVAL; + goto out_clips; + } + + clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); + if (clips == NULL) { + DRM_ERROR("Failed to allocate clip rect list.\n"); + ret = -ENOMEM; + goto out_clips; + } + + ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); + if (ret) { + DRM_ERROR("Failed to copy clip rects from userspace.\n"); + ret = -EFAULT; + goto out_no_copy; + } + + ret = mutex_lock_interruptible(&dev->mode_config.mutex); + if (unlikely(ret != 0)) { + ret = -ERESTARTSYS; + goto out_no_mode_mutex; + } + + obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_ERROR("Invalid framebuffer id.\n"); + ret = -EINVAL; + goto out_no_fb; + } + + vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + if (!vfb->dmabuf) { + DRM_ERROR("Framebuffer not dmabuf backed.\n"); + ret = -EINVAL; + goto out_no_fb; } - return copy_to_user(buffer, fifo_state->last_buffer, arg->used_size); + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + goto out_no_ttm_lock; + + ret = vmw_kms_readback(dev_priv, file_priv, + vfb, user_fence_rep, + clips, num_clips); + + ttm_read_unlock(&vmaster->lock); +out_no_ttm_lock: +out_no_fb: + mutex_unlock(&dev->mode_config.mutex); +out_no_mode_mutex: +out_no_copy: + kfree(clips); +out_clips: + return ret; +} + + +/** + * vmw_fops_poll - wrapper around the drm_poll function + * + * @filp: See the linux fops poll documentation. + * @wait: See the linux fops poll documentation. + * + * Wrapper around the drm_poll function that makes sure the device is + * processing the fifo if drm_poll decides to wait. + */ +unsigned int vmw_fops_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct drm_file *file_priv = filp->private_data; + struct vmw_private *dev_priv = + vmw_priv(file_priv->minor->dev); + + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); + return drm_poll(filp, wait); +} + + +/** + * vmw_fops_read - wrapper around the drm_read function + * + * @filp: See the linux fops read documentation. + * @buffer: See the linux fops read documentation. + * @count: See the linux fops read documentation. + * offset: See the linux fops read documentation. + * + * Wrapper around the drm_read function that makes sure the device is + * processing the fifo if drm_read decides to wait. + */ +ssize_t vmw_fops_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset) +{ + struct drm_file *file_priv = filp->private_data; + struct vmw_private *dev_priv = + vmw_priv(file_priv->minor->dev); + + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); + return drm_read(filp, buffer, count, offset); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c index e92298a..cabc95f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -34,26 +34,33 @@ irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *)arg; struct vmw_private *dev_priv = vmw_priv(dev); - uint32_t status; + uint32_t status, masked_status; spin_lock(&dev_priv->irq_lock); status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); + masked_status = status & dev_priv->irq_mask; spin_unlock(&dev_priv->irq_lock); - if (status & SVGA_IRQFLAG_ANY_FENCE) + if (likely(status)) + outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); + + if (!masked_status) + return IRQ_NONE; + + if (masked_status & (SVGA_IRQFLAG_ANY_FENCE | + SVGA_IRQFLAG_FENCE_GOAL)) { + vmw_fences_update(dev_priv->fman); wake_up_all(&dev_priv->fence_queue); - if (status & SVGA_IRQFLAG_FIFO_PROGRESS) + } + + if (masked_status & SVGA_IRQFLAG_FIFO_PROGRESS) wake_up_all(&dev_priv->fifo_queue); - if (likely(status)) { - outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); - return IRQ_HANDLED; - } - return IRQ_NONE; + return IRQ_HANDLED; } -static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence) +static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t seqno) { uint32_t busy; @@ -64,43 +71,43 @@ static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence) return (busy == 0); } -void vmw_update_sequence(struct vmw_private *dev_priv, +void vmw_update_seqno(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo_state) { __le32 __iomem *fifo_mem = dev_priv->mmio_virt; + uint32_t seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); - uint32_t sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE); - - if (dev_priv->last_read_sequence != sequence) { - dev_priv->last_read_sequence = sequence; - vmw_fence_pull(&fifo_state->fence_queue, sequence); + if (dev_priv->last_read_seqno != seqno) { + dev_priv->last_read_seqno = seqno; + vmw_marker_pull(&fifo_state->marker_queue, seqno); + vmw_fences_update(dev_priv->fman); } } -bool vmw_fence_signaled(struct vmw_private *dev_priv, - uint32_t sequence) +bool vmw_seqno_passed(struct vmw_private *dev_priv, + uint32_t seqno) { struct vmw_fifo_state *fifo_state; bool ret; - if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) + if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP)) return true; fifo_state = &dev_priv->fifo; - vmw_update_sequence(dev_priv, fifo_state); - if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) + vmw_update_seqno(dev_priv, fifo_state); + if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP)) return true; if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) && - vmw_fifo_idle(dev_priv, sequence)) + vmw_fifo_idle(dev_priv, seqno)) return true; /** - * Then check if the sequence is higher than what we've actually + * Then check if the seqno is higher than what we've actually * emitted. Then the fence is stale and signaled. */ - ret = ((atomic_read(&dev_priv->fence_seq) - sequence) + ret = ((atomic_read(&dev_priv->marker_seq) - seqno) > VMW_FENCE_WRAP); return ret; @@ -109,7 +116,7 @@ bool vmw_fence_signaled(struct vmw_private *dev_priv, int vmw_fallback_wait(struct vmw_private *dev_priv, bool lazy, bool fifo_idle, - uint32_t sequence, + uint32_t seqno, bool interruptible, unsigned long timeout) { @@ -123,7 +130,7 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, DEFINE_WAIT(__wait); wait_condition = (fifo_idle) ? &vmw_fifo_idle : - &vmw_fence_signaled; + &vmw_seqno_passed; /** * Block command submission while waiting for idle. @@ -131,14 +138,14 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, if (fifo_idle) down_read(&fifo_state->rwsem); - signal_seq = atomic_read(&dev_priv->fence_seq); + signal_seq = atomic_read(&dev_priv->marker_seq); ret = 0; for (;;) { prepare_to_wait(&dev_priv->fence_queue, &__wait, (interruptible) ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); - if (wait_condition(dev_priv, sequence)) + if (wait_condition(dev_priv, seqno)) break; if (time_after_eq(jiffies, end_jiffies)) { DRM_ERROR("SVGA device lockup.\n"); @@ -175,68 +182,110 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, return ret; } -int vmw_wait_fence(struct vmw_private *dev_priv, - bool lazy, uint32_t sequence, - bool interruptible, unsigned long timeout) +void vmw_seqno_waiter_add(struct vmw_private *dev_priv) +{ + mutex_lock(&dev_priv->hw_mutex); + if (dev_priv->fence_queue_waiters++ == 0) { + unsigned long irq_flags; + + spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); + outl(SVGA_IRQFLAG_ANY_FENCE, + dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); + dev_priv->irq_mask |= SVGA_IRQFLAG_ANY_FENCE; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); + spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); + } + mutex_unlock(&dev_priv->hw_mutex); +} + +void vmw_seqno_waiter_remove(struct vmw_private *dev_priv) +{ + mutex_lock(&dev_priv->hw_mutex); + if (--dev_priv->fence_queue_waiters == 0) { + unsigned long irq_flags; + + spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); + dev_priv->irq_mask &= ~SVGA_IRQFLAG_ANY_FENCE; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); + spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); + } + mutex_unlock(&dev_priv->hw_mutex); +} + + +void vmw_goal_waiter_add(struct vmw_private *dev_priv) +{ + mutex_lock(&dev_priv->hw_mutex); + if (dev_priv->goal_queue_waiters++ == 0) { + unsigned long irq_flags; + + spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); + outl(SVGA_IRQFLAG_FENCE_GOAL, + dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); + dev_priv->irq_mask |= SVGA_IRQFLAG_FENCE_GOAL; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); + spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); + } + mutex_unlock(&dev_priv->hw_mutex); +} + +void vmw_goal_waiter_remove(struct vmw_private *dev_priv) +{ + mutex_lock(&dev_priv->hw_mutex); + if (--dev_priv->goal_queue_waiters == 0) { + unsigned long irq_flags; + + spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); + dev_priv->irq_mask &= ~SVGA_IRQFLAG_FENCE_GOAL; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); + spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); + } + mutex_unlock(&dev_priv->hw_mutex); +} + +int vmw_wait_seqno(struct vmw_private *dev_priv, + bool lazy, uint32_t seqno, + bool interruptible, unsigned long timeout) { long ret; - unsigned long irq_flags; struct vmw_fifo_state *fifo = &dev_priv->fifo; - if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) + if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP)) return 0; - if (likely(vmw_fence_signaled(dev_priv, sequence))) + if (likely(vmw_seqno_passed(dev_priv, seqno))) return 0; vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE)) - return vmw_fallback_wait(dev_priv, lazy, true, sequence, + return vmw_fallback_wait(dev_priv, lazy, true, seqno, interruptible, timeout); if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) - return vmw_fallback_wait(dev_priv, lazy, false, sequence, + return vmw_fallback_wait(dev_priv, lazy, false, seqno, interruptible, timeout); - mutex_lock(&dev_priv->hw_mutex); - if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) { - spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); - outl(SVGA_IRQFLAG_ANY_FENCE, - dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); - vmw_write(dev_priv, SVGA_REG_IRQMASK, - vmw_read(dev_priv, SVGA_REG_IRQMASK) | - SVGA_IRQFLAG_ANY_FENCE); - spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); - } - mutex_unlock(&dev_priv->hw_mutex); + vmw_seqno_waiter_add(dev_priv); if (interruptible) ret = wait_event_interruptible_timeout (dev_priv->fence_queue, - vmw_fence_signaled(dev_priv, sequence), + vmw_seqno_passed(dev_priv, seqno), timeout); else ret = wait_event_timeout (dev_priv->fence_queue, - vmw_fence_signaled(dev_priv, sequence), + vmw_seqno_passed(dev_priv, seqno), timeout); + vmw_seqno_waiter_remove(dev_priv); + if (unlikely(ret == 0)) ret = -EBUSY; else if (likely(ret > 0)) ret = 0; - mutex_lock(&dev_priv->hw_mutex); - if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) { - spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); - vmw_write(dev_priv, SVGA_REG_IRQMASK, - vmw_read(dev_priv, SVGA_REG_IRQMASK) & - ~SVGA_IRQFLAG_ANY_FENCE); - spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); - } - mutex_unlock(&dev_priv->hw_mutex); - return ret; } @@ -273,25 +322,3 @@ void vmw_irq_uninstall(struct drm_device *dev) status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); } - -#define VMW_FENCE_WAIT_TIMEOUT 3*HZ; - -int vmw_fence_wait_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vmw_fence_wait_arg *arg = - (struct drm_vmw_fence_wait_arg *)data; - unsigned long timeout; - - if (!arg->cookie_valid) { - arg->cookie_valid = 1; - arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT; - } - - timeout = jiffies; - if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) - return -EBUSY; - - timeout = (unsigned long)arg->kernel_cookie - timeout; - return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout); -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8a38c91..eb9735e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -27,11 +27,47 @@ #include "vmwgfx_kms.h" + /* Might need a hrtimer here? */ #define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1) -static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb); -static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb); + +struct vmw_clip_rect { + int x1, x2, y1, y2; +}; + +/** + * Clip @num_rects number of @rects against @clip storing the + * results in @out_rects and the number of passed rects in @out_num. + */ +void vmw_clip_cliprects(struct drm_clip_rect *rects, + int num_rects, + struct vmw_clip_rect clip, + SVGASignedRect *out_rects, + int *out_num) +{ + int i, k; + + for (i = 0, k = 0; i < num_rects; i++) { + int x1 = max_t(int, clip.x1, rects[i].x1); + int y1 = max_t(int, clip.y1, rects[i].y1); + int x2 = min_t(int, clip.x2, rects[i].x2); + int y2 = min_t(int, clip.y2, rects[i].y2); + + if (x1 >= x2) + continue; + if (y1 >= y2) + continue; + + out_rects[k].left = x1; + out_rects[k].top = y1; + out_rects[k].right = x2; + out_rects[k].bottom = y2; + k++; + } + + *out_num = k; +} void vmw_display_unit_cleanup(struct vmw_display_unit *du) { @@ -84,6 +120,43 @@ int vmw_cursor_update_image(struct vmw_private *dev_priv, return 0; } +int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, + struct vmw_dma_buffer *dmabuf, + u32 width, u32 height, + u32 hotspotX, u32 hotspotY) +{ + struct ttm_bo_kmap_obj map; + unsigned long kmap_offset; + unsigned long kmap_num; + void *virtual; + bool dummy; + int ret; + + kmap_offset = 0; + kmap_num = (width*height*4 + PAGE_SIZE - 1) >> PAGE_SHIFT; + + ret = ttm_bo_reserve(&dmabuf->base, true, false, false, 0); + if (unlikely(ret != 0)) { + DRM_ERROR("reserve failed\n"); + return -EINVAL; + } + + ret = ttm_bo_kmap(&dmabuf->base, kmap_offset, kmap_num, &map); + if (unlikely(ret != 0)) + goto err_unreserve; + + virtual = ttm_kmap_obj_virtual(&map, &dummy); + ret = vmw_cursor_update_image(dev_priv, virtual, width, height, + hotspotX, hotspotY); + + ttm_bo_kunmap(&map); +err_unreserve: + ttm_bo_unreserve(&dmabuf->base); + + return ret; +} + + void vmw_cursor_update_position(struct vmw_private *dev_priv, bool show, int x, int y) { @@ -107,24 +180,26 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, struct vmw_dma_buffer *dmabuf = NULL; int ret; + /* A lot of the code assumes this */ + if (handle && (width != 64 || height != 64)) + return -EINVAL; + if (handle) { - ret = vmw_user_surface_lookup_handle(dev_priv, tfile, - handle, &surface); - if (!ret) { - if (!surface->snooper.image) { - DRM_ERROR("surface not suitable for cursor\n"); - return -EINVAL; - } - } else { - ret = vmw_user_dmabuf_lookup(tfile, - handle, &dmabuf); - if (ret) { - DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); - return -EINVAL; - } + ret = vmw_user_lookup_handle(dev_priv, tfile, + handle, &surface, &dmabuf); + if (ret) { + DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); + return -EINVAL; } } + /* need to do this before taking down old image */ + if (surface && !surface->snooper.image) { + DRM_ERROR("surface not suitable for cursor\n"); + vmw_surface_unreference(&surface); + return -EINVAL; + } + /* takedown old cursor */ if (du->cursor_surface) { du->cursor_surface->snooper.crtc = NULL; @@ -143,42 +218,19 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, vmw_cursor_update_image(dev_priv, surface->snooper.image, 64, 64, du->hotspot_x, du->hotspot_y); } else if (dmabuf) { - struct ttm_bo_kmap_obj map; - unsigned long kmap_offset; - unsigned long kmap_num; - void *virtual; - bool dummy; - /* vmw_user_surface_lookup takes one reference */ du->cursor_dmabuf = dmabuf; - kmap_offset = 0; - kmap_num = (64*64*4) >> PAGE_SHIFT; - - ret = ttm_bo_reserve(&dmabuf->base, true, false, false, 0); - if (unlikely(ret != 0)) { - DRM_ERROR("reserve failed\n"); - return -EINVAL; - } - - ret = ttm_bo_kmap(&dmabuf->base, kmap_offset, kmap_num, &map); - if (unlikely(ret != 0)) - goto err_unreserve; - - virtual = ttm_kmap_obj_virtual(&map, &dummy); - vmw_cursor_update_image(dev_priv, virtual, 64, 64, - du->hotspot_x, du->hotspot_y); - - ttm_bo_kunmap(&map); -err_unreserve: - ttm_bo_unreserve(&dmabuf->base); - + ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, width, height, + du->hotspot_x, du->hotspot_y); } else { vmw_cursor_update_position(dev_priv, false, 0, 0); return 0; } - vmw_cursor_update_position(dev_priv, true, du->cursor_x, du->cursor_y); + vmw_cursor_update_position(dev_priv, true, + du->cursor_x + du->hotspot_x, + du->cursor_y + du->hotspot_y); return 0; } @@ -193,7 +245,8 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) du->cursor_y = y + crtc->y; vmw_cursor_update_position(dev_priv, shown, - du->cursor_x, du->cursor_y); + du->cursor_x + du->hotspot_x, + du->cursor_y + du->hotspot_y); return 0; } @@ -214,7 +267,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, SVGA3dCmdHeader header; SVGA3dCmdSurfaceDMA dma; } *cmd; - int ret; + int i, ret; cmd = container_of(header, struct vmw_dma_cmd, header); @@ -236,16 +289,19 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) / sizeof(SVGA3dCopyBox); - if (cmd->dma.guest.pitch != (64 * 4) || - cmd->dma.guest.ptr.offset % PAGE_SIZE || + if (cmd->dma.guest.ptr.offset % PAGE_SIZE || box->x != 0 || box->y != 0 || box->z != 0 || box->srcx != 0 || box->srcy != 0 || box->srcz != 0 || - box->w != 64 || box->h != 64 || box->d != 1 || - box_count != 1) { + box->d != 1 || box_count != 1) { /* TODO handle none page aligned offsets */ - /* TODO handle partial uploads and pitch != 256 */ - /* TODO handle more then one copy (size != 64) */ - DRM_ERROR("lazy programmer, can't handle weird stuff\n"); + /* TODO handle more dst & src != 0 */ + /* TODO handle more then one copy */ + DRM_ERROR("Cant snoop dma request for cursor!\n"); + DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n", + box->srcx, box->srcy, box->srcz, + box->x, box->y, box->z, + box->w, box->h, box->d, box_count, + cmd->dma.guest.ptr.offset); return; } @@ -264,7 +320,16 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, virtual = ttm_kmap_obj_virtual(&map, &dummy); - memcpy(srf->snooper.image, virtual, 64*64*4); + if (box->w == 64 && cmd->dma.guest.pitch == 64*4) { + memcpy(srf->snooper.image, virtual, 64*64*4); + } else { + /* Image is unsigned pointer. */ + for (i = 0; i < box->h; i++) + memcpy(srf->snooper.image + i * 64, + virtual + i * cmd->dma.guest.pitch, + box->w * 4); + } + srf->snooper.age++; /* we can't call this function from this function since execbuf has @@ -329,41 +394,10 @@ struct vmw_framebuffer_surface { struct vmw_framebuffer base; struct vmw_surface *surface; struct vmw_dma_buffer *buffer; - struct delayed_work d_work; - struct mutex work_lock; - bool present_fs; struct list_head head; struct drm_master *master; }; -/** - * vmw_kms_idle_workqueues - Flush workqueues on this master - * - * @vmaster - Pointer identifying the master, for the surfaces of which - * we idle the dirty work queues. - * - * This function should be called with the ttm lock held in exclusive mode - * to idle all dirty work queues before the fifo is taken down. - * - * The work task may actually requeue itself, but after the flush returns we're - * sure that there's nothing to present, since the ttm lock is held in - * exclusive mode, so the fifo will never get used. - */ - -void vmw_kms_idle_workqueues(struct vmw_master *vmaster) -{ - struct vmw_framebuffer_surface *entry; - - mutex_lock(&vmaster->fb_surf_mutex); - list_for_each_entry(entry, &vmaster->fb_surf, head) { - if (cancel_delayed_work_sync(&entry->d_work)) - (void) entry->d_work.work.func(&entry->d_work.work); - - (void) cancel_delayed_work_sync(&entry->d_work); - } - mutex_unlock(&vmaster->fb_surf_mutex); -} - void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) { struct vmw_framebuffer_surface *vfbs = @@ -375,65 +409,158 @@ void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) list_del(&vfbs->head); mutex_unlock(&vmaster->fb_surf_mutex); - cancel_delayed_work_sync(&vfbs->d_work); drm_master_put(&vfbs->master); drm_framebuffer_cleanup(framebuffer); vmw_surface_unreference(&vfbs->surface); + ttm_base_object_unref(&vfbs->base.user_obj); kfree(vfbs); } -static void vmw_framebuffer_present_fs_callback(struct work_struct *work) +static int do_surface_dirty_sou(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *framebuffer, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int inc) { - struct delayed_work *d_work = - container_of(work, struct delayed_work, work); - struct vmw_framebuffer_surface *vfbs = - container_of(d_work, struct vmw_framebuffer_surface, d_work); - struct vmw_surface *surf = vfbs->surface; - struct drm_framebuffer *framebuffer = &vfbs->base.base; - struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_clip_rect *clips_ptr; + struct drm_clip_rect *tmp; + struct drm_crtc *crtc; + size_t fifo_size; + int i, num_units; + int ret = 0; /* silence warning */ + int left, right, top, bottom; struct { SVGA3dCmdHeader header; - SVGA3dCmdPresent body; - SVGA3dCopyRect cr; + SVGA3dCmdBlitSurfaceToScreen body; } *cmd; + SVGASignedRect *blits; - /** - * Strictly we should take the ttm_lock in read mode before accessing - * the fifo, to make sure the fifo is present and up. However, - * instead we flush all workqueues under the ttm lock in exclusive mode - * before taking down the fifo. - */ - mutex_lock(&vfbs->work_lock); - if (!vfbs->present_fs) - goto out_unlock; + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, + head) { + if (crtc->fb != &framebuffer->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } - cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); - if (unlikely(cmd == NULL)) - goto out_resched; - - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT); - cmd->header.size = cpu_to_le32(sizeof(cmd->body) + sizeof(cmd->cr)); - cmd->body.sid = cpu_to_le32(surf->res.id); - cmd->cr.x = cpu_to_le32(0); - cmd->cr.y = cpu_to_le32(0); - cmd->cr.srcx = cmd->cr.x; - cmd->cr.srcy = cmd->cr.y; - cmd->cr.w = cpu_to_le32(framebuffer->width); - cmd->cr.h = cpu_to_le32(framebuffer->height); - vfbs->present_fs = false; - vmw_fifo_commit(dev_priv, sizeof(*cmd)); -out_resched: - /** - * Will not re-add if already pending. - */ - schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE); -out_unlock: - mutex_unlock(&vfbs->work_lock); -} + BUG_ON(!clips || !num_clips); + + tmp = kzalloc(sizeof(*tmp) * num_clips, GFP_KERNEL); + if (unlikely(tmp == NULL)) { + DRM_ERROR("Temporary cliprect memory alloc failed.\n"); + return -ENOMEM; + } + + fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips; + cmd = kzalloc(fifo_size, GFP_KERNEL); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Temporary fifo memory alloc failed.\n"); + ret = -ENOMEM; + goto out_free_tmp; + } + + /* setup blits pointer */ + blits = (SVGASignedRect *)&cmd[1]; + + /* initial clip region */ + left = clips->x1; + right = clips->x2; + top = clips->y1; + bottom = clips->y2; + + /* skip the first clip rect */ + for (i = 1, clips_ptr = clips + inc; + i < num_clips; i++, clips_ptr += inc) { + left = min_t(int, left, (int)clips_ptr->x1); + right = max_t(int, right, (int)clips_ptr->x2); + top = min_t(int, top, (int)clips_ptr->y1); + bottom = max_t(int, bottom, (int)clips_ptr->y2); + } + + /* only need to do this once */ + memset(cmd, 0, fifo_size); + cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); + cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); + + cmd->body.srcRect.left = left; + cmd->body.srcRect.right = right; + cmd->body.srcRect.top = top; + cmd->body.srcRect.bottom = bottom; + + clips_ptr = clips; + for (i = 0; i < num_clips; i++, clips_ptr += inc) { + tmp[i].x1 = clips_ptr->x1 - left; + tmp[i].x2 = clips_ptr->x2 - left; + tmp[i].y1 = clips_ptr->y1 - top; + tmp[i].y2 = clips_ptr->y2 - top; + } + + /* do per unit writing, reuse fifo for each */ + for (i = 0; i < num_units; i++) { + struct vmw_display_unit *unit = units[i]; + struct vmw_clip_rect clip; + int num; + + clip.x1 = left - unit->crtc.x; + clip.y1 = top - unit->crtc.y; + clip.x2 = right - unit->crtc.x; + clip.y2 = bottom - unit->crtc.y; + + /* skip any crtcs that misses the clip region */ + if (clip.x1 >= unit->crtc.mode.hdisplay || + clip.y1 >= unit->crtc.mode.vdisplay || + clip.x2 <= 0 || clip.y2 <= 0) + continue; + + /* + * In order for the clip rects to be correctly scaled + * the src and dest rects needs to be the same size. + */ + cmd->body.destRect.left = clip.x1; + cmd->body.destRect.right = clip.x2; + cmd->body.destRect.top = clip.y1; + cmd->body.destRect.bottom = clip.y2; + + /* create a clip rect of the crtc in dest coords */ + clip.x2 = unit->crtc.mode.hdisplay - clip.x1; + clip.y2 = unit->crtc.mode.vdisplay - clip.y1; + clip.x1 = 0 - clip.x1; + clip.y1 = 0 - clip.y1; + + /* need to reset sid as it is changed by execbuf */ + cmd->body.srcImage.sid = cpu_to_le32(framebuffer->user_handle); + cmd->body.destScreenId = unit->unit; + + /* clip and write blits to cmd stream */ + vmw_clip_cliprects(tmp, num_clips, clip, blits, &num); + + /* if no cliprects hit skip this */ + if (num == 0) + continue; + /* recalculate package length */ + fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num; + cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + } + + + kfree(cmd); +out_free_tmp: + kfree(tmp); + + return ret; +} + int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, struct drm_file *file_priv, unsigned flags, unsigned color, @@ -444,44 +571,20 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, struct vmw_master *vmaster = vmw_master(file_priv->master); struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(framebuffer); - struct vmw_surface *surf = vfbs->surface; struct drm_clip_rect norect; - SVGA3dCopyRect *cr; - int i, inc = 1; - int ret; - - struct { - SVGA3dCmdHeader header; - SVGA3dCmdPresent body; - SVGA3dCopyRect cr; - } *cmd; + int ret, inc = 1; if (unlikely(vfbs->master != file_priv->master)) return -EINVAL; + /* Require ScreenObject support for 3D */ + if (!dev_priv->sou_priv) + return -EINVAL; + ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) return ret; - if (!num_clips || - !(dev_priv->fifo.capabilities & - SVGA_FIFO_CAP_SCREEN_OBJECT)) { - int ret; - - mutex_lock(&vfbs->work_lock); - vfbs->present_fs = true; - ret = schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE); - mutex_unlock(&vfbs->work_lock); - if (ret) { - /** - * No work pending, Force immediate present. - */ - vmw_framebuffer_present_fs_callback(&vfbs->d_work.work); - } - ttm_read_unlock(&vmaster->lock); - return 0; - } - if (!num_clips) { num_clips = 1; clips = &norect; @@ -493,29 +596,10 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, inc = 2; /* skip source rects */ } - cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Fifo reserve failed.\n"); - ttm_read_unlock(&vmaster->lock); - return -ENOMEM; - } - - memset(cmd, 0, sizeof(*cmd)); - - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT); - cmd->header.size = cpu_to_le32(sizeof(cmd->body) + num_clips * sizeof(cmd->cr)); - cmd->body.sid = cpu_to_le32(surf->res.id); - - for (i = 0, cr = &cmd->cr; i < num_clips; i++, cr++, clips += inc) { - cr->x = cpu_to_le16(clips->x1); - cr->y = cpu_to_le16(clips->y1); - cr->srcx = cr->x; - cr->srcy = cr->y; - cr->w = cpu_to_le16(clips->x2 - clips->x1); - cr->h = cpu_to_le16(clips->y2 - clips->y1); - } + ret = do_surface_dirty_sou(dev_priv, file_priv, &vfbs->base, + flags, color, + clips, num_clips, inc); - vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); ttm_read_unlock(&vmaster->lock); return 0; } @@ -540,10 +624,18 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; + /* 3D is only supported on HWv8 hosts which supports screen objects */ + if (!dev_priv->sou_priv) + return -ENOSYS; + /* * Sanity checks. */ + /* Surface must be marked as a scanout. */ + if (unlikely(!surface->scanout)) + return -EINVAL; + if (unlikely(surface->mip_levels[0] != 1 || surface->num_sizes != 1 || surface->sizes[0].width < mode_cmd->width || @@ -567,6 +659,9 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, case 15: format = SVGA3D_A1R5G5B5; break; + case 8: + format = SVGA3D_LUMINANCE8; + break; default: DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth); return -EINVAL; @@ -599,14 +694,11 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, vfbs->base.base.depth = mode_cmd->depth; vfbs->base.base.width = mode_cmd->width; vfbs->base.base.height = mode_cmd->height; - vfbs->base.pin = &vmw_surface_dmabuf_pin; - vfbs->base.unpin = &vmw_surface_dmabuf_unpin; vfbs->surface = surface; + vfbs->base.user_handle = mode_cmd->handle; vfbs->master = drm_master_get(file_priv->master); - mutex_init(&vfbs->work_lock); mutex_lock(&vmaster->fb_surf_mutex); - INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback); list_add_tail(&vfbs->head, &vmaster->fb_surf); mutex_unlock(&vmaster->fb_surf_mutex); @@ -641,48 +733,33 @@ void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer) drm_framebuffer_cleanup(framebuffer); vmw_dmabuf_unreference(&vfbd->buffer); + ttm_base_object_unref(&vfbd->base.user_obj); kfree(vfbd); } -int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, - struct drm_file *file_priv, - unsigned flags, unsigned color, - struct drm_clip_rect *clips, - unsigned num_clips) +static int do_dmabuf_dirty_ldu(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int increment) { - struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); - struct vmw_master *vmaster = vmw_master(file_priv->master); - struct drm_clip_rect norect; - int ret; + size_t fifo_size; + int i; + struct { uint32_t header; SVGAFifoCmdUpdate body; } *cmd; - int i, increment = 1; - - ret = ttm_read_lock(&vmaster->lock, true); - if (unlikely(ret != 0)) - return ret; - if (!num_clips) { - num_clips = 1; - clips = &norect; - norect.x1 = norect.y1 = 0; - norect.x2 = framebuffer->width; - norect.y2 = framebuffer->height; - } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) { - num_clips /= 2; - increment = 2; - } - - cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips); + fifo_size = sizeof(*cmd) * num_clips; + cmd = vmw_fifo_reserve(dev_priv, fifo_size); if (unlikely(cmd == NULL)) { DRM_ERROR("Fifo reserve failed.\n"); - ttm_read_unlock(&vmaster->lock); return -ENOMEM; } + memset(cmd, 0, fifo_size); for (i = 0; i < num_clips; i++, clips += increment) { cmd[i].header = cpu_to_le32(SVGA_CMD_UPDATE); cmd[i].body.x = cpu_to_le32(clips->x1); @@ -691,57 +768,196 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, cmd[i].body.height = cpu_to_le32(clips->y2 - clips->y1); } - vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips); - ttm_read_unlock(&vmaster->lock); - + vmw_fifo_commit(dev_priv, fifo_size); return 0; } -static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { - .destroy = vmw_framebuffer_dmabuf_destroy, - .dirty = vmw_framebuffer_dmabuf_dirty, - .create_handle = vmw_framebuffer_create_handle, -}; - -static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb) +static int do_dmabuf_define_gmrfb(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer) { - struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); - struct vmw_framebuffer_surface *vfbs = - vmw_framebuffer_to_vfbs(&vfb->base); - unsigned long size = vfbs->base.base.pitch * vfbs->base.base.height; + int depth = framebuffer->base.depth; + size_t fifo_size; int ret; - vfbs->buffer = kzalloc(sizeof(*vfbs->buffer), GFP_KERNEL); - if (unlikely(vfbs->buffer == NULL)) + struct { + uint32_t header; + SVGAFifoCmdDefineGMRFB body; + } *cmd; + + /* Emulate RGBA support, contrary to svga_reg.h this is not + * supported by hosts. This is only a problem if we are reading + * this value later and expecting what we uploaded back. + */ + if (depth == 32) + depth = 24; + + fifo_size = sizeof(*cmd); + cmd = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed to allocate temporary cmd buffer.\n"); return -ENOMEM; + } - vmw_overlay_pause_all(dev_priv); - ret = vmw_dmabuf_init(dev_priv, vfbs->buffer, size, - &vmw_vram_ne_placement, - false, &vmw_dmabuf_bo_free); - vmw_overlay_resume_all(dev_priv); + memset(cmd, 0, fifo_size); + cmd->header = SVGA_CMD_DEFINE_GMRFB; + cmd->body.format.bitsPerPixel = framebuffer->base.bits_per_pixel; + cmd->body.format.colorDepth = depth; + cmd->body.format.reserved = 0; + cmd->body.bytesPerLine = framebuffer->base.pitch; + cmd->body.ptr.gmrId = framebuffer->user_handle; + cmd->body.ptr.offset = 0; + + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, + fifo_size, 0, NULL); + + kfree(cmd); + + return ret; +} + +static int do_dmabuf_dirty_sou(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int increment) +{ + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_clip_rect *clips_ptr; + int i, k, num_units, ret; + struct drm_crtc *crtc; + size_t fifo_size; + + struct { + uint32_t header; + SVGAFifoCmdBlitGMRFBToScreen body; + } *blits; + + ret = do_dmabuf_define_gmrfb(file_priv, dev_priv, framebuffer); if (unlikely(ret != 0)) - vfbs->buffer = NULL; + return ret; /* define_gmrfb prints warnings */ + + fifo_size = sizeof(*blits) * num_clips; + blits = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(blits == NULL)) { + DRM_ERROR("Failed to allocate temporary cmd buffer.\n"); + return -ENOMEM; + } + + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { + if (crtc->fb != &framebuffer->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + + for (k = 0; k < num_units; k++) { + struct vmw_display_unit *unit = units[k]; + int hit_num = 0; + + clips_ptr = clips; + for (i = 0; i < num_clips; i++, clips_ptr += increment) { + int clip_x1 = clips_ptr->x1 - unit->crtc.x; + int clip_y1 = clips_ptr->y1 - unit->crtc.y; + int clip_x2 = clips_ptr->x2 - unit->crtc.x; + int clip_y2 = clips_ptr->y2 - unit->crtc.y; + int move_x, move_y; + + /* skip any crtcs that misses the clip region */ + if (clip_x1 >= unit->crtc.mode.hdisplay || + clip_y1 >= unit->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + /* clip size to crtc size */ + clip_x2 = min_t(int, clip_x2, unit->crtc.mode.hdisplay); + clip_y2 = min_t(int, clip_y2, unit->crtc.mode.vdisplay); + + /* translate both src and dest to bring clip into screen */ + move_x = min_t(int, clip_x1, 0); + move_y = min_t(int, clip_y1, 0); + + /* actual translate done here */ + blits[hit_num].header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; + blits[hit_num].body.destScreenId = unit->unit; + blits[hit_num].body.srcOrigin.x = clips_ptr->x1 - move_x; + blits[hit_num].body.srcOrigin.y = clips_ptr->y1 - move_y; + blits[hit_num].body.destRect.left = clip_x1 - move_x; + blits[hit_num].body.destRect.top = clip_y1 - move_y; + blits[hit_num].body.destRect.right = clip_x2; + blits[hit_num].body.destRect.bottom = clip_y2; + hit_num++; + } + + /* no clips hit the crtc */ + if (hit_num == 0) + continue; + + fifo_size = sizeof(*blits) * hit_num; + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, blits, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + } + + kfree(blits); return ret; } -static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb) +int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips) { - struct ttm_buffer_object *bo; - struct vmw_framebuffer_surface *vfbs = - vmw_framebuffer_to_vfbs(&vfb->base); + struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); + struct vmw_master *vmaster = vmw_master(file_priv->master); + struct vmw_framebuffer_dmabuf *vfbd = + vmw_framebuffer_to_vfbd(framebuffer); + struct drm_clip_rect norect; + int ret, increment = 1; - if (unlikely(vfbs->buffer == NULL)) - return 0; + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; - bo = &vfbs->buffer->base; - ttm_bo_unref(&bo); - vfbs->buffer = NULL; + if (!num_clips) { + num_clips = 1; + clips = &norect; + norect.x1 = norect.y1 = 0; + norect.x2 = framebuffer->width; + norect.y2 = framebuffer->height; + } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) { + num_clips /= 2; + increment = 2; + } - return 0; + if (dev_priv->ldu_priv) { + ret = do_dmabuf_dirty_ldu(dev_priv, &vfbd->base, + flags, color, + clips, num_clips, increment); + } else { + ret = do_dmabuf_dirty_sou(file_priv, dev_priv, &vfbd->base, + flags, color, + clips, num_clips, increment); + } + + ttm_read_unlock(&vmaster->lock); + return ret; } +static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { + .destroy = vmw_framebuffer_dmabuf_destroy, + .dirty = vmw_framebuffer_dmabuf_dirty, + .create_handle = vmw_framebuffer_create_handle, +}; + +/** + * Pin the dmabuffer to the start of vram. + */ static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb) { struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); @@ -749,10 +965,12 @@ static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb) vmw_framebuffer_to_vfbd(&vfb->base); int ret; + /* This code should not be used with screen objects */ + BUG_ON(dev_priv->sou_priv); vmw_overlay_pause_all(dev_priv); - ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer); + ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer, true, false); vmw_overlay_resume_all(dev_priv); @@ -772,7 +990,7 @@ static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb) return 0; } - return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer); + return vmw_dmabuf_unpin(dev_priv, vfbd->buffer, false); } static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, @@ -794,6 +1012,33 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, return -EINVAL; } + /* Limited framebuffer color depth support for screen objects */ + if (dev_priv->sou_priv) { + switch (mode_cmd->depth) { + case 32: + case 24: + /* Only support 32 bpp for 32 and 24 depth fbs */ + if (mode_cmd->bpp == 32) + break; + + DRM_ERROR("Invalid color depth/bbp: %d %d\n", + mode_cmd->depth, mode_cmd->bpp); + return -EINVAL; + case 16: + case 15: + /* Only support 16 bpp for 16 and 15 depth fbs */ + if (mode_cmd->bpp == 16) + break; + + DRM_ERROR("Invalid color depth/bbp: %d %d\n", + mode_cmd->depth, mode_cmd->bpp); + return -EINVAL; + default: + DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth); + return -EINVAL; + } + } + vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL); if (!vfbd) { ret = -ENOMEM; @@ -815,9 +1060,13 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, vfbd->base.base.depth = mode_cmd->depth; vfbd->base.base.width = mode_cmd->width; vfbd->base.base.height = mode_cmd->height; - vfbd->base.pin = vmw_framebuffer_dmabuf_pin; - vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin; + if (!dev_priv->sou_priv) { + vfbd->base.pin = vmw_framebuffer_dmabuf_pin; + vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin; + } + vfbd->base.dmabuf = true; vfbd->buffer = dmabuf; + vfbd->base.user_handle = mode_cmd->handle; *out = &vfbd->base; return 0; @@ -843,7 +1092,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct vmw_framebuffer *vfb = NULL; struct vmw_surface *surface = NULL; struct vmw_dma_buffer *bo = NULL; - u64 required_size; + struct ttm_base_object *user_obj; int ret; /** @@ -852,69 +1101,308 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, * requested framebuffer. */ - required_size = mode_cmd->pitch * mode_cmd->height; - if (unlikely(required_size > (u64) dev_priv->vram_size)) { + if (!vmw_kms_validate_mode_vram(dev_priv, + mode_cmd->pitch, + mode_cmd->height)) { DRM_ERROR("VRAM size is too small for requested mode.\n"); - return NULL; + return ERR_PTR(-ENOMEM); } - /** - * End conditioned code. + /* + * Take a reference on the user object of the resource + * backing the kms fb. This ensures that user-space handle + * lookups on that resource will always work as long as + * it's registered with a kms framebuffer. This is important, + * since vmw_execbuf_process identifies resources in the + * command stream using user-space handles. */ - ret = vmw_user_surface_lookup_handle(dev_priv, tfile, - mode_cmd->handle, &surface); - if (ret) - goto try_dmabuf; - - if (!surface->scanout) - goto err_not_scanout; - - ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv, surface, - &vfb, mode_cmd); + user_obj = ttm_base_object_lookup(tfile, mode_cmd->handle); + if (unlikely(user_obj == NULL)) { + DRM_ERROR("Could not locate requested kms frame buffer.\n"); + return ERR_PTR(-ENOENT); + } - /* vmw_user_surface_lookup takes one ref so does new_fb */ - vmw_surface_unreference(&surface); + /* returns either a dmabuf or surface */ + ret = vmw_user_lookup_handle(dev_priv, tfile, + mode_cmd->handle, + &surface, &bo); + if (ret) + goto err_out; + + /* Create the new framebuffer depending one what we got back */ + if (bo) + ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb, + mode_cmd); + else if (surface) + ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv, + surface, &vfb, mode_cmd); + else + BUG(); + +err_out: + /* vmw_user_lookup_handle takes one ref so does new_fb */ + if (bo) + vmw_dmabuf_unreference(&bo); + if (surface) + vmw_surface_unreference(&surface); if (ret) { DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); + ttm_base_object_unref(&user_obj); return ERR_PTR(ret); - } + } else + vfb->user_obj = user_obj; + return &vfb->base; +} -try_dmabuf: - DRM_INFO("%s: trying buffer\n", __func__); +static struct drm_mode_config_funcs vmw_kms_funcs = { + .fb_create = vmw_kms_fb_create, +}; - ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo); - if (ret) { - DRM_ERROR("failed to find buffer: %i\n", ret); - return ERR_PTR(-ENOENT); +int vmw_kms_present(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, + struct vmw_surface *surface, + uint32_t sid, + int32_t destX, int32_t destY, + struct drm_vmw_rect *clips, + uint32_t num_clips) +{ + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_clip_rect *tmp; + struct drm_crtc *crtc; + size_t fifo_size; + int i, k, num_units; + int ret = 0; /* silence warning */ + int left, right, top, bottom; + + struct { + SVGA3dCmdHeader header; + SVGA3dCmdBlitSurfaceToScreen body; + } *cmd; + SVGASignedRect *blits; + + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { + if (crtc->fb != &vfb->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); } - ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb, - mode_cmd); + BUG_ON(surface == NULL); + BUG_ON(!clips || !num_clips); - /* vmw_user_dmabuf_lookup takes one ref so does new_fb */ - vmw_dmabuf_unreference(&bo); + tmp = kzalloc(sizeof(*tmp) * num_clips, GFP_KERNEL); + if (unlikely(tmp == NULL)) { + DRM_ERROR("Temporary cliprect memory alloc failed.\n"); + return -ENOMEM; + } - if (ret) { - DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); - return ERR_PTR(ret); + fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips; + cmd = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed to allocate temporary fifo memory.\n"); + ret = -ENOMEM; + goto out_free_tmp; } - return &vfb->base; + left = clips->x; + right = clips->x + clips->w; + top = clips->y; + bottom = clips->y + clips->h; + + for (i = 1; i < num_clips; i++) { + left = min_t(int, left, (int)clips[i].x); + right = max_t(int, right, (int)clips[i].x + clips[i].w); + top = min_t(int, top, (int)clips[i].y); + bottom = max_t(int, bottom, (int)clips[i].y + clips[i].h); + } + + /* only need to do this once */ + memset(cmd, 0, fifo_size); + cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); + + blits = (SVGASignedRect *)&cmd[1]; -err_not_scanout: - DRM_ERROR("surface not marked as scanout\n"); - /* vmw_user_surface_lookup takes one ref */ - vmw_surface_unreference(&surface); + cmd->body.srcRect.left = left; + cmd->body.srcRect.right = right; + cmd->body.srcRect.top = top; + cmd->body.srcRect.bottom = bottom; - return ERR_PTR(-EINVAL); + for (i = 0; i < num_clips; i++) { + tmp[i].x1 = clips[i].x - left; + tmp[i].x2 = clips[i].x + clips[i].w - left; + tmp[i].y1 = clips[i].y - top; + tmp[i].y2 = clips[i].y + clips[i].h - top; + } + + for (k = 0; k < num_units; k++) { + struct vmw_display_unit *unit = units[k]; + struct vmw_clip_rect clip; + int num; + + clip.x1 = left + destX - unit->crtc.x; + clip.y1 = top + destY - unit->crtc.y; + clip.x2 = right + destX - unit->crtc.x; + clip.y2 = bottom + destY - unit->crtc.y; + + /* skip any crtcs that misses the clip region */ + if (clip.x1 >= unit->crtc.mode.hdisplay || + clip.y1 >= unit->crtc.mode.vdisplay || + clip.x2 <= 0 || clip.y2 <= 0) + continue; + + /* + * In order for the clip rects to be correctly scaled + * the src and dest rects needs to be the same size. + */ + cmd->body.destRect.left = clip.x1; + cmd->body.destRect.right = clip.x2; + cmd->body.destRect.top = clip.y1; + cmd->body.destRect.bottom = clip.y2; + + /* create a clip rect of the crtc in dest coords */ + clip.x2 = unit->crtc.mode.hdisplay - clip.x1; + clip.y2 = unit->crtc.mode.vdisplay - clip.y1; + clip.x1 = 0 - clip.x1; + clip.y1 = 0 - clip.y1; + + /* need to reset sid as it is changed by execbuf */ + cmd->body.srcImage.sid = sid; + cmd->body.destScreenId = unit->unit; + + /* clip and write blits to cmd stream */ + vmw_clip_cliprects(tmp, num_clips, clip, blits, &num); + + /* if no cliprects hit skip this */ + if (num == 0) + continue; + + /* recalculate package length */ + fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num; + cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + } + + kfree(cmd); +out_free_tmp: + kfree(tmp); + + return ret; } -static struct drm_mode_config_funcs vmw_kms_funcs = { - .fb_create = vmw_kms_fb_create, -}; +int vmw_kms_readback(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, + struct drm_vmw_fence_rep __user *user_fence_rep, + struct drm_vmw_rect *clips, + uint32_t num_clips) +{ + struct vmw_framebuffer_dmabuf *vfbd = + vmw_framebuffer_to_vfbd(&vfb->base); + struct vmw_dma_buffer *dmabuf = vfbd->buffer; + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_crtc *crtc; + size_t fifo_size; + int i, k, ret, num_units, blits_pos; + + struct { + uint32_t header; + SVGAFifoCmdDefineGMRFB body; + } *cmd; + struct { + uint32_t header; + SVGAFifoCmdBlitScreenToGMRFB body; + } *blits; + + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { + if (crtc->fb != &vfb->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + + BUG_ON(dmabuf == NULL); + BUG_ON(!clips || !num_clips); + + /* take a safe guess at fifo size */ + fifo_size = sizeof(*cmd) + sizeof(*blits) * num_clips * num_units; + cmd = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed to allocate temporary fifo memory.\n"); + return -ENOMEM; + } + + memset(cmd, 0, fifo_size); + cmd->header = SVGA_CMD_DEFINE_GMRFB; + cmd->body.format.bitsPerPixel = vfb->base.bits_per_pixel; + cmd->body.format.colorDepth = vfb->base.depth; + cmd->body.format.reserved = 0; + cmd->body.bytesPerLine = vfb->base.pitch; + cmd->body.ptr.gmrId = vfb->user_handle; + cmd->body.ptr.offset = 0; + + blits = (void *)&cmd[1]; + blits_pos = 0; + for (i = 0; i < num_units; i++) { + struct drm_vmw_rect *c = clips; + for (k = 0; k < num_clips; k++, c++) { + /* transform clip coords to crtc origin based coords */ + int clip_x1 = c->x - units[i]->crtc.x; + int clip_x2 = c->x - units[i]->crtc.x + c->w; + int clip_y1 = c->y - units[i]->crtc.y; + int clip_y2 = c->y - units[i]->crtc.y + c->h; + int dest_x = c->x; + int dest_y = c->y; + + /* compensate for clipping, we negate + * a negative number and add that. + */ + if (clip_x1 < 0) + dest_x += -clip_x1; + if (clip_y1 < 0) + dest_y += -clip_y1; + + /* clip */ + clip_x1 = max(clip_x1, 0); + clip_y1 = max(clip_y1, 0); + clip_x2 = min(clip_x2, units[i]->crtc.mode.hdisplay); + clip_y2 = min(clip_y2, units[i]->crtc.mode.vdisplay); + + /* and cull any rects that misses the crtc */ + if (clip_x1 >= units[i]->crtc.mode.hdisplay || + clip_y1 >= units[i]->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + blits[blits_pos].header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB; + blits[blits_pos].body.srcScreenId = units[i]->unit; + blits[blits_pos].body.destOrigin.x = dest_x; + blits[blits_pos].body.destOrigin.y = dest_y; + + blits[blits_pos].body.srcRect.left = clip_x1; + blits[blits_pos].body.srcRect.top = clip_y1; + blits[blits_pos].body.srcRect.right = clip_x2; + blits[blits_pos].body.srcRect.bottom = clip_y2; + blits_pos++; + } + } + /* reset size here and use calculated exact size from loops */ + fifo_size = sizeof(*cmd) + sizeof(*blits) * blits_pos; + + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size, + 0, user_fence_rep); + + kfree(cmd); + + return ret; +} int vmw_kms_init(struct vmw_private *dev_priv) { @@ -929,7 +1417,9 @@ int vmw_kms_init(struct vmw_private *dev_priv) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; - ret = vmw_kms_init_legacy_display_system(dev_priv); + ret = vmw_kms_init_screen_object_display(dev_priv); + if (ret) /* Fallback */ + (void)vmw_kms_init_legacy_display_system(dev_priv); return 0; } @@ -942,7 +1432,10 @@ int vmw_kms_close(struct vmw_private *dev_priv) * drm_encoder_cleanup which takes the lock we deadlock. */ drm_mode_config_cleanup(dev_priv->dev); - vmw_kms_close_legacy_display_system(dev_priv); + if (dev_priv->sou_priv) + vmw_kms_close_screen_object_display(dev_priv); + else + vmw_kms_close_legacy_display_system(dev_priv); return 0; } @@ -987,9 +1480,9 @@ out: return ret; } -void vmw_kms_write_svga(struct vmw_private *vmw_priv, +int vmw_kms_write_svga(struct vmw_private *vmw_priv, unsigned width, unsigned height, unsigned pitch, - unsigned bbp, unsigned depth) + unsigned bpp, unsigned depth) { if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK) vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch); @@ -997,11 +1490,15 @@ void vmw_kms_write_svga(struct vmw_private *vmw_priv, iowrite32(pitch, vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK); vmw_write(vmw_priv, SVGA_REG_WIDTH, width); vmw_write(vmw_priv, SVGA_REG_HEIGHT, height); - vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bbp); - vmw_write(vmw_priv, SVGA_REG_DEPTH, depth); - vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000); - vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00); - vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff); + vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp); + + if (vmw_read(vmw_priv, SVGA_REG_DEPTH) != depth) { + DRM_ERROR("Invalid depth %u for %u bpp, host expects %u\n", + depth, bpp, vmw_read(vmw_priv, SVGA_REG_DEPTH)); + return -EINVAL; + } + + return 0; } int vmw_kms_save_vga(struct vmw_private *vmw_priv) @@ -1011,12 +1508,7 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv) vmw_priv->vga_width = vmw_read(vmw_priv, SVGA_REG_WIDTH); vmw_priv->vga_height = vmw_read(vmw_priv, SVGA_REG_HEIGHT); - vmw_priv->vga_depth = vmw_read(vmw_priv, SVGA_REG_DEPTH); vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL); - vmw_priv->vga_pseudo = vmw_read(vmw_priv, SVGA_REG_PSEUDOCOLOR); - vmw_priv->vga_red_mask = vmw_read(vmw_priv, SVGA_REG_RED_MASK); - vmw_priv->vga_blue_mask = vmw_read(vmw_priv, SVGA_REG_BLUE_MASK); - vmw_priv->vga_green_mask = vmw_read(vmw_priv, SVGA_REG_GREEN_MASK); if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK) vmw_priv->vga_pitchlock = vmw_read(vmw_priv, SVGA_REG_PITCHLOCK); @@ -1065,12 +1557,7 @@ int vmw_kms_restore_vga(struct vmw_private *vmw_priv) vmw_write(vmw_priv, SVGA_REG_WIDTH, vmw_priv->vga_width); vmw_write(vmw_priv, SVGA_REG_HEIGHT, vmw_priv->vga_height); - vmw_write(vmw_priv, SVGA_REG_DEPTH, vmw_priv->vga_depth); vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, vmw_priv->vga_bpp); - vmw_write(vmw_priv, SVGA_REG_PSEUDOCOLOR, vmw_priv->vga_pseudo); - vmw_write(vmw_priv, SVGA_REG_RED_MASK, vmw_priv->vga_red_mask); - vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, vmw_priv->vga_green_mask); - vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, vmw_priv->vga_blue_mask); if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK) vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, vmw_priv->vga_pitchlock); @@ -1095,6 +1582,322 @@ int vmw_kms_restore_vga(struct vmw_private *vmw_priv) return 0; } +bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, + uint32_t pitch, + uint32_t height) +{ + return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size; +} + + +/** + * Function called by DRM code called with vbl_lock held. + */ +u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc) +{ + return 0; +} + +/** + * Function called by DRM code called with vbl_lock held. + */ +int vmw_enable_vblank(struct drm_device *dev, int crtc) +{ + return -ENOSYS; +} + +/** + * Function called by DRM code called with vbl_lock held. + */ +void vmw_disable_vblank(struct drm_device *dev, int crtc) +{ +} + + +/* + * Small shared kms functions. + */ + +int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, + struct drm_vmw_rect *rects) +{ + struct drm_device *dev = dev_priv->dev; + struct vmw_display_unit *du; + struct drm_connector *con; + + mutex_lock(&dev->mode_config.mutex); + +#if 0 + { + unsigned int i; + + DRM_INFO("%s: new layout ", __func__); + for (i = 0; i < num; i++) + DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y, + rects[i].w, rects[i].h); + DRM_INFO("\n"); + } +#endif + + list_for_each_entry(con, &dev->mode_config.connector_list, head) { + du = vmw_connector_to_du(con); + if (num > du->unit) { + du->pref_width = rects[du->unit].w; + du->pref_height = rects[du->unit].h; + du->pref_active = true; + du->gui_x = rects[du->unit].x; + du->gui_y = rects[du->unit].y; + } else { + du->pref_width = 800; + du->pref_height = 600; + du->pref_active = false; + } + con->status = vmw_du_connector_detect(con, true); + } + + mutex_unlock(&dev->mode_config.mutex); + + return 0; +} + +void vmw_du_crtc_save(struct drm_crtc *crtc) +{ +} + +void vmw_du_crtc_restore(struct drm_crtc *crtc) +{ +} + +void vmw_du_crtc_gamma_set(struct drm_crtc *crtc, + u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size) +{ + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + int i; + + for (i = 0; i < size; i++) { + DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i, + r[i], g[i], b[i]); + vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8); + vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8); + vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8); + } +} + +void vmw_du_connector_dpms(struct drm_connector *connector, int mode) +{ +} + +void vmw_du_connector_save(struct drm_connector *connector) +{ +} + +void vmw_du_connector_restore(struct drm_connector *connector) +{ +} + +enum drm_connector_status +vmw_du_connector_detect(struct drm_connector *connector, bool force) +{ + uint32_t num_displays; + struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_display_unit *du = vmw_connector_to_du(connector); + + mutex_lock(&dev_priv->hw_mutex); + num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); + mutex_unlock(&dev_priv->hw_mutex); + + return ((vmw_connector_to_du(connector)->unit < num_displays && + du->pref_active) ? + connector_status_connected : connector_status_disconnected); +} + +static struct drm_display_mode vmw_kms_connector_builtin[] = { + /* 640x480@60Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, + 752, 800, 0, 480, 489, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 800x600@60Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, + 968, 1056, 0, 600, 601, 605, 628, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1024x768@60Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, + 1184, 1344, 0, 768, 771, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 1152x864@75Hz */ + { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, + 1344, 1600, 0, 864, 865, 868, 900, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x768@60Hz */ + { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, + 1472, 1664, 0, 768, 771, 778, 798, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x800@60Hz */ + { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, + 1480, 1680, 0, 800, 803, 809, 831, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 1280x960@60Hz */ + { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, + 1488, 1800, 0, 960, 961, 964, 1000, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x1024@60Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1360x768@60Hz */ + { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, + 1536, 1792, 0, 768, 771, 777, 795, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x1050@60Hz */ + { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, + 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x900@60Hz */ + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, + 1672, 1904, 0, 900, 903, 909, 934, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1600x1200@60Hz */ + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1680x1050@60Hz */ + { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, + 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1792x1344@60Hz */ + { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, + 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1853x1392@60Hz */ + { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, + 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1200@60Hz */ + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, + 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1440@60Hz */ + { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, + 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 2560x1600@60Hz */ + { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, + 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* Terminate */ + { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, +}; + +/** + * vmw_guess_mode_timing - Provide fake timings for a + * 60Hz vrefresh mode. + * + * @mode - Pointer to a struct drm_display_mode with hdisplay and vdisplay + * members filled in. + */ +static void vmw_guess_mode_timing(struct drm_display_mode *mode) +{ + mode->hsync_start = mode->hdisplay + 50; + mode->hsync_end = mode->hsync_start + 50; + mode->htotal = mode->hsync_end + 50; + + mode->vsync_start = mode->vdisplay + 50; + mode->vsync_end = mode->vsync_start + 50; + mode->vtotal = mode->vsync_end + 50; + + mode->clock = (u32)mode->htotal * (u32)mode->vtotal / 100 * 6; + mode->vrefresh = drm_mode_vrefresh(mode); +} + + +int vmw_du_connector_fill_modes(struct drm_connector *connector, + uint32_t max_width, uint32_t max_height) +{ + struct vmw_display_unit *du = vmw_connector_to_du(connector); + struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_display_mode *mode = NULL; + struct drm_display_mode *bmode; + struct drm_display_mode prefmode = { DRM_MODE("preferred", + DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) + }; + int i; + u32 assumed_bpp = 2; + + /* + * If using screen objects, then assume 32-bpp because that's what the + * SVGA device is assuming + */ + if (dev_priv->sou_priv) + assumed_bpp = 4; + + /* Add preferred mode */ + { + mode = drm_mode_duplicate(dev, &prefmode); + if (!mode) + return 0; + mode->hdisplay = du->pref_width; + mode->vdisplay = du->pref_height; + vmw_guess_mode_timing(mode); + + if (vmw_kms_validate_mode_vram(dev_priv, + mode->hdisplay * assumed_bpp, + mode->vdisplay)) { + drm_mode_probed_add(connector, mode); + } else { + drm_mode_destroy(dev, mode); + mode = NULL; + } + + if (du->pref_mode) { + list_del_init(&du->pref_mode->head); + drm_mode_destroy(dev, du->pref_mode); + } + + /* mode might be null here, this is intended */ + du->pref_mode = mode; + } + + for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) { + bmode = &vmw_kms_connector_builtin[i]; + if (bmode->hdisplay > max_width || + bmode->vdisplay > max_height) + continue; + + if (!vmw_kms_validate_mode_vram(dev_priv, + bmode->hdisplay * assumed_bpp, + bmode->vdisplay)) + continue; + + mode = drm_mode_duplicate(dev, bmode); + if (!mode) + return 0; + mode->vrefresh = drm_mode_vrefresh(mode); + + drm_mode_probed_add(connector, mode); + } + + /* Move the prefered mode first, help apps pick the right mode. */ + if (du->pref_mode) + list_move(&du->pref_mode->head, &connector->probed_modes); + + drm_mode_connector_list_update(connector); + + return 1; +} + +int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + return 0; +} + + int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -1106,6 +1909,8 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_vmw_rect *rects; unsigned rects_size; int ret; + int i; + struct drm_mode_config *mode_config = &dev->mode_config; ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) @@ -1113,12 +1918,13 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, if (!arg->num_outputs) { struct drm_vmw_rect def_rect = {0, 0, 800, 600}; - vmw_kms_ldu_update_layout(dev_priv, 1, &def_rect); + vmw_du_update_layout(dev_priv, 1, &def_rect); goto out_unlock; } rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); - rects = kzalloc(rects_size, GFP_KERNEL); + rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect), + GFP_KERNEL); if (unlikely(!rects)) { ret = -ENOMEM; goto out_unlock; @@ -1132,7 +1938,18 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, goto out_free; } - vmw_kms_ldu_update_layout(dev_priv, arg->num_outputs, rects); + for (i = 0; i < arg->num_outputs; ++i) { + if (rects[i].x < 0 || + rects[i].y < 0 || + rects[i].x + rects[i].w > mode_config->max_width || + rects[i].y + rects[i].h > mode_config->max_height) { + DRM_ERROR("Invalid GUI layout.\n"); + ret = -EINVAL; + goto out_free; + } + } + + vmw_du_update_layout(dev_priv, arg->num_outputs, rects); out_free: kfree(rects); @@ -1140,15 +1957,3 @@ out_unlock: ttm_read_unlock(&vmaster->lock); return ret; } - -bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, - uint32_t pitch, - uint32_t height) -{ - return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size; -} - -u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc) -{ - return 0; -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 8a398a0..e1cb855 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -31,6 +31,8 @@ #include "drmP.h" #include "vmwgfx_drv.h" +#define VMWGFX_NUM_DISPLAY_UNITS 8 + #define vmw_framebuffer_to_vfb(x) \ container_of(x, struct vmw_framebuffer, base) @@ -45,6 +47,9 @@ struct vmw_framebuffer { struct drm_framebuffer base; int (*pin)(struct vmw_framebuffer *fb); int (*unpin)(struct vmw_framebuffer *fb); + bool dmabuf; + struct ttm_base_object *user_obj; + uint32_t user_handle; }; @@ -57,9 +62,14 @@ struct vmw_framebuffer { int vmw_cursor_update_image(struct vmw_private *dev_priv, u32 *image, u32 width, u32 height, u32 hotspotX, u32 hotspotY); +int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, + struct vmw_dma_buffer *dmabuf, + u32 width, u32 height, + u32 hotspotX, u32 hotspotY); void vmw_cursor_update_position(struct vmw_private *dev_priv, bool show, int x, int y); + /** * Base class display unit. * @@ -83,22 +93,65 @@ struct vmw_display_unit { int hotspot_y; unsigned unit; + + /* + * Prefered mode tracking. + */ + unsigned pref_width; + unsigned pref_height; + bool pref_active; + struct drm_display_mode *pref_mode; + + /* + * Gui positioning + */ + int gui_x; + int gui_y; + bool is_implicit; }; +#define vmw_crtc_to_du(x) \ + container_of(x, struct vmw_display_unit, crtc) +#define vmw_connector_to_du(x) \ + container_of(x, struct vmw_display_unit, connector) + + /* * Shared display unit functions - vmwgfx_kms.c */ void vmw_display_unit_cleanup(struct vmw_display_unit *du); +void vmw_du_crtc_save(struct drm_crtc *crtc); +void vmw_du_crtc_restore(struct drm_crtc *crtc); +void vmw_du_crtc_gamma_set(struct drm_crtc *crtc, + u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size); int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height); int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); +void vmw_du_connector_dpms(struct drm_connector *connector, int mode); +void vmw_du_connector_save(struct drm_connector *connector); +void vmw_du_connector_restore(struct drm_connector *connector); +enum drm_connector_status +vmw_du_connector_detect(struct drm_connector *connector, bool force); +int vmw_du_connector_fill_modes(struct drm_connector *connector, + uint32_t max_width, uint32_t max_height); +int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val); + /* * Legacy display unit functions - vmwgfx_ldu.c */ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv); int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv); -int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num, + +/* + * Screen Objects display functions - vmwgfx_scrn.c + */ +int vmw_kms_init_screen_object_display(struct vmw_private *dev_priv); +int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv); +int vmw_kms_sou_update_layout(struct vmw_private *dev_priv, unsigned num, struct drm_vmw_rect *rects); #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index b3a2cd5..8f8dbd4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -27,7 +27,6 @@ #include "vmwgfx_kms.h" -#define VMWGFX_LDU_NUM_DU 8 #define vmw_crtc_to_ldu(x) \ container_of(x, struct vmw_legacy_display_unit, base.crtc) @@ -51,11 +50,6 @@ struct vmw_legacy_display { struct vmw_legacy_display_unit { struct vmw_display_unit base; - unsigned pref_width; - unsigned pref_height; - bool pref_active; - struct drm_display_mode *pref_mode; - struct list_head active; }; @@ -71,20 +65,6 @@ static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu) * Legacy Display Unit CRTC functions */ -static void vmw_ldu_crtc_save(struct drm_crtc *crtc) -{ -} - -static void vmw_ldu_crtc_restore(struct drm_crtc *crtc) -{ -} - -static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc, - u16 *r, u16 *g, u16 *b, - uint32_t start, uint32_t size) -{ -} - static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc) { vmw_ldu_destroy(vmw_crtc_to_ldu(crtc)); @@ -94,9 +74,10 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) { struct vmw_legacy_display *lds = dev_priv->ldu_priv; struct vmw_legacy_display_unit *entry; + struct vmw_display_unit *du = NULL; struct drm_framebuffer *fb = NULL; struct drm_crtc *crtc = NULL; - int i = 0; + int i = 0, ret; /* If there is no display topology the host just assumes * that the guest will set the same layout as the host. @@ -114,10 +95,8 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) return 0; fb = entry->base.crtc.fb; - vmw_kms_write_svga(dev_priv, w, h, fb->pitch, - fb->bits_per_pixel, fb->depth); - - return 0; + return vmw_kms_write_svga(dev_priv, w, h, fb->pitch, + fb->bits_per_pixel, fb->depth); } if (!list_empty(&lds->active)) { @@ -151,6 +130,25 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) lds->last_num_active = lds->num_active; + + /* Find the first du with a cursor. */ + list_for_each_entry(entry, &lds->active, active) { + du = &entry->base; + + if (!du->cursor_dmabuf) + continue; + + ret = vmw_cursor_update_dmabuf(dev_priv, + du->cursor_dmabuf, + 64, 64, + du->hotspot_x, + du->hotspot_y); + if (ret == 0) + break; + + DRM_ERROR("Could not update cursor image\n"); + } + return 0; } @@ -265,9 +263,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) vmw_ldu_del_active(dev_priv, ldu); - vmw_ldu_commit_list(dev_priv); - - return 0; + return vmw_ldu_commit_list(dev_priv); } @@ -292,21 +288,20 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) vmw_ldu_add_active(dev_priv, ldu, vfb); - vmw_ldu_commit_list(dev_priv); - - return 0; + return vmw_ldu_commit_list(dev_priv); } static struct drm_crtc_funcs vmw_legacy_crtc_funcs = { - .save = vmw_ldu_crtc_save, - .restore = vmw_ldu_crtc_restore, + .save = vmw_du_crtc_save, + .restore = vmw_du_crtc_restore, .cursor_set = vmw_du_crtc_cursor_set, .cursor_move = vmw_du_crtc_cursor_move, - .gamma_set = vmw_ldu_crtc_gamma_set, + .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, .set_config = vmw_ldu_crtc_set_config, }; + /* * Legacy Display Unit encoder functions */ @@ -324,183 +319,18 @@ static struct drm_encoder_funcs vmw_legacy_encoder_funcs = { * Legacy Display Unit connector functions */ -static void vmw_ldu_connector_dpms(struct drm_connector *connector, int mode) -{ -} - -static void vmw_ldu_connector_save(struct drm_connector *connector) -{ -} - -static void vmw_ldu_connector_restore(struct drm_connector *connector) -{ -} - -static enum drm_connector_status - vmw_ldu_connector_detect(struct drm_connector *connector, - bool force) -{ - if (vmw_connector_to_ldu(connector)->pref_active) - return connector_status_connected; - return connector_status_disconnected; -} - -static const struct drm_display_mode vmw_ldu_connector_builtin[] = { - /* 640x480@60Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, - 752, 800, 0, 480, 489, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 800x600@60Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, - 968, 1056, 0, 600, 601, 605, 628, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1024x768@60Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 1152x864@75Hz */ - { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, - 1344, 1600, 0, 864, 865, 868, 900, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x768@60Hz */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, - 1472, 1664, 0, 768, 771, 778, 798, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x800@60Hz */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, - 1480, 1680, 0, 800, 803, 809, 831, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 1280x960@60Hz */ - { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, - 1488, 1800, 0, 960, 961, 964, 1000, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x1024@60Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, - 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1360x768@60Hz */ - { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, - 1536, 1792, 0, 768, 771, 777, 795, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x1050@60Hz */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, - 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x900@60Hz */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, - 1672, 1904, 0, 900, 903, 909, 934, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1600x1200@60Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1680x1050@60Hz */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, - 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1792x1344@60Hz */ - { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, - 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1853x1392@60Hz */ - { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, - 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1200@60Hz */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, - 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1440@60Hz */ - { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, - 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 2560x1600@60Hz */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, - 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* Terminate */ - { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, -}; - -static int vmw_ldu_connector_fill_modes(struct drm_connector *connector, - uint32_t max_width, uint32_t max_height) -{ - struct vmw_legacy_display_unit *ldu = vmw_connector_to_ldu(connector); - struct drm_device *dev = connector->dev; - struct vmw_private *dev_priv = vmw_priv(dev); - struct drm_display_mode *mode = NULL; - struct drm_display_mode prefmode = { DRM_MODE("preferred", - DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) - }; - int i; - - /* Add preferred mode */ - { - mode = drm_mode_duplicate(dev, &prefmode); - if (!mode) - return 0; - mode->hdisplay = ldu->pref_width; - mode->vdisplay = ldu->pref_height; - mode->vrefresh = drm_mode_vrefresh(mode); - if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, - mode->vdisplay)) { - drm_mode_probed_add(connector, mode); - - if (ldu->pref_mode) { - list_del_init(&ldu->pref_mode->head); - drm_mode_destroy(dev, ldu->pref_mode); - } - - ldu->pref_mode = mode; - } - } - - for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) { - const struct drm_display_mode *bmode; - - bmode = &vmw_ldu_connector_builtin[i]; - if (bmode->hdisplay > max_width || - bmode->vdisplay > max_height) - continue; - - if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2, - bmode->vdisplay)) - continue; - - mode = drm_mode_duplicate(dev, bmode); - if (!mode) - return 0; - mode->vrefresh = drm_mode_vrefresh(mode); - - drm_mode_probed_add(connector, mode); - } - - drm_mode_connector_list_update(connector); - - return 1; -} - -static int vmw_ldu_connector_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val) -{ - return 0; -} - static void vmw_ldu_connector_destroy(struct drm_connector *connector) { vmw_ldu_destroy(vmw_connector_to_ldu(connector)); } static struct drm_connector_funcs vmw_legacy_connector_funcs = { - .dpms = vmw_ldu_connector_dpms, - .save = vmw_ldu_connector_save, - .restore = vmw_ldu_connector_restore, - .detect = vmw_ldu_connector_detect, - .fill_modes = vmw_ldu_connector_fill_modes, - .set_property = vmw_ldu_connector_set_property, + .dpms = vmw_du_connector_dpms, + .save = vmw_du_connector_save, + .restore = vmw_du_connector_restore, + .detect = vmw_du_connector_detect, + .fill_modes = vmw_du_connector_fill_modes, + .set_property = vmw_du_connector_set_property, .destroy = vmw_ldu_connector_destroy, }; @@ -523,23 +353,26 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) INIT_LIST_HEAD(&ldu->active); - ldu->pref_active = (unit == 0); - ldu->pref_width = 800; - ldu->pref_height = 600; - ldu->pref_mode = NULL; + ldu->base.pref_active = (unit == 0); + ldu->base.pref_width = 800; + ldu->base.pref_height = 600; + ldu->base.pref_mode = NULL; + ldu->base.is_implicit = true; drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); - connector->status = vmw_ldu_connector_detect(connector, true); + DRM_MODE_CONNECTOR_VIRTUAL); + connector->status = vmw_du_connector_detect(connector, true); drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, - DRM_MODE_ENCODER_LVDS); + DRM_MODE_ENCODER_VIRTUAL); drm_mode_connector_attach_encoder(connector, encoder); encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); + drm_connector_attach_property(connector, dev->mode_config.dirty_info_property, 1); @@ -550,8 +383,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) { struct drm_device *dev = dev_priv->dev; - int i; - int ret; + int i, ret; if (dev_priv->ldu_priv) { DRM_INFO("ldu system already on\n"); @@ -559,7 +391,6 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) } dev_priv->ldu_priv = kmalloc(sizeof(*dev_priv->ldu_priv), GFP_KERNEL); - if (!dev_priv->ldu_priv) return -ENOMEM; @@ -568,18 +399,31 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) dev_priv->ldu_priv->last_num_active = 0; dev_priv->ldu_priv->fb = NULL; - drm_mode_create_dirty_info_property(dev_priv->dev); + /* for old hardware without multimon only enable one display */ + if (dev_priv->capabilities & SVGA_CAP_MULTIMON) + ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS); + else + ret = drm_vblank_init(dev, 1); + if (ret != 0) + goto err_free; + + ret = drm_mode_create_dirty_info_property(dev); + if (ret != 0) + goto err_vblank_cleanup; - if (dev_priv->capabilities & SVGA_CAP_MULTIMON) { - for (i = 0; i < VMWGFX_LDU_NUM_DU; ++i) + if (dev_priv->capabilities & SVGA_CAP_MULTIMON) + for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) vmw_ldu_init(dev_priv, i); - ret = drm_vblank_init(dev, VMWGFX_LDU_NUM_DU); - } else { - /* for old hardware without multimon only enable one display */ + else vmw_ldu_init(dev_priv, 0); - ret = drm_vblank_init(dev, 1); - } + return 0; + +err_vblank_cleanup: + drm_vblank_cleanup(dev); +err_free: + kfree(dev_priv->ldu_priv); + dev_priv->ldu_priv = NULL; return ret; } @@ -587,52 +431,14 @@ int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv) { struct drm_device *dev = dev_priv->dev; - drm_vblank_cleanup(dev); if (!dev_priv->ldu_priv) return -ENOSYS; + drm_vblank_cleanup(dev); + BUG_ON(!list_empty(&dev_priv->ldu_priv->active)); kfree(dev_priv->ldu_priv); return 0; } - -int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num, - struct drm_vmw_rect *rects) -{ - struct drm_device *dev = dev_priv->dev; - struct vmw_legacy_display_unit *ldu; - struct drm_connector *con; - int i; - - mutex_lock(&dev->mode_config.mutex); - -#if 0 - DRM_INFO("%s: new layout ", __func__); - for (i = 0; i < (int)num; i++) - DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y, - rects[i].w, rects[i].h); - DRM_INFO("\n"); -#else - (void)i; -#endif - - list_for_each_entry(con, &dev->mode_config.connector_list, head) { - ldu = vmw_connector_to_ldu(con); - if (num > ldu->base.unit) { - ldu->pref_width = rects[ldu->base.unit].w; - ldu->pref_height = rects[ldu->base.unit].h; - ldu->pref_active = true; - } else { - ldu->pref_width = 800; - ldu->pref_height = 600; - ldu->pref_active = false; - } - con->status = vmw_ldu_connector_detect(con, true); - } - - mutex_unlock(&dev->mode_config.mutex); - - return 0; -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c index f1a52f9..14399ee 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c @@ -87,48 +87,6 @@ static inline void fill_flush(struct vmw_escape_video_flush *cmd, } /** - * Pin or unpin a buffer in vram. - * - * @dev_priv: Driver private. - * @buf: DMA buffer to pin or unpin. - * @pin: Pin buffer in vram if true. - * @interruptible: Use interruptible wait. - * - * Takes the current masters ttm lock in read. - * - * Returns - * -ERESTARTSYS if interrupted by a signal. - */ -static int vmw_dmabuf_pin_in_vram(struct vmw_private *dev_priv, - struct vmw_dma_buffer *buf, - bool pin, bool interruptible) -{ - struct ttm_buffer_object *bo = &buf->base; - struct ttm_placement *overlay_placement = &vmw_vram_placement; - int ret; - - ret = ttm_read_lock(&dev_priv->active_master->lock, interruptible); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_bo_reserve(bo, interruptible, false, false, 0); - if (unlikely(ret != 0)) - goto err; - - if (pin) - overlay_placement = &vmw_vram_ne_placement; - - ret = ttm_bo_validate(bo, overlay_placement, interruptible, false, false); - - ttm_bo_unreserve(bo); - -err: - ttm_read_unlock(&dev_priv->active_master->lock); - - return ret; -} - -/** * Send put command to hw. * * Returns @@ -139,68 +97,80 @@ static int vmw_overlay_send_put(struct vmw_private *dev_priv, struct drm_vmw_control_stream_arg *arg, bool interruptible) { + struct vmw_escape_video_flush *flush; + size_t fifo_size; + bool have_so = dev_priv->sou_priv ? true : false; + int i, num_items; + SVGAGuestPtr ptr; + struct { struct vmw_escape_header escape; struct { - struct { - uint32_t cmdType; - uint32_t streamId; - } header; - struct { - uint32_t registerId; - uint32_t value; - } items[SVGA_VIDEO_PITCH_3 + 1]; - } body; - struct vmw_escape_video_flush flush; + uint32_t cmdType; + uint32_t streamId; + } header; } *cmds; - uint32_t offset; - int i, ret; + struct { + uint32_t registerId; + uint32_t value; + } *items; - for (;;) { - cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds)); - if (cmds) - break; + /* defines are a index needs + 1 */ + if (have_so) + num_items = SVGA_VIDEO_DST_SCREEN_ID + 1; + else + num_items = SVGA_VIDEO_PITCH_3 + 1; - ret = vmw_fallback_wait(dev_priv, false, true, 0, - interruptible, 3*HZ); - if (interruptible && ret == -ERESTARTSYS) - return ret; - else - BUG_ON(ret != 0); + fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items; + + cmds = vmw_fifo_reserve(dev_priv, fifo_size); + /* hardware has hung, can't do anything here */ + if (!cmds) + return -ENOMEM; + + items = (typeof(items))&cmds[1]; + flush = (struct vmw_escape_video_flush *)&items[num_items]; + + /* the size is header + number of items */ + fill_escape(&cmds->escape, sizeof(*items) * (num_items + 1)); + + cmds->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; + cmds->header.streamId = arg->stream_id; + + /* the IDs are neatly numbered */ + for (i = 0; i < num_items; i++) + items[i].registerId = i; + + vmw_bo_get_guest_ptr(&buf->base, &ptr); + ptr.offset += arg->offset; + + items[SVGA_VIDEO_ENABLED].value = true; + items[SVGA_VIDEO_FLAGS].value = arg->flags; + items[SVGA_VIDEO_DATA_OFFSET].value = ptr.offset; + items[SVGA_VIDEO_FORMAT].value = arg->format; + items[SVGA_VIDEO_COLORKEY].value = arg->color_key; + items[SVGA_VIDEO_SIZE].value = arg->size; + items[SVGA_VIDEO_WIDTH].value = arg->width; + items[SVGA_VIDEO_HEIGHT].value = arg->height; + items[SVGA_VIDEO_SRC_X].value = arg->src.x; + items[SVGA_VIDEO_SRC_Y].value = arg->src.y; + items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w; + items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h; + items[SVGA_VIDEO_DST_X].value = arg->dst.x; + items[SVGA_VIDEO_DST_Y].value = arg->dst.y; + items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w; + items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h; + items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0]; + items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1]; + items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2]; + if (have_so) { + items[SVGA_VIDEO_DATA_GMRID].value = ptr.gmrId; + items[SVGA_VIDEO_DST_SCREEN_ID].value = SVGA_ID_INVALID; } - fill_escape(&cmds->escape, sizeof(cmds->body)); - cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; - cmds->body.header.streamId = arg->stream_id; - - for (i = 0; i <= SVGA_VIDEO_PITCH_3; i++) - cmds->body.items[i].registerId = i; - - offset = buf->base.offset + arg->offset; - - cmds->body.items[SVGA_VIDEO_ENABLED].value = true; - cmds->body.items[SVGA_VIDEO_FLAGS].value = arg->flags; - cmds->body.items[SVGA_VIDEO_DATA_OFFSET].value = offset; - cmds->body.items[SVGA_VIDEO_FORMAT].value = arg->format; - cmds->body.items[SVGA_VIDEO_COLORKEY].value = arg->color_key; - cmds->body.items[SVGA_VIDEO_SIZE].value = arg->size; - cmds->body.items[SVGA_VIDEO_WIDTH].value = arg->width; - cmds->body.items[SVGA_VIDEO_HEIGHT].value = arg->height; - cmds->body.items[SVGA_VIDEO_SRC_X].value = arg->src.x; - cmds->body.items[SVGA_VIDEO_SRC_Y].value = arg->src.y; - cmds->body.items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w; - cmds->body.items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h; - cmds->body.items[SVGA_VIDEO_DST_X].value = arg->dst.x; - cmds->body.items[SVGA_VIDEO_DST_Y].value = arg->dst.y; - cmds->body.items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w; - cmds->body.items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h; - cmds->body.items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0]; - cmds->body.items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1]; - cmds->body.items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2]; - - fill_flush(&cmds->flush, arg->stream_id); + fill_flush(flush, arg->stream_id); - vmw_fifo_commit(dev_priv, sizeof(*cmds)); + vmw_fifo_commit(dev_priv, fifo_size); return 0; } @@ -248,6 +218,25 @@ static int vmw_overlay_send_stop(struct vmw_private *dev_priv, } /** + * Move a buffer to vram or gmr if @pin is set, else unpin the buffer. + * + * With the introduction of screen objects buffers could now be + * used with GMRs instead of being locked to vram. + */ +static int vmw_overlay_move_buffer(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool inter) +{ + if (!pin) + return vmw_dmabuf_unpin(dev_priv, buf, inter); + + if (!dev_priv->sou_priv) + return vmw_dmabuf_to_vram(dev_priv, buf, true, inter); + + return vmw_dmabuf_to_vram_or_gmr(dev_priv, buf, true, inter); +} + +/** * Stop or pause a stream. * * If the stream is paused the no evict flag is removed from the buffer @@ -279,8 +268,8 @@ static int vmw_overlay_stop(struct vmw_private *dev_priv, return ret; /* We just remove the NO_EVICT flag so no -ENOMEM */ - ret = vmw_dmabuf_pin_in_vram(dev_priv, stream->buf, false, - interruptible); + ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false, + interruptible); if (interruptible && ret == -ERESTARTSYS) return ret; else @@ -342,7 +331,7 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv, /* We don't start the old stream if we are interrupted. * Might return -ENOMEM if it can't fit the buffer in vram. */ - ret = vmw_dmabuf_pin_in_vram(dev_priv, buf, true, interruptible); + ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible); if (ret) return ret; @@ -351,7 +340,8 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv, /* This one needs to happen no matter what. We only remove * the NO_EVICT flag so this is safe from -ENOMEM. */ - BUG_ON(vmw_dmabuf_pin_in_vram(dev_priv, buf, false, false) != 0); + BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false) + != 0); return ret; } @@ -585,11 +575,10 @@ int vmw_overlay_init(struct vmw_private *dev_priv) return -ENOSYS; } - overlay = kmalloc(sizeof(*overlay), GFP_KERNEL); + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); if (!overlay) return -ENOMEM; - memset(overlay, 0, sizeof(*overlay)); mutex_init(&overlay->mutex); for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { overlay->stream[i].buf = NULL; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 5408b1b..0795d17 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -31,10 +31,6 @@ #include "ttm/ttm_placement.h" #include "drmP.h" -#define VMW_RES_CONTEXT ttm_driver_type0 -#define VMW_RES_SURFACE ttm_driver_type1 -#define VMW_RES_STREAM ttm_driver_type2 - struct vmw_user_context { struct ttm_base_object base; struct vmw_resource res; @@ -43,6 +39,7 @@ struct vmw_user_context { struct vmw_user_surface { struct ttm_base_object base; struct vmw_surface srf; + uint32_t size; }; struct vmw_user_dma_buffer { @@ -65,6 +62,17 @@ struct vmw_user_stream { struct vmw_stream stream; }; +struct vmw_surface_offset { + uint32_t face; + uint32_t mip; + uint32_t bo_offset; +}; + + +static uint64_t vmw_user_context_size; +static uint64_t vmw_user_surface_size; +static uint64_t vmw_user_stream_size; + static inline struct vmw_dma_buffer * vmw_dma_buffer(struct ttm_buffer_object *bo) { @@ -84,13 +92,36 @@ struct vmw_resource *vmw_resource_reference(struct vmw_resource *res) return res; } + +/** + * vmw_resource_release_id - release a resource id to the id manager. + * + * @res: Pointer to the resource. + * + * Release the resource id to the resource id manager and set it to -1 + */ +static void vmw_resource_release_id(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + + write_lock(&dev_priv->resource_lock); + if (res->id != -1) + idr_remove(res->idr, res->id); + res->id = -1; + write_unlock(&dev_priv->resource_lock); +} + static void vmw_resource_release(struct kref *kref) { struct vmw_resource *res = container_of(kref, struct vmw_resource, kref); struct vmw_private *dev_priv = res->dev_priv; + int id = res->id; + struct idr *idr = res->idr; - idr_remove(res->idr, res->id); + res->avail = false; + if (res->remove_from_lists != NULL) + res->remove_from_lists(res); write_unlock(&dev_priv->resource_lock); if (likely(res->hw_destroy != NULL)) @@ -102,6 +133,9 @@ static void vmw_resource_release(struct kref *kref) kfree(res); write_lock(&dev_priv->resource_lock); + + if (id != -1) + idr_remove(idr, id); } void vmw_resource_unreference(struct vmw_resource **p_res) @@ -115,28 +149,29 @@ void vmw_resource_unreference(struct vmw_resource **p_res) write_unlock(&dev_priv->resource_lock); } -static int vmw_resource_init(struct vmw_private *dev_priv, - struct vmw_resource *res, - struct idr *idr, - enum ttm_object_type obj_type, - void (*res_free) (struct vmw_resource *res)) + +/** + * vmw_resource_alloc_id - release a resource id to the id manager. + * + * @dev_priv: Pointer to the device private structure. + * @res: Pointer to the resource. + * + * Allocate the lowest free resource from the resource manager, and set + * @res->id to that id. Returns 0 on success and -ENOMEM on failure. + */ +static int vmw_resource_alloc_id(struct vmw_private *dev_priv, + struct vmw_resource *res) { int ret; - kref_init(&res->kref); - res->hw_destroy = NULL; - res->res_free = res_free; - res->res_type = obj_type; - res->idr = idr; - res->avail = false; - res->dev_priv = dev_priv; + BUG_ON(res->id != -1); do { - if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) + if (unlikely(idr_pre_get(res->idr, GFP_KERNEL) == 0)) return -ENOMEM; write_lock(&dev_priv->resource_lock); - ret = idr_get_new_above(idr, res, 1, &res->id); + ret = idr_get_new_above(res->idr, res, 1, &res->id); write_unlock(&dev_priv->resource_lock); } while (ret == -EAGAIN); @@ -144,6 +179,33 @@ static int vmw_resource_init(struct vmw_private *dev_priv, return ret; } + +static int vmw_resource_init(struct vmw_private *dev_priv, + struct vmw_resource *res, + struct idr *idr, + enum ttm_object_type obj_type, + bool delay_id, + void (*res_free) (struct vmw_resource *res), + void (*remove_from_lists) + (struct vmw_resource *res)) +{ + kref_init(&res->kref); + res->hw_destroy = NULL; + res->res_free = res_free; + res->remove_from_lists = remove_from_lists; + res->res_type = obj_type; + res->idr = idr; + res->avail = false; + res->dev_priv = dev_priv; + INIT_LIST_HEAD(&res->query_head); + INIT_LIST_HEAD(&res->validate_head); + res->id = -1; + if (delay_id) + return 0; + else + return vmw_resource_alloc_id(dev_priv, res); +} + /** * vmw_resource_activate * @@ -198,8 +260,12 @@ static void vmw_hw_context_destroy(struct vmw_resource *res) struct { SVGA3dCmdHeader header; SVGA3dCmdDestroyContext body; - } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + } *cmd; + + vmw_execbuf_release_pinned_bo(dev_priv, true, res->id); + + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); if (unlikely(cmd == NULL)) { DRM_ERROR("Failed reserving FIFO space for surface " "destruction.\n"); @@ -211,7 +277,7 @@ static void vmw_hw_context_destroy(struct vmw_resource *res) cmd->body.cid = cpu_to_le32(res->id); vmw_fifo_commit(dev_priv, sizeof(*cmd)); - vmw_3d_resource_dec(dev_priv); + vmw_3d_resource_dec(dev_priv, false); } static int vmw_context_init(struct vmw_private *dev_priv, @@ -226,14 +292,17 @@ static int vmw_context_init(struct vmw_private *dev_priv, } *cmd; ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr, - VMW_RES_CONTEXT, res_free); + VMW_RES_CONTEXT, false, res_free, NULL); if (unlikely(ret != 0)) { - if (res_free == NULL) - kfree(res); - else - res_free(res); - return ret; + DRM_ERROR("Failed to allocate a resource id.\n"); + goto out_early; + } + + if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) { + DRM_ERROR("Out of hw context ids.\n"); + vmw_resource_unreference(&res); + return -ENOMEM; } cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); @@ -248,9 +317,16 @@ static int vmw_context_init(struct vmw_private *dev_priv, cmd->body.cid = cpu_to_le32(res->id); vmw_fifo_commit(dev_priv, sizeof(*cmd)); - (void) vmw_3d_resource_inc(dev_priv); + (void) vmw_3d_resource_inc(dev_priv, false); vmw_resource_activate(res, vmw_hw_context_destroy); return 0; + +out_early: + if (res_free == NULL) + kfree(res); + else + res_free(res); + return ret; } struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv) @@ -273,8 +349,11 @@ static void vmw_user_context_free(struct vmw_resource *res) { struct vmw_user_context *ctx = container_of(res, struct vmw_user_context, res); + struct vmw_private *dev_priv = res->dev_priv; kfree(ctx); + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_context_size); } /** @@ -328,23 +407,56 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + struct vmw_user_context *ctx; struct vmw_resource *res; struct vmw_resource *tmp; struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; - if (unlikely(ctx == NULL)) - return -ENOMEM; + + /* + * Approximate idr memory usage with 128 bytes. It will be limited + * by maximum number_of contexts anyway. + */ + + if (unlikely(vmw_user_context_size == 0)) + vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + vmw_user_context_size, + false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for context" + " creation.\n"); + goto out_unlock; + } + + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + if (unlikely(ctx == NULL)) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_context_size); + ret = -ENOMEM; + goto out_unlock; + } res = &ctx->res; ctx->base.shareable = false; ctx->base.tfile = NULL; + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_context_init(dev_priv, res, vmw_user_context_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock; tmp = vmw_resource_reference(&ctx->res); ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, @@ -358,13 +470,16 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, arg->cid = res->id; out_err: vmw_resource_unreference(&res); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret; } int vmw_context_check(struct vmw_private *dev_priv, struct ttm_object_file *tfile, - int id) + int id, + struct vmw_resource **p_res) { struct vmw_resource *res; int ret = 0; @@ -376,6 +491,8 @@ int vmw_context_check(struct vmw_private *dev_priv, container_of(res, struct vmw_user_context, res); if (ctx->base.tfile != tfile && !ctx->base.shareable) ret = -EPERM; + if (p_res) + *p_res = vmw_resource_reference(res); } else ret = -EINVAL; read_unlock(&dev_priv->resource_lock); @@ -383,102 +500,638 @@ int vmw_context_check(struct vmw_private *dev_priv, return ret; } +struct vmw_bpp { + uint8_t bpp; + uint8_t s_bpp; +}; + +/* + * Size table for the supported SVGA3D surface formats. It consists of + * two values. The bpp value and the s_bpp value which is short for + * "stride bits per pixel" The values are given in such a way that the + * minimum stride for the image is calculated using + * + * min_stride = w*s_bpp + * + * and the total memory requirement for the image is + * + * h*min_stride*bpp/s_bpp + * + */ +static const struct vmw_bpp vmw_sf_bpp[] = { + [SVGA3D_FORMAT_INVALID] = {0, 0}, + [SVGA3D_X8R8G8B8] = {32, 32}, + [SVGA3D_A8R8G8B8] = {32, 32}, + [SVGA3D_R5G6B5] = {16, 16}, + [SVGA3D_X1R5G5B5] = {16, 16}, + [SVGA3D_A1R5G5B5] = {16, 16}, + [SVGA3D_A4R4G4B4] = {16, 16}, + [SVGA3D_Z_D32] = {32, 32}, + [SVGA3D_Z_D16] = {16, 16}, + [SVGA3D_Z_D24S8] = {32, 32}, + [SVGA3D_Z_D15S1] = {16, 16}, + [SVGA3D_LUMINANCE8] = {8, 8}, + [SVGA3D_LUMINANCE4_ALPHA4] = {8, 8}, + [SVGA3D_LUMINANCE16] = {16, 16}, + [SVGA3D_LUMINANCE8_ALPHA8] = {16, 16}, + [SVGA3D_DXT1] = {4, 16}, + [SVGA3D_DXT2] = {8, 32}, + [SVGA3D_DXT3] = {8, 32}, + [SVGA3D_DXT4] = {8, 32}, + [SVGA3D_DXT5] = {8, 32}, + [SVGA3D_BUMPU8V8] = {16, 16}, + [SVGA3D_BUMPL6V5U5] = {16, 16}, + [SVGA3D_BUMPX8L8V8U8] = {32, 32}, + [SVGA3D_ARGB_S10E5] = {16, 16}, + [SVGA3D_ARGB_S23E8] = {32, 32}, + [SVGA3D_A2R10G10B10] = {32, 32}, + [SVGA3D_V8U8] = {16, 16}, + [SVGA3D_Q8W8V8U8] = {32, 32}, + [SVGA3D_CxV8U8] = {16, 16}, + [SVGA3D_X8L8V8U8] = {32, 32}, + [SVGA3D_A2W10V10U10] = {32, 32}, + [SVGA3D_ALPHA8] = {8, 8}, + [SVGA3D_R_S10E5] = {16, 16}, + [SVGA3D_R_S23E8] = {32, 32}, + [SVGA3D_RG_S10E5] = {16, 16}, + [SVGA3D_RG_S23E8] = {32, 32}, + [SVGA3D_BUFFER] = {8, 8}, + [SVGA3D_Z_D24X8] = {32, 32}, + [SVGA3D_V16U16] = {32, 32}, + [SVGA3D_G16R16] = {32, 32}, + [SVGA3D_A16B16G16R16] = {64, 64}, + [SVGA3D_UYVY] = {12, 12}, + [SVGA3D_YUY2] = {12, 12}, + [SVGA3D_NV12] = {12, 8}, + [SVGA3D_AYUV] = {32, 32}, + [SVGA3D_BC4_UNORM] = {4, 16}, + [SVGA3D_BC5_UNORM] = {8, 32}, + [SVGA3D_Z_DF16] = {16, 16}, + [SVGA3D_Z_DF24] = {24, 24}, + [SVGA3D_Z_D24S8_INT] = {32, 32} +}; + /** * Surface management. */ +struct vmw_surface_dma { + SVGA3dCmdHeader header; + SVGA3dCmdSurfaceDMA body; + SVGA3dCopyBox cb; + SVGA3dCmdSurfaceDMASuffix suffix; +}; + +struct vmw_surface_define { + SVGA3dCmdHeader header; + SVGA3dCmdDefineSurface body; +}; + +struct vmw_surface_destroy { + SVGA3dCmdHeader header; + SVGA3dCmdDestroySurface body; +}; + + +/** + * vmw_surface_dma_size - Compute fifo size for a dma command. + * + * @srf: Pointer to a struct vmw_surface + * + * Computes the required size for a surface dma command for backup or + * restoration of the surface represented by @srf. + */ +static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf) +{ + return srf->num_sizes * sizeof(struct vmw_surface_dma); +} + + +/** + * vmw_surface_define_size - Compute fifo size for a surface define command. + * + * @srf: Pointer to a struct vmw_surface + * + * Computes the required size for a surface define command for the definition + * of the surface represented by @srf. + */ +static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf) +{ + return sizeof(struct vmw_surface_define) + srf->num_sizes * + sizeof(SVGA3dSize); +} + + +/** + * vmw_surface_destroy_size - Compute fifo size for a surface destroy command. + * + * Computes the required size for a surface destroy command for the destruction + * of a hw surface. + */ +static inline uint32_t vmw_surface_destroy_size(void) +{ + return sizeof(struct vmw_surface_destroy); +} + +/** + * vmw_surface_destroy_encode - Encode a surface_destroy command. + * + * @id: The surface id + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static void vmw_surface_destroy_encode(uint32_t id, + void *cmd_space) +{ + struct vmw_surface_destroy *cmd = (struct vmw_surface_destroy *) + cmd_space; + + cmd->header.id = SVGA_3D_CMD_SURFACE_DESTROY; + cmd->header.size = sizeof(cmd->body); + cmd->body.sid = id; +} + +/** + * vmw_surface_define_encode - Encode a surface_define command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static void vmw_surface_define_encode(const struct vmw_surface *srf, + void *cmd_space) +{ + struct vmw_surface_define *cmd = (struct vmw_surface_define *) + cmd_space; + struct drm_vmw_size *src_size; + SVGA3dSize *cmd_size; + uint32_t cmd_len; + int i; + + cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); + + cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE; + cmd->header.size = cmd_len; + cmd->body.sid = srf->res.id; + cmd->body.surfaceFlags = srf->flags; + cmd->body.format = cpu_to_le32(srf->format); + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + cmd->body.face[i].numMipLevels = srf->mip_levels[i]; + + cmd += 1; + cmd_size = (SVGA3dSize *) cmd; + src_size = srf->sizes; + + for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) { + cmd_size->width = src_size->width; + cmd_size->height = src_size->height; + cmd_size->depth = src_size->depth; + } +} + + +/** + * vmw_surface_dma_encode - Encode a surface_dma command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents + * should be placed or read from. + * @to_surface: Boolean whether to DMA to the surface or from the surface. + */ +static void vmw_surface_dma_encode(struct vmw_surface *srf, + void *cmd_space, + const SVGAGuestPtr *ptr, + bool to_surface) +{ + uint32_t i; + uint32_t bpp = vmw_sf_bpp[srf->format].bpp; + uint32_t stride_bpp = vmw_sf_bpp[srf->format].s_bpp; + struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space; + + for (i = 0; i < srf->num_sizes; ++i) { + SVGA3dCmdHeader *header = &cmd->header; + SVGA3dCmdSurfaceDMA *body = &cmd->body; + SVGA3dCopyBox *cb = &cmd->cb; + SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix; + const struct vmw_surface_offset *cur_offset = &srf->offsets[i]; + const struct drm_vmw_size *cur_size = &srf->sizes[i]; + + header->id = SVGA_3D_CMD_SURFACE_DMA; + header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix); + + body->guest.ptr = *ptr; + body->guest.ptr.offset += cur_offset->bo_offset; + body->guest.pitch = (cur_size->width * stride_bpp + 7) >> 3; + body->host.sid = srf->res.id; + body->host.face = cur_offset->face; + body->host.mipmap = cur_offset->mip; + body->transfer = ((to_surface) ? SVGA3D_WRITE_HOST_VRAM : + SVGA3D_READ_HOST_VRAM); + cb->x = 0; + cb->y = 0; + cb->z = 0; + cb->srcx = 0; + cb->srcy = 0; + cb->srcz = 0; + cb->w = cur_size->width; + cb->h = cur_size->height; + cb->d = cur_size->depth; + + suffix->suffixSize = sizeof(*suffix); + suffix->maximumOffset = body->guest.pitch*cur_size->height* + cur_size->depth*bpp / stride_bpp; + suffix->flags.discard = 0; + suffix->flags.unsynchronized = 0; + suffix->flags.reserved = 0; + ++cmd; + } +}; + + static void vmw_hw_surface_destroy(struct vmw_resource *res) { struct vmw_private *dev_priv = res->dev_priv; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdDestroySurface body; - } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + struct vmw_surface *srf; + void *cmd; - if (unlikely(cmd == NULL)) { - DRM_ERROR("Failed reserving FIFO space for surface " - "destruction.\n"); - return; - } + if (res->id != -1) { - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DESTROY); - cmd->header.size = cpu_to_le32(sizeof(cmd->body)); - cmd->body.sid = cpu_to_le32(res->id); + cmd = vmw_fifo_reserve(dev_priv, vmw_surface_destroy_size()); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "destruction.\n"); + return; + } - vmw_fifo_commit(dev_priv, sizeof(*cmd)); - vmw_3d_resource_dec(dev_priv); + vmw_surface_destroy_encode(res->id, cmd); + vmw_fifo_commit(dev_priv, vmw_surface_destroy_size()); + + /* + * used_memory_size_atomic, or separate lock + * to avoid taking dev_priv::cmdbuf_mutex in + * the destroy path. + */ + + mutex_lock(&dev_priv->cmdbuf_mutex); + srf = container_of(res, struct vmw_surface, res); + dev_priv->used_memory_size -= srf->backup_size; + mutex_unlock(&dev_priv->cmdbuf_mutex); + + } + vmw_3d_resource_dec(dev_priv, false); } void vmw_surface_res_free(struct vmw_resource *res) { struct vmw_surface *srf = container_of(res, struct vmw_surface, res); + if (srf->backup) + ttm_bo_unref(&srf->backup); + kfree(srf->offsets); kfree(srf->sizes); kfree(srf->snooper.image); kfree(srf); } -int vmw_surface_init(struct vmw_private *dev_priv, - struct vmw_surface *srf, - void (*res_free) (struct vmw_resource *res)) + +/** + * vmw_surface_do_validate - make a surface available to the device. + * + * @dev_priv: Pointer to a device private struct. + * @srf: Pointer to a struct vmw_surface. + * + * If the surface doesn't have a hw id, allocate one, and optionally + * DMA the backed up surface contents to the device. + * + * Returns -EBUSY if there wasn't sufficient device resources to + * complete the validation. Retry after freeing up resources. + * + * May return other errors if the kernel is out of guest resources. + */ +int vmw_surface_do_validate(struct vmw_private *dev_priv, + struct vmw_surface *srf) { - int ret; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdDefineSurface body; - } *cmd; - SVGA3dSize *cmd_size; struct vmw_resource *res = &srf->res; - struct drm_vmw_size *src_size; - size_t submit_size; - uint32_t cmd_len; - int i; + struct list_head val_list; + struct ttm_validate_buffer val_buf; + uint32_t submit_size; + uint8_t *cmd; + int ret; - BUG_ON(res_free == NULL); - ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr, - VMW_RES_SURFACE, res_free); + if (likely(res->id != -1)) + return 0; + if (unlikely(dev_priv->used_memory_size + srf->backup_size >= + dev_priv->memory_size)) + return -EBUSY; + + /* + * Reserve- and validate the backup DMA bo. + */ + + if (srf->backup) { + INIT_LIST_HEAD(&val_list); + val_buf.bo = ttm_bo_reference(srf->backup); + val_buf.new_sync_obj_arg = (void *)((unsigned long) + DRM_VMW_FENCE_FLAG_EXEC); + list_add_tail(&val_buf.head, &val_list); + ret = ttm_eu_reserve_buffers(&val_list); + if (unlikely(ret != 0)) + goto out_no_reserve; + + ret = ttm_bo_validate(srf->backup, &vmw_srf_placement, + true, false, false); + if (unlikely(ret != 0)) + goto out_no_validate; + } + + /* + * Alloc id for the resource. + */ + + ret = vmw_resource_alloc_id(dev_priv, res); if (unlikely(ret != 0)) { - res_free(res); - return ret; + DRM_ERROR("Failed to allocate a surface id.\n"); + goto out_no_id; + } + if (unlikely(res->id >= SVGA3D_MAX_SURFACE_IDS)) { + ret = -EBUSY; + goto out_no_fifo; } - submit_size = sizeof(*cmd) + srf->num_sizes * sizeof(SVGA3dSize); - cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); + + /* + * Encode surface define- and dma commands. + */ + + submit_size = vmw_surface_define_size(srf); + if (srf->backup) + submit_size += vmw_surface_dma_size(srf); cmd = vmw_fifo_reserve(dev_priv, submit_size); if (unlikely(cmd == NULL)) { - DRM_ERROR("Fifo reserve failed for create surface.\n"); - vmw_resource_unreference(&res); - return -ENOMEM; + DRM_ERROR("Failed reserving FIFO space for surface " + "validation.\n"); + ret = -ENOMEM; + goto out_no_fifo; } - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DEFINE); - cmd->header.size = cpu_to_le32(cmd_len); - cmd->body.sid = cpu_to_le32(res->id); - cmd->body.surfaceFlags = cpu_to_le32(srf->flags); - cmd->body.format = cpu_to_le32(srf->format); - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { - cmd->body.face[i].numMipLevels = - cpu_to_le32(srf->mip_levels[i]); + vmw_surface_define_encode(srf, cmd); + if (srf->backup) { + SVGAGuestPtr ptr; + + cmd += vmw_surface_define_size(srf); + vmw_bo_get_guest_ptr(srf->backup, &ptr); + vmw_surface_dma_encode(srf, cmd, &ptr, true); } - cmd += 1; - cmd_size = (SVGA3dSize *) cmd; - src_size = srf->sizes; + vmw_fifo_commit(dev_priv, submit_size); - for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) { - cmd_size->width = cpu_to_le32(src_size->width); - cmd_size->height = cpu_to_le32(src_size->height); - cmd_size->depth = cpu_to_le32(src_size->depth); + /* + * Create a fence object and fence the backup buffer. + */ + + if (srf->backup) { + struct vmw_fence_obj *fence; + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, + &fence, NULL); + ttm_eu_fence_buffer_objects(&val_list, fence); + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); + ttm_bo_unref(&val_buf.bo); + ttm_bo_unref(&srf->backup); + } + + /* + * Surface memory usage accounting. + */ + + dev_priv->used_memory_size += srf->backup_size; + + return 0; + +out_no_fifo: + vmw_resource_release_id(res); +out_no_id: +out_no_validate: + if (srf->backup) + ttm_eu_backoff_reservation(&val_list); +out_no_reserve: + if (srf->backup) + ttm_bo_unref(&val_buf.bo); + return ret; +} + +/** + * vmw_surface_evict - Evict a hw surface. + * + * @dev_priv: Pointer to a device private struct. + * @srf: Pointer to a struct vmw_surface + * + * DMA the contents of a hw surface to a backup guest buffer object, + * and destroy the hw surface, releasing its id. + */ +int vmw_surface_evict(struct vmw_private *dev_priv, + struct vmw_surface *srf) +{ + struct vmw_resource *res = &srf->res; + struct list_head val_list; + struct ttm_validate_buffer val_buf; + uint32_t submit_size; + uint8_t *cmd; + int ret; + struct vmw_fence_obj *fence; + SVGAGuestPtr ptr; + + BUG_ON(res->id == -1); + + /* + * Create a surface backup buffer object. + */ + + if (!srf->backup) { + ret = ttm_bo_create(&dev_priv->bdev, srf->backup_size, + ttm_bo_type_device, + &vmw_srf_placement, 0, 0, true, + NULL, &srf->backup); + if (unlikely(ret != 0)) + return ret; + } + + /* + * Reserve- and validate the backup DMA bo. + */ + + INIT_LIST_HEAD(&val_list); + val_buf.bo = ttm_bo_reference(srf->backup); + val_buf.new_sync_obj_arg = (void *)(unsigned long) + DRM_VMW_FENCE_FLAG_EXEC; + list_add_tail(&val_buf.head, &val_list); + ret = ttm_eu_reserve_buffers(&val_list); + if (unlikely(ret != 0)) + goto out_no_reserve; + + ret = ttm_bo_validate(srf->backup, &vmw_srf_placement, + true, false, false); + if (unlikely(ret != 0)) + goto out_no_validate; + + + /* + * Encode the dma- and surface destroy commands. + */ + + submit_size = vmw_surface_dma_size(srf) + vmw_surface_destroy_size(); + cmd = vmw_fifo_reserve(dev_priv, submit_size); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "eviction.\n"); + ret = -ENOMEM; + goto out_no_fifo; } + vmw_bo_get_guest_ptr(srf->backup, &ptr); + vmw_surface_dma_encode(srf, cmd, &ptr, false); + cmd += vmw_surface_dma_size(srf); + vmw_surface_destroy_encode(res->id, cmd); vmw_fifo_commit(dev_priv, submit_size); - (void) vmw_3d_resource_inc(dev_priv); - vmw_resource_activate(res, vmw_hw_surface_destroy); + + /* + * Surface memory usage accounting. + */ + + dev_priv->used_memory_size -= srf->backup_size; + + /* + * Create a fence object and fence the DMA buffer. + */ + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, + &fence, NULL); + ttm_eu_fence_buffer_objects(&val_list, fence); + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); + ttm_bo_unref(&val_buf.bo); + + /* + * Release the surface ID. + */ + + vmw_resource_release_id(res); + return 0; + +out_no_fifo: +out_no_validate: + if (srf->backup) + ttm_eu_backoff_reservation(&val_list); +out_no_reserve: + ttm_bo_unref(&val_buf.bo); + ttm_bo_unref(&srf->backup); + return ret; +} + + +/** + * vmw_surface_validate - make a surface available to the device, evicting + * other surfaces if needed. + * + * @dev_priv: Pointer to a device private struct. + * @srf: Pointer to a struct vmw_surface. + * + * Try to validate a surface and if it fails due to limited device resources, + * repeatedly try to evict other surfaces until the request can be + * acommodated. + * + * May return errors if out of resources. + */ +int vmw_surface_validate(struct vmw_private *dev_priv, + struct vmw_surface *srf) +{ + int ret; + struct vmw_surface *evict_srf; + + do { + write_lock(&dev_priv->resource_lock); + list_del_init(&srf->lru_head); + write_unlock(&dev_priv->resource_lock); + + ret = vmw_surface_do_validate(dev_priv, srf); + if (likely(ret != -EBUSY)) + break; + + write_lock(&dev_priv->resource_lock); + if (list_empty(&dev_priv->surface_lru)) { + DRM_ERROR("Out of device memory for surfaces.\n"); + ret = -EBUSY; + write_unlock(&dev_priv->resource_lock); + break; + } + + evict_srf = vmw_surface_reference + (list_first_entry(&dev_priv->surface_lru, + struct vmw_surface, + lru_head)); + list_del_init(&evict_srf->lru_head); + + write_unlock(&dev_priv->resource_lock); + (void) vmw_surface_evict(dev_priv, evict_srf); + + vmw_surface_unreference(&evict_srf); + + } while (1); + + if (unlikely(ret != 0 && srf->res.id != -1)) { + write_lock(&dev_priv->resource_lock); + list_add_tail(&srf->lru_head, &dev_priv->surface_lru); + write_unlock(&dev_priv->resource_lock); + } + + return ret; +} + + +/** + * vmw_surface_remove_from_lists - Remove surface resources from lookup lists + * + * @res: Pointer to a struct vmw_resource embedded in a struct vmw_surface + * + * As part of the resource destruction, remove the surface from any + * lookup lists. + */ +static void vmw_surface_remove_from_lists(struct vmw_resource *res) +{ + struct vmw_surface *srf = container_of(res, struct vmw_surface, res); + + list_del_init(&srf->lru_head); +} + +int vmw_surface_init(struct vmw_private *dev_priv, + struct vmw_surface *srf, + void (*res_free) (struct vmw_resource *res)) +{ + int ret; + struct vmw_resource *res = &srf->res; + + BUG_ON(res_free == NULL); + INIT_LIST_HEAD(&srf->lru_head); + ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr, + VMW_RES_SURFACE, true, res_free, + vmw_surface_remove_from_lists); + + if (unlikely(ret != 0)) + res_free(res); + + /* + * The surface won't be visible to hardware until a + * surface validate. + */ + + (void) vmw_3d_resource_inc(dev_priv, false); + vmw_resource_activate(res, vmw_hw_surface_destroy); + return ret; } static void vmw_user_surface_free(struct vmw_resource *res) @@ -486,12 +1139,81 @@ static void vmw_user_surface_free(struct vmw_resource *res) struct vmw_surface *srf = container_of(res, struct vmw_surface, res); struct vmw_user_surface *user_srf = container_of(srf, struct vmw_user_surface, srf); + struct vmw_private *dev_priv = srf->res.dev_priv; + uint32_t size = user_srf->size; + if (srf->backup) + ttm_bo_unref(&srf->backup); + kfree(srf->offsets); kfree(srf->sizes); kfree(srf->snooper.image); kfree(user_srf); + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +} + +/** + * vmw_resource_unreserve - unreserve resources previously reserved for + * command submission. + * + * @list_head: list of resources to unreserve. + * + * Currently only surfaces are considered, and unreserving a surface + * means putting it back on the device's surface lru list, + * so that it can be evicted if necessary. + * This function traverses the resource list and + * checks whether resources are surfaces, and in that case puts them back + * on the device's surface LRU list. + */ +void vmw_resource_unreserve(struct list_head *list) +{ + struct vmw_resource *res; + struct vmw_surface *srf; + rwlock_t *lock = NULL; + + list_for_each_entry(res, list, validate_head) { + + if (res->res_free != &vmw_surface_res_free && + res->res_free != &vmw_user_surface_free) + continue; + + if (unlikely(lock == NULL)) { + lock = &res->dev_priv->resource_lock; + write_lock(lock); + } + + srf = container_of(res, struct vmw_surface, res); + list_del_init(&srf->lru_head); + list_add_tail(&srf->lru_head, &res->dev_priv->surface_lru); + } + + if (lock != NULL) + write_unlock(lock); } +/** + * Helper function that looks either a surface or dmabuf. + * + * The pointer this pointed at by out_surf and out_buf needs to be null. + */ +int vmw_user_lookup_handle(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t handle, + struct vmw_surface **out_surf, + struct vmw_dma_buffer **out_buf) +{ + int ret; + + BUG_ON(*out_surf || *out_buf); + + ret = vmw_user_surface_lookup_handle(dev_priv, tfile, handle, out_surf); + if (!ret) + return 0; + + ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf); + return ret; +} + + int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, struct ttm_object_file *tfile, uint32_t handle, struct vmw_surface **out) @@ -556,8 +1278,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_surface *user_srf = - kmalloc(sizeof(*user_srf), GFP_KERNEL); + struct vmw_user_surface *user_srf; struct vmw_surface *srf; struct vmw_resource *res; struct vmw_resource *tmp; @@ -568,10 +1289,51 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct drm_vmw_size __user *user_sizes; int ret; - int i; + int i, j; + uint32_t cur_bo_offset; + struct drm_vmw_size *cur_size; + struct vmw_surface_offset *cur_offset; + uint32_t stride_bpp; + uint32_t bpp; + uint32_t num_sizes; + uint32_t size; + struct vmw_master *vmaster = vmw_master(file_priv->master); - if (unlikely(user_srf == NULL)) - return -ENOMEM; + if (unlikely(vmw_user_surface_size == 0)) + vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + + 128; + + num_sizes = 0; + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + num_sizes += req->mip_levels[i]; + + if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * + DRM_VMW_MAX_MIP_LEVELS) + return -EINVAL; + + size = vmw_user_surface_size + 128 + + ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) + + ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset)); + + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + size, false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for surface" + " creation.\n"); + goto out_unlock; + } + + user_srf = kmalloc(sizeof(*user_srf), GFP_KERNEL); + if (unlikely(user_srf == NULL)) { + ret = -ENOMEM; + goto out_no_user_srf; + } srf = &user_srf->srf; res = &srf->res; @@ -579,21 +1341,22 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, srf->flags = req->flags; srf->format = req->format; srf->scanout = req->scanout; - memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); - srf->num_sizes = 0; - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) - srf->num_sizes += srf->mip_levels[i]; + srf->backup = NULL; - if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES * - DRM_VMW_MAX_MIP_LEVELS) { - ret = -EINVAL; - goto out_err0; - } + memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); + srf->num_sizes = num_sizes; + user_srf->size = size; srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); if (unlikely(srf->sizes == NULL)) { ret = -ENOMEM; - goto out_err0; + goto out_no_sizes; + } + srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets), + GFP_KERNEL); + if (unlikely(srf->sizes == NULL)) { + ret = -ENOMEM; + goto out_no_offsets; } user_sizes = (struct drm_vmw_size __user *)(unsigned long) @@ -603,23 +1366,44 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, srf->num_sizes * sizeof(*srf->sizes)); if (unlikely(ret != 0)) { ret = -EFAULT; - goto out_err1; + goto out_no_copy; } + cur_bo_offset = 0; + cur_offset = srf->offsets; + cur_size = srf->sizes; + + bpp = vmw_sf_bpp[srf->format].bpp; + stride_bpp = vmw_sf_bpp[srf->format].s_bpp; + + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { + for (j = 0; j < srf->mip_levels[i]; ++j) { + uint32_t stride = + (cur_size->width * stride_bpp + 7) >> 3; + + cur_offset->face = i; + cur_offset->mip = j; + cur_offset->bo_offset = cur_bo_offset; + cur_bo_offset += stride * cur_size->height * + cur_size->depth * bpp / stride_bpp; + ++cur_offset; + ++cur_size; + } + } + srf->backup_size = cur_bo_offset; + if (srf->scanout && srf->num_sizes == 1 && srf->sizes[0].width == 64 && srf->sizes[0].height == 64 && srf->format == SVGA3D_A8R8G8B8) { - srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL); - /* clear the image */ - if (srf->snooper.image) { - memset(srf->snooper.image, 0x00, 64 * 64 * 4); - } else { + /* allocate image area and clear it */ + srf->snooper.image = kzalloc(64 * 64 * 4, GFP_KERNEL); + if (!srf->snooper.image) { DRM_ERROR("Failed to allocate cursor_image\n"); ret = -ENOMEM; - goto out_err1; + goto out_no_copy; } } else { srf->snooper.image = NULL; @@ -636,7 +1420,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock; tmp = vmw_resource_reference(&srf->res); ret = ttm_base_object_init(tfile, &user_srf->base, @@ -646,7 +1430,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, if (unlikely(ret != 0)) { vmw_resource_unreference(&tmp); vmw_resource_unreference(&res); - return ret; + goto out_unlock; } rep->sid = user_srf->base.hash.key; @@ -654,11 +1438,19 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, DRM_ERROR("Created bad Surface ID.\n"); vmw_resource_unreference(&res); + + ttm_read_unlock(&vmaster->lock); return 0; -out_err1: +out_no_copy: + kfree(srf->offsets); +out_no_offsets: kfree(srf->sizes); -out_err0: +out_no_sizes: kfree(user_srf); +out_no_user_srf: + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret; } @@ -972,7 +1764,7 @@ static int vmw_stream_init(struct vmw_private *dev_priv, int ret; ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr, - VMW_RES_STREAM, res_free); + VMW_RES_STREAM, false, res_free, NULL); if (unlikely(ret != 0)) { if (res_free == NULL) @@ -1002,8 +1794,11 @@ static void vmw_user_stream_free(struct vmw_resource *res) { struct vmw_user_stream *stream = container_of(res, struct vmw_user_stream, stream.res); + struct vmw_private *dev_priv = res->dev_priv; kfree(stream); + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_stream_size); } /** @@ -1057,23 +1852,56 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL); + struct vmw_user_stream *stream; struct vmw_resource *res; struct vmw_resource *tmp; struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; - if (unlikely(stream == NULL)) - return -ENOMEM; + /* + * Approximate idr memory usage with 128 bytes. It will be limited + * by maximum number_of streams anyway? + */ + + if (unlikely(vmw_user_stream_size == 0)) + vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + vmw_user_stream_size, + false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for stream" + " creation.\n"); + goto out_unlock; + } + + + stream = kmalloc(sizeof(*stream), GFP_KERNEL); + if (unlikely(stream == NULL)) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_stream_size); + ret = -ENOMEM; + goto out_unlock; + } res = &stream->stream.res; stream->base.shareable = false; stream->base.tfile = NULL; + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock; tmp = vmw_resource_reference(res); ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, @@ -1087,6 +1915,8 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, arg->stream_id = res->id; out_err: vmw_resource_unreference(&res); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret; } @@ -1120,3 +1950,76 @@ err_ref: vmw_resource_unreference(&res); return ret; } + + +int vmw_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_master *vmaster = vmw_master(file_priv->master); + struct vmw_user_dma_buffer *vmw_user_bo; + struct ttm_buffer_object *tmp; + int ret; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + vmw_user_bo = kzalloc(sizeof(*vmw_user_bo), GFP_KERNEL); + if (vmw_user_bo == NULL) + return -ENOMEM; + + ret = ttm_read_lock(&vmaster->lock, true); + if (ret != 0) { + kfree(vmw_user_bo); + return ret; + } + + ret = vmw_dmabuf_init(dev_priv, &vmw_user_bo->dma, args->size, + &vmw_vram_sys_placement, true, + &vmw_user_dmabuf_destroy); + if (ret != 0) + goto out_no_dmabuf; + + tmp = ttm_bo_reference(&vmw_user_bo->dma.base); + ret = ttm_base_object_init(vmw_fpriv(file_priv)->tfile, + &vmw_user_bo->base, + false, + ttm_buffer_type, + &vmw_user_dmabuf_release, NULL); + if (unlikely(ret != 0)) + goto out_no_base_object; + + args->handle = vmw_user_bo->base.hash.key; + +out_no_base_object: + ttm_bo_unref(&tmp); +out_no_dmabuf: + ttm_read_unlock(&vmaster->lock); + return ret; +} + +int vmw_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, + uint64_t *offset) +{ + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_dma_buffer *out_buf; + int ret; + + ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf); + if (ret != 0) + return -EINVAL; + + *offset = out_buf->base.addr_space_offset; + vmw_dmabuf_unreference(&out_buf); + return 0; +} + +int vmw_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle) +{ + return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + handle, TTM_REF_USAGE); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c index 1e8eedd..d3c11f5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c @@ -34,9 +34,8 @@ int vmw_mmap(struct file *filp, struct vm_area_struct *vma) struct vmw_private *dev_priv; if (unlikely(vma->vm_pgoff < VMWGFX_FILE_PAGE_OFFSET)) { - if (vmw_fifo_mmap(filp, vma) == 0) - return 0; - return drm_mmap(filp, vma); + DRM_ERROR("Illegal attempt to mmap old fifo space.\n"); + return -EINVAL; } file_priv = filp->private_data; |