Merge remote-tracking branch 'pfdo/drm-next' into drm-next
Pull in drm-next for the object find API changes. Fix the one place the API crashes. Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
commit
bd21a37d41
@ -68,6 +68,8 @@ Optional properties:
|
||||
- adi,disable-timing-generator: Only for ADV7533. Disables the internal timing
|
||||
generator. The chip will rely on the sync signals in the DSI data lanes,
|
||||
rather than generate its own timings for HDMI output.
|
||||
- clocks: from common clock binding: reference to the CEC clock.
|
||||
- clock-names: from common clock binding: must be "cec".
|
||||
|
||||
Required nodes:
|
||||
|
||||
@ -89,6 +91,8 @@ Example
|
||||
reg = <39>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
|
||||
clocks = <&cec_clock>;
|
||||
clock-names = "cec";
|
||||
|
||||
adi,input-depth = <8>;
|
||||
adi,input-colorspace = "rgb";
|
||||
|
49
Documentation/devicetree/bindings/display/bridge/sii9234.txt
Normal file
49
Documentation/devicetree/bindings/display/bridge/sii9234.txt
Normal file
@ -0,0 +1,49 @@
|
||||
Silicon Image SiI9234 HDMI/MHL bridge bindings
|
||||
|
||||
Required properties:
|
||||
- compatible : "sil,sii9234".
|
||||
- reg : I2C address for TPI interface, use 0x39
|
||||
- avcc33-supply : MHL/USB Switch Supply Voltage (3.3V)
|
||||
- iovcc18-supply : I/O Supply Voltage (1.8V)
|
||||
- avcc12-supply : TMDS Analog Supply Voltage (1.2V)
|
||||
- cvcc12-supply : Digital Core Supply Voltage (1.2V)
|
||||
- interrupts, interrupt-parent: interrupt specifier of INT pin
|
||||
- reset-gpios: gpio specifier of RESET pin (active low)
|
||||
- video interfaces: Device node can contain two video interface port
|
||||
nodes for HDMI encoder and connector according to [1].
|
||||
- port@0 - MHL to HDMI
|
||||
- port@1 - MHL to connector
|
||||
|
||||
[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
|
||||
|
||||
|
||||
Example:
|
||||
sii9234@39 {
|
||||
compatible = "sil,sii9234";
|
||||
reg = <0x39>;
|
||||
avcc33-supply = <&vcc33mhl>;
|
||||
iovcc18-supply = <&vcc18mhl>;
|
||||
avcc12-supply = <&vsil12>;
|
||||
cvcc12-supply = <&vsil12>;
|
||||
reset-gpios = <&gpf3 4 GPIO_ACTIVE_LOW>;
|
||||
interrupt-parent = <&gpf3>;
|
||||
interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
mhl_to_hdmi: endpoint {
|
||||
remote-endpoint = <&hdmi_to_mhl>;
|
||||
};
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
mhl_to_connector: endpoint {
|
||||
remote-endpoint = <&connector_to_mhl>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,49 @@
|
||||
This binding covers the official 7" (800x480) Raspberry Pi touchscreen
|
||||
panel.
|
||||
|
||||
This DSI panel contains:
|
||||
|
||||
- TC358762 DSI->DPI bridge
|
||||
- Atmel microcontroller on I2C for power sequencing the DSI bridge and
|
||||
controlling backlight
|
||||
- Touchscreen controller on I2C for touch input
|
||||
|
||||
and this binding covers the DSI display parts but not its touch input.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "raspberrypi,7inch-touchscreen-panel"
|
||||
- reg: Must be "45"
|
||||
- port: See panel-common.txt
|
||||
|
||||
Example:
|
||||
|
||||
dsi1: dsi@7e700000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
<...>
|
||||
|
||||
port {
|
||||
dsi_out_port: endpoint {
|
||||
remote-endpoint = <&panel_dsi_port>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
i2c_dsi: i2c {
|
||||
compatible = "i2c-gpio";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
gpios = <&gpio 28 0
|
||||
&gpio 29 0>;
|
||||
|
||||
lcd@45 {
|
||||
compatible = "raspberrypi,7inch-touchscreen-panel";
|
||||
reg = <0x45>;
|
||||
|
||||
port {
|
||||
panel_dsi_port: endpoint {
|
||||
remote-endpoint = <&dsi_out_port>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -41,14 +41,17 @@ CEC. It is one end of the pipeline.
|
||||
Required properties:
|
||||
- compatible: value must be one of:
|
||||
* allwinner,sun5i-a10s-hdmi
|
||||
* allwinner,sun6i-a31-hdmi
|
||||
- reg: base address and size of memory-mapped region
|
||||
- interrupts: interrupt associated to this IP
|
||||
- clocks: phandles to the clocks feeding the HDMI encoder
|
||||
* ahb: the HDMI interface clock
|
||||
* mod: the HDMI module clock
|
||||
* ddc: the HDMI ddc clock (A31 only)
|
||||
* pll-0: the first video PLL
|
||||
* pll-1: the second video PLL
|
||||
- clock-names: the clock names mentioned above
|
||||
- resets: phandle to the reset control for the HDMI encoder (A31 only)
|
||||
- dmas: phandles to the DMA channels used by the HDMI encoder
|
||||
* ddc-tx: The channel for DDC transmission
|
||||
* ddc-rx: The channel for DDC reception
|
||||
|
@ -266,8 +266,7 @@ EXPORT_SYMBOL(reservation_object_add_excl_fence);
|
||||
* @dst: the destination reservation object
|
||||
* @src: the source reservation object
|
||||
*
|
||||
* Copy all fences from src to dst. Both src->lock as well as dst-lock must be
|
||||
* held.
|
||||
* Copy all fences from src to dst. dst-lock must be held.
|
||||
*/
|
||||
int reservation_object_copy_fences(struct reservation_object *dst,
|
||||
struct reservation_object *src)
|
||||
@ -277,33 +276,62 @@ int reservation_object_copy_fences(struct reservation_object *dst,
|
||||
size_t size;
|
||||
unsigned i;
|
||||
|
||||
src_list = reservation_object_get_list(src);
|
||||
rcu_read_lock();
|
||||
src_list = rcu_dereference(src->fence);
|
||||
|
||||
retry:
|
||||
if (src_list) {
|
||||
size = offsetof(typeof(*src_list),
|
||||
shared[src_list->shared_count]);
|
||||
unsigned shared_count = src_list->shared_count;
|
||||
|
||||
size = offsetof(typeof(*src_list), shared[shared_count]);
|
||||
rcu_read_unlock();
|
||||
|
||||
dst_list = kmalloc(size, GFP_KERNEL);
|
||||
if (!dst_list)
|
||||
return -ENOMEM;
|
||||
|
||||
dst_list->shared_count = src_list->shared_count;
|
||||
dst_list->shared_max = src_list->shared_count;
|
||||
for (i = 0; i < src_list->shared_count; ++i)
|
||||
dst_list->shared[i] =
|
||||
dma_fence_get(src_list->shared[i]);
|
||||
rcu_read_lock();
|
||||
src_list = rcu_dereference(src->fence);
|
||||
if (!src_list || src_list->shared_count > shared_count) {
|
||||
kfree(dst_list);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
dst_list->shared_count = 0;
|
||||
dst_list->shared_max = shared_count;
|
||||
for (i = 0; i < src_list->shared_count; ++i) {
|
||||
struct dma_fence *fence;
|
||||
|
||||
fence = rcu_dereference(src_list->shared[i]);
|
||||
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
|
||||
&fence->flags))
|
||||
continue;
|
||||
|
||||
if (!dma_fence_get_rcu(fence)) {
|
||||
kfree(dst_list);
|
||||
src_list = rcu_dereference(src->fence);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (dma_fence_is_signaled(fence)) {
|
||||
dma_fence_put(fence);
|
||||
continue;
|
||||
}
|
||||
|
||||
dst_list->shared[dst_list->shared_count++] = fence;
|
||||
}
|
||||
} else {
|
||||
dst_list = NULL;
|
||||
}
|
||||
|
||||
new = dma_fence_get_rcu_safe(&src->fence_excl);
|
||||
rcu_read_unlock();
|
||||
|
||||
kfree(dst->staged);
|
||||
dst->staged = NULL;
|
||||
|
||||
src_list = reservation_object_get_list(dst);
|
||||
|
||||
old = reservation_object_get_excl(dst);
|
||||
new = reservation_object_get_excl(src);
|
||||
|
||||
dma_fence_get(new);
|
||||
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&dst->seq);
|
||||
|
@ -231,7 +231,7 @@ amdgpu_connector_update_scratch_regs(struct drm_connector *connector,
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev,
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
@ -256,7 +256,7 @@ amdgpu_connector_find_encoder(struct drm_connector *connector,
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
encoder = drm_encoder_find(connector->dev,
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
@ -372,7 +372,7 @@ amdgpu_connector_best_single_encoder(struct drm_connector *connector)
|
||||
|
||||
/* pick the encoder ids */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1077,7 +1077,7 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
@ -1134,7 +1134,7 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector)
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
@ -1153,7 +1153,7 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector)
|
||||
/* then check use digitial */
|
||||
/* pick the first one */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1294,7 +1294,7 @@ u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev,
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
@ -1323,7 +1323,7 @@ static bool amdgpu_connector_encoder_is_hbr2(struct drm_connector *connector)
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
encoder = drm_encoder_find(connector->dev,
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
@ -288,7 +288,7 @@ dce_virtual_encoder(struct drm_connector *connector)
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
@ -298,7 +298,7 @@ dce_virtual_encoder(struct drm_connector *connector)
|
||||
|
||||
/* pick the first one */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2638,7 +2638,7 @@ static struct drm_encoder *best_encoder(struct drm_connector *connector)
|
||||
|
||||
/* pick the encoder ids */
|
||||
if (enc_id) {
|
||||
obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
|
||||
obj = drm_mode_object_find(connector->dev, NULL, enc_id, DRM_MODE_OBJECT_ENCODER);
|
||||
if (!obj) {
|
||||
DRM_ERROR("Couldn't find a matching encoder for our connector\n");
|
||||
return NULL;
|
||||
|
@ -713,7 +713,7 @@ static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connect
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
/* pick the encoder ids */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ bochs_connector_best_encoder(struct drm_connector *connector)
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
/* pick the encoder ids */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ config DRM_PARADE_PS8622
|
||||
|
||||
config DRM_SIL_SII8620
|
||||
tristate "Silicon Image SII8620 HDMI/MHL bridge"
|
||||
depends on OF
|
||||
depends on OF && RC_CORE
|
||||
select DRM_KMS_HELPER
|
||||
help
|
||||
Silicon Image SII8620 HDMI/MHL bridge chip driver.
|
||||
@ -84,6 +84,14 @@ config DRM_SII902X
|
||||
---help---
|
||||
Silicon Image sii902x bridge chip driver.
|
||||
|
||||
config DRM_SII9234
|
||||
tristate "Silicon Image SII9234 HDMI/MHL bridge"
|
||||
depends on OF
|
||||
---help---
|
||||
Say Y here if you want support for the MHL interface.
|
||||
It is an I2C driver, that detects connection of MHL bridge
|
||||
and starts encapsulation of HDMI signal.
|
||||
|
||||
config DRM_TOSHIBA_TC358767
|
||||
tristate "Toshiba TC358767 eDP bridge"
|
||||
depends on OF
|
||||
|
@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
|
||||
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
|
||||
obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
|
||||
obj-$(CONFIG_DRM_SII902X) += sii902x.o
|
||||
obj-$(CONFIG_DRM_SII9234) += sii9234.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
|
||||
obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
|
||||
|
@ -21,3 +21,11 @@ config DRM_I2C_ADV7533
|
||||
default y
|
||||
help
|
||||
Support for the Analog Devices ADV7533 DSI to HDMI encoder.
|
||||
|
||||
config DRM_I2C_ADV7511_CEC
|
||||
bool "ADV7511/33 HDMI CEC driver"
|
||||
depends on DRM_I2C_ADV7511
|
||||
select CEC_CORE
|
||||
default y
|
||||
help
|
||||
When selected the HDMI transmitter will support the CEC feature.
|
||||
|
@ -1,4 +1,5 @@
|
||||
adv7511-y := adv7511_drv.o
|
||||
adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.o
|
||||
adv7511-$(CONFIG_DRM_I2C_ADV7511_CEC) += adv7511_cec.o
|
||||
adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
|
||||
|
@ -195,6 +195,25 @@
|
||||
#define ADV7511_PACKET_GM(x) ADV7511_PACKET(5, x)
|
||||
#define ADV7511_PACKET_SPARE(x) ADV7511_PACKET(6, x)
|
||||
|
||||
#define ADV7511_REG_CEC_TX_FRAME_HDR 0x00
|
||||
#define ADV7511_REG_CEC_TX_FRAME_DATA0 0x01
|
||||
#define ADV7511_REG_CEC_TX_FRAME_LEN 0x10
|
||||
#define ADV7511_REG_CEC_TX_ENABLE 0x11
|
||||
#define ADV7511_REG_CEC_TX_RETRY 0x12
|
||||
#define ADV7511_REG_CEC_TX_LOW_DRV_CNT 0x14
|
||||
#define ADV7511_REG_CEC_RX_FRAME_HDR 0x15
|
||||
#define ADV7511_REG_CEC_RX_FRAME_DATA0 0x16
|
||||
#define ADV7511_REG_CEC_RX_FRAME_LEN 0x25
|
||||
#define ADV7511_REG_CEC_RX_ENABLE 0x26
|
||||
#define ADV7511_REG_CEC_RX_BUFFERS 0x4a
|
||||
#define ADV7511_REG_CEC_LOG_ADDR_MASK 0x4b
|
||||
#define ADV7511_REG_CEC_LOG_ADDR_0_1 0x4c
|
||||
#define ADV7511_REG_CEC_LOG_ADDR_2 0x4d
|
||||
#define ADV7511_REG_CEC_CLK_DIV 0x4e
|
||||
#define ADV7511_REG_CEC_SOFT_RESET 0x50
|
||||
|
||||
#define ADV7533_REG_CEC_OFFSET 0x70
|
||||
|
||||
enum adv7511_input_clock {
|
||||
ADV7511_INPUT_CLOCK_1X,
|
||||
ADV7511_INPUT_CLOCK_2X,
|
||||
@ -297,6 +316,8 @@ enum adv7511_type {
|
||||
ADV7533,
|
||||
};
|
||||
|
||||
#define ADV7511_MAX_ADDRS 3
|
||||
|
||||
struct adv7511 {
|
||||
struct i2c_client *i2c_main;
|
||||
struct i2c_client *i2c_edid;
|
||||
@ -341,15 +362,27 @@ struct adv7511 {
|
||||
|
||||
enum adv7511_type type;
|
||||
struct platform_device *audio_pdev;
|
||||
|
||||
struct cec_adapter *cec_adap;
|
||||
u8 cec_addr[ADV7511_MAX_ADDRS];
|
||||
u8 cec_valid_addrs;
|
||||
bool cec_enabled_adap;
|
||||
struct clk *cec_clk;
|
||||
u32 cec_clk_freq;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM_I2C_ADV7511_CEC
|
||||
int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
|
||||
unsigned int offset);
|
||||
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_I2C_ADV7533
|
||||
void adv7533_dsi_power_on(struct adv7511 *adv);
|
||||
void adv7533_dsi_power_off(struct adv7511 *adv);
|
||||
void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode);
|
||||
int adv7533_patch_registers(struct adv7511 *adv);
|
||||
void adv7533_uninit_cec(struct adv7511 *adv);
|
||||
int adv7533_init_cec(struct adv7511 *adv);
|
||||
int adv7533_patch_cec_registers(struct adv7511 *adv);
|
||||
int adv7533_attach_dsi(struct adv7511 *adv);
|
||||
void adv7533_detach_dsi(struct adv7511 *adv);
|
||||
int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);
|
||||
@ -372,11 +405,7 @@ static inline int adv7533_patch_registers(struct adv7511 *adv)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void adv7533_uninit_cec(struct adv7511 *adv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adv7533_init_cec(struct adv7511 *adv)
|
||||
static inline int adv7533_patch_cec_registers(struct adv7511 *adv)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
337
drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
Normal file
337
drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* adv7511_cec.c - Analog Devices ADV7511/33 cec driver
|
||||
*
|
||||
* Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
#include "adv7511.h"
|
||||
|
||||
#define ADV7511_INT1_CEC_MASK \
|
||||
(ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
|
||||
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1)
|
||||
|
||||
static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
|
||||
{
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int val;
|
||||
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_ENABLE + offset, &val))
|
||||
return;
|
||||
|
||||
if ((val & 0x01) == 0)
|
||||
return;
|
||||
|
||||
if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) {
|
||||
cec_transmit_attempt_done(adv7511->cec_adap,
|
||||
CEC_TX_STATUS_ARB_LOST);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) {
|
||||
u8 status;
|
||||
u8 err_cnt = 0;
|
||||
u8 nack_cnt = 0;
|
||||
u8 low_drive_cnt = 0;
|
||||
unsigned int cnt;
|
||||
|
||||
/*
|
||||
* We set this status bit since this hardware performs
|
||||
* retransmissions.
|
||||
*/
|
||||
status = CEC_TX_STATUS_MAX_RETRIES;
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_LOW_DRV_CNT + offset, &cnt)) {
|
||||
err_cnt = 1;
|
||||
status |= CEC_TX_STATUS_ERROR;
|
||||
} else {
|
||||
nack_cnt = cnt & 0xf;
|
||||
if (nack_cnt)
|
||||
status |= CEC_TX_STATUS_NACK;
|
||||
low_drive_cnt = cnt >> 4;
|
||||
if (low_drive_cnt)
|
||||
status |= CEC_TX_STATUS_LOW_DRIVE;
|
||||
}
|
||||
cec_transmit_done(adv7511->cec_adap, status,
|
||||
0, nack_cnt, low_drive_cnt, err_cnt);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) {
|
||||
cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
|
||||
{
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
|
||||
ADV7511_INT1_CEC_TX_ARBIT_LOST |
|
||||
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
|
||||
struct cec_msg msg = {};
|
||||
unsigned int len;
|
||||
unsigned int val;
|
||||
u8 i;
|
||||
|
||||
if (irq1 & irq_tx_mask)
|
||||
adv_cec_tx_raw_status(adv7511, irq1);
|
||||
|
||||
if (!(irq1 & ADV7511_INT1_CEC_RX_READY1))
|
||||
return;
|
||||
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_FRAME_LEN + offset, &len))
|
||||
return;
|
||||
|
||||
msg.len = len & 0x1f;
|
||||
|
||||
if (msg.len > 16)
|
||||
msg.len = 16;
|
||||
|
||||
if (!msg.len)
|
||||
return;
|
||||
|
||||
for (i = 0; i < msg.len; i++) {
|
||||
regmap_read(adv7511->regmap_cec,
|
||||
i + ADV7511_REG_CEC_RX_FRAME_HDR + offset, &val);
|
||||
msg.msg[i] = val;
|
||||
}
|
||||
|
||||
/* toggle to re-enable rx 1 */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 1);
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
|
||||
cec_received_msg(adv7511->cec_adap, &msg);
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
|
||||
if (adv7511->i2c_cec == NULL)
|
||||
return -EIO;
|
||||
|
||||
if (!adv7511->cec_enabled_adap && enable) {
|
||||
/* power up cec section */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_CLK_DIV + offset,
|
||||
0x03, 0x01);
|
||||
/* legacy mode and clear all rx buffers */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x07);
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
|
||||
/* initially disable tx */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);
|
||||
/* enabled irqs: */
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready 1 */
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1), 0x3f,
|
||||
ADV7511_INT1_CEC_MASK);
|
||||
} else if (adv7511->cec_enabled_adap && !enable) {
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1), 0x3f, 0);
|
||||
/* disable address mask 1-3 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x70, 0x00);
|
||||
/* power down cec section */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_CLK_DIV + offset,
|
||||
0x03, 0x00);
|
||||
adv7511->cec_valid_addrs = 0;
|
||||
}
|
||||
adv7511->cec_enabled_adap = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int i, free_idx = ADV7511_MAX_ADDRS;
|
||||
|
||||
if (!adv7511->cec_enabled_adap)
|
||||
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
|
||||
|
||||
if (addr == CEC_LOG_ADDR_INVALID) {
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x70, 0);
|
||||
adv7511->cec_valid_addrs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
|
||||
bool is_valid = adv7511->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
|
||||
free_idx = i;
|
||||
if (is_valid && adv7511->cec_addr[i] == addr)
|
||||
return 0;
|
||||
}
|
||||
if (i == ADV7511_MAX_ADDRS) {
|
||||
i = free_idx;
|
||||
if (i == ADV7511_MAX_ADDRS)
|
||||
return -ENXIO;
|
||||
}
|
||||
adv7511->cec_addr[i] = addr;
|
||||
adv7511->cec_valid_addrs |= 1 << i;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* enable address mask 0 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x10, 0x10);
|
||||
/* set address for mask 0 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
|
||||
0x0f, addr);
|
||||
break;
|
||||
case 1:
|
||||
/* enable address mask 1 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x20, 0x20);
|
||||
/* set address for mask 1 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
|
||||
0xf0, addr << 4);
|
||||
break;
|
||||
case 2:
|
||||
/* enable address mask 2 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x40, 0x40);
|
||||
/* set address for mask 1 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_2 + offset,
|
||||
0x0f, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* The number of retries is the number of attempts - 1, but retry
|
||||
* at least once. It's not clear if a value of 0 is allowed, so
|
||||
* let's do at least one retry.
|
||||
*/
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_RETRY + offset,
|
||||
0x70, max(1, attempts - 1) << 4);
|
||||
|
||||
/* blocking, clear cec tx irq status */
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38);
|
||||
|
||||
/* write data */
|
||||
for (i = 0; i < len; i++)
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
i + ADV7511_REG_CEC_TX_FRAME_HDR + offset,
|
||||
msg->msg[i]);
|
||||
|
||||
/* set length (data + header) */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_FRAME_LEN + offset, len);
|
||||
/* start transmit, enable tx */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_ENABLE + offset, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops adv7511_cec_adap_ops = {
|
||||
.adap_enable = adv7511_cec_adap_enable,
|
||||
.adap_log_addr = adv7511_cec_adap_log_addr,
|
||||
.adap_transmit = adv7511_cec_adap_transmit,
|
||||
};
|
||||
|
||||
static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)
|
||||
{
|
||||
adv7511->cec_clk = devm_clk_get(dev, "cec");
|
||||
if (IS_ERR(adv7511->cec_clk)) {
|
||||
int ret = PTR_ERR(adv7511->cec_clk);
|
||||
|
||||
adv7511->cec_clk = NULL;
|
||||
return ret;
|
||||
}
|
||||
clk_prepare_enable(adv7511->cec_clk);
|
||||
adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
|
||||
unsigned int offset)
|
||||
{
|
||||
int ret = adv7511_cec_parse_dt(dev, adv7511);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
|
||||
adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);
|
||||
if (IS_ERR(adv7511->cec_adap))
|
||||
return PTR_ERR(adv7511->cec_adap);
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, 0);
|
||||
/* cec soft reset */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_SOFT_RESET + offset, 0x01);
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);
|
||||
|
||||
/* legacy mode */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x00);
|
||||
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_CLK_DIV + offset,
|
||||
((adv7511->cec_clk_freq / 750000) - 1) << 2);
|
||||
|
||||
ret = cec_register_adapter(adv7511->cec_adap, dev);
|
||||
if (ret) {
|
||||
cec_delete_adapter(adv7511->cec_adap);
|
||||
adv7511->cec_adap = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -11,12 +11,15 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
#include "adv7511.h"
|
||||
|
||||
/* ADI recommended values for proper operation. */
|
||||
@ -336,8 +339,10 @@ static void __adv7511_power_on(struct adv7511 *adv7511)
|
||||
*/
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
|
||||
ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD);
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
|
||||
ADV7511_INT1_DDC_ERROR);
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1),
|
||||
ADV7511_INT1_DDC_ERROR,
|
||||
ADV7511_INT1_DDC_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -373,6 +378,9 @@ static void __adv7511_power_off(struct adv7511 *adv7511)
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN,
|
||||
ADV7511_POWER_POWER_DOWN);
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1),
|
||||
ADV7511_INT1_DDC_ERROR, 0);
|
||||
regcache_mark_dirty(adv7511->regmap);
|
||||
}
|
||||
|
||||
@ -423,6 +431,8 @@ static void adv7511_hpd_work(struct work_struct *work)
|
||||
|
||||
if (adv7511->connector.status != status) {
|
||||
adv7511->connector.status = status;
|
||||
if (status == connector_status_disconnected)
|
||||
cec_phys_addr_invalidate(adv7511->cec_adap);
|
||||
drm_kms_helper_hotplug_event(adv7511->connector.dev);
|
||||
}
|
||||
}
|
||||
@ -453,6 +463,10 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
|
||||
wake_up_all(&adv7511->wq);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_I2C_ADV7511_CEC
|
||||
adv7511_cec_irq_process(adv7511, irq1);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -595,6 +609,8 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
|
||||
|
||||
kfree(edid);
|
||||
|
||||
cec_s_phys_addr_from_edid(adv7511->cec_adap, edid);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -919,6 +935,65 @@ static void adv7511_uninit_regulators(struct adv7511 *adv)
|
||||
regulator_bulk_disable(adv->num_supplies, adv->supplies);
|
||||
}
|
||||
|
||||
static bool adv7511_cec_register_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (adv7511->type == ADV7533)
|
||||
reg -= ADV7533_REG_CEC_OFFSET;
|
||||
|
||||
switch (reg) {
|
||||
case ADV7511_REG_CEC_RX_FRAME_HDR:
|
||||
case ADV7511_REG_CEC_RX_FRAME_DATA0...
|
||||
ADV7511_REG_CEC_RX_FRAME_DATA0 + 14:
|
||||
case ADV7511_REG_CEC_RX_FRAME_LEN:
|
||||
case ADV7511_REG_CEC_RX_BUFFERS:
|
||||
case ADV7511_REG_CEC_TX_LOW_DRV_CNT:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config adv7511_cec_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = 0xff,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = adv7511_cec_register_volatile,
|
||||
};
|
||||
|
||||
static int adv7511_init_cec_regmap(struct adv7511 *adv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter,
|
||||
adv->i2c_main->addr - 1);
|
||||
if (!adv->i2c_cec)
|
||||
return -ENOMEM;
|
||||
i2c_set_clientdata(adv->i2c_cec, adv);
|
||||
|
||||
adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
|
||||
&adv7511_cec_regmap_config);
|
||||
if (IS_ERR(adv->regmap_cec)) {
|
||||
ret = PTR_ERR(adv->regmap_cec);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (adv->type == ADV7533) {
|
||||
ret = adv7533_patch_cec_registers(adv);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
i2c_unregister_device(adv->i2c_cec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adv7511_parse_dt(struct device_node *np,
|
||||
struct adv7511_link_config *config)
|
||||
{
|
||||
@ -1009,6 +1084,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
struct device *dev = &i2c->dev;
|
||||
unsigned int main_i2c_addr = i2c->addr << 1;
|
||||
unsigned int edid_i2c_addr = main_i2c_addr + 4;
|
||||
unsigned int offset;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
@ -1092,11 +1168,9 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
goto uninit_regulators;
|
||||
}
|
||||
|
||||
if (adv7511->type == ADV7533) {
|
||||
ret = adv7533_init_cec(adv7511);
|
||||
if (ret)
|
||||
goto err_i2c_unregister_edid;
|
||||
}
|
||||
ret = adv7511_init_cec_regmap(adv7511);
|
||||
if (ret)
|
||||
goto err_i2c_unregister_edid;
|
||||
|
||||
INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
|
||||
|
||||
@ -1111,10 +1185,6 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
goto err_unregister_cec;
|
||||
}
|
||||
|
||||
/* CEC is unused for now */
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,
|
||||
ADV7511_CEC_CTRL_POWER_DOWN);
|
||||
|
||||
adv7511_power_off(adv7511);
|
||||
|
||||
i2c_set_clientdata(i2c, adv7511);
|
||||
@ -1129,10 +1199,23 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
|
||||
adv7511_audio_init(dev, adv7511);
|
||||
|
||||
offset = adv7511->type == ADV7533 ? ADV7533_REG_CEC_OFFSET : 0;
|
||||
|
||||
#ifdef CONFIG_DRM_I2C_ADV7511_CEC
|
||||
ret = adv7511_cec_init(dev, adv7511, offset);
|
||||
if (ret)
|
||||
goto err_unregister_cec;
|
||||
#else
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
|
||||
ADV7511_CEC_CTRL_POWER_DOWN);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_cec:
|
||||
adv7533_uninit_cec(adv7511);
|
||||
i2c_unregister_device(adv7511->i2c_cec);
|
||||
if (adv7511->cec_clk)
|
||||
clk_disable_unprepare(adv7511->cec_clk);
|
||||
err_i2c_unregister_edid:
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
uninit_regulators:
|
||||
@ -1145,10 +1228,11 @@ static int adv7511_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (adv7511->type == ADV7533) {
|
||||
if (adv7511->type == ADV7533)
|
||||
adv7533_detach_dsi(adv7511);
|
||||
adv7533_uninit_cec(adv7511);
|
||||
}
|
||||
i2c_unregister_device(adv7511->i2c_cec);
|
||||
if (adv7511->cec_clk)
|
||||
clk_disable_unprepare(adv7511->cec_clk);
|
||||
|
||||
adv7511_uninit_regulators(adv7511);
|
||||
|
||||
@ -1156,6 +1240,8 @@ static int adv7511_remove(struct i2c_client *i2c)
|
||||
|
||||
adv7511_audio_exit(adv7511);
|
||||
|
||||
cec_unregister_adapter(adv7511->cec_adap);
|
||||
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
|
||||
return 0;
|
||||
|
@ -32,14 +32,6 @@ static const struct reg_sequence adv7533_cec_fixed_registers[] = {
|
||||
{ 0x05, 0xc8 },
|
||||
};
|
||||
|
||||
static const struct regmap_config adv7533_cec_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = 0xff,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = adv->dsi;
|
||||
@ -145,37 +137,11 @@ int adv7533_patch_registers(struct adv7511 *adv)
|
||||
ARRAY_SIZE(adv7533_fixed_registers));
|
||||
}
|
||||
|
||||
void adv7533_uninit_cec(struct adv7511 *adv)
|
||||
int adv7533_patch_cec_registers(struct adv7511 *adv)
|
||||
{
|
||||
i2c_unregister_device(adv->i2c_cec);
|
||||
}
|
||||
|
||||
int adv7533_init_cec(struct adv7511 *adv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter,
|
||||
adv->i2c_main->addr - 1);
|
||||
if (!adv->i2c_cec)
|
||||
return -ENOMEM;
|
||||
|
||||
adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
|
||||
&adv7533_cec_regmap_config);
|
||||
if (IS_ERR(adv->regmap_cec)) {
|
||||
ret = PTR_ERR(adv->regmap_cec);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = regmap_register_patch(adv->regmap_cec,
|
||||
return regmap_register_patch(adv->regmap_cec,
|
||||
adv7533_cec_fixed_registers,
|
||||
ARRAY_SIZE(adv7533_cec_fixed_registers));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
adv7533_uninit_cec(adv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adv7533_attach_dsi(struct adv7511 *adv)
|
||||
|
@ -188,7 +188,15 @@ EXPORT_SYMBOL(drm_panel_bridge_add);
|
||||
*/
|
||||
void drm_panel_bridge_remove(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
struct panel_bridge *panel_bridge;
|
||||
|
||||
if (!bridge)
|
||||
return;
|
||||
|
||||
if (bridge->funcs != &panel_bridge_bridge_funcs)
|
||||
return;
|
||||
|
||||
panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_bridge_remove(bridge);
|
||||
devm_kfree(panel_bridge->panel->dev, bridge);
|
||||
|
994
drivers/gpu/drm/bridge/sii9234.c
Normal file
994
drivers/gpu/drm/bridge/sii9234.c
Normal file
@ -0,0 +1,994 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Samsung Electronics
|
||||
*
|
||||
* Authors:
|
||||
* Tomasz Stanislawski <t.stanislaws@samsung.com>
|
||||
* Maciej Purski <m.purski@samsung.com>
|
||||
*
|
||||
* Based on sii9234 driver created by:
|
||||
* Adam Hampson <ahampson@sta.samsung.com>
|
||||
* Erik Gilling <konkers@android.com>
|
||||
* Shankar Bandal <shankar.b@samsung.com>
|
||||
* Dharam Kumar <dharam.kr@samsung.com>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#include <drm/bridge/mhl.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define CBUS_DEVCAP_OFFSET 0x80
|
||||
|
||||
#define SII9234_MHL_VERSION 0x11
|
||||
#define SII9234_SCRATCHPAD_SIZE 0x10
|
||||
#define SII9234_INT_STAT_SIZE 0x33
|
||||
|
||||
#define BIT_TMDS_CCTRL_TMDS_OE BIT(4)
|
||||
#define MHL_HPD_OUT_OVR_EN BIT(4)
|
||||
#define MHL_HPD_OUT_OVR_VAL BIT(5)
|
||||
#define MHL_INIT_TIMEOUT 0x0C
|
||||
|
||||
/* MHL Tx registers and bits */
|
||||
#define MHL_TX_SRST 0x05
|
||||
#define MHL_TX_SYSSTAT_REG 0x09
|
||||
#define MHL_TX_INTR1_REG 0x71
|
||||
#define MHL_TX_INTR4_REG 0x74
|
||||
#define MHL_TX_INTR1_ENABLE_REG 0x75
|
||||
#define MHL_TX_INTR4_ENABLE_REG 0x78
|
||||
#define MHL_TX_INT_CTRL_REG 0x79
|
||||
#define MHL_TX_TMDS_CCTRL 0x80
|
||||
#define MHL_TX_DISC_CTRL1_REG 0x90
|
||||
#define MHL_TX_DISC_CTRL2_REG 0x91
|
||||
#define MHL_TX_DISC_CTRL3_REG 0x92
|
||||
#define MHL_TX_DISC_CTRL4_REG 0x93
|
||||
#define MHL_TX_DISC_CTRL5_REG 0x94
|
||||
#define MHL_TX_DISC_CTRL6_REG 0x95
|
||||
#define MHL_TX_DISC_CTRL7_REG 0x96
|
||||
#define MHL_TX_DISC_CTRL8_REG 0x97
|
||||
#define MHL_TX_STAT2_REG 0x99
|
||||
#define MHL_TX_MHLTX_CTL1_REG 0xA0
|
||||
#define MHL_TX_MHLTX_CTL2_REG 0xA1
|
||||
#define MHL_TX_MHLTX_CTL4_REG 0xA3
|
||||
#define MHL_TX_MHLTX_CTL6_REG 0xA5
|
||||
#define MHL_TX_MHLTX_CTL7_REG 0xA6
|
||||
|
||||
#define RSEN_STATUS BIT(2)
|
||||
#define HPD_CHANGE_INT BIT(6)
|
||||
#define RSEN_CHANGE_INT BIT(5)
|
||||
#define RGND_READY_INT BIT(6)
|
||||
#define VBUS_LOW_INT BIT(5)
|
||||
#define CBUS_LKOUT_INT BIT(4)
|
||||
#define MHL_DISC_FAIL_INT BIT(3)
|
||||
#define MHL_EST_INT BIT(2)
|
||||
#define HPD_CHANGE_INT_MASK BIT(6)
|
||||
#define RSEN_CHANGE_INT_MASK BIT(5)
|
||||
|
||||
#define RGND_READY_MASK BIT(6)
|
||||
#define CBUS_LKOUT_MASK BIT(4)
|
||||
#define MHL_DISC_FAIL_MASK BIT(3)
|
||||
#define MHL_EST_MASK BIT(2)
|
||||
|
||||
#define SKIP_GND BIT(6)
|
||||
|
||||
#define ATT_THRESH_SHIFT 0x04
|
||||
#define ATT_THRESH_MASK (0x03 << ATT_THRESH_SHIFT)
|
||||
#define USB_D_OEN BIT(3)
|
||||
#define DEGLITCH_TIME_MASK 0x07
|
||||
#define DEGLITCH_TIME_2MS 0
|
||||
#define DEGLITCH_TIME_4MS 1
|
||||
#define DEGLITCH_TIME_8MS 2
|
||||
#define DEGLITCH_TIME_16MS 3
|
||||
#define DEGLITCH_TIME_40MS 4
|
||||
#define DEGLITCH_TIME_50MS 5
|
||||
#define DEGLITCH_TIME_60MS 6
|
||||
#define DEGLITCH_TIME_128MS 7
|
||||
|
||||
#define USB_D_OVR BIT(7)
|
||||
#define USB_ID_OVR BIT(6)
|
||||
#define DVRFLT_SEL BIT(5)
|
||||
#define BLOCK_RGND_INT BIT(4)
|
||||
#define SKIP_DEG BIT(3)
|
||||
#define CI2CA_POL BIT(2)
|
||||
#define CI2CA_WKUP BIT(1)
|
||||
#define SINGLE_ATT BIT(0)
|
||||
|
||||
#define USB_D_ODN BIT(5)
|
||||
#define VBUS_CHECK BIT(2)
|
||||
#define RGND_INTP_MASK 0x03
|
||||
#define RGND_INTP_OPEN 0
|
||||
#define RGND_INTP_2K 1
|
||||
#define RGND_INTP_1K 2
|
||||
#define RGND_INTP_SHORT 3
|
||||
|
||||
/* HDMI registers */
|
||||
#define HDMI_RX_TMDS0_CCTRL1_REG 0x10
|
||||
#define HDMI_RX_TMDS_CLK_EN_REG 0x11
|
||||
#define HDMI_RX_TMDS_CH_EN_REG 0x12
|
||||
#define HDMI_RX_PLL_CALREFSEL_REG 0x17
|
||||
#define HDMI_RX_PLL_VCOCAL_REG 0x1A
|
||||
#define HDMI_RX_EQ_DATA0_REG 0x22
|
||||
#define HDMI_RX_EQ_DATA1_REG 0x23
|
||||
#define HDMI_RX_EQ_DATA2_REG 0x24
|
||||
#define HDMI_RX_EQ_DATA3_REG 0x25
|
||||
#define HDMI_RX_EQ_DATA4_REG 0x26
|
||||
#define HDMI_RX_TMDS_ZONE_CTRL_REG 0x4C
|
||||
#define HDMI_RX_TMDS_MODE_CTRL_REG 0x4D
|
||||
|
||||
/* CBUS registers */
|
||||
#define CBUS_INT_STATUS_1_REG 0x08
|
||||
#define CBUS_INTR1_ENABLE_REG 0x09
|
||||
#define CBUS_MSC_REQ_ABORT_REASON_REG 0x0D
|
||||
#define CBUS_INT_STATUS_2_REG 0x1E
|
||||
#define CBUS_INTR2_ENABLE_REG 0x1F
|
||||
#define CBUS_LINK_CONTROL_2_REG 0x31
|
||||
#define CBUS_MHL_STATUS_REG_0 0xB0
|
||||
#define CBUS_MHL_STATUS_REG_1 0xB1
|
||||
|
||||
#define BIT_CBUS_RESET BIT(3)
|
||||
#define SET_HPD_DOWNSTREAM BIT(6)
|
||||
|
||||
/* TPI registers */
|
||||
#define TPI_DPD_REG 0x3D
|
||||
|
||||
/* Timeouts in msec */
|
||||
#define T_SRC_VBUS_CBUS_TO_STABLE 200
|
||||
#define T_SRC_CBUS_FLOAT 100
|
||||
#define T_SRC_CBUS_DEGLITCH 2
|
||||
#define T_SRC_RXSENSE_DEGLITCH 110
|
||||
|
||||
#define MHL1_MAX_CLK 75000 /* in kHz */
|
||||
|
||||
#define I2C_TPI_ADDR 0x3D
|
||||
#define I2C_HDMI_ADDR 0x49
|
||||
#define I2C_CBUS_ADDR 0x64
|
||||
|
||||
enum sii9234_state {
|
||||
ST_OFF,
|
||||
ST_D3,
|
||||
ST_RGND_INIT,
|
||||
ST_RGND_1K,
|
||||
ST_RSEN_HIGH,
|
||||
ST_MHL_ESTABLISHED,
|
||||
ST_FAILURE_DISCOVERY,
|
||||
ST_FAILURE,
|
||||
};
|
||||
|
||||
struct sii9234 {
|
||||
struct i2c_client *client[4];
|
||||
struct drm_bridge bridge;
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio_reset;
|
||||
int i2c_error;
|
||||
struct regulator_bulk_data supplies[4];
|
||||
|
||||
struct mutex lock; /* Protects fields below and device registers */
|
||||
enum sii9234_state state;
|
||||
};
|
||||
|
||||
enum sii9234_client_id {
|
||||
I2C_MHL,
|
||||
I2C_TPI,
|
||||
I2C_HDMI,
|
||||
I2C_CBUS,
|
||||
};
|
||||
|
||||
static const char * const sii9234_client_name[] = {
|
||||
[I2C_MHL] = "MHL",
|
||||
[I2C_TPI] = "TPI",
|
||||
[I2C_HDMI] = "HDMI",
|
||||
[I2C_CBUS] = "CBUS",
|
||||
};
|
||||
|
||||
static int sii9234_writeb(struct sii9234 *ctx, int id, int offset,
|
||||
int value)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = ctx->client[id];
|
||||
|
||||
if (ctx->i2c_error)
|
||||
return ctx->i2c_error;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, offset, value);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "writeb: %4s[0x%02x] <- 0x%02x\n",
|
||||
sii9234_client_name[id], offset, value);
|
||||
ctx->i2c_error = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sii9234_writebm(struct sii9234 *ctx, int id, int offset,
|
||||
int value, int mask)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = ctx->client[id];
|
||||
|
||||
if (ctx->i2c_error)
|
||||
return ctx->i2c_error;
|
||||
|
||||
ret = i2c_smbus_write_byte(client, offset);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
|
||||
sii9234_client_name[id], offset, value);
|
||||
ctx->i2c_error = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
|
||||
sii9234_client_name[id], offset, value);
|
||||
ctx->i2c_error = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
value = (value & mask) | (ret & ~mask);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, offset, value);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
|
||||
sii9234_client_name[id], offset, value);
|
||||
ctx->i2c_error = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sii9234_readb(struct sii9234 *ctx, int id, int offset)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = ctx->client[id];
|
||||
|
||||
if (ctx->i2c_error)
|
||||
return ctx->i2c_error;
|
||||
|
||||
ret = i2c_smbus_write_byte(client, offset);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "readb: %4s[0x%02x]\n",
|
||||
sii9234_client_name[id], offset);
|
||||
ctx->i2c_error = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "readb: %4s[0x%02x]\n",
|
||||
sii9234_client_name[id], offset);
|
||||
ctx->i2c_error = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sii9234_clear_error(struct sii9234 *ctx)
|
||||
{
|
||||
int ret = ctx->i2c_error;
|
||||
|
||||
ctx->i2c_error = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define mhl_tx_writeb(sii9234, offset, value) \
|
||||
sii9234_writeb(sii9234, I2C_MHL, offset, value)
|
||||
#define mhl_tx_writebm(sii9234, offset, value, mask) \
|
||||
sii9234_writebm(sii9234, I2C_MHL, offset, value, mask)
|
||||
#define mhl_tx_readb(sii9234, offset) \
|
||||
sii9234_readb(sii9234, I2C_MHL, offset)
|
||||
#define cbus_writeb(sii9234, offset, value) \
|
||||
sii9234_writeb(sii9234, I2C_CBUS, offset, value)
|
||||
#define cbus_writebm(sii9234, offset, value, mask) \
|
||||
sii9234_writebm(sii9234, I2C_CBUS, offset, value, mask)
|
||||
#define cbus_readb(sii9234, offset) \
|
||||
sii9234_readb(sii9234, I2C_CBUS, offset)
|
||||
#define hdmi_writeb(sii9234, offset, value) \
|
||||
sii9234_writeb(sii9234, I2C_HDMI, offset, value)
|
||||
#define hdmi_writebm(sii9234, offset, value, mask) \
|
||||
sii9234_writebm(sii9234, I2C_HDMI, offset, value, mask)
|
||||
#define hdmi_readb(sii9234, offset) \
|
||||
sii9234_readb(sii9234, I2C_HDMI, offset)
|
||||
#define tpi_writeb(sii9234, offset, value) \
|
||||
sii9234_writeb(sii9234, I2C_TPI, offset, value)
|
||||
#define tpi_writebm(sii9234, offset, value, mask) \
|
||||
sii9234_writebm(sii9234, I2C_TPI, offset, value, mask)
|
||||
#define tpi_readb(sii9234, offset) \
|
||||
sii9234_readb(sii9234, I2C_TPI, offset)
|
||||
|
||||
static u8 sii9234_tmds_control(struct sii9234 *ctx, bool enable)
|
||||
{
|
||||
mhl_tx_writebm(ctx, MHL_TX_TMDS_CCTRL, enable ? ~0 : 0,
|
||||
BIT_TMDS_CCTRL_TMDS_OE);
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, enable ? ~0 : 0,
|
||||
MHL_HPD_OUT_OVR_EN | MHL_HPD_OUT_OVR_VAL);
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_cbus_reset(struct sii9234 *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
mhl_tx_writebm(ctx, MHL_TX_SRST, ~0, BIT_CBUS_RESET);
|
||||
msleep(T_SRC_CBUS_DEGLITCH);
|
||||
mhl_tx_writebm(ctx, MHL_TX_SRST, 0, BIT_CBUS_RESET);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
/*
|
||||
* Enable WRITE_STAT interrupt for writes to all
|
||||
* 4 MSC Status registers.
|
||||
*/
|
||||
cbus_writeb(ctx, 0xE0 + i, 0xF2);
|
||||
/*
|
||||
* Enable SET_INT interrupt for writes to all
|
||||
* 4 MSC Interrupt registers.
|
||||
*/
|
||||
cbus_writeb(ctx, 0xF0 + i, 0xF2);
|
||||
}
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
/* Require to chek mhl imformation of samsung in cbus_init_register */
|
||||
static int sii9234_cbus_init(struct sii9234 *ctx)
|
||||
{
|
||||
cbus_writeb(ctx, 0x07, 0xF2);
|
||||
cbus_writeb(ctx, 0x40, 0x03);
|
||||
cbus_writeb(ctx, 0x42, 0x06);
|
||||
cbus_writeb(ctx, 0x36, 0x0C);
|
||||
cbus_writeb(ctx, 0x3D, 0xFD);
|
||||
cbus_writeb(ctx, 0x1C, 0x01);
|
||||
cbus_writeb(ctx, 0x1D, 0x0F);
|
||||
cbus_writeb(ctx, 0x44, 0x02);
|
||||
/* Setup our devcap */
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEV_STATE, 0x00);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_MHL_VERSION,
|
||||
SII9234_MHL_VERSION);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_CAT,
|
||||
MHL_DCAP_CAT_SOURCE);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_ADOPTER_ID_H, 0x01);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_ADOPTER_ID_L, 0x41);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_VID_LINK_MODE,
|
||||
MHL_DCAP_VID_LINK_RGB444 | MHL_DCAP_VID_LINK_YCBCR444);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_VIDEO_TYPE,
|
||||
MHL_DCAP_VT_GRAPHICS);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_LOG_DEV_MAP,
|
||||
MHL_DCAP_LD_GUI);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_BANDWIDTH, 0x0F);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_FEATURE_FLAG,
|
||||
MHL_DCAP_FEATURE_RCP_SUPPORT | MHL_DCAP_FEATURE_RAP_SUPPORT
|
||||
| MHL_DCAP_FEATURE_SP_SUPPORT);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEVICE_ID_H, 0x0);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEVICE_ID_L, 0x0);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_SCRATCHPAD_SIZE,
|
||||
SII9234_SCRATCHPAD_SIZE);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_INT_STAT_SIZE,
|
||||
SII9234_INT_STAT_SIZE);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_RESERVED, 0);
|
||||
cbus_writebm(ctx, 0x31, 0x0C, 0x0C);
|
||||
cbus_writeb(ctx, 0x30, 0x01);
|
||||
cbus_writebm(ctx, 0x3C, 0x30, 0x38);
|
||||
cbus_writebm(ctx, 0x22, 0x0D, 0x0F);
|
||||
cbus_writebm(ctx, 0x2E, 0x15, 0x15);
|
||||
cbus_writeb(ctx, CBUS_INTR1_ENABLE_REG, 0);
|
||||
cbus_writeb(ctx, CBUS_INTR2_ENABLE_REG, 0);
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static void force_usb_id_switch_open(struct sii9234 *ctx)
|
||||
{
|
||||
/* Disable CBUS discovery */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, 0, 0x01);
|
||||
/* Force USB ID switch to open */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, USB_ID_OVR);
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL3_REG, ~0, 0x86);
|
||||
/* Force upstream HPD to 0 when not in MHL mode. */
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 0x30);
|
||||
}
|
||||
|
||||
static void release_usb_id_switch_open(struct sii9234 *ctx)
|
||||
{
|
||||
msleep(T_SRC_CBUS_FLOAT);
|
||||
/* Clear USB ID switch to open */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, 0, USB_ID_OVR);
|
||||
/* Enable CBUS discovery */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, ~0, 0x01);
|
||||
}
|
||||
|
||||
static int sii9234_power_init(struct sii9234 *ctx)
|
||||
{
|
||||
/* Force the SiI9234 into the D0 state. */
|
||||
tpi_writeb(ctx, TPI_DPD_REG, 0x3F);
|
||||
/* Enable TxPLL Clock */
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS_CLK_EN_REG, 0x01);
|
||||
/* Enable Tx Clock Path & Equalizer */
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS_CH_EN_REG, 0x15);
|
||||
/* Power Up TMDS */
|
||||
mhl_tx_writeb(ctx, 0x08, 0x35);
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_hdmi_init(struct sii9234 *ctx)
|
||||
{
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
|
||||
hdmi_writeb(ctx, HDMI_RX_PLL_CALREFSEL_REG, 0x03);
|
||||
hdmi_writeb(ctx, HDMI_RX_PLL_VCOCAL_REG, 0x20);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA0_REG, 0x8A);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA1_REG, 0x6A);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA2_REG, 0xAA);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA3_REG, 0xCA);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA4_REG, 0xEA);
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS_ZONE_CTRL_REG, 0xA0);
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS_MODE_CTRL_REG, 0x00);
|
||||
mhl_tx_writeb(ctx, MHL_TX_TMDS_CCTRL, 0x34);
|
||||
hdmi_writeb(ctx, 0x45, 0x44);
|
||||
hdmi_writeb(ctx, 0x31, 0x0A);
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_mhl_tx_ctl_int(struct sii9234 *ctx)
|
||||
{
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL1_REG, 0xD0);
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL2_REG, 0xFC);
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL4_REG, 0xEB);
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL7_REG, 0x0C);
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_reset(struct sii9234 *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sii9234_clear_error(ctx);
|
||||
|
||||
ret = sii9234_power_init(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sii9234_cbus_reset(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sii9234_hdmi_init(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sii9234_mhl_tx_ctl_int(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable HDCP Compliance safety */
|
||||
mhl_tx_writeb(ctx, 0x2B, 0x01);
|
||||
/* CBUS discovery cycle time for each drive and float = 150us */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, 0x04, 0x06);
|
||||
/* Clear bit 6 (reg_skip_rgnd) */
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL2_REG, (1 << 7) /* Reserved */
|
||||
| 2 << ATT_THRESH_SHIFT | DEGLITCH_TIME_50MS);
|
||||
/*
|
||||
* Changed from 66 to 65 for 94[1:0] = 01 = 5k reg_cbusmhl_pup_sel
|
||||
* 1.8V CBUS VTH & GND threshold
|
||||
* to meet CTS 3.3.7.2 spec
|
||||
*/
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL5_REG, 0x77);
|
||||
cbus_writebm(ctx, CBUS_LINK_CONTROL_2_REG, ~0, MHL_INIT_TIMEOUT);
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL6_REG, 0xA0);
|
||||
/* RGND & single discovery attempt (RGND blocking) */
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL6_REG, BLOCK_RGND_INT |
|
||||
DVRFLT_SEL | SINGLE_ATT);
|
||||
/* Use VBUS path of discovery state machine */
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL8_REG, 0);
|
||||
/* 0x92[3] sets the CBUS / ID switch */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, USB_ID_OVR);
|
||||
/*
|
||||
* To allow RGND engine to operate correctly.
|
||||
* When moving the chip from D2 to D0 (power up, init regs)
|
||||
* the values should be
|
||||
* 94[1:0] = 01 reg_cbusmhl_pup_sel[1:0] should be set for 5k
|
||||
* 93[7:6] = 10 reg_cbusdisc_pup_sel[1:0] should be
|
||||
* set for 10k (default)
|
||||
* 93[5:4] = 00 reg_cbusidle_pup_sel[1:0] = open (default)
|
||||
*/
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL3_REG, ~0, 0x86);
|
||||
/*
|
||||
* Change from CC to 8C to match 5K
|
||||
* to meet CTS 3.3.72 spec
|
||||
*/
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, ~0, 0x8C);
|
||||
/* Configure the interrupt as active high */
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 0x06);
|
||||
|
||||
msleep(25);
|
||||
|
||||
/* Release usb_id switch */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, 0, USB_ID_OVR);
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL1_REG, 0x27);
|
||||
|
||||
ret = sii9234_clear_error(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sii9234_cbus_init(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable Auto soft reset on SCDT = 0 */
|
||||
mhl_tx_writeb(ctx, 0x05, 0x04);
|
||||
/* HDMI Transcode mode enable */
|
||||
mhl_tx_writeb(ctx, 0x0D, 0x1C);
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR4_ENABLE_REG,
|
||||
RGND_READY_MASK | CBUS_LKOUT_MASK
|
||||
| MHL_DISC_FAIL_MASK | MHL_EST_MASK);
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR1_ENABLE_REG, 0x60);
|
||||
|
||||
/* This point is very important before measure RGND impedance */
|
||||
force_usb_id_switch_open(ctx);
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, 0, 0xF0);
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL5_REG, 0, 0x03);
|
||||
release_usb_id_switch_open(ctx);
|
||||
|
||||
/* Force upstream HPD to 0 when not in MHL mode */
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 1 << 5);
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, ~0, 1 << 4);
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_goto_d3(struct sii9234 *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(ctx->dev, "sii9234: detection started d3\n");
|
||||
|
||||
ret = sii9234_reset(ctx);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
hdmi_writeb(ctx, 0x01, 0x03);
|
||||
tpi_writebm(ctx, TPI_DPD_REG, 0, 1);
|
||||
/* I2C above is expected to fail because power goes down */
|
||||
sii9234_clear_error(ctx);
|
||||
|
||||
ctx->state = ST_D3;
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
dev_err(ctx->dev, "%s failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sii9234_hw_on(struct sii9234 *ctx)
|
||||
{
|
||||
return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
}
|
||||
|
||||
static void sii9234_hw_off(struct sii9234 *ctx)
|
||||
{
|
||||
gpiod_set_value(ctx->gpio_reset, 1);
|
||||
msleep(20);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
}
|
||||
|
||||
static void sii9234_hw_reset(struct sii9234 *ctx)
|
||||
{
|
||||
gpiod_set_value(ctx->gpio_reset, 1);
|
||||
msleep(20);
|
||||
gpiod_set_value(ctx->gpio_reset, 0);
|
||||
}
|
||||
|
||||
static void sii9234_cable_in(struct sii9234 *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
if (ctx->state != ST_OFF)
|
||||
goto unlock;
|
||||
ret = sii9234_hw_on(ctx);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
sii9234_hw_reset(ctx);
|
||||
sii9234_goto_d3(ctx);
|
||||
/* To avoid irq storm, when hw is in meta state */
|
||||
enable_irq(to_i2c_client(ctx->dev)->irq);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
static void sii9234_cable_out(struct sii9234 *ctx)
|
||||
{
|
||||
mutex_lock(&ctx->lock);
|
||||
|
||||
if (ctx->state == ST_OFF)
|
||||
goto unlock;
|
||||
|
||||
disable_irq(to_i2c_client(ctx->dev)->irq);
|
||||
tpi_writeb(ctx, TPI_DPD_REG, 0);
|
||||
/* Turn on&off hpd festure for only QCT HDMI */
|
||||
sii9234_hw_off(ctx);
|
||||
|
||||
ctx->state = ST_OFF;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
static enum sii9234_state sii9234_rgnd_ready_irq(struct sii9234 *ctx)
|
||||
{
|
||||
int value;
|
||||
|
||||
if (ctx->state == ST_D3) {
|
||||
int ret;
|
||||
|
||||
dev_dbg(ctx->dev, "RGND_READY_INT\n");
|
||||
sii9234_hw_reset(ctx);
|
||||
|
||||
ret = sii9234_reset(ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "sii9234_reset() failed\n");
|
||||
return ST_FAILURE;
|
||||
}
|
||||
|
||||
return ST_RGND_INIT;
|
||||
}
|
||||
|
||||
/* Got interrupt in inappropriate state */
|
||||
if (ctx->state != ST_RGND_INIT)
|
||||
return ST_FAILURE;
|
||||
|
||||
value = mhl_tx_readb(ctx, MHL_TX_STAT2_REG);
|
||||
if (sii9234_clear_error(ctx))
|
||||
return ST_FAILURE;
|
||||
|
||||
if ((value & RGND_INTP_MASK) != RGND_INTP_1K) {
|
||||
dev_warn(ctx->dev, "RGND is not 1k\n");
|
||||
return ST_RGND_INIT;
|
||||
}
|
||||
dev_dbg(ctx->dev, "RGND 1K!!\n");
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, ~0, 0x8C);
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL5_REG, 0x77);
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, 0x05);
|
||||
if (sii9234_clear_error(ctx))
|
||||
return ST_FAILURE;
|
||||
|
||||
msleep(T_SRC_VBUS_CBUS_TO_STABLE);
|
||||
return ST_RGND_1K;
|
||||
}
|
||||
|
||||
static enum sii9234_state sii9234_mhl_established(struct sii9234 *ctx)
|
||||
{
|
||||
dev_dbg(ctx->dev, "mhl est interrupt\n");
|
||||
|
||||
/* Discovery override */
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL1_REG, 0x10);
|
||||
/* Increase DDC translation layer timer (byte mode) */
|
||||
cbus_writeb(ctx, 0x07, 0x32);
|
||||
cbus_writebm(ctx, 0x44, ~0, 1 << 1);
|
||||
/* Keep the discovery enabled. Need RGND interrupt */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, ~0, 1);
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR1_ENABLE_REG,
|
||||
RSEN_CHANGE_INT_MASK | HPD_CHANGE_INT_MASK);
|
||||
|
||||
if (sii9234_clear_error(ctx))
|
||||
return ST_FAILURE;
|
||||
|
||||
return ST_MHL_ESTABLISHED;
|
||||
}
|
||||
|
||||
static enum sii9234_state sii9234_hpd_change(struct sii9234 *ctx)
|
||||
{
|
||||
int value;
|
||||
|
||||
value = cbus_readb(ctx, CBUS_MSC_REQ_ABORT_REASON_REG);
|
||||
if (sii9234_clear_error(ctx))
|
||||
return ST_FAILURE;
|
||||
|
||||
if (value & SET_HPD_DOWNSTREAM) {
|
||||
/* Downstream HPD High, Enable TMDS */
|
||||
sii9234_tmds_control(ctx, true);
|
||||
} else {
|
||||
/* Downstream HPD Low, Disable TMDS */
|
||||
sii9234_tmds_control(ctx, false);
|
||||
}
|
||||
|
||||
return ctx->state;
|
||||
}
|
||||
|
||||
static enum sii9234_state sii9234_rsen_change(struct sii9234 *ctx)
|
||||
{
|
||||
int value;
|
||||
|
||||
/* Work_around code to handle wrong interrupt */
|
||||
if (ctx->state != ST_RGND_1K) {
|
||||
dev_err(ctx->dev, "RSEN_HIGH without RGND_1K\n");
|
||||
return ST_FAILURE;
|
||||
}
|
||||
value = mhl_tx_readb(ctx, MHL_TX_SYSSTAT_REG);
|
||||
if (value < 0)
|
||||
return ST_FAILURE;
|
||||
|
||||
if (value & RSEN_STATUS) {
|
||||
dev_dbg(ctx->dev, "MHL cable connected.. RSEN High\n");
|
||||
return ST_RSEN_HIGH;
|
||||
}
|
||||
dev_dbg(ctx->dev, "RSEN lost\n");
|
||||
/*
|
||||
* Once RSEN loss is confirmed,we need to check
|
||||
* based on cable status and chip power status,whether
|
||||
* it is SINK Loss(HDMI cable not connected, TV Off)
|
||||
* or MHL cable disconnection
|
||||
* TODO: Define the below mhl_disconnection()
|
||||
*/
|
||||
msleep(T_SRC_RXSENSE_DEGLITCH);
|
||||
value = mhl_tx_readb(ctx, MHL_TX_SYSSTAT_REG);
|
||||
if (value < 0)
|
||||
return ST_FAILURE;
|
||||
dev_dbg(ctx->dev, "sys_stat: %x\n", value);
|
||||
|
||||
if (value & RSEN_STATUS) {
|
||||
dev_dbg(ctx->dev, "RSEN recovery\n");
|
||||
return ST_RSEN_HIGH;
|
||||
}
|
||||
dev_dbg(ctx->dev, "RSEN Really LOW\n");
|
||||
/* To meet CTS 3.3.22.2 spec */
|
||||
sii9234_tmds_control(ctx, false);
|
||||
force_usb_id_switch_open(ctx);
|
||||
release_usb_id_switch_open(ctx);
|
||||
|
||||
return ST_FAILURE;
|
||||
}
|
||||
|
||||
static irqreturn_t sii9234_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct sii9234 *ctx = data;
|
||||
int intr1, intr4;
|
||||
int intr1_en, intr4_en;
|
||||
int cbus_intr1, cbus_intr2;
|
||||
|
||||
dev_dbg(ctx->dev, "%s\n", __func__);
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
|
||||
intr1 = mhl_tx_readb(ctx, MHL_TX_INTR1_REG);
|
||||
intr4 = mhl_tx_readb(ctx, MHL_TX_INTR4_REG);
|
||||
intr1_en = mhl_tx_readb(ctx, MHL_TX_INTR1_ENABLE_REG);
|
||||
intr4_en = mhl_tx_readb(ctx, MHL_TX_INTR4_ENABLE_REG);
|
||||
cbus_intr1 = cbus_readb(ctx, CBUS_INT_STATUS_1_REG);
|
||||
cbus_intr2 = cbus_readb(ctx, CBUS_INT_STATUS_2_REG);
|
||||
|
||||
if (sii9234_clear_error(ctx))
|
||||
goto done;
|
||||
|
||||
dev_dbg(ctx->dev, "irq %02x/%02x %02x/%02x %02x/%02x\n",
|
||||
intr1, intr1_en, intr4, intr4_en, cbus_intr1, cbus_intr2);
|
||||
|
||||
if (intr4 & RGND_READY_INT)
|
||||
ctx->state = sii9234_rgnd_ready_irq(ctx);
|
||||
if (intr1 & RSEN_CHANGE_INT)
|
||||
ctx->state = sii9234_rsen_change(ctx);
|
||||
if (intr4 & MHL_EST_INT)
|
||||
ctx->state = sii9234_mhl_established(ctx);
|
||||
if (intr1 & HPD_CHANGE_INT)
|
||||
ctx->state = sii9234_hpd_change(ctx);
|
||||
if (intr4 & CBUS_LKOUT_INT)
|
||||
ctx->state = ST_FAILURE;
|
||||
if (intr4 & MHL_DISC_FAIL_INT)
|
||||
ctx->state = ST_FAILURE_DISCOVERY;
|
||||
|
||||
done:
|
||||
/* Clean interrupt status and pending flags */
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR1_REG, intr1);
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR4_REG, intr4);
|
||||
cbus_writeb(ctx, CBUS_MHL_STATUS_REG_0, 0xFF);
|
||||
cbus_writeb(ctx, CBUS_MHL_STATUS_REG_1, 0xFF);
|
||||
cbus_writeb(ctx, CBUS_INT_STATUS_1_REG, cbus_intr1);
|
||||
cbus_writeb(ctx, CBUS_INT_STATUS_2_REG, cbus_intr2);
|
||||
|
||||
sii9234_clear_error(ctx);
|
||||
|
||||
if (ctx->state == ST_FAILURE) {
|
||||
dev_dbg(ctx->dev, "try to reset after failure\n");
|
||||
sii9234_hw_reset(ctx);
|
||||
sii9234_goto_d3(ctx);
|
||||
}
|
||||
|
||||
if (ctx->state == ST_FAILURE_DISCOVERY) {
|
||||
dev_err(ctx->dev, "discovery failed, no power for MHL?\n");
|
||||
tpi_writebm(ctx, TPI_DPD_REG, 0, 1);
|
||||
ctx->state = ST_D3;
|
||||
}
|
||||
|
||||
mutex_unlock(&ctx->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sii9234_init_resources(struct sii9234 *ctx,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
int ret;
|
||||
|
||||
if (!ctx->dev->of_node) {
|
||||
dev_err(ctx->dev, "not DT device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ctx->gpio_reset = devm_gpiod_get(ctx->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->gpio_reset)) {
|
||||
dev_err(ctx->dev, "failed to get reset gpio from DT\n");
|
||||
return PTR_ERR(ctx->gpio_reset);
|
||||
}
|
||||
|
||||
ctx->supplies[0].supply = "avcc12";
|
||||
ctx->supplies[1].supply = "avcc33";
|
||||
ctx->supplies[2].supply = "iovcc18";
|
||||
ctx->supplies[3].supply = "cvcc12";
|
||||
ret = devm_regulator_bulk_get(ctx->dev, 4, ctx->supplies);
|
||||
if (ret) {
|
||||
dev_err(ctx->dev, "regulator_bulk failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->client[I2C_MHL] = client;
|
||||
|
||||
ctx->client[I2C_TPI] = i2c_new_dummy(adapter, I2C_TPI_ADDR);
|
||||
if (!ctx->client[I2C_TPI]) {
|
||||
dev_err(ctx->dev, "failed to create TPI client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ctx->client[I2C_HDMI] = i2c_new_dummy(adapter, I2C_HDMI_ADDR);
|
||||
if (!ctx->client[I2C_HDMI]) {
|
||||
dev_err(ctx->dev, "failed to create HDMI RX client\n");
|
||||
goto fail_tpi;
|
||||
}
|
||||
|
||||
ctx->client[I2C_CBUS] = i2c_new_dummy(adapter, I2C_CBUS_ADDR);
|
||||
if (!ctx->client[I2C_CBUS]) {
|
||||
dev_err(ctx->dev, "failed to create CBUS client\n");
|
||||
goto fail_hdmi;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_hdmi:
|
||||
i2c_unregister_device(ctx->client[I2C_HDMI]);
|
||||
fail_tpi:
|
||||
i2c_unregister_device(ctx->client[I2C_TPI]);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void sii9234_deinit_resources(struct sii9234 *ctx)
|
||||
{
|
||||
i2c_unregister_device(ctx->client[I2C_CBUS]);
|
||||
i2c_unregister_device(ctx->client[I2C_HDMI]);
|
||||
i2c_unregister_device(ctx->client[I2C_TPI]);
|
||||
}
|
||||
|
||||
static inline struct sii9234 *bridge_to_sii9234(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct sii9234, bridge);
|
||||
}
|
||||
|
||||
static enum drm_mode_status sii9234_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock > MHL1_MAX_CLK)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs sii9234_bridge_funcs = {
|
||||
.mode_valid = sii9234_mode_valid,
|
||||
};
|
||||
|
||||
static int sii9234_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct sii9234 *ctx;
|
||||
struct device *dev = &client->dev;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->dev = dev;
|
||||
mutex_init(&ctx->lock);
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(dev, "I2C adapter lacks SMBUS feature\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(dev, "no irq provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
sii9234_irq_thread,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"sii9234", ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to install IRQ handler\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sii9234_init_resources(ctx, client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i2c_set_clientdata(client, ctx);
|
||||
|
||||
ctx->bridge.funcs = &sii9234_bridge_funcs;
|
||||
ctx->bridge.of_node = dev->of_node;
|
||||
drm_bridge_add(&ctx->bridge);
|
||||
|
||||
sii9234_cable_in(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sii9234_remove(struct i2c_client *client)
|
||||
{
|
||||
struct sii9234 *ctx = i2c_get_clientdata(client);
|
||||
|
||||
sii9234_cable_out(ctx);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
sii9234_deinit_resources(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sii9234_dt_match[] = {
|
||||
{ .compatible = "sil,sii9234" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sii9234_dt_match);
|
||||
|
||||
static const struct i2c_device_id sii9234_id[] = {
|
||||
{ "SII9234", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sii9234_id);
|
||||
|
||||
static struct i2c_driver sii9234_driver = {
|
||||
.driver = {
|
||||
.name = "sii9234",
|
||||
.of_match_table = sii9234_dt_match,
|
||||
},
|
||||
.probe = sii9234_probe,
|
||||
.remove = sii9234_remove,
|
||||
.id_table = sii9234_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(sii9234_driver);
|
||||
MODULE_LICENSE("GPL");
|
@ -28,6 +28,8 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <media/rc-core.h>
|
||||
|
||||
#include "sil-sii8620.h"
|
||||
|
||||
#define SII8620_BURST_BUF_LEN 288
|
||||
@ -58,6 +60,7 @@ enum sii8620_mt_state {
|
||||
struct sii8620 {
|
||||
struct drm_bridge bridge;
|
||||
struct device *dev;
|
||||
struct rc_dev *rc_dev;
|
||||
struct clk *clk_xtal;
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct gpio_desc *gpio_int;
|
||||
@ -431,6 +434,16 @@ static void sii8620_mt_rap(struct sii8620 *ctx, u8 code)
|
||||
sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code);
|
||||
}
|
||||
|
||||
static void sii8620_mt_rcpk(struct sii8620 *ctx, u8 code)
|
||||
{
|
||||
sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPK, code);
|
||||
}
|
||||
|
||||
static void sii8620_mt_rcpe(struct sii8620 *ctx, u8 code)
|
||||
{
|
||||
sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPE, code);
|
||||
}
|
||||
|
||||
static void sii8620_mt_read_devcap_send(struct sii8620 *ctx,
|
||||
struct sii8620_mt_msg *msg)
|
||||
{
|
||||
@ -1753,6 +1766,25 @@ static void sii8620_send_features(struct sii8620 *ctx)
|
||||
sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf));
|
||||
}
|
||||
|
||||
static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode)
|
||||
{
|
||||
bool pressed = !(scancode & MHL_RCP_KEY_RELEASED_MASK);
|
||||
|
||||
scancode &= MHL_RCP_KEY_ID_MASK;
|
||||
|
||||
if (!ctx->rc_dev) {
|
||||
dev_dbg(ctx->dev, "RCP input device not initialized\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pressed)
|
||||
rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0);
|
||||
else
|
||||
rc_keyup(ctx->rc_dev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sii8620_msc_mr_set_int(struct sii8620 *ctx)
|
||||
{
|
||||
u8 ints[MHL_INT_SIZE];
|
||||
@ -1804,19 +1836,25 @@ static void sii8620_msc_mt_done(struct sii8620 *ctx)
|
||||
|
||||
static void sii8620_msc_mr_msc_msg(struct sii8620 *ctx)
|
||||
{
|
||||
struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx);
|
||||
struct sii8620_mt_msg *msg;
|
||||
u8 buf[2];
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2);
|
||||
|
||||
switch (buf[0]) {
|
||||
case MHL_MSC_MSG_RAPK:
|
||||
msg = sii8620_msc_msg_first(ctx);
|
||||
if (!msg)
|
||||
return;
|
||||
msg->ret = buf[1];
|
||||
ctx->mt_state = MT_STATE_DONE;
|
||||
break;
|
||||
case MHL_MSC_MSG_RCP:
|
||||
if (!sii8620_rcp_consume(ctx, buf[1]))
|
||||
sii8620_mt_rcpe(ctx,
|
||||
MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE);
|
||||
sii8620_mt_rcpk(ctx, buf[1]);
|
||||
break;
|
||||
default:
|
||||
dev_err(ctx->dev, "%s message type %d,%d not supported",
|
||||
__func__, buf[0], buf[1]);
|
||||
@ -2102,11 +2140,57 @@ static void sii8620_cable_in(struct sii8620 *ctx)
|
||||
enable_irq(to_i2c_client(ctx->dev)->irq);
|
||||
}
|
||||
|
||||
static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
|
||||
{
|
||||
struct rc_dev *rc_dev;
|
||||
int ret;
|
||||
|
||||
rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
|
||||
if (!rc_dev) {
|
||||
dev_err(ctx->dev, "Failed to allocate RC device\n");
|
||||
ctx->error = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
rc_dev->input_phys = "sii8620/input0";
|
||||
rc_dev->input_id.bustype = BUS_VIRTUAL;
|
||||
rc_dev->map_name = RC_MAP_CEC;
|
||||
rc_dev->allowed_protocols = RC_PROTO_BIT_CEC;
|
||||
rc_dev->driver_name = "sii8620";
|
||||
rc_dev->device_name = "sii8620";
|
||||
|
||||
ret = rc_register_device(rc_dev);
|
||||
|
||||
if (ret) {
|
||||
dev_err(ctx->dev, "Failed to register RC device\n");
|
||||
ctx->error = ret;
|
||||
rc_free_device(ctx->rc_dev);
|
||||
return;
|
||||
}
|
||||
ctx->rc_dev = rc_dev;
|
||||
}
|
||||
|
||||
static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct sii8620, bridge);
|
||||
}
|
||||
|
||||
static int sii8620_attach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct sii8620 *ctx = bridge_to_sii8620(bridge);
|
||||
|
||||
sii8620_init_rcp_input_dev(ctx);
|
||||
|
||||
return sii8620_clear_error(ctx);
|
||||
}
|
||||
|
||||
static void sii8620_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct sii8620 *ctx = bridge_to_sii8620(bridge);
|
||||
|
||||
rc_unregister_device(ctx->rc_dev);
|
||||
}
|
||||
|
||||
static bool sii8620_mode_fixup(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
@ -2151,6 +2235,8 @@ end:
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs sii8620_bridge_funcs = {
|
||||
.attach = sii8620_attach,
|
||||
.detach = sii8620_detach,
|
||||
.mode_fixup = sii8620_mode_fixup,
|
||||
};
|
||||
|
||||
@ -2217,8 +2303,8 @@ static int sii8620_remove(struct i2c_client *client)
|
||||
struct sii8620 *ctx = i2c_get_clientdata(client);
|
||||
|
||||
disable_irq(to_i2c_client(ctx->dev)->irq);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
sii8620_hw_off(ctx);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -221,7 +221,6 @@ struct dw_mipi_dsi {
|
||||
struct drm_bridge bridge;
|
||||
struct mipi_dsi_host dsi_host;
|
||||
struct drm_bridge *panel_bridge;
|
||||
bool is_panel_bridge;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
|
||||
@ -297,7 +296,6 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
|
||||
if (IS_ERR(bridge))
|
||||
return PTR_ERR(bridge);
|
||||
dsi->is_panel_bridge = true;
|
||||
}
|
||||
|
||||
dsi->panel_bridge = bridge;
|
||||
@ -312,8 +310,7 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
|
||||
{
|
||||
struct dw_mipi_dsi *dsi = host_to_dsi(host);
|
||||
|
||||
if (dsi->is_panel_bridge)
|
||||
drm_panel_bridge_remove(dsi->panel_bridge);
|
||||
drm_of_panel_bridge_remove(host->dev->of_node, 1, 0);
|
||||
|
||||
drm_bridge_remove(&dsi->bridge);
|
||||
|
||||
|
@ -457,7 +457,7 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
/* pick the encoder ids */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -182,9 +182,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
|
||||
for (i = 0; i < state->num_private_objs; i++) {
|
||||
struct drm_private_obj *obj = state->private_objs[i].ptr;
|
||||
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
obj->funcs->atomic_destroy_state(obj,
|
||||
state->private_objs[i].state);
|
||||
state->private_objs[i].ptr = NULL;
|
||||
@ -718,7 +715,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
|
||||
if (property == config->prop_fb_id) {
|
||||
struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val);
|
||||
struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val);
|
||||
drm_atomic_set_fb_for_plane(state, fb);
|
||||
if (fb)
|
||||
drm_framebuffer_put(fb);
|
||||
@ -734,7 +731,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
|
||||
return -EINVAL;
|
||||
|
||||
} else if (property == config->prop_crtc_id) {
|
||||
struct drm_crtc *crtc = drm_crtc_find(dev, val);
|
||||
struct drm_crtc *crtc = drm_crtc_find(dev, NULL, val);
|
||||
return drm_atomic_set_crtc_for_plane(state, crtc);
|
||||
} else if (property == config->prop_crtc_x) {
|
||||
state->crtc_x = U642I64(val);
|
||||
@ -1149,7 +1146,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
|
||||
if (property == config->prop_crtc_id) {
|
||||
struct drm_crtc *crtc = drm_crtc_find(dev, val);
|
||||
struct drm_crtc *crtc = drm_crtc_find(dev, NULL, val);
|
||||
return drm_atomic_set_crtc_for_connector(state, crtc);
|
||||
} else if (property == config->dpms_property) {
|
||||
/* setting DPMS property requires special handling, which
|
||||
@ -2259,7 +2256,7 @@ retry:
|
||||
goto out;
|
||||
}
|
||||
|
||||
obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
|
||||
obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY);
|
||||
if (!obj) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
|
@ -1704,7 +1704,7 @@ crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
|
||||
* drm_atomic_helper_commit_cleanup_done().
|
||||
*
|
||||
* This is all implemented by in drm_atomic_helper_commit(), giving drivers a
|
||||
* complete and esay-to-use default implementation of the atomic_commit() hook.
|
||||
* complete and easy-to-use default implementation of the atomic_commit() hook.
|
||||
*
|
||||
* The tracking of asynchronously executed and still pending commits is done
|
||||
* using the core structure &drm_crtc_commit.
|
||||
@ -1819,7 +1819,7 @@ EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
|
||||
* This function waits for all preceeding commits that touch the same CRTC as
|
||||
* @old_state to both be committed to the hardware (as signalled by
|
||||
* drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled
|
||||
* by calling drm_crtc_vblank_send_event() on the &drm_crtc_state.event).
|
||||
* by calling drm_crtc_send_vblank_event() on the &drm_crtc_state.event).
|
||||
*
|
||||
* This is part of the atomic helper support for nonblocking commits, see
|
||||
* drm_atomic_helper_setup_commit() for an overview.
|
||||
@ -3052,6 +3052,7 @@ out:
|
||||
drm_modeset_backoff(&ctx);
|
||||
}
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
|
||||
@ -3206,7 +3207,7 @@ struct drm_encoder *
|
||||
drm_atomic_helper_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
WARN_ON(connector->encoder_ids[1]);
|
||||
return drm_encoder_find(connector->dev, connector->encoder_ids[0]);
|
||||
return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_best_encoder);
|
||||
|
||||
|
@ -230,7 +230,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
|
||||
crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
|
||||
if (!crtc)
|
||||
return -ENOENT;
|
||||
|
||||
@ -308,7 +308,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
|
||||
crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
|
||||
if (!crtc)
|
||||
return -ENOENT;
|
||||
|
||||
|
@ -1310,7 +1310,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
|
||||
|
||||
memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
|
||||
|
||||
connector = drm_connector_lookup(dev, out_resp->connector_id);
|
||||
connector = drm_connector_lookup(dev, file_priv, out_resp->connector_id);
|
||||
if (!connector)
|
||||
return -ENOENT;
|
||||
|
||||
|
@ -402,7 +402,7 @@ int drm_mode_getcrtc(struct drm_device *dev,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
|
||||
crtc = drm_crtc_find(dev, file_priv, crtc_resp->crtc_id);
|
||||
if (!crtc)
|
||||
return -ENOENT;
|
||||
|
||||
@ -569,7 +569,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
|
||||
return -ERANGE;
|
||||
|
||||
crtc = drm_crtc_find(dev, crtc_req->crtc_id);
|
||||
crtc = drm_crtc_find(dev, file_priv, crtc_req->crtc_id);
|
||||
if (!crtc) {
|
||||
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
|
||||
return -ENOENT;
|
||||
@ -595,7 +595,7 @@ retry:
|
||||
/* Make refcounting symmetric with the lookup path. */
|
||||
drm_framebuffer_get(fb);
|
||||
} else {
|
||||
fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
|
||||
fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id);
|
||||
if (!fb) {
|
||||
DRM_DEBUG_KMS("Unknown FB ID%d\n",
|
||||
crtc_req->fb_id);
|
||||
@ -680,7 +680,7 @@ retry:
|
||||
goto out;
|
||||
}
|
||||
|
||||
connector = drm_connector_lookup(dev, out_id);
|
||||
connector = drm_connector_lookup(dev, file_priv, out_id);
|
||||
if (!connector) {
|
||||
DRM_DEBUG_KMS("Connector id %d unknown\n",
|
||||
out_id);
|
||||
|
@ -562,12 +562,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
* Allocate space for the backup of all (non-pointer) encoder and
|
||||
* connector data.
|
||||
*/
|
||||
save_encoder_crtcs = kzalloc(dev->mode_config.num_encoder *
|
||||
save_encoder_crtcs = kcalloc(dev->mode_config.num_encoder,
|
||||
sizeof(struct drm_crtc *), GFP_KERNEL);
|
||||
if (!save_encoder_crtcs)
|
||||
return -ENOMEM;
|
||||
|
||||
save_connector_encoders = kzalloc(dev->mode_config.num_connector *
|
||||
save_connector_encoders = kcalloc(dev->mode_config.num_connector,
|
||||
sizeof(struct drm_encoder *), GFP_KERNEL);
|
||||
if (!save_connector_encoders) {
|
||||
kfree(save_encoder_crtcs);
|
||||
|
@ -106,6 +106,7 @@ int drm_mode_object_add(struct drm_device *dev, struct drm_mode_object *obj,
|
||||
void drm_mode_object_register(struct drm_device *dev,
|
||||
struct drm_mode_object *obj);
|
||||
struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
uint32_t id, uint32_t type);
|
||||
void drm_mode_object_unregister(struct drm_device *dev,
|
||||
struct drm_mode_object *object);
|
||||
|
@ -137,8 +137,10 @@ EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
|
||||
u8 drm_dp_link_rate_to_bw_code(int link_rate)
|
||||
{
|
||||
switch (link_rate) {
|
||||
case 162000:
|
||||
default:
|
||||
WARN(1, "unknown DP link rate %d, using %x\n", link_rate,
|
||||
DP_LINK_BW_1_62);
|
||||
case 162000:
|
||||
return DP_LINK_BW_1_62;
|
||||
case 270000:
|
||||
return DP_LINK_BW_2_7;
|
||||
@ -151,8 +153,9 @@ EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code);
|
||||
int drm_dp_bw_code_to_link_rate(u8 link_bw)
|
||||
{
|
||||
switch (link_bw) {
|
||||
case DP_LINK_BW_1_62:
|
||||
default:
|
||||
WARN(1, "unknown DP link BW code %x, using 162000\n", link_bw);
|
||||
case DP_LINK_BW_1_62:
|
||||
return 162000;
|
||||
case DP_LINK_BW_2_7:
|
||||
return 270000;
|
||||
|
@ -220,7 +220,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
encoder = drm_encoder_find(dev, enc_resp->encoder_id);
|
||||
encoder = drm_encoder_find(dev, file_priv, enc_resp->encoder_id);
|
||||
if (!encoder)
|
||||
return -ENOENT;
|
||||
|
||||
|
@ -2266,7 +2266,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
|
||||
if (modes[n] == NULL)
|
||||
return best_score;
|
||||
|
||||
crtcs = kzalloc(fb_helper->connector_count *
|
||||
crtcs = kcalloc(fb_helper->connector_count,
|
||||
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
|
||||
if (!crtcs)
|
||||
return best_score;
|
||||
|
@ -381,7 +381,7 @@ int drm_mode_rmfb(struct drm_device *dev,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
fb = drm_framebuffer_lookup(dev, *id);
|
||||
fb = drm_framebuffer_lookup(dev, file_priv, *id);
|
||||
if (!fb)
|
||||
return -ENOENT;
|
||||
|
||||
@ -450,7 +450,7 @@ int drm_mode_getfb(struct drm_device *dev,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
fb = drm_framebuffer_lookup(dev, r->fb_id);
|
||||
fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
|
||||
if (!fb)
|
||||
return -ENOENT;
|
||||
|
||||
@ -515,7 +515,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
fb = drm_framebuffer_lookup(dev, r->fb_id);
|
||||
fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
|
||||
if (!fb)
|
||||
return -ENOENT;
|
||||
|
||||
@ -688,12 +688,13 @@ EXPORT_SYMBOL(drm_framebuffer_init);
|
||||
* again, using drm_framebuffer_put().
|
||||
*/
|
||||
struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
uint32_t id)
|
||||
{
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
|
||||
obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_FB);
|
||||
obj = __drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_FB);
|
||||
if (obj)
|
||||
fb = obj_to_fb(obj);
|
||||
return fb;
|
||||
|
@ -27,19 +27,24 @@
|
||||
* DOC: overview
|
||||
*
|
||||
* This library provides helpers for drivers that don't subclass
|
||||
* &drm_framebuffer and and use &drm_gem_object for their backing storage.
|
||||
* &drm_framebuffer and use &drm_gem_object for their backing storage.
|
||||
*
|
||||
* Drivers without additional needs to validate framebuffers can simply use
|
||||
* drm_gem_fb_create() and everything is wired up automatically. But all
|
||||
* parts can be used individually.
|
||||
* drm_gem_fb_create() and everything is wired up automatically. Other drivers
|
||||
* can use all parts independently.
|
||||
*/
|
||||
|
||||
/**
|
||||
* drm_gem_fb_get_obj() - Get GEM object for framebuffer
|
||||
* @fb: The framebuffer
|
||||
* @plane: Which plane
|
||||
* drm_gem_fb_get_obj() - Get GEM object backing the framebuffer
|
||||
* @fb: Framebuffer
|
||||
* @plane: Plane index
|
||||
*
|
||||
* Returns the GEM object for given framebuffer.
|
||||
* No additional reference is taken beyond the one that the &drm_frambuffer
|
||||
* already holds.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to &drm_gem_object for the given framebuffer and plane index or NULL
|
||||
* if it does not exist.
|
||||
*/
|
||||
struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
|
||||
unsigned int plane)
|
||||
@ -82,7 +87,7 @@ drm_gem_fb_alloc(struct drm_device *dev,
|
||||
|
||||
/**
|
||||
* drm_gem_fb_destroy - Free GEM backed framebuffer
|
||||
* @fb: DRM framebuffer
|
||||
* @fb: Framebuffer
|
||||
*
|
||||
* Frees a GEM backed framebuffer with its backing buffer(s) and the structure
|
||||
* itself. Drivers can use this as their &drm_framebuffer_funcs->destroy
|
||||
@ -102,12 +107,13 @@ EXPORT_SYMBOL(drm_gem_fb_destroy);
|
||||
|
||||
/**
|
||||
* drm_gem_fb_create_handle - Create handle for GEM backed framebuffer
|
||||
* @fb: DRM framebuffer
|
||||
* @file: drm file
|
||||
* @handle: handle created
|
||||
* @fb: Framebuffer
|
||||
* @file: DRM file to register the handle for
|
||||
* @handle: Pointer to return the created handle
|
||||
*
|
||||
* This function creates a handle for the GEM object backing the framebuffer.
|
||||
* Drivers can use this as their &drm_framebuffer_funcs->create_handle
|
||||
* callback.
|
||||
* callback. The GETFB IOCTL calls into this callback.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
@ -120,18 +126,21 @@ int drm_gem_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file,
|
||||
EXPORT_SYMBOL(drm_gem_fb_create_handle);
|
||||
|
||||
/**
|
||||
* drm_gem_fb_create_with_funcs() - helper function for the
|
||||
* drm_gem_fb_create_with_funcs() - Helper function for the
|
||||
* &drm_mode_config_funcs.fb_create
|
||||
* callback
|
||||
* @dev: DRM device
|
||||
* @file: drm file for the ioctl call
|
||||
* @mode_cmd: metadata from the userspace fb creation request
|
||||
* @file: DRM file that holds the GEM handle(s) backing the framebuffer
|
||||
* @mode_cmd: Metadata from the userspace framebuffer creation request
|
||||
* @funcs: vtable to be used for the new framebuffer object
|
||||
*
|
||||
* This can be used to set &drm_framebuffer_funcs for drivers that need the
|
||||
* &drm_framebuffer_funcs.dirty callback. Use drm_gem_fb_create() if you don't
|
||||
* need to change &drm_framebuffer_funcs.
|
||||
* The function does buffer size validation.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to a &drm_framebuffer on success or an error pointer on failure.
|
||||
*/
|
||||
struct drm_framebuffer *
|
||||
drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
|
||||
@ -192,15 +201,26 @@ static const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_gem_fb_create() - &drm_mode_config_funcs.fb_create callback function
|
||||
* drm_gem_fb_create() - Helper function for the
|
||||
* &drm_mode_config_funcs.fb_create callback
|
||||
* @dev: DRM device
|
||||
* @file: drm file for the ioctl call
|
||||
* @mode_cmd: metadata from the userspace fb creation request
|
||||
* @file: DRM file that holds the GEM handle(s) backing the framebuffer
|
||||
* @mode_cmd: Metadata from the userspace framebuffer creation request
|
||||
*
|
||||
* This function creates a new framebuffer object described by
|
||||
* &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
|
||||
* backing the framebuffer.
|
||||
*
|
||||
* If your hardware has special alignment or pitch requirements these should be
|
||||
* checked before calling this function. The function does buffer size
|
||||
* validation. Use drm_gem_fb_create_with_funcs() if you need to set
|
||||
* &drm_framebuffer_funcs.dirty.
|
||||
*
|
||||
* Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
|
||||
* The ADDFB2 IOCTL calls into this callback.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to a &drm_framebuffer on success or an error pointer on failure.
|
||||
*/
|
||||
struct drm_framebuffer *
|
||||
drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
|
||||
@ -212,15 +232,15 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
|
||||
EXPORT_SYMBOL_GPL(drm_gem_fb_create);
|
||||
|
||||
/**
|
||||
* drm_gem_fb_prepare_fb() - Prepare gem framebuffer
|
||||
* @plane: Which plane
|
||||
* @state: Plane state attach fence to
|
||||
* drm_gem_fb_prepare_fb() - Prepare a GEM backed framebuffer
|
||||
* @plane: Plane
|
||||
* @state: Plane state the fence will be attached to
|
||||
*
|
||||
* This can be used as the &drm_plane_helper_funcs.prepare_fb hook.
|
||||
*
|
||||
* This function checks if the plane FB has an dma-buf attached, extracts
|
||||
* the exclusive fence and attaches it to plane state for the atomic helper
|
||||
* to wait on.
|
||||
* This function prepares a GEM backed framebuffer for scanout by checking if
|
||||
* the plane framebuffer has a DMA-BUF attached. If it does, it extracts the
|
||||
* exclusive fence and attaches it to the plane state for the atomic helper to
|
||||
* wait on. This function can be used as the &drm_plane_helper_funcs.prepare_fb
|
||||
* callback.
|
||||
*
|
||||
* There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple
|
||||
* gem based framebuffer drivers which have their buffers always pinned in
|
||||
@ -246,17 +266,19 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane,
|
||||
EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);
|
||||
|
||||
/**
|
||||
* drm_gem_fbdev_fb_create - Create a drm_framebuffer for fbdev emulation
|
||||
* drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev
|
||||
* emulation
|
||||
* @dev: DRM device
|
||||
* @sizes: fbdev size description
|
||||
* @pitch_align: optional pitch alignment
|
||||
* @pitch_align: Optional pitch alignment
|
||||
* @obj: GEM object backing the framebuffer
|
||||
* @funcs: vtable to be used for the new framebuffer object
|
||||
*
|
||||
* This function creates a framebuffer for use with fbdev emulation.
|
||||
* This function creates a framebuffer from a &drm_fb_helper_surface_size
|
||||
* description for use in the &drm_fb_helper_funcs.fb_probe callback.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to a drm_framebuffer on success or an error pointer on failure.
|
||||
* Pointer to a &drm_framebuffer on success or an error pointer on failure.
|
||||
*/
|
||||
struct drm_framebuffer *
|
||||
drm_gem_fbdev_fb_create(struct drm_device *dev,
|
||||
|
@ -55,7 +55,6 @@ int drm_clients_info(struct seq_file *m, void* data);
|
||||
int drm_gem_name_info(struct seq_file *m, void *data);
|
||||
|
||||
/* drm_vblank.c */
|
||||
extern unsigned int drm_timestamp_monotonic;
|
||||
void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
|
||||
void drm_vblank_cleanup(struct drm_device *dev);
|
||||
|
||||
|
@ -235,7 +235,7 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_
|
||||
/* Only some caps make sense with UMS/render-only drivers. */
|
||||
switch (req->capability) {
|
||||
case DRM_CAP_TIMESTAMP_MONOTONIC:
|
||||
req->value = drm_timestamp_monotonic;
|
||||
req->value = 1;
|
||||
return 0;
|
||||
case DRM_CAP_PRIME:
|
||||
req->value |= dev->driver->prime_fd_to_handle ? DRM_PRIME_CAP_IMPORT : 0;
|
||||
|
@ -105,6 +105,7 @@ void drm_mode_object_unregister(struct drm_device *dev,
|
||||
}
|
||||
|
||||
struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
uint32_t id, uint32_t type)
|
||||
{
|
||||
struct drm_mode_object *obj = NULL;
|
||||
@ -127,7 +128,7 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
|
||||
|
||||
/**
|
||||
* drm_mode_object_find - look up a drm object with static lifetime
|
||||
* @dev: drm device
|
||||
* @file_priv: drm file
|
||||
* @id: id of the mode object
|
||||
* @type: type of the mode object
|
||||
*
|
||||
@ -136,11 +137,12 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
|
||||
* by callind drm_mode_object_put().
|
||||
*/
|
||||
struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
uint32_t id, uint32_t type)
|
||||
{
|
||||
struct drm_mode_object *obj = NULL;
|
||||
|
||||
obj = __drm_mode_object_find(dev, id, type);
|
||||
obj = __drm_mode_object_find(dev, file_priv, id, type);
|
||||
return obj;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_object_find);
|
||||
@ -359,7 +361,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
|
||||
obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
|
||||
obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type);
|
||||
if (!obj) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
@ -481,7 +483,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
|
||||
arg_obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type);
|
||||
if (!arg_obj)
|
||||
return -ENOENT;
|
||||
|
||||
|
@ -513,7 +513,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
plane = drm_plane_find(dev, plane_resp->plane_id);
|
||||
plane = drm_plane_find(dev, file_priv, plane_resp->plane_id);
|
||||
if (!plane)
|
||||
return -ENOENT;
|
||||
|
||||
@ -703,7 +703,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
|
||||
* First, find the plane, crtc, and fb objects. If not available,
|
||||
* we don't bother to call the driver.
|
||||
*/
|
||||
plane = drm_plane_find(dev, plane_req->plane_id);
|
||||
plane = drm_plane_find(dev, file_priv, plane_req->plane_id);
|
||||
if (!plane) {
|
||||
DRM_DEBUG_KMS("Unknown plane ID %d\n",
|
||||
plane_req->plane_id);
|
||||
@ -711,14 +711,14 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
|
||||
}
|
||||
|
||||
if (plane_req->fb_id) {
|
||||
fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
|
||||
fb = drm_framebuffer_lookup(dev, file_priv, plane_req->fb_id);
|
||||
if (!fb) {
|
||||
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
|
||||
plane_req->fb_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
crtc = drm_crtc_find(dev, plane_req->crtc_id);
|
||||
crtc = drm_crtc_find(dev, file_priv, plane_req->crtc_id);
|
||||
if (!crtc) {
|
||||
drm_framebuffer_put(fb);
|
||||
DRM_DEBUG_KMS("Unknown crtc ID %d\n",
|
||||
@ -829,7 +829,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
|
||||
if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
|
||||
return -EINVAL;
|
||||
|
||||
crtc = drm_crtc_find(dev, req->crtc_id);
|
||||
crtc = drm_crtc_find(dev, file_priv, req->crtc_id);
|
||||
if (!crtc) {
|
||||
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
|
||||
return -ENOENT;
|
||||
@ -944,7 +944,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
||||
if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
|
||||
return -EINVAL;
|
||||
|
||||
crtc = drm_crtc_find(dev, page_flip->crtc_id);
|
||||
crtc = drm_crtc_find(dev, file_priv, page_flip->crtc_id);
|
||||
if (!crtc)
|
||||
return -ENOENT;
|
||||
|
||||
@ -1005,7 +1005,7 @@ retry:
|
||||
goto out;
|
||||
}
|
||||
|
||||
fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
|
||||
fb = drm_framebuffer_lookup(dev, file_priv, page_flip->fb_id);
|
||||
if (!fb) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
|
@ -354,7 +354,7 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
/* Find current connectors for CRTC */
|
||||
num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
|
||||
BUG_ON(num_connectors == 0);
|
||||
connector_list = kzalloc(num_connectors * sizeof(*connector_list),
|
||||
connector_list = kcalloc(num_connectors, sizeof(*connector_list),
|
||||
GFP_KERNEL);
|
||||
if (!connector_list)
|
||||
return -ENOMEM;
|
||||
|
@ -99,7 +99,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
|
||||
|
||||
/* Step 2: Validate against encoders and crtcs */
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
struct drm_encoder *encoder = drm_encoder_find(dev, ids[i]);
|
||||
struct drm_encoder *encoder = drm_encoder_find(dev, NULL, ids[i]);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (!encoder)
|
||||
|
@ -450,7 +450,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
property = drm_property_find(dev, out_resp->prop_id);
|
||||
property = drm_property_find(dev, file_priv, out_resp->prop_id);
|
||||
if (!property)
|
||||
return -ENOENT;
|
||||
|
||||
@ -634,7 +634,7 @@ struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_property_blob *blob = NULL;
|
||||
|
||||
obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
|
||||
obj = __drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_BLOB);
|
||||
if (obj)
|
||||
blob = obj_to_blob(obj);
|
||||
return blob;
|
||||
@ -897,7 +897,7 @@ bool drm_property_change_valid_get(struct drm_property *property,
|
||||
if (value == 0)
|
||||
return true;
|
||||
|
||||
*ref = __drm_mode_object_find(property->dev, value,
|
||||
*ref = __drm_mode_object_find(property->dev, NULL, value,
|
||||
property->values[0]);
|
||||
return *ref != NULL;
|
||||
}
|
||||
|
@ -845,7 +845,8 @@ static int drm_syncobj_array_wait(struct drm_device *dev,
|
||||
}
|
||||
|
||||
static int drm_syncobj_array_find(struct drm_file *file_private,
|
||||
void *user_handles, uint32_t count_handles,
|
||||
void __user *user_handles,
|
||||
uint32_t count_handles,
|
||||
struct drm_syncobj ***syncobjs_out)
|
||||
{
|
||||
uint32_t i, *handles;
|
||||
|
@ -78,28 +78,20 @@
|
||||
|
||||
static bool
|
||||
drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
|
||||
struct timeval *tvblank, bool in_vblank_irq);
|
||||
ktime_t *tvblank, bool in_vblank_irq);
|
||||
|
||||
static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */
|
||||
|
||||
/*
|
||||
* Default to use monotonic timestamps for wait-for-vblank and page-flip
|
||||
* complete events.
|
||||
*/
|
||||
unsigned int drm_timestamp_monotonic = 1;
|
||||
|
||||
static int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
|
||||
|
||||
module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
|
||||
module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
|
||||
module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
|
||||
MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
|
||||
MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
|
||||
MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
|
||||
|
||||
static void store_vblank(struct drm_device *dev, unsigned int pipe,
|
||||
u32 vblank_count_inc,
|
||||
struct timeval *t_vblank, u32 last)
|
||||
ktime_t t_vblank, u32 last)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
|
||||
@ -108,7 +100,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe,
|
||||
vblank->last = last;
|
||||
|
||||
write_seqlock(&vblank->seqlock);
|
||||
vblank->time = *t_vblank;
|
||||
vblank->time = t_vblank;
|
||||
vblank->count += vblank_count_inc;
|
||||
write_sequnlock(&vblank->seqlock);
|
||||
}
|
||||
@ -151,7 +143,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
|
||||
{
|
||||
u32 cur_vblank;
|
||||
bool rc;
|
||||
struct timeval t_vblank;
|
||||
ktime_t t_vblank;
|
||||
int count = DRM_TIMESTAMP_MAXRETRIES;
|
||||
|
||||
spin_lock(&dev->vblank_time_lock);
|
||||
@ -171,13 +163,13 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
|
||||
* interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
|
||||
*/
|
||||
if (!rc)
|
||||
t_vblank = (struct timeval) {0, 0};
|
||||
t_vblank = 0;
|
||||
|
||||
/*
|
||||
* +1 to make sure user will never see the same
|
||||
* vblank counter value before and after a modeset
|
||||
*/
|
||||
store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
|
||||
store_vblank(dev, pipe, 1, t_vblank, cur_vblank);
|
||||
|
||||
spin_unlock(&dev->vblank_time_lock);
|
||||
}
|
||||
@ -200,7 +192,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
u32 cur_vblank, diff;
|
||||
bool rc;
|
||||
struct timeval t_vblank;
|
||||
ktime_t t_vblank;
|
||||
int count = DRM_TIMESTAMP_MAXRETRIES;
|
||||
int framedur_ns = vblank->framedur_ns;
|
||||
|
||||
@ -225,11 +217,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
/* trust the hw counter when it's around */
|
||||
diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
|
||||
} else if (rc && framedur_ns) {
|
||||
const struct timeval *t_old;
|
||||
u64 diff_ns;
|
||||
|
||||
t_old = &vblank->time;
|
||||
diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
|
||||
u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
|
||||
|
||||
/*
|
||||
* Figure out how many vblanks we've missed based
|
||||
@ -278,9 +266,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
* for now, to mark the vblanktimestamp as invalid.
|
||||
*/
|
||||
if (!rc && !in_vblank_irq)
|
||||
t_vblank = (struct timeval) {0, 0};
|
||||
t_vblank = 0;
|
||||
|
||||
store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
|
||||
store_vblank(dev, pipe, diff, t_vblank, cur_vblank);
|
||||
}
|
||||
|
||||
static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
|
||||
@ -556,7 +544,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
|
||||
* @pipe: index of CRTC whose vblank timestamp to retrieve
|
||||
* @max_error: Desired maximum allowable error in timestamps (nanosecs)
|
||||
* On return contains true maximum error of timestamp
|
||||
* @vblank_time: Pointer to struct timeval which should receive the timestamp
|
||||
* @vblank_time: Pointer to time which should receive the timestamp
|
||||
* @in_vblank_irq:
|
||||
* True when called from drm_crtc_handle_vblank(). Some drivers
|
||||
* need to apply some workarounds for gpu-specific vblank irq quirks
|
||||
@ -584,10 +572,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
|
||||
bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
|
||||
unsigned int pipe,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
ktime_t *vblank_time,
|
||||
bool in_vblank_irq)
|
||||
{
|
||||
struct timeval tv_etime;
|
||||
struct timespec64 ts_etime, ts_vblank_time;
|
||||
ktime_t stime, etime;
|
||||
bool vbl_status;
|
||||
struct drm_crtc *crtc;
|
||||
@ -676,41 +664,31 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
|
||||
delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
|
||||
mode->crtc_clock);
|
||||
|
||||
if (!drm_timestamp_monotonic)
|
||||
etime = ktime_mono_to_real(etime);
|
||||
|
||||
/* save this only for debugging purposes */
|
||||
tv_etime = ktime_to_timeval(etime);
|
||||
ts_etime = ktime_to_timespec64(etime);
|
||||
ts_vblank_time = ktime_to_timespec64(*vblank_time);
|
||||
/* Subtract time delta from raw timestamp to get final
|
||||
* vblank_time timestamp for end of vblank.
|
||||
*/
|
||||
etime = ktime_sub_ns(etime, delta_ns);
|
||||
*vblank_time = ktime_to_timeval(etime);
|
||||
*vblank_time = etime;
|
||||
|
||||
DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
|
||||
DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %lld.%06ld -> %lld.%06ld [e %d us, %d rep]\n",
|
||||
pipe, hpos, vpos,
|
||||
(long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
|
||||
(long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
|
||||
duration_ns/1000, i);
|
||||
(u64)ts_etime.tv_sec, ts_etime.tv_nsec / 1000,
|
||||
(u64)ts_vblank_time.tv_sec, ts_vblank_time.tv_nsec / 1000,
|
||||
duration_ns / 1000, i);
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
|
||||
|
||||
static struct timeval get_drm_timestamp(void)
|
||||
{
|
||||
ktime_t now;
|
||||
|
||||
now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
|
||||
return ktime_to_timeval(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
|
||||
* vblank interval
|
||||
* @dev: DRM device
|
||||
* @pipe: index of CRTC whose vblank timestamp to retrieve
|
||||
* @tvblank: Pointer to target struct timeval which should receive the timestamp
|
||||
* @tvblank: Pointer to target time which should receive the timestamp
|
||||
* @in_vblank_irq:
|
||||
* True when called from drm_crtc_handle_vblank(). Some drivers
|
||||
* need to apply some workarounds for gpu-specific vblank irq quirks
|
||||
@ -728,7 +706,7 @@ static struct timeval get_drm_timestamp(void)
|
||||
*/
|
||||
static bool
|
||||
drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
|
||||
struct timeval *tvblank, bool in_vblank_irq)
|
||||
ktime_t *tvblank, bool in_vblank_irq)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
@ -744,7 +722,7 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
|
||||
* Return current monotonic/gettimeofday timestamp as best estimate.
|
||||
*/
|
||||
if (!ret)
|
||||
*tvblank = get_drm_timestamp();
|
||||
*tvblank = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -769,14 +747,14 @@ u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
|
||||
EXPORT_SYMBOL(drm_crtc_vblank_count);
|
||||
|
||||
static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
|
||||
struct timeval *vblanktime)
|
||||
ktime_t *vblanktime)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
u32 vblank_count;
|
||||
unsigned int seq;
|
||||
|
||||
if (WARN_ON(pipe >= dev->num_crtcs)) {
|
||||
*vblanktime = (struct timeval) { 0 };
|
||||
*vblanktime = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -793,7 +771,7 @@ static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
|
||||
* drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
|
||||
* and the system timestamp corresponding to that vblank counter value
|
||||
* @crtc: which counter to retrieve
|
||||
* @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
|
||||
* @vblanktime: Pointer to time to receive the vblank timestamp.
|
||||
*
|
||||
* Fetches the "cooked" vblank count value that represents the number of
|
||||
* vblank events since the system was booted, including lost events due to
|
||||
@ -801,7 +779,7 @@ static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
|
||||
* of the vblank interval that corresponds to the current vblank counter value.
|
||||
*/
|
||||
u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
|
||||
struct timeval *vblanktime)
|
||||
ktime_t *vblanktime)
|
||||
{
|
||||
return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
|
||||
vblanktime);
|
||||
@ -810,11 +788,18 @@ EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
|
||||
|
||||
static void send_vblank_event(struct drm_device *dev,
|
||||
struct drm_pending_vblank_event *e,
|
||||
unsigned long seq, struct timeval *now)
|
||||
unsigned long seq, ktime_t now)
|
||||
{
|
||||
struct timespec64 tv = ktime_to_timespec64(now);
|
||||
|
||||
e->event.sequence = seq;
|
||||
e->event.tv_sec = now->tv_sec;
|
||||
e->event.tv_usec = now->tv_usec;
|
||||
/*
|
||||
* e->event is a user space structure, with hardcoded unsigned
|
||||
* 32-bit seconds/microseconds. This is safe as we always use
|
||||
* monotonic timestamps since linux-4.15
|
||||
*/
|
||||
e->event.tv_sec = tv.tv_sec;
|
||||
e->event.tv_usec = tv.tv_nsec / 1000;
|
||||
|
||||
trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
|
||||
e->event.sequence);
|
||||
@ -869,7 +854,7 @@ void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
|
||||
assert_spin_locked(&dev->event_lock);
|
||||
|
||||
e->pipe = pipe;
|
||||
e->event.sequence = drm_vblank_count(dev, pipe);
|
||||
e->event.sequence = drm_crtc_accurate_vblank_count(crtc) + 1;
|
||||
e->event.crtc_id = crtc->base.id;
|
||||
list_add_tail(&e->base.link, &dev->vblank_event_list);
|
||||
}
|
||||
@ -891,18 +876,18 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned int seq, pipe = drm_crtc_index(crtc);
|
||||
struct timeval now;
|
||||
ktime_t now;
|
||||
|
||||
if (dev->num_crtcs > 0) {
|
||||
seq = drm_vblank_count_and_time(dev, pipe, &now);
|
||||
} else {
|
||||
seq = 0;
|
||||
|
||||
now = get_drm_timestamp();
|
||||
now = ktime_get();
|
||||
}
|
||||
e->pipe = pipe;
|
||||
e->event.crtc_id = crtc->base.id;
|
||||
send_vblank_event(dev, e, seq, &now);
|
||||
send_vblank_event(dev, e, seq, now);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_send_vblank_event);
|
||||
|
||||
@ -1100,7 +1085,8 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_pending_vblank_event *e, *t;
|
||||
struct timeval now;
|
||||
|
||||
ktime_t now;
|
||||
unsigned long irqflags;
|
||||
unsigned int seq;
|
||||
|
||||
@ -1141,7 +1127,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
|
||||
e->event.sequence, seq);
|
||||
list_del(&e->base.link);
|
||||
drm_vblank_put(dev, pipe);
|
||||
send_vblank_event(dev, e, seq, &now);
|
||||
send_vblank_event(dev, e, seq, now);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->event_lock, irqflags);
|
||||
|
||||
@ -1321,7 +1307,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_pending_vblank_event *e;
|
||||
struct timeval now;
|
||||
ktime_t now;
|
||||
unsigned long flags;
|
||||
unsigned int seq;
|
||||
int ret;
|
||||
@ -1367,7 +1353,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
|
||||
e->event.sequence = vblwait->request.sequence;
|
||||
if (vblank_passed(seq, vblwait->request.sequence)) {
|
||||
drm_vblank_put(dev, pipe);
|
||||
send_vblank_event(dev, e, seq, &now);
|
||||
send_vblank_event(dev, e, seq, now);
|
||||
vblwait->reply.sequence = seq;
|
||||
} else {
|
||||
/* drm_handle_vblank_events will call drm_vblank_put */
|
||||
@ -1398,6 +1384,23 @@ static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
|
||||
_DRM_VBLANK_NEXTONMISS));
|
||||
}
|
||||
|
||||
static void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe,
|
||||
struct drm_wait_vblank_reply *reply)
|
||||
{
|
||||
ktime_t now;
|
||||
struct timespec64 ts;
|
||||
|
||||
/*
|
||||
* drm_wait_vblank_reply is a UAPI structure that uses 'long'
|
||||
* to store the seconds. This is safe as we always use monotonic
|
||||
* timestamps since linux-4.15.
|
||||
*/
|
||||
reply->sequence = drm_vblank_count_and_time(dev, pipe, &now);
|
||||
ts = ktime_to_timespec64(now);
|
||||
reply->tval_sec = (u32)ts.tv_sec;
|
||||
reply->tval_usec = ts.tv_nsec / 1000;
|
||||
}
|
||||
|
||||
int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
@ -1439,12 +1442,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
|
||||
if (dev->vblank_disable_immediate &&
|
||||
drm_wait_vblank_is_query(vblwait) &&
|
||||
READ_ONCE(vblank->enabled)) {
|
||||
struct timeval now;
|
||||
|
||||
vblwait->reply.sequence =
|
||||
drm_vblank_count_and_time(dev, pipe, &now);
|
||||
vblwait->reply.tval_sec = now.tv_sec;
|
||||
vblwait->reply.tval_usec = now.tv_usec;
|
||||
drm_wait_vblank_reply(dev, pipe, &vblwait->reply);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1487,11 +1485,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
|
||||
}
|
||||
|
||||
if (ret != -EINTR) {
|
||||
struct timeval now;
|
||||
|
||||
vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
|
||||
vblwait->reply.tval_sec = now.tv_sec;
|
||||
vblwait->reply.tval_usec = now.tv_usec;
|
||||
drm_wait_vblank_reply(dev, pipe, &vblwait->reply);
|
||||
|
||||
DRM_DEBUG("crtc %d returning %u to client\n",
|
||||
pipe, vblwait->reply.sequence);
|
||||
@ -1507,7 +1501,7 @@ done:
|
||||
static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_pending_vblank_event *e, *t;
|
||||
struct timeval now;
|
||||
ktime_t now;
|
||||
unsigned int seq;
|
||||
|
||||
assert_spin_locked(&dev->event_lock);
|
||||
@ -1525,7 +1519,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
|
||||
|
||||
list_del(&e->base.link);
|
||||
drm_vblank_put(dev, pipe);
|
||||
send_vblank_event(dev, e, seq, &now);
|
||||
send_vblank_event(dev, e, seq, now);
|
||||
}
|
||||
|
||||
trace_drm_vblank_event(pipe, seq);
|
||||
|
@ -7,8 +7,6 @@ config DRM_ETNAVIV
|
||||
select SHMEM
|
||||
select SYNC_FILE
|
||||
select TMPFS
|
||||
select IOMMU_API
|
||||
select IOMMU_SUPPORT
|
||||
select WANT_DEV_COREDUMP
|
||||
select CMA if HAVE_DMA_CONTIGUOUS
|
||||
select DMA_CMA if HAVE_DMA_CONTIGUOUS
|
||||
|
@ -10,6 +10,7 @@ etnaviv-y := \
|
||||
etnaviv_gpu.o \
|
||||
etnaviv_iommu_v2.o \
|
||||
etnaviv_iommu.o \
|
||||
etnaviv_mmu.o
|
||||
etnaviv_mmu.o \
|
||||
etnaviv_perfmon.o
|
||||
|
||||
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o
|
||||
|
@ -250,6 +250,42 @@ void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
|
||||
}
|
||||
}
|
||||
|
||||
/* Append a 'sync point' to the ring buffer. */
|
||||
void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event)
|
||||
{
|
||||
struct etnaviv_cmdbuf *buffer = gpu->buffer;
|
||||
unsigned int waitlink_offset = buffer->user_size - 16;
|
||||
u32 dwords, target;
|
||||
|
||||
/*
|
||||
* We need at most 3 dwords in the return target:
|
||||
* 1 event + 1 end + 1 wait + 1 link.
|
||||
*/
|
||||
dwords = 4;
|
||||
target = etnaviv_buffer_reserve(gpu, buffer, dwords);
|
||||
|
||||
/* Signal sync point event */
|
||||
CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
|
||||
VIVS_GL_EVENT_FROM_PE);
|
||||
|
||||
/* Stop the FE to 'pause' the GPU */
|
||||
CMD_END(buffer);
|
||||
|
||||
/* Append waitlink */
|
||||
CMD_WAIT(buffer);
|
||||
CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer) +
|
||||
buffer->user_size - 4);
|
||||
|
||||
/*
|
||||
* Kick off the 'sync point' command by replacing the previous
|
||||
* WAIT with a link to the address in the ring buffer.
|
||||
*/
|
||||
etnaviv_buffer_replace_wait(buffer, waitlink_offset,
|
||||
VIV_FE_LINK_HEADER_OP_LINK |
|
||||
VIV_FE_LINK_HEADER_PREFETCH(dwords),
|
||||
target);
|
||||
}
|
||||
|
||||
/* Append a command buffer to the ring buffer. */
|
||||
void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
|
||||
struct etnaviv_cmdbuf *cmdbuf)
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "etnaviv_cmdbuf.h"
|
||||
#include "etnaviv_gpu.h"
|
||||
#include "etnaviv_mmu.h"
|
||||
#include "etnaviv_perfmon.h"
|
||||
|
||||
#define SUBALLOC_SIZE SZ_256K
|
||||
#define SUBALLOC_GRANULE SZ_4K
|
||||
@ -87,9 +88,10 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc)
|
||||
|
||||
struct etnaviv_cmdbuf *
|
||||
etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size,
|
||||
size_t nr_bos)
|
||||
size_t nr_bos, size_t nr_pmrs)
|
||||
{
|
||||
struct etnaviv_cmdbuf *cmdbuf;
|
||||
struct etnaviv_perfmon_request *pmrs;
|
||||
size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo_map[0]),
|
||||
sizeof(*cmdbuf));
|
||||
int granule_offs, order, ret;
|
||||
@ -98,6 +100,12 @@ etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size,
|
||||
if (!cmdbuf)
|
||||
return NULL;
|
||||
|
||||
sz = sizeof(*pmrs) * nr_pmrs;
|
||||
pmrs = kzalloc(sz, GFP_KERNEL);
|
||||
if (!pmrs)
|
||||
goto out_free_cmdbuf;
|
||||
|
||||
cmdbuf->pmrs = pmrs;
|
||||
cmdbuf->suballoc = suballoc;
|
||||
cmdbuf->size = size;
|
||||
|
||||
@ -124,6 +132,10 @@ retry:
|
||||
cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset;
|
||||
|
||||
return cmdbuf;
|
||||
|
||||
out_free_cmdbuf:
|
||||
kfree(cmdbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf)
|
||||
@ -139,6 +151,7 @@ void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf)
|
||||
suballoc->free_space = 1;
|
||||
mutex_unlock(&suballoc->lock);
|
||||
wake_up_all(&suballoc->free_event);
|
||||
kfree(cmdbuf->pmrs);
|
||||
kfree(cmdbuf);
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
struct etnaviv_gpu;
|
||||
struct etnaviv_cmdbuf_suballoc;
|
||||
struct etnaviv_perfmon_request;
|
||||
|
||||
struct etnaviv_cmdbuf {
|
||||
/* suballocator this cmdbuf is allocated from */
|
||||
@ -38,6 +39,9 @@ struct etnaviv_cmdbuf {
|
||||
u32 exec_state;
|
||||
/* per GPU in-flight list */
|
||||
struct list_head node;
|
||||
/* perfmon requests */
|
||||
unsigned int nr_pmrs;
|
||||
struct etnaviv_perfmon_request *pmrs;
|
||||
/* BOs attached to this command buffer */
|
||||
unsigned int nr_bos;
|
||||
struct etnaviv_vram_mapping *bo_map[0];
|
||||
@ -49,7 +53,7 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc);
|
||||
|
||||
struct etnaviv_cmdbuf *
|
||||
etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size,
|
||||
size_t nr_bos);
|
||||
size_t nr_bos, size_t nr_pmrs);
|
||||
void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf);
|
||||
|
||||
u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "etnaviv_gpu.h"
|
||||
#include "etnaviv_gem.h"
|
||||
#include "etnaviv_mmu.h"
|
||||
#include "etnaviv_perfmon.h"
|
||||
|
||||
#ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
|
||||
static bool reglog;
|
||||
@ -451,6 +452,40 @@ static int etnaviv_ioctl_gem_wait(struct drm_device *dev, void *data,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int etnaviv_ioctl_pm_query_dom(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct etnaviv_drm_private *priv = dev->dev_private;
|
||||
struct drm_etnaviv_pm_domain *args = data;
|
||||
struct etnaviv_gpu *gpu;
|
||||
|
||||
if (args->pipe >= ETNA_MAX_PIPES)
|
||||
return -EINVAL;
|
||||
|
||||
gpu = priv->gpu[args->pipe];
|
||||
if (!gpu)
|
||||
return -ENXIO;
|
||||
|
||||
return etnaviv_pm_query_dom(gpu, args);
|
||||
}
|
||||
|
||||
static int etnaviv_ioctl_pm_query_sig(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct etnaviv_drm_private *priv = dev->dev_private;
|
||||
struct drm_etnaviv_pm_signal *args = data;
|
||||
struct etnaviv_gpu *gpu;
|
||||
|
||||
if (args->pipe >= ETNA_MAX_PIPES)
|
||||
return -EINVAL;
|
||||
|
||||
gpu = priv->gpu[args->pipe];
|
||||
if (!gpu)
|
||||
return -ENXIO;
|
||||
|
||||
return etnaviv_pm_query_sig(gpu, args);
|
||||
}
|
||||
|
||||
static const struct drm_ioctl_desc etnaviv_ioctls[] = {
|
||||
#define ETNA_IOCTL(n, func, flags) \
|
||||
DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags)
|
||||
@ -463,6 +498,8 @@ static const struct drm_ioctl_desc etnaviv_ioctls[] = {
|
||||
ETNA_IOCTL(WAIT_FENCE, wait_fence, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
ETNA_IOCTL(GEM_USERPTR, gem_userptr, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
ETNA_IOCTL(GEM_WAIT, gem_wait, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
ETNA_IOCTL(PM_QUERY_DOM, pm_query_dom, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
ETNA_IOCTL(PM_QUERY_SIG, pm_query_sig, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
};
|
||||
|
||||
static const struct vm_operations_struct vm_ops = {
|
||||
@ -513,7 +550,7 @@ static struct drm_driver etnaviv_drm_driver = {
|
||||
.desc = "etnaviv DRM",
|
||||
.date = "20151214",
|
||||
.major = 1,
|
||||
.minor = 1,
|
||||
.minor = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
@ -92,15 +91,12 @@ int etnaviv_gem_cpu_fini(struct drm_gem_object *obj);
|
||||
void etnaviv_gem_free_object(struct drm_gem_object *obj);
|
||||
int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
|
||||
u32 size, u32 flags, u32 *handle);
|
||||
struct drm_gem_object *etnaviv_gem_new_locked(struct drm_device *dev,
|
||||
u32 size, u32 flags);
|
||||
struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
|
||||
u32 size, u32 flags);
|
||||
int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
|
||||
uintptr_t ptr, u32 size, u32 flags, u32 *handle);
|
||||
u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
|
||||
u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr);
|
||||
void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
|
||||
void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event);
|
||||
void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
|
||||
struct etnaviv_cmdbuf *cmdbuf);
|
||||
void etnaviv_validate_init(void);
|
||||
|
@ -704,25 +704,6 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
|
||||
u32 size, u32 flags)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
obj = __etnaviv_gem_new(dev, size, flags);
|
||||
if (IS_ERR(obj))
|
||||
return obj;
|
||||
|
||||
ret = etnaviv_gem_obj_add(dev, obj);
|
||||
if (ret < 0) {
|
||||
drm_gem_object_put_unlocked(obj);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags,
|
||||
struct reservation_object *robj, const struct etnaviv_gem_ops *ops,
|
||||
struct etnaviv_gem_object **res)
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "etnaviv_drv.h"
|
||||
#include "etnaviv_gpu.h"
|
||||
#include "etnaviv_gem.h"
|
||||
#include "etnaviv_perfmon.h"
|
||||
|
||||
/*
|
||||
* Cmdstream submission:
|
||||
@ -283,6 +284,54 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int submit_perfmon_validate(struct etnaviv_gem_submit *submit,
|
||||
struct etnaviv_cmdbuf *cmdbuf,
|
||||
const struct drm_etnaviv_gem_submit_pmr *pmrs,
|
||||
u32 nr_pms)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < nr_pms; i++) {
|
||||
const struct drm_etnaviv_gem_submit_pmr *r = pmrs + i;
|
||||
struct etnaviv_gem_submit_bo *bo;
|
||||
int ret;
|
||||
|
||||
ret = submit_bo(submit, r->read_idx, &bo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* at offset 0 a sequence number gets stored used for userspace sync */
|
||||
if (r->read_offset == 0) {
|
||||
DRM_ERROR("perfmon request: offset is 0");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (r->read_offset >= bo->obj->base.size - sizeof(u32)) {
|
||||
DRM_ERROR("perfmon request: offset %u outside object", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (r->flags & ~(ETNA_PM_PROCESS_PRE | ETNA_PM_PROCESS_POST)) {
|
||||
DRM_ERROR("perfmon request: flags are not valid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (etnaviv_pm_req_validate(r, cmdbuf->exec_state)) {
|
||||
DRM_ERROR("perfmon request: domain or signal not valid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmdbuf->pmrs[i].flags = r->flags;
|
||||
cmdbuf->pmrs[i].domain = r->domain;
|
||||
cmdbuf->pmrs[i].signal = r->signal;
|
||||
cmdbuf->pmrs[i].sequence = r->sequence;
|
||||
cmdbuf->pmrs[i].offset = r->read_offset;
|
||||
cmdbuf->pmrs[i].bo_vma = etnaviv_gem_vmap(&bo->obj->base);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void submit_cleanup(struct etnaviv_gem_submit *submit)
|
||||
{
|
||||
unsigned i;
|
||||
@ -306,6 +355,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
struct etnaviv_drm_private *priv = dev->dev_private;
|
||||
struct drm_etnaviv_gem_submit *args = data;
|
||||
struct drm_etnaviv_gem_submit_reloc *relocs;
|
||||
struct drm_etnaviv_gem_submit_pmr *pmrs;
|
||||
struct drm_etnaviv_gem_submit_bo *bos;
|
||||
struct etnaviv_gem_submit *submit;
|
||||
struct etnaviv_cmdbuf *cmdbuf;
|
||||
@ -347,11 +397,12 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
*/
|
||||
bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL);
|
||||
relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL);
|
||||
pmrs = kvmalloc_array(args->nr_pmrs, sizeof(*pmrs), GFP_KERNEL);
|
||||
stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL);
|
||||
cmdbuf = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc,
|
||||
ALIGN(args->stream_size, 8) + 8,
|
||||
args->nr_bos);
|
||||
if (!bos || !relocs || !stream || !cmdbuf) {
|
||||
args->nr_bos, args->nr_pmrs);
|
||||
if (!bos || !relocs || !pmrs || !stream || !cmdbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_submit_cmds;
|
||||
}
|
||||
@ -373,6 +424,14 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
goto err_submit_cmds;
|
||||
}
|
||||
|
||||
ret = copy_from_user(pmrs, u64_to_user_ptr(args->pmrs),
|
||||
args->nr_pmrs * sizeof(*pmrs));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto err_submit_cmds;
|
||||
}
|
||||
cmdbuf->nr_pmrs = args->nr_pmrs;
|
||||
|
||||
ret = copy_from_user(stream, u64_to_user_ptr(args->stream),
|
||||
args->stream_size);
|
||||
if (ret) {
|
||||
@ -441,6 +500,10 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = submit_perfmon_validate(submit, cmdbuf, pmrs, args->nr_pmrs);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
memcpy(cmdbuf->vaddr, stream, args->stream_size);
|
||||
cmdbuf->user_size = ALIGN(args->stream_size, 8);
|
||||
|
||||
@ -496,6 +559,8 @@ err_submit_cmds:
|
||||
kvfree(bos);
|
||||
if (relocs)
|
||||
kvfree(relocs);
|
||||
if (pmrs)
|
||||
kvfree(pmrs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "etnaviv_gpu.h"
|
||||
#include "etnaviv_gem.h"
|
||||
#include "etnaviv_mmu.h"
|
||||
#include "etnaviv_perfmon.h"
|
||||
#include "common.xml.h"
|
||||
#include "state.xml.h"
|
||||
#include "state_hi.xml.h"
|
||||
@ -420,9 +421,10 @@ static void etnaviv_gpu_update_clock(struct etnaviv_gpu *gpu)
|
||||
gpu->base_rate_shader >> gpu->freq_scale);
|
||||
} else {
|
||||
unsigned int fscale = 1 << (6 - gpu->freq_scale);
|
||||
u32 clock = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
|
||||
VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale);
|
||||
u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
|
||||
|
||||
clock &= ~VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK;
|
||||
clock |= VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale);
|
||||
etnaviv_gpu_load_clock(gpu, clock);
|
||||
}
|
||||
}
|
||||
@ -433,24 +435,14 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
|
||||
unsigned long timeout;
|
||||
bool failed = true;
|
||||
|
||||
/* TODO
|
||||
*
|
||||
* - clock gating
|
||||
* - puls eater
|
||||
* - what about VG?
|
||||
*/
|
||||
|
||||
/* We hope that the GPU resets in under one second */
|
||||
timeout = jiffies + msecs_to_jiffies(1000);
|
||||
|
||||
while (time_is_after_jiffies(timeout)) {
|
||||
/* enable clock */
|
||||
etnaviv_gpu_update_clock(gpu);
|
||||
|
||||
control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
|
||||
|
||||
/* Wait for stable clock. Vivante's code waited for 1ms */
|
||||
usleep_range(1000, 10000);
|
||||
unsigned int fscale = 1 << (6 - gpu->freq_scale);
|
||||
control = VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale);
|
||||
etnaviv_gpu_load_clock(gpu, control);
|
||||
|
||||
/* isolate the GPU. */
|
||||
control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU;
|
||||
@ -461,7 +453,7 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
|
||||
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
|
||||
|
||||
/* wait for reset. */
|
||||
msleep(1);
|
||||
usleep_range(10, 20);
|
||||
|
||||
/* reset soft reset bit. */
|
||||
control &= ~VIVS_HI_CLOCK_CONTROL_SOFT_RESET;
|
||||
@ -490,6 +482,10 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* disable debug registers, as they are not normally needed */
|
||||
control |= VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
|
||||
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
|
||||
|
||||
failed = false;
|
||||
break;
|
||||
}
|
||||
@ -721,7 +717,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
|
||||
}
|
||||
|
||||
/* Create buffer: */
|
||||
gpu->buffer = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, PAGE_SIZE, 0);
|
||||
gpu->buffer = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, PAGE_SIZE, 0, 0);
|
||||
if (!gpu->buffer) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(gpu->dev, "could not create command buffer\n");
|
||||
@ -739,10 +735,9 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
|
||||
/* Setup event management */
|
||||
spin_lock_init(&gpu->event_spinlock);
|
||||
init_completion(&gpu->event_free);
|
||||
for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
|
||||
gpu->event[i].used = false;
|
||||
bitmap_zero(gpu->event_bitmap, ETNA_NR_EVENTS);
|
||||
for (i = 0; i < ARRAY_SIZE(gpu->event); i++)
|
||||
complete(&gpu->event_free);
|
||||
}
|
||||
|
||||
/* Now program the hardware */
|
||||
mutex_lock(&gpu->lock);
|
||||
@ -926,7 +921,7 @@ static void recover_worker(struct work_struct *work)
|
||||
struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
|
||||
recover_work);
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
unsigned int i = 0;
|
||||
|
||||
dev_err(gpu->dev, "hangcheck recover!\n");
|
||||
|
||||
@ -945,14 +940,12 @@ static void recover_worker(struct work_struct *work)
|
||||
|
||||
/* complete all events, the GPU won't do it after the reset */
|
||||
spin_lock_irqsave(&gpu->event_spinlock, flags);
|
||||
for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
|
||||
if (!gpu->event[i].used)
|
||||
continue;
|
||||
for_each_set_bit_from(i, gpu->event_bitmap, ETNA_NR_EVENTS) {
|
||||
dma_fence_signal(gpu->event[i].fence);
|
||||
gpu->event[i].fence = NULL;
|
||||
gpu->event[i].used = false;
|
||||
complete(&gpu->event_free);
|
||||
}
|
||||
bitmap_zero(gpu->event_bitmap, ETNA_NR_EVENTS);
|
||||
spin_unlock_irqrestore(&gpu->event_spinlock, flags);
|
||||
gpu->completed_fence = gpu->active_fence;
|
||||
|
||||
@ -1140,30 +1133,45 @@ int etnaviv_gpu_fence_sync_obj(struct etnaviv_gem_object *etnaviv_obj,
|
||||
* event management:
|
||||
*/
|
||||
|
||||
static unsigned int event_alloc(struct etnaviv_gpu *gpu)
|
||||
static int event_alloc(struct etnaviv_gpu *gpu, unsigned nr_events,
|
||||
unsigned int *events)
|
||||
{
|
||||
unsigned long ret, flags;
|
||||
unsigned int i, event = ~0U;
|
||||
unsigned long flags, timeout = msecs_to_jiffies(10 * 10000);
|
||||
unsigned i, acquired = 0;
|
||||
|
||||
ret = wait_for_completion_timeout(&gpu->event_free,
|
||||
msecs_to_jiffies(10 * 10000));
|
||||
if (!ret)
|
||||
dev_err(gpu->dev, "wait_for_completion_timeout failed");
|
||||
for (i = 0; i < nr_events; i++) {
|
||||
unsigned long ret;
|
||||
|
||||
ret = wait_for_completion_timeout(&gpu->event_free, timeout);
|
||||
|
||||
if (!ret) {
|
||||
dev_err(gpu->dev, "wait_for_completion_timeout failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
acquired++;
|
||||
timeout = ret;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&gpu->event_spinlock, flags);
|
||||
|
||||
/* find first free event */
|
||||
for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
|
||||
if (gpu->event[i].used == false) {
|
||||
gpu->event[i].used = true;
|
||||
event = i;
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < nr_events; i++) {
|
||||
int event = find_first_zero_bit(gpu->event_bitmap, ETNA_NR_EVENTS);
|
||||
|
||||
events[i] = event;
|
||||
memset(&gpu->event[event], 0, sizeof(struct etnaviv_event));
|
||||
set_bit(event, gpu->event_bitmap);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&gpu->event_spinlock, flags);
|
||||
|
||||
return event;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
for (i = 0; i < acquired; i++)
|
||||
complete(&gpu->event_free);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
|
||||
@ -1172,12 +1180,12 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
|
||||
|
||||
spin_lock_irqsave(&gpu->event_spinlock, flags);
|
||||
|
||||
if (gpu->event[event].used == false) {
|
||||
if (!test_bit(event, gpu->event_bitmap)) {
|
||||
dev_warn(gpu->dev, "event %u is already marked as free",
|
||||
event);
|
||||
spin_unlock_irqrestore(&gpu->event_spinlock, flags);
|
||||
} else {
|
||||
gpu->event[event].used = false;
|
||||
clear_bit(event, gpu->event_bitmap);
|
||||
spin_unlock_irqrestore(&gpu->event_spinlock, flags);
|
||||
|
||||
complete(&gpu->event_free);
|
||||
@ -1311,12 +1319,71 @@ void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu)
|
||||
pm_runtime_put_autosuspend(gpu->dev);
|
||||
}
|
||||
|
||||
static void sync_point_perfmon_sample(struct etnaviv_gpu *gpu,
|
||||
struct etnaviv_event *event, unsigned int flags)
|
||||
{
|
||||
const struct etnaviv_cmdbuf *cmdbuf = event->cmdbuf;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < cmdbuf->nr_pmrs; i++) {
|
||||
const struct etnaviv_perfmon_request *pmr = cmdbuf->pmrs + i;
|
||||
|
||||
if (pmr->flags == flags)
|
||||
etnaviv_perfmon_process(gpu, pmr);
|
||||
}
|
||||
}
|
||||
|
||||
static void sync_point_perfmon_sample_pre(struct etnaviv_gpu *gpu,
|
||||
struct etnaviv_event *event)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* disable clock gating */
|
||||
val = gpu_read(gpu, VIVS_PM_POWER_CONTROLS);
|
||||
val &= ~VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING;
|
||||
gpu_write(gpu, VIVS_PM_POWER_CONTROLS, val);
|
||||
|
||||
/* enable debug register */
|
||||
val = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
|
||||
val &= ~VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
|
||||
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, val);
|
||||
|
||||
sync_point_perfmon_sample(gpu, event, ETNA_PM_PROCESS_PRE);
|
||||
}
|
||||
|
||||
static void sync_point_perfmon_sample_post(struct etnaviv_gpu *gpu,
|
||||
struct etnaviv_event *event)
|
||||
{
|
||||
const struct etnaviv_cmdbuf *cmdbuf = event->cmdbuf;
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
|
||||
sync_point_perfmon_sample(gpu, event, ETNA_PM_PROCESS_POST);
|
||||
|
||||
for (i = 0; i < cmdbuf->nr_pmrs; i++) {
|
||||
const struct etnaviv_perfmon_request *pmr = cmdbuf->pmrs + i;
|
||||
|
||||
*pmr->bo_vma = pmr->sequence;
|
||||
}
|
||||
|
||||
/* disable debug register */
|
||||
val = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
|
||||
val |= VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
|
||||
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, val);
|
||||
|
||||
/* enable clock gating */
|
||||
val = gpu_read(gpu, VIVS_PM_POWER_CONTROLS);
|
||||
val |= VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING;
|
||||
gpu_write(gpu, VIVS_PM_POWER_CONTROLS, val);
|
||||
}
|
||||
|
||||
|
||||
/* add bo's to gpu's ring, and kick gpu: */
|
||||
int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
|
||||
struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf)
|
||||
{
|
||||
struct dma_fence *fence;
|
||||
unsigned int event, i;
|
||||
unsigned int i, nr_events = 1, event[3];
|
||||
int ret;
|
||||
|
||||
ret = etnaviv_gpu_pm_get_sync(gpu);
|
||||
@ -1332,10 +1399,19 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
|
||||
*
|
||||
*/
|
||||
|
||||
event = event_alloc(gpu);
|
||||
if (unlikely(event == ~0U)) {
|
||||
DRM_ERROR("no free event\n");
|
||||
ret = -EBUSY;
|
||||
/*
|
||||
* if there are performance monitor requests we need to have
|
||||
* - a sync point to re-configure gpu and process ETNA_PM_PROCESS_PRE
|
||||
* requests.
|
||||
* - a sync point to re-configure gpu, process ETNA_PM_PROCESS_POST requests
|
||||
* and update the sequence number for userspace.
|
||||
*/
|
||||
if (cmdbuf->nr_pmrs)
|
||||
nr_events = 3;
|
||||
|
||||
ret = event_alloc(gpu, nr_events, event);
|
||||
if (ret) {
|
||||
DRM_ERROR("no free events\n");
|
||||
goto out_pm_put;
|
||||
}
|
||||
|
||||
@ -1343,12 +1419,14 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
|
||||
|
||||
fence = etnaviv_gpu_fence_alloc(gpu);
|
||||
if (!fence) {
|
||||
event_free(gpu, event);
|
||||
for (i = 0; i < nr_events; i++)
|
||||
event_free(gpu, event[i]);
|
||||
|
||||
ret = -ENOMEM;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
gpu->event[event].fence = fence;
|
||||
gpu->event[event[0]].fence = fence;
|
||||
submit->fence = dma_fence_get(fence);
|
||||
gpu->active_fence = submit->fence->seqno;
|
||||
|
||||
@ -1358,7 +1436,19 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
|
||||
gpu->lastctx = cmdbuf->ctx;
|
||||
}
|
||||
|
||||
etnaviv_buffer_queue(gpu, event, cmdbuf);
|
||||
if (cmdbuf->nr_pmrs) {
|
||||
gpu->event[event[1]].sync_point = &sync_point_perfmon_sample_pre;
|
||||
gpu->event[event[1]].cmdbuf = cmdbuf;
|
||||
etnaviv_sync_point_queue(gpu, event[1]);
|
||||
}
|
||||
|
||||
etnaviv_buffer_queue(gpu, event[0], cmdbuf);
|
||||
|
||||
if (cmdbuf->nr_pmrs) {
|
||||
gpu->event[event[2]].sync_point = &sync_point_perfmon_sample_post;
|
||||
gpu->event[event[2]].cmdbuf = cmdbuf;
|
||||
etnaviv_sync_point_queue(gpu, event[2]);
|
||||
}
|
||||
|
||||
cmdbuf->fence = fence;
|
||||
list_add_tail(&cmdbuf->node, &gpu->active_cmd_list);
|
||||
@ -1394,6 +1484,24 @@ out_pm_put:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void etnaviv_process_sync_point(struct etnaviv_gpu *gpu,
|
||||
struct etnaviv_event *event)
|
||||
{
|
||||
u32 addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
|
||||
|
||||
event->sync_point(gpu, event);
|
||||
etnaviv_gpu_start_fe(gpu, addr + 2, 2);
|
||||
}
|
||||
|
||||
static void sync_point_worker(struct work_struct *work)
|
||||
{
|
||||
struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
|
||||
sync_point_work);
|
||||
|
||||
etnaviv_process_sync_point(gpu, &gpu->event[gpu->sync_point_event]);
|
||||
event_free(gpu, gpu->sync_point_event);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init/Cleanup:
|
||||
*/
|
||||
@ -1440,7 +1548,15 @@ static irqreturn_t irq_handler(int irq, void *data)
|
||||
|
||||
dev_dbg(gpu->dev, "event %u\n", event);
|
||||
|
||||
if (gpu->event[event].sync_point) {
|
||||
gpu->sync_point_event = event;
|
||||
etnaviv_queue_work(gpu->drm, &gpu->sync_point_work);
|
||||
}
|
||||
|
||||
fence = gpu->event[event].fence;
|
||||
if (!fence)
|
||||
continue;
|
||||
|
||||
gpu->event[event].fence = NULL;
|
||||
dma_fence_signal(fence);
|
||||
|
||||
@ -1645,6 +1761,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
|
||||
|
||||
INIT_LIST_HEAD(&gpu->active_cmd_list);
|
||||
INIT_WORK(&gpu->retire_work, retire_worker);
|
||||
INIT_WORK(&gpu->sync_point_work, sync_point_worker);
|
||||
INIT_WORK(&gpu->recover_work, recover_worker);
|
||||
init_waitqueue_head(&gpu->fence_event);
|
||||
|
||||
|
@ -88,13 +88,17 @@ struct etnaviv_chip_identity {
|
||||
};
|
||||
|
||||
struct etnaviv_event {
|
||||
bool used;
|
||||
struct dma_fence *fence;
|
||||
struct etnaviv_cmdbuf *cmdbuf;
|
||||
|
||||
void (*sync_point)(struct etnaviv_gpu *gpu, struct etnaviv_event *event);
|
||||
};
|
||||
|
||||
struct etnaviv_cmdbuf_suballoc;
|
||||
struct etnaviv_cmdbuf;
|
||||
|
||||
#define ETNA_NR_EVENTS 30
|
||||
|
||||
struct etnaviv_gpu {
|
||||
struct drm_device *drm;
|
||||
struct thermal_cooling_device *cooling;
|
||||
@ -112,7 +116,8 @@ struct etnaviv_gpu {
|
||||
u32 memory_base;
|
||||
|
||||
/* event management: */
|
||||
struct etnaviv_event event[30];
|
||||
DECLARE_BITMAP(event_bitmap, ETNA_NR_EVENTS);
|
||||
struct etnaviv_event event[ETNA_NR_EVENTS];
|
||||
struct completion event_free;
|
||||
spinlock_t event_spinlock;
|
||||
|
||||
@ -133,6 +138,10 @@ struct etnaviv_gpu {
|
||||
/* worker for handling active-list retiring: */
|
||||
struct work_struct retire_work;
|
||||
|
||||
/* worker for handling 'sync' points: */
|
||||
struct work_struct sync_point_work;
|
||||
int sync_point_event;
|
||||
|
||||
void __iomem *mmio;
|
||||
int irq;
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
@ -31,174 +30,115 @@
|
||||
|
||||
#define GPU_MEM_START 0x80000000
|
||||
|
||||
struct etnaviv_iommu_domain_pgtable {
|
||||
u32 *pgtable;
|
||||
dma_addr_t paddr;
|
||||
struct etnaviv_iommuv1_domain {
|
||||
struct etnaviv_iommu_domain base;
|
||||
u32 *pgtable_cpu;
|
||||
dma_addr_t pgtable_dma;
|
||||
};
|
||||
|
||||
struct etnaviv_iommu_domain {
|
||||
struct iommu_domain domain;
|
||||
struct device *dev;
|
||||
void *bad_page_cpu;
|
||||
dma_addr_t bad_page_dma;
|
||||
struct etnaviv_iommu_domain_pgtable pgtable;
|
||||
spinlock_t map_lock;
|
||||
};
|
||||
|
||||
static struct etnaviv_iommu_domain *to_etnaviv_domain(struct iommu_domain *domain)
|
||||
static struct etnaviv_iommuv1_domain *
|
||||
to_etnaviv_domain(struct etnaviv_iommu_domain *domain)
|
||||
{
|
||||
return container_of(domain, struct etnaviv_iommu_domain, domain);
|
||||
return container_of(domain, struct etnaviv_iommuv1_domain, base);
|
||||
}
|
||||
|
||||
static int pgtable_alloc(struct etnaviv_iommu_domain_pgtable *pgtable,
|
||||
size_t size)
|
||||
{
|
||||
pgtable->pgtable = dma_alloc_coherent(NULL, size, &pgtable->paddr, GFP_KERNEL);
|
||||
if (!pgtable->pgtable)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pgtable_free(struct etnaviv_iommu_domain_pgtable *pgtable,
|
||||
size_t size)
|
||||
{
|
||||
dma_free_coherent(NULL, size, pgtable->pgtable, pgtable->paddr);
|
||||
}
|
||||
|
||||
static u32 pgtable_read(struct etnaviv_iommu_domain_pgtable *pgtable,
|
||||
unsigned long iova)
|
||||
{
|
||||
/* calcuate index into page table */
|
||||
unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
|
||||
phys_addr_t paddr;
|
||||
|
||||
paddr = pgtable->pgtable[index];
|
||||
|
||||
return paddr;
|
||||
}
|
||||
|
||||
static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable,
|
||||
unsigned long iova, phys_addr_t paddr)
|
||||
{
|
||||
/* calcuate index into page table */
|
||||
unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
|
||||
|
||||
pgtable->pgtable[index] = paddr;
|
||||
}
|
||||
|
||||
static int __etnaviv_iommu_init(struct etnaviv_iommu_domain *etnaviv_domain)
|
||||
static int __etnaviv_iommu_init(struct etnaviv_iommuv1_domain *etnaviv_domain)
|
||||
{
|
||||
u32 *p;
|
||||
int ret, i;
|
||||
int i;
|
||||
|
||||
etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev,
|
||||
SZ_4K,
|
||||
&etnaviv_domain->bad_page_dma,
|
||||
GFP_KERNEL);
|
||||
if (!etnaviv_domain->bad_page_cpu)
|
||||
etnaviv_domain->base.bad_page_cpu = dma_alloc_coherent(
|
||||
etnaviv_domain->base.dev,
|
||||
SZ_4K,
|
||||
&etnaviv_domain->base.bad_page_dma,
|
||||
GFP_KERNEL);
|
||||
if (!etnaviv_domain->base.bad_page_cpu)
|
||||
return -ENOMEM;
|
||||
|
||||
p = etnaviv_domain->bad_page_cpu;
|
||||
p = etnaviv_domain->base.bad_page_cpu;
|
||||
for (i = 0; i < SZ_4K / 4; i++)
|
||||
*p++ = 0xdead55aa;
|
||||
|
||||
ret = pgtable_alloc(&etnaviv_domain->pgtable, PT_SIZE);
|
||||
if (ret < 0) {
|
||||
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
|
||||
etnaviv_domain->bad_page_cpu,
|
||||
etnaviv_domain->bad_page_dma);
|
||||
return ret;
|
||||
etnaviv_domain->pgtable_cpu =
|
||||
dma_alloc_coherent(etnaviv_domain->base.dev, PT_SIZE,
|
||||
&etnaviv_domain->pgtable_dma,
|
||||
GFP_KERNEL);
|
||||
if (!etnaviv_domain->pgtable_cpu) {
|
||||
dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
|
||||
etnaviv_domain->base.bad_page_cpu,
|
||||
etnaviv_domain->base.bad_page_dma);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < PT_ENTRIES; i++)
|
||||
etnaviv_domain->pgtable.pgtable[i] =
|
||||
etnaviv_domain->bad_page_dma;
|
||||
|
||||
spin_lock_init(&etnaviv_domain->map_lock);
|
||||
etnaviv_domain->pgtable_cpu[i] =
|
||||
etnaviv_domain->base.bad_page_dma;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void etnaviv_domain_free(struct iommu_domain *domain)
|
||||
static void etnaviv_iommuv1_domain_free(struct etnaviv_iommu_domain *domain)
|
||||
{
|
||||
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
|
||||
struct etnaviv_iommuv1_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
|
||||
pgtable_free(&etnaviv_domain->pgtable, PT_SIZE);
|
||||
dma_free_coherent(etnaviv_domain->base.dev, PT_SIZE,
|
||||
etnaviv_domain->pgtable_cpu,
|
||||
etnaviv_domain->pgtable_dma);
|
||||
|
||||
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
|
||||
etnaviv_domain->bad_page_cpu,
|
||||
etnaviv_domain->bad_page_dma);
|
||||
dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
|
||||
etnaviv_domain->base.bad_page_cpu,
|
||||
etnaviv_domain->base.bad_page_dma);
|
||||
|
||||
kfree(etnaviv_domain);
|
||||
}
|
||||
|
||||
static int etnaviv_iommuv1_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
static int etnaviv_iommuv1_map(struct etnaviv_iommu_domain *domain,
|
||||
unsigned long iova, phys_addr_t paddr,
|
||||
size_t size, int prot)
|
||||
{
|
||||
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
|
||||
struct etnaviv_iommuv1_domain *etnaviv_domain = to_etnaviv_domain(domain);
|
||||
unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
|
||||
|
||||
if (size != SZ_4K)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&etnaviv_domain->map_lock);
|
||||
pgtable_write(&etnaviv_domain->pgtable, iova, paddr);
|
||||
spin_unlock(&etnaviv_domain->map_lock);
|
||||
etnaviv_domain->pgtable_cpu[index] = paddr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t etnaviv_iommuv1_unmap(struct iommu_domain *domain,
|
||||
static size_t etnaviv_iommuv1_unmap(struct etnaviv_iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
{
|
||||
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
|
||||
struct etnaviv_iommuv1_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
|
||||
|
||||
if (size != SZ_4K)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&etnaviv_domain->map_lock);
|
||||
pgtable_write(&etnaviv_domain->pgtable, iova,
|
||||
etnaviv_domain->bad_page_dma);
|
||||
spin_unlock(&etnaviv_domain->map_lock);
|
||||
etnaviv_domain->pgtable_cpu[index] = etnaviv_domain->base.bad_page_dma;
|
||||
|
||||
return SZ_4K;
|
||||
}
|
||||
|
||||
static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain,
|
||||
dma_addr_t iova)
|
||||
{
|
||||
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
|
||||
|
||||
return pgtable_read(&etnaviv_domain->pgtable, iova);
|
||||
}
|
||||
|
||||
static size_t etnaviv_iommuv1_dump_size(struct iommu_domain *domain)
|
||||
static size_t etnaviv_iommuv1_dump_size(struct etnaviv_iommu_domain *domain)
|
||||
{
|
||||
return PT_SIZE;
|
||||
}
|
||||
|
||||
static void etnaviv_iommuv1_dump(struct iommu_domain *domain, void *buf)
|
||||
static void etnaviv_iommuv1_dump(struct etnaviv_iommu_domain *domain, void *buf)
|
||||
{
|
||||
struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
|
||||
struct etnaviv_iommuv1_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
|
||||
memcpy(buf, etnaviv_domain->pgtable.pgtable, PT_SIZE);
|
||||
memcpy(buf, etnaviv_domain->pgtable_cpu, PT_SIZE);
|
||||
}
|
||||
|
||||
static const struct etnaviv_iommu_ops etnaviv_iommu_ops = {
|
||||
.ops = {
|
||||
.domain_free = etnaviv_domain_free,
|
||||
.map = etnaviv_iommuv1_map,
|
||||
.unmap = etnaviv_iommuv1_unmap,
|
||||
.iova_to_phys = etnaviv_iommu_iova_to_phys,
|
||||
.pgsize_bitmap = SZ_4K,
|
||||
},
|
||||
.dump_size = etnaviv_iommuv1_dump_size,
|
||||
.dump = etnaviv_iommuv1_dump,
|
||||
};
|
||||
|
||||
void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu)
|
||||
{
|
||||
struct etnaviv_iommu_domain *etnaviv_domain =
|
||||
struct etnaviv_iommuv1_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(gpu->mmu->domain);
|
||||
u32 pgtable;
|
||||
|
||||
@ -210,7 +150,7 @@ void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu)
|
||||
gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base);
|
||||
|
||||
/* set page table address in MC */
|
||||
pgtable = (u32)etnaviv_domain->pgtable.paddr;
|
||||
pgtable = (u32)etnaviv_domain->pgtable_dma;
|
||||
|
||||
gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
|
||||
gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable);
|
||||
@ -219,28 +159,37 @@ void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu)
|
||||
gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
|
||||
}
|
||||
|
||||
struct iommu_domain *etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu)
|
||||
const struct etnaviv_iommu_domain_ops etnaviv_iommuv1_ops = {
|
||||
.free = etnaviv_iommuv1_domain_free,
|
||||
.map = etnaviv_iommuv1_map,
|
||||
.unmap = etnaviv_iommuv1_unmap,
|
||||
.dump_size = etnaviv_iommuv1_dump_size,
|
||||
.dump = etnaviv_iommuv1_dump,
|
||||
};
|
||||
|
||||
struct etnaviv_iommu_domain *
|
||||
etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu)
|
||||
{
|
||||
struct etnaviv_iommu_domain *etnaviv_domain;
|
||||
struct etnaviv_iommuv1_domain *etnaviv_domain;
|
||||
struct etnaviv_iommu_domain *domain;
|
||||
int ret;
|
||||
|
||||
etnaviv_domain = kzalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
|
||||
if (!etnaviv_domain)
|
||||
return NULL;
|
||||
|
||||
etnaviv_domain->dev = gpu->dev;
|
||||
domain = &etnaviv_domain->base;
|
||||
|
||||
etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING;
|
||||
etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops;
|
||||
etnaviv_domain->domain.pgsize_bitmap = SZ_4K;
|
||||
etnaviv_domain->domain.geometry.aperture_start = GPU_MEM_START;
|
||||
etnaviv_domain->domain.geometry.aperture_end = GPU_MEM_START + PT_ENTRIES * SZ_4K - 1;
|
||||
domain->dev = gpu->dev;
|
||||
domain->base = GPU_MEM_START;
|
||||
domain->size = PT_ENTRIES * SZ_4K;
|
||||
domain->ops = &etnaviv_iommuv1_ops;
|
||||
|
||||
ret = __etnaviv_iommu_init(etnaviv_domain);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
return &etnaviv_domain->domain;
|
||||
return &etnaviv_domain->base;
|
||||
|
||||
out_free:
|
||||
kfree(etnaviv_domain);
|
||||
|
@ -18,11 +18,14 @@
|
||||
#define __ETNAVIV_IOMMU_H__
|
||||
|
||||
struct etnaviv_gpu;
|
||||
struct etnaviv_iommu_domain;
|
||||
|
||||
struct iommu_domain *etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu);
|
||||
struct etnaviv_iommu_domain *
|
||||
etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu);
|
||||
void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu);
|
||||
|
||||
struct iommu_domain *etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu);
|
||||
struct etnaviv_iommu_domain *
|
||||
etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu);
|
||||
void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu);
|
||||
|
||||
#endif /* __ETNAVIV_IOMMU_H__ */
|
||||
|
@ -14,7 +14,6 @@
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
@ -40,10 +39,7 @@
|
||||
#define MMUv2_MAX_STLB_ENTRIES 1024
|
||||
|
||||
struct etnaviv_iommuv2_domain {
|
||||
struct iommu_domain domain;
|
||||
struct device *dev;
|
||||
void *bad_page_cpu;
|
||||
dma_addr_t bad_page_dma;
|
||||
struct etnaviv_iommu_domain base;
|
||||
/* M(aster) TLB aka first level pagetable */
|
||||
u32 *mtlb_cpu;
|
||||
dma_addr_t mtlb_dma;
|
||||
@ -52,13 +48,15 @@ struct etnaviv_iommuv2_domain {
|
||||
dma_addr_t stlb_dma[1024];
|
||||
};
|
||||
|
||||
static struct etnaviv_iommuv2_domain *to_etnaviv_domain(struct iommu_domain *domain)
|
||||
static struct etnaviv_iommuv2_domain *
|
||||
to_etnaviv_domain(struct etnaviv_iommu_domain *domain)
|
||||
{
|
||||
return container_of(domain, struct etnaviv_iommuv2_domain, domain);
|
||||
return container_of(domain, struct etnaviv_iommuv2_domain, base);
|
||||
}
|
||||
|
||||
static int etnaviv_iommuv2_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
static int etnaviv_iommuv2_map(struct etnaviv_iommu_domain *domain,
|
||||
unsigned long iova, phys_addr_t paddr,
|
||||
size_t size, int prot)
|
||||
{
|
||||
struct etnaviv_iommuv2_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
@ -68,7 +66,7 @@ static int etnaviv_iommuv2_map(struct iommu_domain *domain, unsigned long iova,
|
||||
if (size != SZ_4K)
|
||||
return -EINVAL;
|
||||
|
||||
if (prot & IOMMU_WRITE)
|
||||
if (prot & ETNAVIV_PROT_WRITE)
|
||||
entry |= MMUv2_PTE_WRITEABLE;
|
||||
|
||||
mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
|
||||
@ -79,8 +77,8 @@ static int etnaviv_iommuv2_map(struct iommu_domain *domain, unsigned long iova,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t etnaviv_iommuv2_unmap(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
static size_t etnaviv_iommuv2_unmap(struct etnaviv_iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
{
|
||||
struct etnaviv_iommuv2_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
@ -97,38 +95,26 @@ static size_t etnaviv_iommuv2_unmap(struct iommu_domain *domain,
|
||||
return SZ_4K;
|
||||
}
|
||||
|
||||
static phys_addr_t etnaviv_iommuv2_iova_to_phys(struct iommu_domain *domain,
|
||||
dma_addr_t iova)
|
||||
{
|
||||
struct etnaviv_iommuv2_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
int mtlb_entry, stlb_entry;
|
||||
|
||||
mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
|
||||
stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT;
|
||||
|
||||
return etnaviv_domain->stlb_cpu[mtlb_entry][stlb_entry] & ~(SZ_4K - 1);
|
||||
}
|
||||
|
||||
static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain)
|
||||
{
|
||||
u32 *p;
|
||||
int ret, i, j;
|
||||
|
||||
/* allocate scratch page */
|
||||
etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev,
|
||||
SZ_4K,
|
||||
&etnaviv_domain->bad_page_dma,
|
||||
GFP_KERNEL);
|
||||
if (!etnaviv_domain->bad_page_cpu) {
|
||||
etnaviv_domain->base.bad_page_cpu = dma_alloc_coherent(
|
||||
etnaviv_domain->base.dev,
|
||||
SZ_4K,
|
||||
&etnaviv_domain->base.bad_page_dma,
|
||||
GFP_KERNEL);
|
||||
if (!etnaviv_domain->base.bad_page_cpu) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_mem;
|
||||
}
|
||||
p = etnaviv_domain->bad_page_cpu;
|
||||
p = etnaviv_domain->base.bad_page_cpu;
|
||||
for (i = 0; i < SZ_4K / 4; i++)
|
||||
*p++ = 0xdead55aa;
|
||||
|
||||
etnaviv_domain->mtlb_cpu = dma_alloc_coherent(etnaviv_domain->dev,
|
||||
etnaviv_domain->mtlb_cpu = dma_alloc_coherent(etnaviv_domain->base.dev,
|
||||
SZ_4K,
|
||||
&etnaviv_domain->mtlb_dma,
|
||||
GFP_KERNEL);
|
||||
@ -140,7 +126,7 @@ static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain)
|
||||
/* pre-populate STLB pages (may want to switch to on-demand later) */
|
||||
for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
|
||||
etnaviv_domain->stlb_cpu[i] =
|
||||
dma_alloc_coherent(etnaviv_domain->dev,
|
||||
dma_alloc_coherent(etnaviv_domain->base.dev,
|
||||
SZ_4K,
|
||||
&etnaviv_domain->stlb_dma[i],
|
||||
GFP_KERNEL);
|
||||
@ -159,19 +145,19 @@ static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain)
|
||||
return 0;
|
||||
|
||||
fail_mem:
|
||||
if (etnaviv_domain->bad_page_cpu)
|
||||
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
|
||||
etnaviv_domain->bad_page_cpu,
|
||||
etnaviv_domain->bad_page_dma);
|
||||
if (etnaviv_domain->base.bad_page_cpu)
|
||||
dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
|
||||
etnaviv_domain->base.bad_page_cpu,
|
||||
etnaviv_domain->base.bad_page_dma);
|
||||
|
||||
if (etnaviv_domain->mtlb_cpu)
|
||||
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
|
||||
dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
|
||||
etnaviv_domain->mtlb_cpu,
|
||||
etnaviv_domain->mtlb_dma);
|
||||
|
||||
for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
|
||||
if (etnaviv_domain->stlb_cpu[i])
|
||||
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
|
||||
dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
|
||||
etnaviv_domain->stlb_cpu[i],
|
||||
etnaviv_domain->stlb_dma[i]);
|
||||
}
|
||||
@ -179,23 +165,23 @@ fail_mem:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void etnaviv_iommuv2_domain_free(struct iommu_domain *domain)
|
||||
static void etnaviv_iommuv2_domain_free(struct etnaviv_iommu_domain *domain)
|
||||
{
|
||||
struct etnaviv_iommuv2_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
int i;
|
||||
|
||||
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
|
||||
etnaviv_domain->bad_page_cpu,
|
||||
etnaviv_domain->bad_page_dma);
|
||||
dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
|
||||
etnaviv_domain->base.bad_page_cpu,
|
||||
etnaviv_domain->base.bad_page_dma);
|
||||
|
||||
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
|
||||
dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
|
||||
etnaviv_domain->mtlb_cpu,
|
||||
etnaviv_domain->mtlb_dma);
|
||||
|
||||
for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
|
||||
if (etnaviv_domain->stlb_cpu[i])
|
||||
dma_free_coherent(etnaviv_domain->dev, SZ_4K,
|
||||
dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
|
||||
etnaviv_domain->stlb_cpu[i],
|
||||
etnaviv_domain->stlb_dma[i]);
|
||||
}
|
||||
@ -203,7 +189,7 @@ static void etnaviv_iommuv2_domain_free(struct iommu_domain *domain)
|
||||
vfree(etnaviv_domain);
|
||||
}
|
||||
|
||||
static size_t etnaviv_iommuv2_dump_size(struct iommu_domain *domain)
|
||||
static size_t etnaviv_iommuv2_dump_size(struct etnaviv_iommu_domain *domain)
|
||||
{
|
||||
struct etnaviv_iommuv2_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
@ -217,7 +203,7 @@ static size_t etnaviv_iommuv2_dump_size(struct iommu_domain *domain)
|
||||
return dump_size;
|
||||
}
|
||||
|
||||
static void etnaviv_iommuv2_dump(struct iommu_domain *domain, void *buf)
|
||||
static void etnaviv_iommuv2_dump(struct etnaviv_iommu_domain *domain, void *buf)
|
||||
{
|
||||
struct etnaviv_iommuv2_domain *etnaviv_domain =
|
||||
to_etnaviv_domain(domain);
|
||||
@ -230,18 +216,6 @@ static void etnaviv_iommuv2_dump(struct iommu_domain *domain, void *buf)
|
||||
memcpy(buf, etnaviv_domain->stlb_cpu[i], SZ_4K);
|
||||
}
|
||||
|
||||
static const struct etnaviv_iommu_ops etnaviv_iommu_ops = {
|
||||
.ops = {
|
||||
.domain_free = etnaviv_iommuv2_domain_free,
|
||||
.map = etnaviv_iommuv2_map,
|
||||
.unmap = etnaviv_iommuv2_unmap,
|
||||
.iova_to_phys = etnaviv_iommuv2_iova_to_phys,
|
||||
.pgsize_bitmap = SZ_4K,
|
||||
},
|
||||
.dump_size = etnaviv_iommuv2_dump_size,
|
||||
.dump = etnaviv_iommuv2_dump,
|
||||
};
|
||||
|
||||
void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu)
|
||||
{
|
||||
struct etnaviv_iommuv2_domain *etnaviv_domain =
|
||||
@ -254,35 +228,45 @@ void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu)
|
||||
|
||||
prefetch = etnaviv_buffer_config_mmuv2(gpu,
|
||||
(u32)etnaviv_domain->mtlb_dma,
|
||||
(u32)etnaviv_domain->bad_page_dma);
|
||||
(u32)etnaviv_domain->base.bad_page_dma);
|
||||
etnaviv_gpu_start_fe(gpu, (u32)etnaviv_cmdbuf_get_pa(gpu->buffer),
|
||||
prefetch);
|
||||
etnaviv_gpu_wait_idle(gpu, 100);
|
||||
|
||||
gpu_write(gpu, VIVS_MMUv2_CONTROL, VIVS_MMUv2_CONTROL_ENABLE);
|
||||
}
|
||||
struct iommu_domain *etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu)
|
||||
|
||||
const struct etnaviv_iommu_domain_ops etnaviv_iommuv2_ops = {
|
||||
.free = etnaviv_iommuv2_domain_free,
|
||||
.map = etnaviv_iommuv2_map,
|
||||
.unmap = etnaviv_iommuv2_unmap,
|
||||
.dump_size = etnaviv_iommuv2_dump_size,
|
||||
.dump = etnaviv_iommuv2_dump,
|
||||
};
|
||||
|
||||
struct etnaviv_iommu_domain *
|
||||
etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu)
|
||||
{
|
||||
struct etnaviv_iommuv2_domain *etnaviv_domain;
|
||||
struct etnaviv_iommu_domain *domain;
|
||||
int ret;
|
||||
|
||||
etnaviv_domain = vzalloc(sizeof(*etnaviv_domain));
|
||||
if (!etnaviv_domain)
|
||||
return NULL;
|
||||
|
||||
etnaviv_domain->dev = gpu->dev;
|
||||
domain = &etnaviv_domain->base;
|
||||
|
||||
etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING;
|
||||
etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops;
|
||||
etnaviv_domain->domain.pgsize_bitmap = SZ_4K;
|
||||
etnaviv_domain->domain.geometry.aperture_start = 0;
|
||||
etnaviv_domain->domain.geometry.aperture_end = ~0UL & ~(SZ_4K - 1);
|
||||
domain->dev = gpu->dev;
|
||||
domain->base = 0;
|
||||
domain->size = (u64)SZ_1G * 4;
|
||||
domain->ops = &etnaviv_iommuv2_ops;
|
||||
|
||||
ret = etnaviv_iommuv2_init(etnaviv_domain);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
return &etnaviv_domain->domain;
|
||||
return &etnaviv_domain->base;
|
||||
|
||||
out_free:
|
||||
vfree(etnaviv_domain);
|
||||
|
@ -22,17 +22,64 @@
|
||||
#include "etnaviv_iommu.h"
|
||||
#include "etnaviv_mmu.h"
|
||||
|
||||
static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev,
|
||||
unsigned long iova, int flags, void *arg)
|
||||
static void etnaviv_domain_unmap(struct etnaviv_iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
{
|
||||
DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
|
||||
return 0;
|
||||
size_t unmapped_page, unmapped = 0;
|
||||
size_t pgsize = SZ_4K;
|
||||
|
||||
if (!IS_ALIGNED(iova | size, pgsize)) {
|
||||
pr_err("unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
|
||||
iova, size, pgsize);
|
||||
return;
|
||||
}
|
||||
|
||||
while (unmapped < size) {
|
||||
unmapped_page = domain->ops->unmap(domain, iova, pgsize);
|
||||
if (!unmapped_page)
|
||||
break;
|
||||
|
||||
iova += unmapped_page;
|
||||
unmapped += unmapped_page;
|
||||
}
|
||||
}
|
||||
|
||||
int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
|
||||
struct sg_table *sgt, unsigned len, int prot)
|
||||
static int etnaviv_domain_map(struct etnaviv_iommu_domain *domain,
|
||||
unsigned long iova, phys_addr_t paddr,
|
||||
size_t size, int prot)
|
||||
{
|
||||
struct iommu_domain *domain = iommu->domain;
|
||||
unsigned long orig_iova = iova;
|
||||
size_t pgsize = SZ_4K;
|
||||
size_t orig_size = size;
|
||||
int ret = 0;
|
||||
|
||||
if (!IS_ALIGNED(iova | paddr | size, pgsize)) {
|
||||
pr_err("unaligned: iova 0x%lx pa %pa size 0x%zx min_pagesz 0x%x\n",
|
||||
iova, &paddr, size, pgsize);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (size) {
|
||||
ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
iova += pgsize;
|
||||
paddr += pgsize;
|
||||
size -= pgsize;
|
||||
}
|
||||
|
||||
/* unroll mapping in case something went wrong */
|
||||
if (ret)
|
||||
etnaviv_domain_unmap(domain, orig_iova, orig_size - size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
|
||||
struct sg_table *sgt, unsigned len, int prot)
|
||||
{
|
||||
struct etnaviv_iommu_domain *domain = iommu->domain;
|
||||
struct scatterlist *sg;
|
||||
unsigned int da = iova;
|
||||
unsigned int i, j;
|
||||
@ -47,7 +94,7 @@ int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
|
||||
|
||||
VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes);
|
||||
|
||||
ret = iommu_map(domain, da, pa, bytes, prot);
|
||||
ret = etnaviv_domain_map(domain, da, pa, bytes, prot);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
@ -62,27 +109,24 @@ fail:
|
||||
for_each_sg(sgt->sgl, sg, i, j) {
|
||||
size_t bytes = sg_dma_len(sg) + sg->offset;
|
||||
|
||||
iommu_unmap(domain, da, bytes);
|
||||
etnaviv_domain_unmap(domain, da, bytes);
|
||||
da += bytes;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
|
||||
struct sg_table *sgt, unsigned len)
|
||||
static void etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
|
||||
struct sg_table *sgt, unsigned len)
|
||||
{
|
||||
struct iommu_domain *domain = iommu->domain;
|
||||
struct etnaviv_iommu_domain *domain = iommu->domain;
|
||||
struct scatterlist *sg;
|
||||
unsigned int da = iova;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
|
||||
size_t bytes = sg_dma_len(sg) + sg->offset;
|
||||
size_t unmapped;
|
||||
|
||||
unmapped = iommu_unmap(domain, da, bytes);
|
||||
if (unmapped < bytes)
|
||||
return unmapped;
|
||||
etnaviv_domain_unmap(domain, da, bytes);
|
||||
|
||||
VERB("unmap[%d]: %08x(%zx)", i, iova, bytes);
|
||||
|
||||
@ -90,8 +134,6 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
|
||||
|
||||
da += bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu *mmu,
|
||||
@ -237,7 +279,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
|
||||
mmu->last_iova = node->start + etnaviv_obj->base.size;
|
||||
mapping->iova = node->start;
|
||||
ret = etnaviv_iommu_map(mmu, node->start, sgt, etnaviv_obj->base.size,
|
||||
IOMMU_READ | IOMMU_WRITE);
|
||||
ETNAVIV_PROT_READ | ETNAVIV_PROT_WRITE);
|
||||
|
||||
if (ret < 0) {
|
||||
drm_mm_remove_node(node);
|
||||
@ -271,7 +313,7 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
|
||||
void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
|
||||
{
|
||||
drm_mm_takedown(&mmu->mm);
|
||||
iommu_domain_free(mmu->domain);
|
||||
mmu->domain->ops->free(mmu->domain);
|
||||
kfree(mmu);
|
||||
}
|
||||
|
||||
@ -303,11 +345,7 @@ struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu)
|
||||
mutex_init(&mmu->lock);
|
||||
INIT_LIST_HEAD(&mmu->mappings);
|
||||
|
||||
drm_mm_init(&mmu->mm, mmu->domain->geometry.aperture_start,
|
||||
mmu->domain->geometry.aperture_end -
|
||||
mmu->domain->geometry.aperture_start + 1);
|
||||
|
||||
iommu_set_fault_handler(mmu->domain, etnaviv_fault_handler, gpu->dev);
|
||||
drm_mm_init(&mmu->mm, mmu->domain->base, mmu->domain->size);
|
||||
|
||||
return mmu;
|
||||
}
|
||||
@ -338,8 +376,8 @@ int etnaviv_iommu_get_suballoc_va(struct etnaviv_gpu *gpu, dma_addr_t paddr,
|
||||
mutex_unlock(&mmu->lock);
|
||||
return ret;
|
||||
}
|
||||
ret = iommu_map(mmu->domain, vram_node->start, paddr, size,
|
||||
IOMMU_READ);
|
||||
ret = etnaviv_domain_map(mmu->domain, vram_node->start, paddr,
|
||||
size, ETNAVIV_PROT_READ);
|
||||
if (ret < 0) {
|
||||
drm_mm_remove_node(vram_node);
|
||||
mutex_unlock(&mmu->lock);
|
||||
@ -362,25 +400,17 @@ void etnaviv_iommu_put_suballoc_va(struct etnaviv_gpu *gpu,
|
||||
|
||||
if (mmu->version == ETNAVIV_IOMMU_V2) {
|
||||
mutex_lock(&mmu->lock);
|
||||
iommu_unmap(mmu->domain,iova, size);
|
||||
etnaviv_domain_unmap(mmu->domain, iova, size);
|
||||
drm_mm_remove_node(vram_node);
|
||||
mutex_unlock(&mmu->lock);
|
||||
}
|
||||
}
|
||||
size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu)
|
||||
{
|
||||
struct etnaviv_iommu_ops *ops;
|
||||
|
||||
ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops);
|
||||
|
||||
return ops->dump_size(iommu->domain);
|
||||
return iommu->domain->ops->dump_size(iommu->domain);
|
||||
}
|
||||
|
||||
void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf)
|
||||
{
|
||||
struct etnaviv_iommu_ops *ops;
|
||||
|
||||
ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops);
|
||||
|
||||
ops->dump(iommu->domain, buf);
|
||||
iommu->domain->ops->dump(iommu->domain, buf);
|
||||
}
|
||||
|
@ -17,7 +17,8 @@
|
||||
#ifndef __ETNAVIV_MMU_H__
|
||||
#define __ETNAVIV_MMU_H__
|
||||
|
||||
#include <linux/iommu.h>
|
||||
#define ETNAVIV_PROT_READ (1 << 0)
|
||||
#define ETNAVIV_PROT_WRITE (1 << 1)
|
||||
|
||||
enum etnaviv_iommu_version {
|
||||
ETNAVIV_IOMMU_V1 = 0,
|
||||
@ -26,16 +27,31 @@ enum etnaviv_iommu_version {
|
||||
|
||||
struct etnaviv_gpu;
|
||||
struct etnaviv_vram_mapping;
|
||||
struct etnaviv_iommu_domain;
|
||||
|
||||
struct etnaviv_iommu_ops {
|
||||
struct iommu_ops ops;
|
||||
size_t (*dump_size)(struct iommu_domain *);
|
||||
void (*dump)(struct iommu_domain *, void *);
|
||||
struct etnaviv_iommu_domain_ops {
|
||||
void (*free)(struct etnaviv_iommu_domain *);
|
||||
int (*map)(struct etnaviv_iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot);
|
||||
size_t (*unmap)(struct etnaviv_iommu_domain *domain, unsigned long iova,
|
||||
size_t size);
|
||||
size_t (*dump_size)(struct etnaviv_iommu_domain *);
|
||||
void (*dump)(struct etnaviv_iommu_domain *, void *);
|
||||
};
|
||||
|
||||
struct etnaviv_iommu_domain {
|
||||
struct device *dev;
|
||||
void *bad_page_cpu;
|
||||
dma_addr_t bad_page_dma;
|
||||
u64 base;
|
||||
u64 size;
|
||||
|
||||
const struct etnaviv_iommu_domain_ops *ops;
|
||||
};
|
||||
|
||||
struct etnaviv_iommu {
|
||||
struct etnaviv_gpu *gpu;
|
||||
struct iommu_domain *domain;
|
||||
struct etnaviv_iommu_domain *domain;
|
||||
|
||||
enum etnaviv_iommu_version version;
|
||||
|
||||
@ -49,18 +65,11 @@ struct etnaviv_iommu {
|
||||
|
||||
struct etnaviv_gem_object;
|
||||
|
||||
int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names,
|
||||
int cnt);
|
||||
int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
|
||||
struct sg_table *sgt, unsigned len, int prot);
|
||||
int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
|
||||
struct sg_table *sgt, unsigned len);
|
||||
int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
|
||||
struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
|
||||
struct etnaviv_vram_mapping *mapping);
|
||||
void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
|
||||
struct etnaviv_vram_mapping *mapping);
|
||||
void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
|
||||
|
||||
int etnaviv_iommu_get_suballoc_va(struct etnaviv_gpu *gpu, dma_addr_t paddr,
|
||||
struct drm_mm_node *vram_node, size_t size,
|
||||
@ -73,6 +82,7 @@ size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu);
|
||||
void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf);
|
||||
|
||||
struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu);
|
||||
void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
|
||||
void etnaviv_iommu_restore(struct etnaviv_gpu *gpu);
|
||||
|
||||
#endif /* __ETNAVIV_MMU_H__ */
|
||||
|
495
drivers/gpu/drm/etnaviv/etnaviv_perfmon.c
Normal file
495
drivers/gpu/drm/etnaviv/etnaviv_perfmon.c
Normal file
@ -0,0 +1,495 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Etnaviv Project
|
||||
* Copyright (C) 2017 Zodiac Inflight Innovations
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "etnaviv_gpu.h"
|
||||
#include "etnaviv_perfmon.h"
|
||||
#include "state_hi.xml.h"
|
||||
|
||||
struct etnaviv_pm_domain;
|
||||
|
||||
struct etnaviv_pm_signal {
|
||||
char name[64];
|
||||
u32 data;
|
||||
|
||||
u32 (*sample)(struct etnaviv_gpu *gpu,
|
||||
const struct etnaviv_pm_domain *domain,
|
||||
const struct etnaviv_pm_signal *signal);
|
||||
};
|
||||
|
||||
struct etnaviv_pm_domain {
|
||||
char name[64];
|
||||
|
||||
/* profile register */
|
||||
u32 profile_read;
|
||||
u32 profile_config;
|
||||
|
||||
u8 nr_signals;
|
||||
const struct etnaviv_pm_signal *signal;
|
||||
};
|
||||
|
||||
struct etnaviv_pm_domain_meta {
|
||||
const struct etnaviv_pm_domain *domains;
|
||||
u32 nr_domains;
|
||||
};
|
||||
|
||||
static u32 simple_reg_read(struct etnaviv_gpu *gpu,
|
||||
const struct etnaviv_pm_domain *domain,
|
||||
const struct etnaviv_pm_signal *signal)
|
||||
{
|
||||
return gpu_read(gpu, signal->data);
|
||||
}
|
||||
|
||||
static u32 perf_reg_read(struct etnaviv_gpu *gpu,
|
||||
const struct etnaviv_pm_domain *domain,
|
||||
const struct etnaviv_pm_signal *signal)
|
||||
{
|
||||
gpu_write(gpu, domain->profile_config, signal->data);
|
||||
|
||||
return gpu_read(gpu, domain->profile_read);
|
||||
}
|
||||
|
||||
static u32 pipe_reg_read(struct etnaviv_gpu *gpu,
|
||||
const struct etnaviv_pm_domain *domain,
|
||||
const struct etnaviv_pm_signal *signal)
|
||||
{
|
||||
u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
|
||||
u32 value = 0;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < gpu->identity.pixel_pipes; i++) {
|
||||
clock &= ~(VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK);
|
||||
clock |= VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(i);
|
||||
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock);
|
||||
gpu_write(gpu, domain->profile_config, signal->data);
|
||||
value += gpu_read(gpu, domain->profile_read);
|
||||
}
|
||||
|
||||
/* switch back to pixel pipe 0 to prevent GPU hang */
|
||||
clock &= ~(VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK);
|
||||
clock |= VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(0);
|
||||
gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static const struct etnaviv_pm_domain doms_3d[] = {
|
||||
{
|
||||
.name = "HI",
|
||||
.profile_read = VIVS_MC_PROFILE_HI_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG2,
|
||||
.nr_signals = 5,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"TOTAL_CYCLES",
|
||||
VIVS_HI_PROFILE_TOTAL_CYCLES,
|
||||
&simple_reg_read
|
||||
},
|
||||
{
|
||||
"IDLE_CYCLES",
|
||||
VIVS_HI_PROFILE_IDLE_CYCLES,
|
||||
&simple_reg_read
|
||||
},
|
||||
{
|
||||
"AXI_CYCLES_READ_REQUEST_STALLED",
|
||||
VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_READ_REQUEST_STALLED,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"AXI_CYCLES_WRITE_REQUEST_STALLED",
|
||||
VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_REQUEST_STALLED,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"AXI_CYCLES_WRITE_DATA_STALLED",
|
||||
VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_DATA_STALLED,
|
||||
&perf_reg_read
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "PE",
|
||||
.profile_read = VIVS_MC_PROFILE_PE_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG0,
|
||||
.nr_signals = 5,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"PIXEL_COUNT_KILLED_BY_COLOR_PIPE",
|
||||
VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_COLOR_PIPE,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"PIXEL_COUNT_KILLED_BY_DEPTH_PIPE",
|
||||
VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_DEPTH_PIPE,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"PIXEL_COUNT_DRAWN_BY_COLOR_PIPE",
|
||||
VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_COLOR_PIPE,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE",
|
||||
VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE,
|
||||
&pipe_reg_read
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "SH",
|
||||
.profile_read = VIVS_MC_PROFILE_SH_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG0,
|
||||
.nr_signals = 9,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"SHADER_CYCLES",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_SHADER_CYCLES,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"PS_INST_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_PS_INST_COUNTER,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"RENDERED_PIXEL_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_PIXEL_COUNTER,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"VS_INST_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_VS_INST_COUNTER,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"RENDERED_VERTICE_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_VERTICE_COUNTER,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"VTX_BRANCH_INST_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_VTX_BRANCH_INST_COUNTER,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"VTX_TEXLD_INST_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_VTX_TEXLD_INST_COUNTER,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"PXL_BRANCH_INST_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_PXL_BRANCH_INST_COUNTER,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"PXL_TEXLD_INST_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG0_SH_PXL_TEXLD_INST_COUNTER,
|
||||
&pipe_reg_read
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "PA",
|
||||
.profile_read = VIVS_MC_PROFILE_PA_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG1,
|
||||
.nr_signals = 6,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"INPUT_VTX_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG1_PA_INPUT_VTX_COUNTER,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"INPUT_PRIM_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG1_PA_INPUT_PRIM_COUNTER,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"OUTPUT_PRIM_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG1_PA_OUTPUT_PRIM_COUNTER,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"DEPTH_CLIPPED_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG1_PA_DEPTH_CLIPPED_COUNTER,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"TRIVIAL_REJECTED_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG1_PA_TRIVIAL_REJECTED_COUNTER,
|
||||
&pipe_reg_read
|
||||
},
|
||||
{
|
||||
"CULLED_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG1_PA_CULLED_COUNTER,
|
||||
&pipe_reg_read
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "SE",
|
||||
.profile_read = VIVS_MC_PROFILE_SE_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG1,
|
||||
.nr_signals = 2,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"CULLED_TRIANGLE_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_SE_CULLED_TRIANGLE_COUNT,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"CULLED_LINES_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_SE_CULLED_LINES_COUNT,
|
||||
&perf_reg_read
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "RA",
|
||||
.profile_read = VIVS_MC_PROFILE_RA_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG1,
|
||||
.nr_signals = 7,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"VALID_PIXEL_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_RA_VALID_PIXEL_COUNT,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"TOTAL_QUAD_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_QUAD_COUNT,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"VALID_QUAD_COUNT_AFTER_EARLY_Z",
|
||||
VIVS_MC_PROFILE_CONFIG1_RA_VALID_QUAD_COUNT_AFTER_EARLY_Z,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"TOTAL_PRIMITIVE_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_PRIMITIVE_COUNT,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"PIPE_CACHE_MISS_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG1_RA_PIPE_CACHE_MISS_COUNTER,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"PREFETCH_CACHE_MISS_COUNTER",
|
||||
VIVS_MC_PROFILE_CONFIG1_RA_PREFETCH_CACHE_MISS_COUNTER,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"CULLED_QUAD_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_RA_CULLED_QUAD_COUNT,
|
||||
&perf_reg_read
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "TX",
|
||||
.profile_read = VIVS_MC_PROFILE_TX_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG1,
|
||||
.nr_signals = 9,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"TOTAL_BILINEAR_REQUESTS",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_BILINEAR_REQUESTS,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"TOTAL_TRILINEAR_REQUESTS",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TRILINEAR_REQUESTS,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"TOTAL_DISCARDED_TEXTURE_REQUESTS",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_DISCARDED_TEXTURE_REQUESTS,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"TOTAL_TEXTURE_REQUESTS",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TEXTURE_REQUESTS,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"MEM_READ_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_COUNT,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"MEM_READ_IN_8B_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_IN_8B_COUNT,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"CACHE_MISS_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_COUNT,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"CACHE_HIT_TEXEL_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_CACHE_HIT_TEXEL_COUNT,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"CACHE_MISS_TEXEL_COUNT",
|
||||
VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_TEXEL_COUNT,
|
||||
&perf_reg_read
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "MC",
|
||||
.profile_read = VIVS_MC_PROFILE_MC_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG2,
|
||||
.nr_signals = 3,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"TOTAL_READ_REQ_8B_FROM_PIPELINE",
|
||||
VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_PIPELINE,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"TOTAL_READ_REQ_8B_FROM_IP",
|
||||
VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_IP,
|
||||
&perf_reg_read
|
||||
},
|
||||
{
|
||||
"TOTAL_WRITE_REQ_8B_FROM_PIPELINE",
|
||||
VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_WRITE_REQ_8B_FROM_PIPELINE,
|
||||
&perf_reg_read
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const struct etnaviv_pm_domain doms_2d[] = {
|
||||
{
|
||||
.name = "PE",
|
||||
.profile_read = VIVS_MC_PROFILE_PE_READ,
|
||||
.profile_config = VIVS_MC_PROFILE_CONFIG0,
|
||||
.nr_signals = 1,
|
||||
.signal = (const struct etnaviv_pm_signal[]) {
|
||||
{
|
||||
"PIXELS_RENDERED_2D",
|
||||
VIVS_MC_PROFILE_CONFIG0_PE_PIXELS_RENDERED_2D,
|
||||
&pipe_reg_read
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const struct etnaviv_pm_domain doms_vg[] = {
|
||||
};
|
||||
|
||||
static const struct etnaviv_pm_domain_meta doms_meta[] = {
|
||||
{
|
||||
.nr_domains = ARRAY_SIZE(doms_3d),
|
||||
.domains = &doms_3d[0]
|
||||
},
|
||||
{
|
||||
.nr_domains = ARRAY_SIZE(doms_2d),
|
||||
.domains = &doms_2d[0]
|
||||
},
|
||||
{
|
||||
.nr_domains = ARRAY_SIZE(doms_vg),
|
||||
.domains = &doms_vg[0]
|
||||
}
|
||||
};
|
||||
|
||||
int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu,
|
||||
struct drm_etnaviv_pm_domain *domain)
|
||||
{
|
||||
const struct etnaviv_pm_domain_meta *meta = &doms_meta[domain->pipe];
|
||||
const struct etnaviv_pm_domain *dom;
|
||||
|
||||
if (domain->iter >= meta->nr_domains)
|
||||
return -EINVAL;
|
||||
|
||||
dom = meta->domains + domain->iter;
|
||||
|
||||
domain->id = domain->iter;
|
||||
domain->nr_signals = dom->nr_signals;
|
||||
strncpy(domain->name, dom->name, sizeof(domain->name));
|
||||
|
||||
domain->iter++;
|
||||
if (domain->iter == meta->nr_domains)
|
||||
domain->iter = 0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
|
||||
struct drm_etnaviv_pm_signal *signal)
|
||||
{
|
||||
const struct etnaviv_pm_domain_meta *meta = &doms_meta[signal->pipe];
|
||||
const struct etnaviv_pm_domain *dom;
|
||||
const struct etnaviv_pm_signal *sig;
|
||||
|
||||
if (signal->domain >= meta->nr_domains)
|
||||
return -EINVAL;
|
||||
|
||||
dom = meta->domains + signal->domain;
|
||||
|
||||
if (signal->iter > dom->nr_signals)
|
||||
return -EINVAL;
|
||||
|
||||
sig = &dom->signal[signal->iter];
|
||||
|
||||
signal->id = signal->iter;
|
||||
strncpy(signal->name, sig->name, sizeof(signal->name));
|
||||
|
||||
signal->iter++;
|
||||
if (signal->iter == dom->nr_signals)
|
||||
signal->iter = 0xffff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r,
|
||||
u32 exec_state)
|
||||
{
|
||||
const struct etnaviv_pm_domain_meta *meta = &doms_meta[exec_state];
|
||||
const struct etnaviv_pm_domain *dom;
|
||||
|
||||
if (r->domain >= meta->nr_domains)
|
||||
return -EINVAL;
|
||||
|
||||
dom = meta->domains + r->domain;
|
||||
|
||||
if (r->signal > dom->nr_signals)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void etnaviv_perfmon_process(struct etnaviv_gpu *gpu,
|
||||
const struct etnaviv_perfmon_request *pmr)
|
||||
{
|
||||
const struct etnaviv_pm_domain_meta *meta = &doms_meta[gpu->exec_state];
|
||||
const struct etnaviv_pm_domain *dom;
|
||||
const struct etnaviv_pm_signal *sig;
|
||||
u32 *bo = pmr->bo_vma;
|
||||
u32 val;
|
||||
|
||||
dom = meta->domains + pmr->domain;
|
||||
sig = &dom->signal[pmr->signal];
|
||||
val = sig->sample(gpu, dom, sig);
|
||||
|
||||
*(bo + pmr->offset) = val;
|
||||
}
|
49
drivers/gpu/drm/etnaviv/etnaviv_perfmon.h
Normal file
49
drivers/gpu/drm/etnaviv/etnaviv_perfmon.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Etnaviv Project
|
||||
* Copyright (C) 2017 Zodiac Inflight Innovations
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ETNAVIV_PERFMON_H__
|
||||
#define __ETNAVIV_PERFMON_H__
|
||||
|
||||
struct etnaviv_gpu;
|
||||
struct drm_etnaviv_pm_domain;
|
||||
struct drm_etnaviv_pm_signal;
|
||||
|
||||
struct etnaviv_perfmon_request
|
||||
{
|
||||
u32 flags;
|
||||
u8 domain;
|
||||
u8 signal;
|
||||
u32 sequence;
|
||||
|
||||
/* bo to store a value */
|
||||
u32 *bo_vma;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu,
|
||||
struct drm_etnaviv_pm_domain *domain);
|
||||
|
||||
int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
|
||||
struct drm_etnaviv_pm_signal *signal);
|
||||
|
||||
int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r,
|
||||
u32 exec_state);
|
||||
|
||||
void etnaviv_perfmon_process(struct etnaviv_gpu *gpu,
|
||||
const struct etnaviv_perfmon_request *pmr);
|
||||
|
||||
#endif /* __ETNAVIV_PERFMON_H__ */
|
@ -237,7 +237,7 @@ static int mid_get_vbt_data_r10(struct drm_psb_private *dev_priv, u32 addr)
|
||||
|
||||
gct = kmalloc(sizeof(*gct) * vbt.panel_count, GFP_KERNEL);
|
||||
if (!gct)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
gct_virtual = ioremap(addr + sizeof(vbt),
|
||||
sizeof(*gct) * vbt.panel_count);
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_sdvo_regs.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define SDVO_TMDS_MASK (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)
|
||||
#define SDVO_RGB_MASK (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1)
|
||||
@ -62,8 +63,6 @@ static const char *tv_format_names[] = {
|
||||
"SECAM_60"
|
||||
};
|
||||
|
||||
#define TV_FORMAT_NUM (sizeof(tv_format_names) / sizeof(*tv_format_names))
|
||||
|
||||
struct psb_intel_sdvo {
|
||||
struct gma_encoder base;
|
||||
|
||||
@ -148,7 +147,7 @@ struct psb_intel_sdvo_connector {
|
||||
int force_audio;
|
||||
|
||||
/* This contains all current supported TV format */
|
||||
u8 tv_format_supported[TV_FORMAT_NUM];
|
||||
u8 tv_format_supported[ARRAY_SIZE(tv_format_names)];
|
||||
int format_supported_num;
|
||||
struct drm_property *tv_format;
|
||||
|
||||
@ -1709,7 +1708,7 @@ psb_intel_sdvo_set_property(struct drm_connector *connector,
|
||||
}
|
||||
|
||||
if (property == psb_intel_sdvo_connector->tv_format) {
|
||||
if (val >= TV_FORMAT_NUM)
|
||||
if (val >= ARRAY_SIZE(tv_format_names))
|
||||
return -EINVAL;
|
||||
|
||||
if (psb_intel_sdvo->tv_format_index ==
|
||||
@ -2269,7 +2268,7 @@ static bool psb_intel_sdvo_tv_create_property(struct psb_intel_sdvo *psb_intel_s
|
||||
return false;
|
||||
|
||||
psb_intel_sdvo_connector->format_supported_num = 0;
|
||||
for (i = 0 ; i < TV_FORMAT_NUM; i++)
|
||||
for (i = 0 ; i < ARRAY_SIZE(tv_format_names); i++)
|
||||
if (format_map & (1 << i))
|
||||
psb_intel_sdvo_connector->tv_format_supported[psb_intel_sdvo_connector->format_supported_num++] = i;
|
||||
|
||||
|
@ -36,7 +36,7 @@ static int hibmc_connector_mode_valid(struct drm_connector *connector,
|
||||
static struct drm_encoder *
|
||||
hibmc_connector_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
return drm_encoder_find(connector->dev, connector->encoder_ids[0]);
|
||||
return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
|
@ -237,8 +237,8 @@ static int kirin_drm_platform_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
remote = of_graph_get_remote_node(np, 0, 0);
|
||||
if (IS_ERR(remote))
|
||||
return PTR_ERR(remote);
|
||||
if (!remote)
|
||||
return -ENODEV;
|
||||
|
||||
drm_of_component_match_add(dev, &match, compare_of, remote);
|
||||
of_node_put(remote);
|
||||
|
@ -12,6 +12,7 @@ config DRM_I915
|
||||
select DRM_PANEL
|
||||
select DRM_MIPI_DSI
|
||||
select RELAY
|
||||
select IRQ_WORK
|
||||
# i915 depends on ACPI_VIDEO when ACPI is enabled
|
||||
# but for select to work, need to select ACPI_VIDEO's dependencies, ick
|
||||
select BACKLIGHT_LCD_SUPPORT if ACPI
|
||||
|
@ -139,7 +139,8 @@ i915-y += i915_perf.o \
|
||||
i915_oa_bxt.o \
|
||||
i915_oa_kblgt2.o \
|
||||
i915_oa_kblgt3.o \
|
||||
i915_oa_glk.o
|
||||
i915_oa_glk.o \
|
||||
i915_oa_cflgt2.o
|
||||
|
||||
ifeq ($(CONFIG_DRM_I915_GVT),y)
|
||||
i915-y += intel_gvt.o
|
||||
|
@ -101,7 +101,7 @@ int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
if (WARN_ON(bytes > 4))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(offset + bytes > INTEL_GVT_MAX_CFG_SPACE_SZ))
|
||||
if (WARN_ON(offset + bytes > vgpu->gvt->device_info.cfg_space_size))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(p_data, vgpu_cfg_space(vgpu) + offset, bytes);
|
||||
@ -110,13 +110,25 @@ int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
|
||||
static int map_aperture(struct intel_vgpu *vgpu, bool map)
|
||||
{
|
||||
u64 first_gfn, first_mfn;
|
||||
phys_addr_t aperture_pa = vgpu_aperture_pa_base(vgpu);
|
||||
unsigned long aperture_sz = vgpu_aperture_sz(vgpu);
|
||||
u64 first_gfn;
|
||||
u64 val;
|
||||
int ret;
|
||||
|
||||
if (map == vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked)
|
||||
return 0;
|
||||
|
||||
if (map) {
|
||||
vgpu->gm.aperture_va = memremap(aperture_pa, aperture_sz,
|
||||
MEMREMAP_WC);
|
||||
if (!vgpu->gm.aperture_va)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
memunmap(vgpu->gm.aperture_va);
|
||||
vgpu->gm.aperture_va = NULL;
|
||||
}
|
||||
|
||||
val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_2];
|
||||
if (val & PCI_BASE_ADDRESS_MEM_TYPE_64)
|
||||
val = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2);
|
||||
@ -124,14 +136,16 @@ static int map_aperture(struct intel_vgpu *vgpu, bool map)
|
||||
val = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2);
|
||||
|
||||
first_gfn = (val + vgpu_aperture_offset(vgpu)) >> PAGE_SHIFT;
|
||||
first_mfn = vgpu_aperture_pa_base(vgpu) >> PAGE_SHIFT;
|
||||
|
||||
ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, first_gfn,
|
||||
first_mfn,
|
||||
vgpu_aperture_sz(vgpu) >>
|
||||
PAGE_SHIFT, map);
|
||||
if (ret)
|
||||
aperture_pa >> PAGE_SHIFT,
|
||||
aperture_sz >> PAGE_SHIFT,
|
||||
map);
|
||||
if (ret) {
|
||||
memunmap(vgpu->gm.aperture_va);
|
||||
vgpu->gm.aperture_va = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked = map;
|
||||
return 0;
|
||||
@ -275,7 +289,7 @@ int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
if (WARN_ON(bytes > 4))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(offset + bytes > INTEL_GVT_MAX_CFG_SPACE_SZ))
|
||||
if (WARN_ON(offset + bytes > vgpu->gvt->device_info.cfg_space_size))
|
||||
return -EINVAL;
|
||||
|
||||
/* First check if it's PCI_COMMAND */
|
||||
|
@ -1576,11 +1576,11 @@ static int batch_buffer_needs_scan(struct parser_exec_state *s)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t find_bb_size(struct parser_exec_state *s)
|
||||
static int find_bb_size(struct parser_exec_state *s)
|
||||
{
|
||||
unsigned long gma = 0;
|
||||
struct cmd_info *info;
|
||||
uint32_t bb_size = 0;
|
||||
int bb_size = 0;
|
||||
uint32_t cmd_len = 0;
|
||||
bool met_bb_end = false;
|
||||
struct intel_vgpu *vgpu = s->vgpu;
|
||||
@ -1637,6 +1637,8 @@ static int perform_bb_shadow(struct parser_exec_state *s)
|
||||
|
||||
/* get the size of the batch buffer */
|
||||
bb_size = find_bb_size(s);
|
||||
if (bb_size < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* allocate shadow batch buffer */
|
||||
entry_obj = kmalloc(sizeof(*entry_obj), GFP_KERNEL);
|
||||
@ -2603,7 +2605,8 @@ static int shadow_workload_ring_buffer(struct intel_vgpu_workload *workload)
|
||||
{
|
||||
struct intel_vgpu *vgpu = workload->vgpu;
|
||||
unsigned long gma_head, gma_tail, gma_top, guest_rb_size;
|
||||
u32 *cs;
|
||||
void *shadow_ring_buffer_va;
|
||||
int ring_id = workload->ring_id;
|
||||
int ret;
|
||||
|
||||
guest_rb_size = _RING_CTL_BUF_SIZE(workload->rb_ctl);
|
||||
@ -2616,34 +2619,42 @@ static int shadow_workload_ring_buffer(struct intel_vgpu_workload *workload)
|
||||
gma_tail = workload->rb_start + workload->rb_tail;
|
||||
gma_top = workload->rb_start + guest_rb_size;
|
||||
|
||||
/* allocate shadow ring buffer */
|
||||
cs = intel_ring_begin(workload->req, workload->rb_len / sizeof(u32));
|
||||
if (IS_ERR(cs))
|
||||
return PTR_ERR(cs);
|
||||
if (workload->rb_len > vgpu->reserve_ring_buffer_size[ring_id]) {
|
||||
void *va = vgpu->reserve_ring_buffer_va[ring_id];
|
||||
/* realloc the new ring buffer if needed */
|
||||
vgpu->reserve_ring_buffer_va[ring_id] =
|
||||
krealloc(va, workload->rb_len, GFP_KERNEL);
|
||||
if (!vgpu->reserve_ring_buffer_va[ring_id]) {
|
||||
gvt_vgpu_err("fail to alloc reserve ring buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
vgpu->reserve_ring_buffer_size[ring_id] = workload->rb_len;
|
||||
}
|
||||
|
||||
shadow_ring_buffer_va = vgpu->reserve_ring_buffer_va[ring_id];
|
||||
|
||||
/* get shadow ring buffer va */
|
||||
workload->shadow_ring_buffer_va = cs;
|
||||
workload->shadow_ring_buffer_va = shadow_ring_buffer_va;
|
||||
|
||||
/* head > tail --> copy head <-> top */
|
||||
if (gma_head > gma_tail) {
|
||||
ret = copy_gma_to_hva(vgpu, vgpu->gtt.ggtt_mm,
|
||||
gma_head, gma_top, cs);
|
||||
gma_head, gma_top, shadow_ring_buffer_va);
|
||||
if (ret < 0) {
|
||||
gvt_vgpu_err("fail to copy guest ring buffer\n");
|
||||
return ret;
|
||||
}
|
||||
cs += ret / sizeof(u32);
|
||||
shadow_ring_buffer_va += ret;
|
||||
gma_head = workload->rb_start;
|
||||
}
|
||||
|
||||
/* copy head or start <-> tail */
|
||||
ret = copy_gma_to_hva(vgpu, vgpu->gtt.ggtt_mm, gma_head, gma_tail, cs);
|
||||
ret = copy_gma_to_hva(vgpu, vgpu->gtt.ggtt_mm, gma_head, gma_tail,
|
||||
shadow_ring_buffer_va);
|
||||
if (ret < 0) {
|
||||
gvt_vgpu_err("fail to copy guest ring buffer\n");
|
||||
return ret;
|
||||
}
|
||||
cs += ret / sizeof(u32);
|
||||
intel_ring_advance(workload->req, cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,7 @@ static void free_workload(struct intel_vgpu_workload *workload)
|
||||
#define get_desc_from_elsp_dwords(ed, i) \
|
||||
((struct execlist_ctx_descriptor_format *)&((ed)->data[i * 2]))
|
||||
|
||||
static void prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload)
|
||||
static int prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload)
|
||||
{
|
||||
const int gmadr_bytes = workload->vgpu->gvt->device_info.gmadr_bytes_in_cmd;
|
||||
struct intel_shadow_bb_entry *entry_obj;
|
||||
@ -379,7 +379,7 @@ static void prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload)
|
||||
|
||||
vma = i915_gem_object_ggtt_pin(entry_obj->obj, NULL, 0, 4, 0);
|
||||
if (IS_ERR(vma)) {
|
||||
return;
|
||||
return PTR_ERR(vma);
|
||||
}
|
||||
|
||||
/* FIXME: we are not tracking our pinned VMA leaving it
|
||||
@ -392,6 +392,7 @@ static void prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload)
|
||||
if (gmadr_bytes == 8)
|
||||
entry_obj->bb_start_cmd_va[2] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_wa_ctx_2_shadow_ctx(struct intel_shadow_wa_ctx *wa_ctx)
|
||||
@ -420,7 +421,7 @@ static int update_wa_ctx_2_shadow_ctx(struct intel_shadow_wa_ctx *wa_ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prepare_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
|
||||
static int prepare_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
|
||||
{
|
||||
struct i915_vma *vma;
|
||||
unsigned char *per_ctx_va =
|
||||
@ -428,12 +429,12 @@ static void prepare_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
|
||||
wa_ctx->indirect_ctx.size;
|
||||
|
||||
if (wa_ctx->indirect_ctx.size == 0)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
vma = i915_gem_object_ggtt_pin(wa_ctx->indirect_ctx.obj, NULL,
|
||||
0, CACHELINE_BYTES, 0);
|
||||
if (IS_ERR(vma)) {
|
||||
return;
|
||||
return PTR_ERR(vma);
|
||||
}
|
||||
|
||||
/* FIXME: we are not tracking our pinned VMA leaving it
|
||||
@ -447,26 +448,7 @@ static void prepare_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
|
||||
memset(per_ctx_va, 0, CACHELINE_BYTES);
|
||||
|
||||
update_wa_ctx_2_shadow_ctx(wa_ctx);
|
||||
}
|
||||
|
||||
static int prepare_execlist_workload(struct intel_vgpu_workload *workload)
|
||||
{
|
||||
struct intel_vgpu *vgpu = workload->vgpu;
|
||||
struct execlist_ctx_descriptor_format ctx[2];
|
||||
int ring_id = workload->ring_id;
|
||||
|
||||
intel_vgpu_pin_mm(workload->shadow_mm);
|
||||
intel_vgpu_sync_oos_pages(workload->vgpu);
|
||||
intel_vgpu_flush_post_shadow(workload->vgpu);
|
||||
prepare_shadow_batch_buffer(workload);
|
||||
prepare_shadow_wa_ctx(&workload->wa_ctx);
|
||||
if (!workload->emulate_schedule_in)
|
||||
return 0;
|
||||
|
||||
ctx[0] = *get_desc_from_elsp_dwords(&workload->elsp_dwords, 1);
|
||||
ctx[1] = *get_desc_from_elsp_dwords(&workload->elsp_dwords, 0);
|
||||
|
||||
return emulate_execlist_schedule_in(&vgpu->execlist[ring_id], ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload)
|
||||
@ -489,13 +471,62 @@ static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload)
|
||||
}
|
||||
}
|
||||
|
||||
static void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
|
||||
static int prepare_execlist_workload(struct intel_vgpu_workload *workload)
|
||||
{
|
||||
if (!wa_ctx->indirect_ctx.obj)
|
||||
return;
|
||||
struct intel_vgpu *vgpu = workload->vgpu;
|
||||
struct execlist_ctx_descriptor_format ctx[2];
|
||||
int ring_id = workload->ring_id;
|
||||
int ret;
|
||||
|
||||
i915_gem_object_unpin_map(wa_ctx->indirect_ctx.obj);
|
||||
i915_gem_object_put(wa_ctx->indirect_ctx.obj);
|
||||
ret = intel_vgpu_pin_mm(workload->shadow_mm);
|
||||
if (ret) {
|
||||
gvt_vgpu_err("fail to vgpu pin mm\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = intel_vgpu_sync_oos_pages(workload->vgpu);
|
||||
if (ret) {
|
||||
gvt_vgpu_err("fail to vgpu sync oos pages\n");
|
||||
goto err_unpin_mm;
|
||||
}
|
||||
|
||||
ret = intel_vgpu_flush_post_shadow(workload->vgpu);
|
||||
if (ret) {
|
||||
gvt_vgpu_err("fail to flush post shadow\n");
|
||||
goto err_unpin_mm;
|
||||
}
|
||||
|
||||
ret = prepare_shadow_batch_buffer(workload);
|
||||
if (ret) {
|
||||
gvt_vgpu_err("fail to prepare_shadow_batch_buffer\n");
|
||||
goto err_unpin_mm;
|
||||
}
|
||||
|
||||
ret = prepare_shadow_wa_ctx(&workload->wa_ctx);
|
||||
if (ret) {
|
||||
gvt_vgpu_err("fail to prepare_shadow_wa_ctx\n");
|
||||
goto err_shadow_batch;
|
||||
}
|
||||
|
||||
if (!workload->emulate_schedule_in)
|
||||
return 0;
|
||||
|
||||
ctx[0] = *get_desc_from_elsp_dwords(&workload->elsp_dwords, 1);
|
||||
ctx[1] = *get_desc_from_elsp_dwords(&workload->elsp_dwords, 0);
|
||||
|
||||
ret = emulate_execlist_schedule_in(&vgpu->execlist[ring_id], ctx);
|
||||
if (!ret)
|
||||
goto out;
|
||||
else
|
||||
gvt_vgpu_err("fail to emulate execlist schedule in\n");
|
||||
|
||||
release_shadow_wa_ctx(&workload->wa_ctx);
|
||||
err_shadow_batch:
|
||||
release_shadow_batch_buffer(workload);
|
||||
err_unpin_mm:
|
||||
intel_vgpu_unpin_mm(workload->shadow_mm);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int complete_execlist_workload(struct intel_vgpu_workload *workload)
|
||||
@ -511,8 +542,10 @@ static int complete_execlist_workload(struct intel_vgpu_workload *workload)
|
||||
gvt_dbg_el("complete workload %p status %d\n", workload,
|
||||
workload->status);
|
||||
|
||||
release_shadow_batch_buffer(workload);
|
||||
release_shadow_wa_ctx(&workload->wa_ctx);
|
||||
if (!workload->status) {
|
||||
release_shadow_batch_buffer(workload);
|
||||
release_shadow_wa_ctx(&workload->wa_ctx);
|
||||
}
|
||||
|
||||
if (workload->status || (vgpu->resetting_eng & ENGINE_MASK(ring_id))) {
|
||||
/* if workload->status is not successful means HW GPU
|
||||
@ -820,10 +853,21 @@ static void clean_workloads(struct intel_vgpu *vgpu, unsigned long engine_mask)
|
||||
|
||||
void intel_vgpu_clean_execlist(struct intel_vgpu *vgpu)
|
||||
{
|
||||
enum intel_engine_id i;
|
||||
struct intel_engine_cs *engine;
|
||||
|
||||
clean_workloads(vgpu, ALL_ENGINES);
|
||||
kmem_cache_destroy(vgpu->workloads);
|
||||
|
||||
for_each_engine(engine, vgpu->gvt->dev_priv, i) {
|
||||
kfree(vgpu->reserve_ring_buffer_va[i]);
|
||||
vgpu->reserve_ring_buffer_va[i] = NULL;
|
||||
vgpu->reserve_ring_buffer_size[i] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define RESERVE_RING_BUFFER_SIZE ((1 * PAGE_SIZE)/8)
|
||||
int intel_vgpu_init_execlist(struct intel_vgpu *vgpu)
|
||||
{
|
||||
enum intel_engine_id i;
|
||||
@ -843,7 +887,26 @@ int intel_vgpu_init_execlist(struct intel_vgpu *vgpu)
|
||||
if (!vgpu->workloads)
|
||||
return -ENOMEM;
|
||||
|
||||
/* each ring has a shadow ring buffer until vgpu destroyed */
|
||||
for_each_engine(engine, vgpu->gvt->dev_priv, i) {
|
||||
vgpu->reserve_ring_buffer_va[i] =
|
||||
kmalloc(RESERVE_RING_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (!vgpu->reserve_ring_buffer_va[i]) {
|
||||
gvt_vgpu_err("fail to alloc reserve ring buffer\n");
|
||||
goto out;
|
||||
}
|
||||
vgpu->reserve_ring_buffer_size[i] = RESERVE_RING_BUFFER_SIZE;
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
for_each_engine(engine, vgpu->gvt->dev_priv, i) {
|
||||
if (vgpu->reserve_ring_buffer_size[i]) {
|
||||
kfree(vgpu->reserve_ring_buffer_va[i]);
|
||||
vgpu->reserve_ring_buffer_va[i] = NULL;
|
||||
vgpu->reserve_ring_buffer_size[i] = 0;
|
||||
}
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void intel_vgpu_reset_execlist(struct intel_vgpu *vgpu,
|
||||
|
@ -1647,14 +1647,13 @@ int intel_vgpu_pin_mm(struct intel_vgpu_mm *mm)
|
||||
if (WARN_ON(mm->type != INTEL_GVT_MM_PPGTT))
|
||||
return 0;
|
||||
|
||||
atomic_inc(&mm->pincount);
|
||||
|
||||
if (!mm->shadowed) {
|
||||
ret = shadow_mm(mm);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
atomic_inc(&mm->pincount);
|
||||
list_del_init(&mm->lru_list);
|
||||
list_add_tail(&mm->lru_list, &mm->vgpu->gvt->gtt.mm_lru_list_head);
|
||||
return 0;
|
||||
@ -1972,7 +1971,7 @@ static int alloc_scratch_pages(struct intel_vgpu *vgpu,
|
||||
*/
|
||||
se.val64 |= _PAGE_PRESENT | _PAGE_RW;
|
||||
if (type == GTT_TYPE_PPGTT_PDE_PT)
|
||||
se.val64 |= PPAT_CACHED_INDEX;
|
||||
se.val64 |= PPAT_CACHED;
|
||||
|
||||
for (i = 0; i < page_entry_num; i++)
|
||||
ops->set_entry(scratch_pt, &se, i, false, 0, vgpu);
|
||||
|
@ -111,7 +111,7 @@ static void init_device_info(struct intel_gvt *gvt)
|
||||
if (IS_BROADWELL(gvt->dev_priv) || IS_SKYLAKE(gvt->dev_priv)
|
||||
|| IS_KABYLAKE(gvt->dev_priv)) {
|
||||
info->max_support_vgpus = 8;
|
||||
info->cfg_space_size = 256;
|
||||
info->cfg_space_size = PCI_CFG_SPACE_EXP_SIZE;
|
||||
info->mmio_size = 2 * 1024 * 1024;
|
||||
info->mmio_bar = 0;
|
||||
info->gtt_start_offset = 8 * 1024 * 1024;
|
||||
|
@ -80,6 +80,7 @@ struct intel_gvt_device_info {
|
||||
struct intel_vgpu_gm {
|
||||
u64 aperture_sz;
|
||||
u64 hidden_sz;
|
||||
void *aperture_va;
|
||||
struct drm_mm_node low_gm_node;
|
||||
struct drm_mm_node high_gm_node;
|
||||
};
|
||||
@ -99,7 +100,6 @@ struct intel_vgpu_mmio {
|
||||
bool disable_warn_untrack;
|
||||
};
|
||||
|
||||
#define INTEL_GVT_MAX_CFG_SPACE_SZ 256
|
||||
#define INTEL_GVT_MAX_BAR_NUM 4
|
||||
|
||||
struct intel_vgpu_pci_bar {
|
||||
@ -108,7 +108,7 @@ struct intel_vgpu_pci_bar {
|
||||
};
|
||||
|
||||
struct intel_vgpu_cfg_space {
|
||||
unsigned char virtual_cfg_space[INTEL_GVT_MAX_CFG_SPACE_SZ];
|
||||
unsigned char virtual_cfg_space[PCI_CFG_SPACE_EXP_SIZE];
|
||||
struct intel_vgpu_pci_bar bar[INTEL_GVT_MAX_BAR_NUM];
|
||||
};
|
||||
|
||||
@ -165,6 +165,9 @@ struct intel_vgpu {
|
||||
struct list_head workload_q_head[I915_NUM_ENGINES];
|
||||
struct kmem_cache *workloads;
|
||||
atomic_t running_workload_num;
|
||||
/* 1/2K for each reserve ring buffer */
|
||||
void *reserve_ring_buffer_va[I915_NUM_ENGINES];
|
||||
int reserve_ring_buffer_size[I915_NUM_ENGINES];
|
||||
DECLARE_BITMAP(tlb_handle_pending, I915_NUM_ENGINES);
|
||||
struct i915_gem_context *shadow_ctx;
|
||||
DECLARE_BITMAP(shadow_ctx_desc_updated, I915_NUM_ENGINES);
|
||||
@ -474,6 +477,13 @@ int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
void *p_data, unsigned int bytes);
|
||||
|
||||
static inline u64 intel_vgpu_get_bar_gpa(struct intel_vgpu *vgpu, int bar)
|
||||
{
|
||||
/* We are 64bit bar. */
|
||||
return (*(u64 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
|
||||
PCI_BASE_ADDRESS_MEM_MASK;
|
||||
}
|
||||
|
||||
void intel_gvt_clean_opregion(struct intel_gvt *gvt);
|
||||
int intel_gvt_init_opregion(struct intel_gvt *gvt);
|
||||
|
||||
|
@ -609,21 +609,20 @@ static void intel_vgpu_release_work(struct work_struct *work)
|
||||
__intel_vgpu_release(vgpu);
|
||||
}
|
||||
|
||||
static uint64_t intel_vgpu_get_bar0_addr(struct intel_vgpu *vgpu)
|
||||
static uint64_t intel_vgpu_get_bar_addr(struct intel_vgpu *vgpu, int bar)
|
||||
{
|
||||
u32 start_lo, start_hi;
|
||||
u32 mem_type;
|
||||
int pos = PCI_BASE_ADDRESS_0;
|
||||
|
||||
start_lo = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + pos)) &
|
||||
start_lo = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
|
||||
PCI_BASE_ADDRESS_MEM_MASK;
|
||||
mem_type = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + pos)) &
|
||||
mem_type = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_MASK;
|
||||
|
||||
switch (mem_type) {
|
||||
case PCI_BASE_ADDRESS_MEM_TYPE_64:
|
||||
start_hi = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space
|
||||
+ pos + 4));
|
||||
+ bar + 4));
|
||||
break;
|
||||
case PCI_BASE_ADDRESS_MEM_TYPE_32:
|
||||
case PCI_BASE_ADDRESS_MEM_TYPE_1M:
|
||||
@ -637,6 +636,21 @@ static uint64_t intel_vgpu_get_bar0_addr(struct intel_vgpu *vgpu)
|
||||
return ((u64)start_hi << 32) | start_lo;
|
||||
}
|
||||
|
||||
static int intel_vgpu_bar_rw(struct intel_vgpu *vgpu, int bar, uint64_t off,
|
||||
void *buf, unsigned int count, bool is_write)
|
||||
{
|
||||
uint64_t bar_start = intel_vgpu_get_bar_addr(vgpu, bar);
|
||||
int ret;
|
||||
|
||||
if (is_write)
|
||||
ret = intel_gvt_ops->emulate_mmio_write(vgpu,
|
||||
bar_start + off, buf, count);
|
||||
else
|
||||
ret = intel_gvt_ops->emulate_mmio_read(vgpu,
|
||||
bar_start + off, buf, count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf,
|
||||
size_t count, loff_t *ppos, bool is_write)
|
||||
{
|
||||
@ -661,20 +675,14 @@ static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf,
|
||||
buf, count);
|
||||
break;
|
||||
case VFIO_PCI_BAR0_REGION_INDEX:
|
||||
case VFIO_PCI_BAR1_REGION_INDEX:
|
||||
if (is_write) {
|
||||
uint64_t bar0_start = intel_vgpu_get_bar0_addr(vgpu);
|
||||
|
||||
ret = intel_gvt_ops->emulate_mmio_write(vgpu,
|
||||
bar0_start + pos, buf, count);
|
||||
} else {
|
||||
uint64_t bar0_start = intel_vgpu_get_bar0_addr(vgpu);
|
||||
|
||||
ret = intel_gvt_ops->emulate_mmio_read(vgpu,
|
||||
bar0_start + pos, buf, count);
|
||||
}
|
||||
ret = intel_vgpu_bar_rw(vgpu, PCI_BASE_ADDRESS_0, pos,
|
||||
buf, count, is_write);
|
||||
break;
|
||||
case VFIO_PCI_BAR2_REGION_INDEX:
|
||||
ret = intel_vgpu_bar_rw(vgpu, PCI_BASE_ADDRESS_2, pos,
|
||||
buf, count, is_write);
|
||||
break;
|
||||
case VFIO_PCI_BAR1_REGION_INDEX:
|
||||
case VFIO_PCI_BAR3_REGION_INDEX:
|
||||
case VFIO_PCI_BAR4_REGION_INDEX:
|
||||
case VFIO_PCI_BAR5_REGION_INDEX:
|
||||
@ -970,7 +978,7 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd,
|
||||
switch (info.index) {
|
||||
case VFIO_PCI_CONFIG_REGION_INDEX:
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.size = INTEL_GVT_MAX_CFG_SPACE_SZ;
|
||||
info.size = vgpu->gvt->device_info.cfg_space_size;
|
||||
info.flags = VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE;
|
||||
break;
|
||||
|
@ -45,8 +45,7 @@
|
||||
*/
|
||||
int intel_vgpu_gpa_to_mmio_offset(struct intel_vgpu *vgpu, u64 gpa)
|
||||
{
|
||||
u64 gttmmio_gpa = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0) &
|
||||
~GENMASK(3, 0);
|
||||
u64 gttmmio_gpa = intel_vgpu_get_bar_gpa(vgpu, PCI_BASE_ADDRESS_0);
|
||||
return gpa - gttmmio_gpa;
|
||||
}
|
||||
|
||||
@ -57,6 +56,38 @@ int intel_vgpu_gpa_to_mmio_offset(struct intel_vgpu *vgpu, u64 gpa)
|
||||
(reg >= gvt->device_info.gtt_start_offset \
|
||||
&& reg < gvt->device_info.gtt_start_offset + gvt_ggtt_sz(gvt))
|
||||
|
||||
static bool vgpu_gpa_is_aperture(struct intel_vgpu *vgpu, uint64_t gpa)
|
||||
{
|
||||
u64 aperture_gpa = intel_vgpu_get_bar_gpa(vgpu, PCI_BASE_ADDRESS_2);
|
||||
u64 aperture_sz = vgpu_aperture_sz(vgpu);
|
||||
|
||||
return gpa >= aperture_gpa && gpa < aperture_gpa + aperture_sz;
|
||||
}
|
||||
|
||||
static int vgpu_aperture_rw(struct intel_vgpu *vgpu, uint64_t gpa,
|
||||
void *pdata, unsigned int size, bool is_read)
|
||||
{
|
||||
u64 aperture_gpa = intel_vgpu_get_bar_gpa(vgpu, PCI_BASE_ADDRESS_2);
|
||||
u64 offset = gpa - aperture_gpa;
|
||||
|
||||
if (!vgpu_gpa_is_aperture(vgpu, gpa + size - 1)) {
|
||||
gvt_vgpu_err("Aperture rw out of range, offset %llx, size %d\n",
|
||||
offset, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!vgpu->gm.aperture_va) {
|
||||
gvt_vgpu_err("BAR is not enabled\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (is_read)
|
||||
memcpy(pdata, vgpu->gm.aperture_va + offset, size);
|
||||
else
|
||||
memcpy(vgpu->gm.aperture_va + offset, pdata, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void failsafe_emulate_mmio_rw(struct intel_vgpu *vgpu, uint64_t pa,
|
||||
void *p_data, unsigned int bytes, bool read)
|
||||
{
|
||||
@ -133,6 +164,12 @@ int intel_vgpu_emulate_mmio_read(struct intel_vgpu *vgpu, uint64_t pa,
|
||||
}
|
||||
mutex_lock(&gvt->lock);
|
||||
|
||||
if (vgpu_gpa_is_aperture(vgpu, pa)) {
|
||||
ret = vgpu_aperture_rw(vgpu, pa, p_data, bytes, true);
|
||||
mutex_unlock(&gvt->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (atomic_read(&vgpu->gtt.n_write_protected_guest_page)) {
|
||||
struct intel_vgpu_guest_page *gp;
|
||||
|
||||
@ -224,6 +261,12 @@ int intel_vgpu_emulate_mmio_write(struct intel_vgpu *vgpu, uint64_t pa,
|
||||
|
||||
mutex_lock(&gvt->lock);
|
||||
|
||||
if (vgpu_gpa_is_aperture(vgpu, pa)) {
|
||||
ret = vgpu_aperture_rw(vgpu, pa, p_data, bytes, false);
|
||||
mutex_unlock(&gvt->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (atomic_read(&vgpu->gtt.n_write_protected_guest_page)) {
|
||||
struct intel_vgpu_guest_page *gp;
|
||||
|
||||
|
@ -293,7 +293,7 @@ static void switch_mmio_to_vgpu(struct intel_vgpu *vgpu, int ring_id)
|
||||
*/
|
||||
if (mmio->in_context &&
|
||||
((ctx_ctrl & inhibit_mask) != inhibit_mask) &&
|
||||
i915.enable_execlists)
|
||||
i915_modparams.enable_execlists)
|
||||
continue;
|
||||
|
||||
if (mmio->mask)
|
||||
|
@ -87,7 +87,7 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
page = i915_gem_object_get_page(ctx_obj, LRC_PPHWSP_PN + i);
|
||||
page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i);
|
||||
dst = kmap(page);
|
||||
intel_gvt_hypervisor_read_gpa(vgpu, context_gpa, dst,
|
||||
GTT_PAGE_SIZE);
|
||||
@ -201,6 +201,43 @@ static void shadow_context_descriptor_update(struct i915_gem_context *ctx,
|
||||
ce->lrc_desc = desc;
|
||||
}
|
||||
|
||||
static int copy_workload_to_ring_buffer(struct intel_vgpu_workload *workload)
|
||||
{
|
||||
struct intel_vgpu *vgpu = workload->vgpu;
|
||||
void *shadow_ring_buffer_va;
|
||||
u32 *cs;
|
||||
|
||||
/* allocate shadow ring buffer */
|
||||
cs = intel_ring_begin(workload->req, workload->rb_len / sizeof(u32));
|
||||
if (IS_ERR(cs)) {
|
||||
gvt_vgpu_err("fail to alloc size =%ld shadow ring buffer\n",
|
||||
workload->rb_len);
|
||||
return PTR_ERR(cs);
|
||||
}
|
||||
|
||||
shadow_ring_buffer_va = workload->shadow_ring_buffer_va;
|
||||
|
||||
/* get shadow ring buffer va */
|
||||
workload->shadow_ring_buffer_va = cs;
|
||||
|
||||
memcpy(cs, shadow_ring_buffer_va,
|
||||
workload->rb_len);
|
||||
|
||||
cs += workload->rb_len / sizeof(u32);
|
||||
intel_ring_advance(workload->req, cs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
|
||||
{
|
||||
if (!wa_ctx->indirect_ctx.obj)
|
||||
return;
|
||||
|
||||
i915_gem_object_unpin_map(wa_ctx->indirect_ctx.obj);
|
||||
i915_gem_object_put(wa_ctx->indirect_ctx.obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_gvt_scan_and_shadow_workload - audit the workload by scanning and
|
||||
* shadow it as well, include ringbuffer,wa_ctx and ctx.
|
||||
@ -214,8 +251,10 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload)
|
||||
int ring_id = workload->ring_id;
|
||||
struct i915_gem_context *shadow_ctx = workload->vgpu->shadow_ctx;
|
||||
struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv;
|
||||
struct intel_engine_cs *engine = dev_priv->engine[ring_id];
|
||||
struct drm_i915_gem_request *rq;
|
||||
struct intel_vgpu *vgpu = workload->vgpu;
|
||||
struct intel_ring *ring;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&dev_priv->drm.struct_mutex);
|
||||
@ -231,61 +270,15 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload)
|
||||
shadow_context_descriptor_update(shadow_ctx,
|
||||
dev_priv->engine[ring_id]);
|
||||
|
||||
rq = i915_gem_request_alloc(dev_priv->engine[ring_id], shadow_ctx);
|
||||
if (IS_ERR(rq)) {
|
||||
gvt_vgpu_err("fail to allocate gem request\n");
|
||||
ret = PTR_ERR(rq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
gvt_dbg_sched("ring id %d get i915 gem request %p\n", ring_id, rq);
|
||||
|
||||
workload->req = i915_gem_request_get(rq);
|
||||
|
||||
ret = intel_gvt_scan_and_shadow_ringbuffer(workload);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto err_scan;
|
||||
|
||||
if ((workload->ring_id == RCS) &&
|
||||
(workload->wa_ctx.indirect_ctx.size != 0)) {
|
||||
ret = intel_gvt_scan_and_shadow_wa_ctx(&workload->wa_ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = populate_shadow_context(workload);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
workload->shadowed = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dispatch_workload(struct intel_vgpu_workload *workload)
|
||||
{
|
||||
int ring_id = workload->ring_id;
|
||||
struct i915_gem_context *shadow_ctx = workload->vgpu->shadow_ctx;
|
||||
struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv;
|
||||
struct intel_engine_cs *engine = dev_priv->engine[ring_id];
|
||||
struct intel_vgpu *vgpu = workload->vgpu;
|
||||
struct intel_ring *ring;
|
||||
int ret = 0;
|
||||
|
||||
gvt_dbg_sched("ring id %d prepare to dispatch workload %p\n",
|
||||
ring_id, workload);
|
||||
|
||||
mutex_lock(&dev_priv->drm.struct_mutex);
|
||||
|
||||
ret = intel_gvt_scan_and_shadow_workload(workload);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (workload->prepare) {
|
||||
ret = workload->prepare(workload);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto err_scan;
|
||||
}
|
||||
|
||||
/* pin shadow context by gvt even the shadow context will be pinned
|
||||
@ -299,7 +292,60 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
|
||||
if (IS_ERR(ring)) {
|
||||
ret = PTR_ERR(ring);
|
||||
gvt_vgpu_err("fail to pin shadow context\n");
|
||||
goto err_shadow;
|
||||
}
|
||||
|
||||
ret = populate_shadow_context(workload);
|
||||
if (ret)
|
||||
goto err_unpin;
|
||||
|
||||
rq = i915_gem_request_alloc(dev_priv->engine[ring_id], shadow_ctx);
|
||||
if (IS_ERR(rq)) {
|
||||
gvt_vgpu_err("fail to allocate gem request\n");
|
||||
ret = PTR_ERR(rq);
|
||||
goto err_unpin;
|
||||
}
|
||||
|
||||
gvt_dbg_sched("ring id %d get i915 gem request %p\n", ring_id, rq);
|
||||
|
||||
workload->req = i915_gem_request_get(rq);
|
||||
ret = copy_workload_to_ring_buffer(workload);
|
||||
if (ret)
|
||||
goto err_unpin;
|
||||
workload->shadowed = true;
|
||||
return 0;
|
||||
|
||||
err_unpin:
|
||||
engine->context_unpin(engine, shadow_ctx);
|
||||
err_shadow:
|
||||
release_shadow_wa_ctx(&workload->wa_ctx);
|
||||
err_scan:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dispatch_workload(struct intel_vgpu_workload *workload)
|
||||
{
|
||||
int ring_id = workload->ring_id;
|
||||
struct i915_gem_context *shadow_ctx = workload->vgpu->shadow_ctx;
|
||||
struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv;
|
||||
struct intel_engine_cs *engine = dev_priv->engine[ring_id];
|
||||
int ret = 0;
|
||||
|
||||
gvt_dbg_sched("ring id %d prepare to dispatch workload %p\n",
|
||||
ring_id, workload);
|
||||
|
||||
mutex_lock(&dev_priv->drm.struct_mutex);
|
||||
|
||||
ret = intel_gvt_scan_and_shadow_workload(workload);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (workload->prepare) {
|
||||
ret = workload->prepare(workload);
|
||||
if (ret) {
|
||||
engine->context_unpin(engine, shadow_ctx);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
@ -408,7 +454,7 @@ static void update_guest_context(struct intel_vgpu_workload *workload)
|
||||
return;
|
||||
}
|
||||
|
||||
page = i915_gem_object_get_page(ctx_obj, LRC_PPHWSP_PN + i);
|
||||
page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i);
|
||||
src = kmap(page);
|
||||
intel_gvt_hypervisor_write_gpa(vgpu, context_gpa, src,
|
||||
GTT_PAGE_SIZE);
|
||||
|
@ -140,4 +140,5 @@ int intel_vgpu_init_gvt_context(struct intel_vgpu *vgpu);
|
||||
|
||||
void intel_vgpu_clean_gvt_context(struct intel_vgpu *vgpu);
|
||||
|
||||
void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx);
|
||||
#endif
|
||||
|
@ -67,7 +67,7 @@ static int i915_capabilities(struct seq_file *m, void *data)
|
||||
#undef PRINT_FLAG
|
||||
|
||||
kernel_param_lock(THIS_MODULE);
|
||||
#define PRINT_PARAM(T, x) seq_print_param(m, #x, #T, &i915.x);
|
||||
#define PRINT_PARAM(T, x, ...) seq_print_param(m, #x, #T, &i915_modparams.x);
|
||||
I915_PARAMS_FOR_EACH(PRINT_PARAM);
|
||||
#undef PRINT_PARAM
|
||||
kernel_param_unlock(THIS_MODULE);
|
||||
@ -1267,7 +1267,7 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused)
|
||||
if (waitqueue_active(&dev_priv->gpu_error.reset_queue))
|
||||
seq_puts(m, "struct_mutex blocked for reset\n");
|
||||
|
||||
if (!i915.enable_hangcheck) {
|
||||
if (!i915_modparams.enable_hangcheck) {
|
||||
seq_puts(m, "Hangcheck disabled\n");
|
||||
return 0;
|
||||
}
|
||||
@ -1422,6 +1422,9 @@ static int i915_forcewake_domains(struct seq_file *m, void *data)
|
||||
struct intel_uncore_forcewake_domain *fw_domain;
|
||||
unsigned int tmp;
|
||||
|
||||
seq_printf(m, "user.bypass_count = %u\n",
|
||||
i915->uncore.user_forcewake.count);
|
||||
|
||||
for_each_fw_domain(fw_domain, i915, tmp)
|
||||
seq_printf(m, "%s.wake_count = %u\n",
|
||||
intel_uncore_forcewake_domain_to_str(fw_domain->id),
|
||||
@ -1699,7 +1702,7 @@ static int i915_ips_status(struct seq_file *m, void *unused)
|
||||
intel_runtime_pm_get(dev_priv);
|
||||
|
||||
seq_printf(m, "Enabled by kernel parameter: %s\n",
|
||||
yesno(i915.enable_ips));
|
||||
yesno(i915_modparams.enable_ips));
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 8) {
|
||||
seq_puts(m, "Currently: unknown\n");
|
||||
@ -2014,7 +2017,7 @@ static int i915_dump_lrc(struct seq_file *m, void *unused)
|
||||
enum intel_engine_id id;
|
||||
int ret;
|
||||
|
||||
if (!i915.enable_execlists) {
|
||||
if (!i915_modparams.enable_execlists) {
|
||||
seq_printf(m, "Logical Ring Contexts are disabled\n");
|
||||
return 0;
|
||||
}
|
||||
@ -2443,12 +2446,8 @@ static void i915_guc_client_info(struct seq_file *m,
|
||||
|
||||
seq_printf(m, "\tPriority %d, GuC stage index: %u, PD offset 0x%x\n",
|
||||
client->priority, client->stage_id, client->proc_desc_offset);
|
||||
seq_printf(m, "\tDoorbell id %d, offset: 0x%lx, cookie 0x%x\n",
|
||||
client->doorbell_id, client->doorbell_offset, client->doorbell_cookie);
|
||||
seq_printf(m, "\tWQ size %d, offset: 0x%x, tail %d\n",
|
||||
client->wq_size, client->wq_offset, client->wq_tail);
|
||||
|
||||
seq_printf(m, "\tWork queue full: %u\n", client->no_wq_space);
|
||||
seq_printf(m, "\tDoorbell id %d, offset: 0x%lx\n",
|
||||
client->doorbell_id, client->doorbell_offset);
|
||||
|
||||
for_each_engine(engine, dev_priv, id) {
|
||||
u64 submissions = client->submissions[id];
|
||||
@ -2594,7 +2593,7 @@ static int i915_guc_log_control_get(void *data, u64 *val)
|
||||
if (!dev_priv->guc.log.vma)
|
||||
return -EINVAL;
|
||||
|
||||
*val = i915.guc_log_level;
|
||||
*val = i915_modparams.guc_log_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3312,7 +3311,9 @@ static int i915_engine_info(struct seq_file *m, void *unused)
|
||||
seq_printf(m, "\tBBADDR: 0x%08x_%08x\n",
|
||||
upper_32_bits(addr), lower_32_bits(addr));
|
||||
|
||||
if (i915.enable_execlists) {
|
||||
if (i915_modparams.enable_execlists) {
|
||||
const u32 *hws = &engine->status_page.page_addr[I915_HWS_CSB_BUF0_INDEX];
|
||||
struct intel_engine_execlists * const execlists = &engine->execlists;
|
||||
u32 ptr, read, write;
|
||||
unsigned int idx;
|
||||
|
||||
@ -3323,8 +3324,10 @@ static int i915_engine_info(struct seq_file *m, void *unused)
|
||||
ptr = I915_READ(RING_CONTEXT_STATUS_PTR(engine));
|
||||
read = GEN8_CSB_READ_PTR(ptr);
|
||||
write = GEN8_CSB_WRITE_PTR(ptr);
|
||||
seq_printf(m, "\tExeclist CSB read %d, write %d, interrupt posted? %s\n",
|
||||
read, write,
|
||||
seq_printf(m, "\tExeclist CSB read %d [%d cached], write %d [%d from hws], interrupt posted? %s\n",
|
||||
read, execlists->csb_head,
|
||||
write,
|
||||
intel_read_status_page(engine, intel_hws_csb_write_index(engine->i915)),
|
||||
yesno(test_bit(ENGINE_IRQ_EXECLIST,
|
||||
&engine->irq_posted)));
|
||||
if (read >= GEN8_CSB_ENTRIES)
|
||||
@ -3335,18 +3338,19 @@ static int i915_engine_info(struct seq_file *m, void *unused)
|
||||
write += GEN8_CSB_ENTRIES;
|
||||
while (read < write) {
|
||||
idx = ++read % GEN8_CSB_ENTRIES;
|
||||
seq_printf(m, "\tExeclist CSB[%d]: 0x%08x, context: %d\n",
|
||||
seq_printf(m, "\tExeclist CSB[%d]: 0x%08x [0x%08x in hwsp], context: %d [%d in hwsp]\n",
|
||||
idx,
|
||||
I915_READ(RING_CONTEXT_STATUS_BUF_LO(engine, idx)),
|
||||
I915_READ(RING_CONTEXT_STATUS_BUF_HI(engine, idx)));
|
||||
hws[idx * 2],
|
||||
I915_READ(RING_CONTEXT_STATUS_BUF_HI(engine, idx)),
|
||||
hws[idx * 2 + 1]);
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
for (idx = 0; idx < ARRAY_SIZE(engine->execlist_port); idx++) {
|
||||
for (idx = 0; idx < execlists_num_ports(execlists); idx++) {
|
||||
unsigned int count;
|
||||
|
||||
rq = port_unpack(&engine->execlist_port[idx],
|
||||
&count);
|
||||
rq = port_unpack(&execlists->port[idx], &count);
|
||||
if (rq) {
|
||||
seq_printf(m, "\t\tELSP[%d] count=%d, ",
|
||||
idx, count);
|
||||
@ -3359,7 +3363,7 @@ static int i915_engine_info(struct seq_file *m, void *unused)
|
||||
rcu_read_unlock();
|
||||
|
||||
spin_lock_irq(&engine->timeline->lock);
|
||||
for (rb = engine->execlist_first; rb; rb = rb_next(rb)){
|
||||
for (rb = execlists->first; rb; rb = rb_next(rb)) {
|
||||
struct i915_priolist *p =
|
||||
rb_entry(rb, typeof(*p), node);
|
||||
|
||||
@ -3403,7 +3407,7 @@ static int i915_semaphore_status(struct seq_file *m, void *unused)
|
||||
enum intel_engine_id id;
|
||||
int j, ret;
|
||||
|
||||
if (!i915.semaphores) {
|
||||
if (!i915_modparams.semaphores) {
|
||||
seq_puts(m, "Semaphores are disabled\n");
|
||||
return 0;
|
||||
}
|
||||
@ -3523,6 +3527,57 @@ static int i915_wa_registers(struct seq_file *m, void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_ipc_status_show(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = m->private;
|
||||
|
||||
seq_printf(m, "Isochronous Priority Control: %s\n",
|
||||
yesno(dev_priv->ipc_enabled));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_ipc_status_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = inode->i_private;
|
||||
|
||||
if (!HAS_IPC(dev_priv))
|
||||
return -ENODEV;
|
||||
|
||||
return single_open(file, i915_ipc_status_show, dev_priv);
|
||||
}
|
||||
|
||||
static ssize_t i915_ipc_status_write(struct file *file, const char __user *ubuf,
|
||||
size_t len, loff_t *offp)
|
||||
{
|
||||
struct seq_file *m = file->private_data;
|
||||
struct drm_i915_private *dev_priv = m->private;
|
||||
int ret;
|
||||
bool enable;
|
||||
|
||||
ret = kstrtobool_from_user(ubuf, len, &enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
intel_runtime_pm_get(dev_priv);
|
||||
if (!dev_priv->ipc_enabled && enable)
|
||||
DRM_INFO("Enabling IPC: WM will be proper only after next commit\n");
|
||||
dev_priv->wm.distrust_bios_wm = true;
|
||||
dev_priv->ipc_enabled = enable;
|
||||
intel_enable_ipc(dev_priv);
|
||||
intel_runtime_pm_put(dev_priv);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct file_operations i915_ipc_status_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = i915_ipc_status_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.write = i915_ipc_status_write
|
||||
};
|
||||
|
||||
static int i915_ddb_info(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = node_to_i915(m->private);
|
||||
@ -4674,26 +4729,26 @@ static int i915_sseu_status(struct seq_file *m, void *unused)
|
||||
|
||||
static int i915_forcewake_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = inode->i_private;
|
||||
struct drm_i915_private *i915 = inode->i_private;
|
||||
|
||||
if (INTEL_GEN(dev_priv) < 6)
|
||||
if (INTEL_GEN(i915) < 6)
|
||||
return 0;
|
||||
|
||||
intel_runtime_pm_get(dev_priv);
|
||||
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
|
||||
intel_runtime_pm_get(i915);
|
||||
intel_uncore_forcewake_user_get(i915);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_forcewake_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = inode->i_private;
|
||||
struct drm_i915_private *i915 = inode->i_private;
|
||||
|
||||
if (INTEL_GEN(dev_priv) < 6)
|
||||
if (INTEL_GEN(i915) < 6)
|
||||
return 0;
|
||||
|
||||
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
|
||||
intel_runtime_pm_put(dev_priv);
|
||||
intel_uncore_forcewake_user_put(i915);
|
||||
intel_runtime_pm_put(i915);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4859,7 +4914,8 @@ static const struct i915_debugfs_files {
|
||||
{"i915_dp_test_type", &i915_displayport_test_type_fops},
|
||||
{"i915_dp_test_active", &i915_displayport_test_active_fops},
|
||||
{"i915_guc_log_control", &i915_guc_log_control_fops},
|
||||
{"i915_hpd_storm_ctl", &i915_hpd_storm_ctl_fops}
|
||||
{"i915_hpd_storm_ctl", &i915_hpd_storm_ctl_fops},
|
||||
{"i915_ipc_status", &i915_ipc_status_fops}
|
||||
};
|
||||
|
||||
int i915_debugfs_register(struct drm_i915_private *dev_priv)
|
||||
|
@ -58,12 +58,12 @@ static unsigned int i915_load_fail_count;
|
||||
|
||||
bool __i915_inject_load_failure(const char *func, int line)
|
||||
{
|
||||
if (i915_load_fail_count >= i915.inject_load_failure)
|
||||
if (i915_load_fail_count >= i915_modparams.inject_load_failure)
|
||||
return false;
|
||||
|
||||
if (++i915_load_fail_count == i915.inject_load_failure) {
|
||||
if (++i915_load_fail_count == i915_modparams.inject_load_failure) {
|
||||
DRM_INFO("Injecting failure at checkpoint %u [%s:%d]\n",
|
||||
i915.inject_load_failure, func, line);
|
||||
i915_modparams.inject_load_failure, func, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -106,8 +106,8 @@ __i915_printk(struct drm_i915_private *dev_priv, const char *level,
|
||||
|
||||
static bool i915_error_injected(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
return i915.inject_load_failure &&
|
||||
i915_load_fail_count == i915.inject_load_failure;
|
||||
return i915_modparams.inject_load_failure &&
|
||||
i915_load_fail_count == i915_modparams.inject_load_failure;
|
||||
}
|
||||
|
||||
#define i915_load_error(dev_priv, fmt, ...) \
|
||||
@ -321,7 +321,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
|
||||
value = USES_PPGTT(dev_priv);
|
||||
break;
|
||||
case I915_PARAM_HAS_SEMAPHORES:
|
||||
value = i915.semaphores;
|
||||
value = i915_modparams.semaphores;
|
||||
break;
|
||||
case I915_PARAM_HAS_SECURE_BATCHES:
|
||||
value = capable(CAP_SYS_ADMIN);
|
||||
@ -340,7 +340,8 @@ static int i915_getparam(struct drm_device *dev, void *data,
|
||||
return -ENODEV;
|
||||
break;
|
||||
case I915_PARAM_HAS_GPU_RESET:
|
||||
value = i915.enable_hangcheck && intel_has_gpu_reset(dev_priv);
|
||||
value = i915_modparams.enable_hangcheck &&
|
||||
intel_has_gpu_reset(dev_priv);
|
||||
if (value && intel_has_reset_engine(dev_priv))
|
||||
value = 2;
|
||||
break;
|
||||
@ -869,6 +870,10 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
|
||||
memcpy(device_info, match_info, sizeof(*device_info));
|
||||
device_info->device_id = dev_priv->drm.pdev->device;
|
||||
|
||||
BUILD_BUG_ON(INTEL_MAX_PLATFORMS >
|
||||
sizeof(device_info->platform_mask) * BITS_PER_BYTE);
|
||||
device_info->platform_mask = BIT(device_info->platform);
|
||||
|
||||
BUG_ON(device_info->gen > sizeof(device_info->gen_mask) * BITS_PER_BYTE);
|
||||
device_info->gen_mask = BIT(device_info->gen - 1);
|
||||
|
||||
@ -1031,9 +1036,9 @@ static void i915_driver_cleanup_mmio(struct drm_i915_private *dev_priv)
|
||||
|
||||
static void intel_sanitize_options(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
i915.enable_execlists =
|
||||
i915_modparams.enable_execlists =
|
||||
intel_sanitize_enable_execlists(dev_priv,
|
||||
i915.enable_execlists);
|
||||
i915_modparams.enable_execlists);
|
||||
|
||||
/*
|
||||
* i915.enable_ppgtt is read-only, so do an early pass to validate the
|
||||
@ -1041,12 +1046,15 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv)
|
||||
* do this now so that we can print out any log messages once rather
|
||||
* than every time we check intel_enable_ppgtt().
|
||||
*/
|
||||
i915.enable_ppgtt =
|
||||
intel_sanitize_enable_ppgtt(dev_priv, i915.enable_ppgtt);
|
||||
DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915.enable_ppgtt);
|
||||
i915_modparams.enable_ppgtt =
|
||||
intel_sanitize_enable_ppgtt(dev_priv,
|
||||
i915_modparams.enable_ppgtt);
|
||||
DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915_modparams.enable_ppgtt);
|
||||
|
||||
i915.semaphores = intel_sanitize_semaphores(dev_priv, i915.semaphores);
|
||||
DRM_DEBUG_DRIVER("use GPU semaphores? %s\n", yesno(i915.semaphores));
|
||||
i915_modparams.semaphores =
|
||||
intel_sanitize_semaphores(dev_priv, i915_modparams.semaphores);
|
||||
DRM_DEBUG_DRIVER("use GPU semaphores? %s\n",
|
||||
yesno(i915_modparams.semaphores));
|
||||
|
||||
intel_uc_sanitize_options(dev_priv);
|
||||
|
||||
@ -1277,7 +1285,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
int ret;
|
||||
|
||||
/* Enable nuclear pageflip on ILK+ */
|
||||
if (!i915.nuclear_pageflip && match_info->gen < 5)
|
||||
if (!i915_modparams.nuclear_pageflip && match_info->gen < 5)
|
||||
driver.driver_features &= ~DRIVER_ATOMIC;
|
||||
|
||||
ret = -ENOMEM;
|
||||
@ -1341,7 +1349,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
|
||||
intel_runtime_pm_enable(dev_priv);
|
||||
|
||||
dev_priv->ipc_enabled = false;
|
||||
intel_init_ipc(dev_priv);
|
||||
|
||||
if (IS_ENABLED(CONFIG_DRM_I915_DEBUG))
|
||||
DRM_INFO("DRM_I915_DEBUG enabled\n");
|
||||
@ -2609,6 +2617,8 @@ static int intel_runtime_resume(struct device *kdev)
|
||||
if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
|
||||
intel_hpd_init(dev_priv);
|
||||
|
||||
intel_enable_ipc(dev_priv);
|
||||
|
||||
enable_rpm_wakeref_asserts(dev_priv);
|
||||
|
||||
if (ret)
|
||||
|
@ -80,8 +80,8 @@
|
||||
|
||||
#define DRIVER_NAME "i915"
|
||||
#define DRIVER_DESC "Intel Graphics"
|
||||
#define DRIVER_DATE "20170907"
|
||||
#define DRIVER_TIMESTAMP 1504772900
|
||||
#define DRIVER_DATE "20170929"
|
||||
#define DRIVER_TIMESTAMP 1506682238
|
||||
|
||||
/* Use I915_STATE_WARN(x) and I915_STATE_WARN_ON() (rather than WARN() and
|
||||
* WARN_ON()) for hw state sanity checks to check for unexpected conditions
|
||||
@ -93,7 +93,7 @@
|
||||
#define I915_STATE_WARN(condition, format...) ({ \
|
||||
int __ret_warn_on = !!(condition); \
|
||||
if (unlikely(__ret_warn_on)) \
|
||||
if (!WARN(i915.verbose_state_checks, format)) \
|
||||
if (!WARN(i915_modparams.verbose_state_checks, format)) \
|
||||
DRM_ERROR(format); \
|
||||
unlikely(__ret_warn_on); \
|
||||
})
|
||||
@ -126,7 +126,7 @@ static inline uint_fixed_16_16_t u32_to_fixed16(uint32_t val)
|
||||
{
|
||||
uint_fixed_16_16_t fp;
|
||||
|
||||
WARN_ON(val >> 16);
|
||||
WARN_ON(val > U16_MAX);
|
||||
|
||||
fp.val = val << 16;
|
||||
return fp;
|
||||
@ -163,8 +163,8 @@ static inline uint_fixed_16_16_t max_fixed16(uint_fixed_16_16_t max1,
|
||||
static inline uint_fixed_16_16_t clamp_u64_to_fixed16(uint64_t val)
|
||||
{
|
||||
uint_fixed_16_16_t fp;
|
||||
WARN_ON(val >> 32);
|
||||
fp.val = clamp_t(uint32_t, val, 0, ~0);
|
||||
WARN_ON(val > U32_MAX);
|
||||
fp.val = (uint32_t) val;
|
||||
return fp;
|
||||
}
|
||||
|
||||
@ -181,8 +181,8 @@ static inline uint32_t mul_round_up_u32_fixed16(uint32_t val,
|
||||
|
||||
intermediate_val = (uint64_t) val * mul.val;
|
||||
intermediate_val = DIV_ROUND_UP_ULL(intermediate_val, 1 << 16);
|
||||
WARN_ON(intermediate_val >> 32);
|
||||
return clamp_t(uint32_t, intermediate_val, 0, ~0);
|
||||
WARN_ON(intermediate_val > U32_MAX);
|
||||
return (uint32_t) intermediate_val;
|
||||
}
|
||||
|
||||
static inline uint_fixed_16_16_t mul_fixed16(uint_fixed_16_16_t val,
|
||||
@ -211,8 +211,8 @@ static inline uint32_t div_round_up_u32_fixed16(uint32_t val,
|
||||
|
||||
interm_val = (uint64_t)val << 16;
|
||||
interm_val = DIV_ROUND_UP_ULL(interm_val, d.val);
|
||||
WARN_ON(interm_val >> 32);
|
||||
return clamp_t(uint32_t, interm_val, 0, ~0);
|
||||
WARN_ON(interm_val > U32_MAX);
|
||||
return (uint32_t) interm_val;
|
||||
}
|
||||
|
||||
static inline uint_fixed_16_16_t mul_u32_fixed16(uint32_t val,
|
||||
@ -776,7 +776,6 @@ struct intel_csr {
|
||||
func(has_fpga_dbg); \
|
||||
func(has_full_ppgtt); \
|
||||
func(has_full_48bit_ppgtt); \
|
||||
func(has_gmbus_irq); \
|
||||
func(has_gmch_display); \
|
||||
func(has_guc); \
|
||||
func(has_guc_ct); \
|
||||
@ -797,7 +796,8 @@ struct intel_csr {
|
||||
func(cursor_needs_physical); \
|
||||
func(hws_needs_physical); \
|
||||
func(overlay_needs_physical); \
|
||||
func(supports_tv);
|
||||
func(supports_tv); \
|
||||
func(has_ipc);
|
||||
|
||||
struct sseu_dev_info {
|
||||
u8 slice_mask;
|
||||
@ -851,21 +851,28 @@ enum intel_platform {
|
||||
};
|
||||
|
||||
struct intel_device_info {
|
||||
u32 display_mmio_offset;
|
||||
u16 device_id;
|
||||
u16 gen_mask;
|
||||
|
||||
u8 gen;
|
||||
u8 gt; /* GT number, 0 if undefined */
|
||||
u8 num_rings;
|
||||
u8 ring_mask; /* Rings supported by the HW */
|
||||
|
||||
enum intel_platform platform;
|
||||
u32 platform_mask;
|
||||
|
||||
u32 display_mmio_offset;
|
||||
|
||||
u8 num_pipes;
|
||||
u8 num_sprites[I915_MAX_PIPES];
|
||||
u8 num_scalers[I915_MAX_PIPES];
|
||||
u8 gen;
|
||||
u16 gen_mask;
|
||||
enum intel_platform platform;
|
||||
u8 gt; /* GT number, 0 if undefined */
|
||||
u8 ring_mask; /* Rings supported by the HW */
|
||||
u8 num_rings;
|
||||
|
||||
#define DEFINE_FLAG(name) u8 name:1
|
||||
DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG);
|
||||
#undef DEFINE_FLAG
|
||||
u16 ddb_size; /* in blocks */
|
||||
|
||||
/* Register offsets for the various display pipes and transcoders */
|
||||
int pipe_offsets[I915_MAX_TRANSCODERS];
|
||||
int trans_offsets[I915_MAX_TRANSCODERS];
|
||||
@ -1000,7 +1007,8 @@ struct i915_gpu_state {
|
||||
u32 seqno;
|
||||
u32 head;
|
||||
u32 tail;
|
||||
} *requests, execlist[2];
|
||||
} *requests, execlist[EXECLIST_MAX_PORTS];
|
||||
unsigned int num_ports;
|
||||
|
||||
struct drm_i915_error_waiter {
|
||||
char comm[TASK_COMM_LEN];
|
||||
@ -1178,6 +1186,14 @@ struct i915_psr {
|
||||
bool y_cord_support;
|
||||
bool colorimetry_support;
|
||||
bool alpm;
|
||||
|
||||
void (*enable_source)(struct intel_dp *,
|
||||
const struct intel_crtc_state *);
|
||||
void (*disable_source)(struct intel_dp *,
|
||||
const struct intel_crtc_state *);
|
||||
void (*enable_sink)(struct intel_dp *);
|
||||
void (*activate)(struct intel_dp *);
|
||||
void (*setup_vsc)(struct intel_dp *, const struct intel_crtc_state *);
|
||||
};
|
||||
|
||||
enum intel_pch {
|
||||
@ -1836,6 +1852,20 @@ struct skl_wm_level {
|
||||
uint8_t plane_res_l;
|
||||
};
|
||||
|
||||
/* Stores plane specific WM parameters */
|
||||
struct skl_wm_params {
|
||||
bool x_tiled, y_tiled;
|
||||
bool rc_surface;
|
||||
uint32_t width;
|
||||
uint8_t cpp;
|
||||
uint32_t plane_pixel_rate;
|
||||
uint32_t y_min_scanlines;
|
||||
uint32_t plane_bytes_per_line;
|
||||
uint_fixed_16_16_t plane_blocks_per_line;
|
||||
uint_fixed_16_16_t y_tile_minimum;
|
||||
uint32_t linetime_us;
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct helps tracking the state needed for runtime PM, which puts the
|
||||
* device in PCI D3 state. Notice that when this happens, nothing on the
|
||||
@ -2331,6 +2361,8 @@ struct drm_i915_private {
|
||||
DECLARE_HASHTABLE(mm_structs, 7);
|
||||
struct mutex mm_lock;
|
||||
|
||||
struct intel_ppat ppat;
|
||||
|
||||
/* Kernel Modesetting */
|
||||
|
||||
struct intel_crtc *plane_to_crtc_mapping[I915_MAX_PIPES];
|
||||
@ -2811,8 +2843,8 @@ static inline struct scatterlist *__sg_next(struct scatterlist *sg)
|
||||
#define for_each_sgt_dma(__dmap, __iter, __sgt) \
|
||||
for ((__iter) = __sgt_iter((__sgt)->sgl, true); \
|
||||
((__dmap) = (__iter).dma + (__iter).curr); \
|
||||
(((__iter).curr += PAGE_SIZE) < (__iter).max) || \
|
||||
((__iter) = __sgt_iter(__sg_next((__iter).sgp), true), 0))
|
||||
(((__iter).curr += PAGE_SIZE) >= (__iter).max) ? \
|
||||
(__iter) = __sgt_iter(__sg_next((__iter).sgp), true), 0 : 0)
|
||||
|
||||
/**
|
||||
* for_each_sgt_page - iterate over the pages of the given sg_table
|
||||
@ -2824,8 +2856,23 @@ static inline struct scatterlist *__sg_next(struct scatterlist *sg)
|
||||
for ((__iter) = __sgt_iter((__sgt)->sgl, false); \
|
||||
((__pp) = (__iter).pfn == 0 ? NULL : \
|
||||
pfn_to_page((__iter).pfn + ((__iter).curr >> PAGE_SHIFT))); \
|
||||
(((__iter).curr += PAGE_SIZE) < (__iter).max) || \
|
||||
((__iter) = __sgt_iter(__sg_next((__iter).sgp), false), 0))
|
||||
(((__iter).curr += PAGE_SIZE) >= (__iter).max) ? \
|
||||
(__iter) = __sgt_iter(__sg_next((__iter).sgp), false), 0 : 0)
|
||||
|
||||
static inline unsigned int i915_sg_segment_size(void)
|
||||
{
|
||||
unsigned int size = swiotlb_max_segment();
|
||||
|
||||
if (size == 0)
|
||||
return SCATTERLIST_MAX_SEGMENT;
|
||||
|
||||
size = rounddown(size, PAGE_SIZE);
|
||||
/* swiotlb_max_segment_size can return 1 byte when it means one page. */
|
||||
if (size < PAGE_SIZE)
|
||||
size = PAGE_SIZE;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline const struct intel_device_info *
|
||||
intel_info(const struct drm_i915_private *dev_priv)
|
||||
@ -2842,23 +2889,21 @@ intel_info(const struct drm_i915_private *dev_priv)
|
||||
#define INTEL_REVID(dev_priv) ((dev_priv)->drm.pdev->revision)
|
||||
|
||||
#define GEN_FOREVER (0)
|
||||
|
||||
#define INTEL_GEN_MASK(s, e) ( \
|
||||
BUILD_BUG_ON_ZERO(!__builtin_constant_p(s)) + \
|
||||
BUILD_BUG_ON_ZERO(!__builtin_constant_p(e)) + \
|
||||
GENMASK((e) != GEN_FOREVER ? (e) - 1 : BITS_PER_LONG - 1, \
|
||||
(s) != GEN_FOREVER ? (s) - 1 : 0) \
|
||||
)
|
||||
|
||||
/*
|
||||
* Returns true if Gen is in inclusive range [Start, End].
|
||||
*
|
||||
* Use GEN_FOREVER for unbound start and or end.
|
||||
*/
|
||||
#define IS_GEN(dev_priv, s, e) ({ \
|
||||
unsigned int __s = (s), __e = (e); \
|
||||
BUILD_BUG_ON(!__builtin_constant_p(s)); \
|
||||
BUILD_BUG_ON(!__builtin_constant_p(e)); \
|
||||
if ((__s) != GEN_FOREVER) \
|
||||
__s = (s) - 1; \
|
||||
if ((__e) == GEN_FOREVER) \
|
||||
__e = BITS_PER_LONG - 1; \
|
||||
else \
|
||||
__e = (e) - 1; \
|
||||
!!((dev_priv)->info.gen_mask & GENMASK((__e), (__s))); \
|
||||
})
|
||||
#define IS_GEN(dev_priv, s, e) \
|
||||
(!!((dev_priv)->info.gen_mask & INTEL_GEN_MASK((s), (e))))
|
||||
|
||||
/*
|
||||
* Return true if revision is in range [since,until] inclusive.
|
||||
@ -2868,37 +2913,39 @@ intel_info(const struct drm_i915_private *dev_priv)
|
||||
#define IS_REVID(p, since, until) \
|
||||
(INTEL_REVID(p) >= (since) && INTEL_REVID(p) <= (until))
|
||||
|
||||
#define IS_I830(dev_priv) ((dev_priv)->info.platform == INTEL_I830)
|
||||
#define IS_I845G(dev_priv) ((dev_priv)->info.platform == INTEL_I845G)
|
||||
#define IS_I85X(dev_priv) ((dev_priv)->info.platform == INTEL_I85X)
|
||||
#define IS_I865G(dev_priv) ((dev_priv)->info.platform == INTEL_I865G)
|
||||
#define IS_I915G(dev_priv) ((dev_priv)->info.platform == INTEL_I915G)
|
||||
#define IS_I915GM(dev_priv) ((dev_priv)->info.platform == INTEL_I915GM)
|
||||
#define IS_I945G(dev_priv) ((dev_priv)->info.platform == INTEL_I945G)
|
||||
#define IS_I945GM(dev_priv) ((dev_priv)->info.platform == INTEL_I945GM)
|
||||
#define IS_I965G(dev_priv) ((dev_priv)->info.platform == INTEL_I965G)
|
||||
#define IS_I965GM(dev_priv) ((dev_priv)->info.platform == INTEL_I965GM)
|
||||
#define IS_G45(dev_priv) ((dev_priv)->info.platform == INTEL_G45)
|
||||
#define IS_GM45(dev_priv) ((dev_priv)->info.platform == INTEL_GM45)
|
||||
#define IS_PLATFORM(dev_priv, p) ((dev_priv)->info.platform_mask & BIT(p))
|
||||
|
||||
#define IS_I830(dev_priv) IS_PLATFORM(dev_priv, INTEL_I830)
|
||||
#define IS_I845G(dev_priv) IS_PLATFORM(dev_priv, INTEL_I845G)
|
||||
#define IS_I85X(dev_priv) IS_PLATFORM(dev_priv, INTEL_I85X)
|
||||
#define IS_I865G(dev_priv) IS_PLATFORM(dev_priv, INTEL_I865G)
|
||||
#define IS_I915G(dev_priv) IS_PLATFORM(dev_priv, INTEL_I915G)
|
||||
#define IS_I915GM(dev_priv) IS_PLATFORM(dev_priv, INTEL_I915GM)
|
||||
#define IS_I945G(dev_priv) IS_PLATFORM(dev_priv, INTEL_I945G)
|
||||
#define IS_I945GM(dev_priv) IS_PLATFORM(dev_priv, INTEL_I945GM)
|
||||
#define IS_I965G(dev_priv) IS_PLATFORM(dev_priv, INTEL_I965G)
|
||||
#define IS_I965GM(dev_priv) IS_PLATFORM(dev_priv, INTEL_I965GM)
|
||||
#define IS_G45(dev_priv) IS_PLATFORM(dev_priv, INTEL_G45)
|
||||
#define IS_GM45(dev_priv) IS_PLATFORM(dev_priv, INTEL_GM45)
|
||||
#define IS_G4X(dev_priv) (IS_G45(dev_priv) || IS_GM45(dev_priv))
|
||||
#define IS_PINEVIEW_G(dev_priv) (INTEL_DEVID(dev_priv) == 0xa001)
|
||||
#define IS_PINEVIEW_M(dev_priv) (INTEL_DEVID(dev_priv) == 0xa011)
|
||||
#define IS_PINEVIEW(dev_priv) ((dev_priv)->info.platform == INTEL_PINEVIEW)
|
||||
#define IS_G33(dev_priv) ((dev_priv)->info.platform == INTEL_G33)
|
||||
#define IS_PINEVIEW(dev_priv) IS_PLATFORM(dev_priv, INTEL_PINEVIEW)
|
||||
#define IS_G33(dev_priv) IS_PLATFORM(dev_priv, INTEL_G33)
|
||||
#define IS_IRONLAKE_M(dev_priv) (INTEL_DEVID(dev_priv) == 0x0046)
|
||||
#define IS_IVYBRIDGE(dev_priv) ((dev_priv)->info.platform == INTEL_IVYBRIDGE)
|
||||
#define IS_IVYBRIDGE(dev_priv) IS_PLATFORM(dev_priv, INTEL_IVYBRIDGE)
|
||||
#define IS_IVB_GT1(dev_priv) (IS_IVYBRIDGE(dev_priv) && \
|
||||
(dev_priv)->info.gt == 1)
|
||||
#define IS_VALLEYVIEW(dev_priv) ((dev_priv)->info.platform == INTEL_VALLEYVIEW)
|
||||
#define IS_CHERRYVIEW(dev_priv) ((dev_priv)->info.platform == INTEL_CHERRYVIEW)
|
||||
#define IS_HASWELL(dev_priv) ((dev_priv)->info.platform == INTEL_HASWELL)
|
||||
#define IS_BROADWELL(dev_priv) ((dev_priv)->info.platform == INTEL_BROADWELL)
|
||||
#define IS_SKYLAKE(dev_priv) ((dev_priv)->info.platform == INTEL_SKYLAKE)
|
||||
#define IS_BROXTON(dev_priv) ((dev_priv)->info.platform == INTEL_BROXTON)
|
||||
#define IS_KABYLAKE(dev_priv) ((dev_priv)->info.platform == INTEL_KABYLAKE)
|
||||
#define IS_GEMINILAKE(dev_priv) ((dev_priv)->info.platform == INTEL_GEMINILAKE)
|
||||
#define IS_COFFEELAKE(dev_priv) ((dev_priv)->info.platform == INTEL_COFFEELAKE)
|
||||
#define IS_CANNONLAKE(dev_priv) ((dev_priv)->info.platform == INTEL_CANNONLAKE)
|
||||
#define IS_VALLEYVIEW(dev_priv) IS_PLATFORM(dev_priv, INTEL_VALLEYVIEW)
|
||||
#define IS_CHERRYVIEW(dev_priv) IS_PLATFORM(dev_priv, INTEL_CHERRYVIEW)
|
||||
#define IS_HASWELL(dev_priv) IS_PLATFORM(dev_priv, INTEL_HASWELL)
|
||||
#define IS_BROADWELL(dev_priv) IS_PLATFORM(dev_priv, INTEL_BROADWELL)
|
||||
#define IS_SKYLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_SKYLAKE)
|
||||
#define IS_BROXTON(dev_priv) IS_PLATFORM(dev_priv, INTEL_BROXTON)
|
||||
#define IS_KABYLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_KABYLAKE)
|
||||
#define IS_GEMINILAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_GEMINILAKE)
|
||||
#define IS_COFFEELAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_COFFEELAKE)
|
||||
#define IS_CANNONLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_CANNONLAKE)
|
||||
#define IS_MOBILE(dev_priv) ((dev_priv)->info.is_mobile)
|
||||
#define IS_HSW_EARLY_SDV(dev_priv) (IS_HASWELL(dev_priv) && \
|
||||
(INTEL_DEVID(dev_priv) & 0xFF00) == 0x0C00)
|
||||
@ -2946,6 +2993,8 @@ intel_info(const struct drm_i915_private *dev_priv)
|
||||
(dev_priv)->info.gt == 3)
|
||||
#define IS_CFL_ULT(dev_priv) (IS_COFFEELAKE(dev_priv) && \
|
||||
(INTEL_DEVID(dev_priv) & 0x00F0) == 0x00A0)
|
||||
#define IS_CFL_GT2(dev_priv) (IS_COFFEELAKE(dev_priv) && \
|
||||
(dev_priv)->info.gt == 2)
|
||||
|
||||
#define IS_ALPHA_SUPPORT(intel_info) ((intel_info)->is_alpha_support)
|
||||
|
||||
@ -3036,9 +3085,9 @@ intel_info(const struct drm_i915_private *dev_priv)
|
||||
|
||||
#define HAS_LOGICAL_RING_CONTEXTS(dev_priv) \
|
||||
((dev_priv)->info.has_logical_ring_contexts)
|
||||
#define USES_PPGTT(dev_priv) (i915.enable_ppgtt)
|
||||
#define USES_FULL_PPGTT(dev_priv) (i915.enable_ppgtt >= 2)
|
||||
#define USES_FULL_48BIT_PPGTT(dev_priv) (i915.enable_ppgtt == 3)
|
||||
#define USES_PPGTT(dev_priv) (i915_modparams.enable_ppgtt)
|
||||
#define USES_FULL_PPGTT(dev_priv) (i915_modparams.enable_ppgtt >= 2)
|
||||
#define USES_FULL_48BIT_PPGTT(dev_priv) (i915_modparams.enable_ppgtt == 3)
|
||||
|
||||
#define HAS_OVERLAY(dev_priv) ((dev_priv)->info.has_overlay)
|
||||
#define OVERLAY_NEEDS_PHYSICAL(dev_priv) \
|
||||
@ -3056,9 +3105,12 @@ intel_info(const struct drm_i915_private *dev_priv)
|
||||
* even when in MSI mode. This results in spurious interrupt warnings if the
|
||||
* legacy irq no. is shared with another device. The kernel then disables that
|
||||
* interrupt source and so prevents the other device from working properly.
|
||||
*
|
||||
* Since we don't enable MSI anymore on gen4, we can always use GMBUS/AUX
|
||||
* interrupts.
|
||||
*/
|
||||
#define HAS_AUX_IRQ(dev_priv) ((dev_priv)->info.gen >= 5)
|
||||
#define HAS_GMBUS_IRQ(dev_priv) ((dev_priv)->info.has_gmbus_irq)
|
||||
#define HAS_AUX_IRQ(dev_priv) true
|
||||
#define HAS_GMBUS_IRQ(dev_priv) (INTEL_GEN(dev_priv) >= 4)
|
||||
|
||||
/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
|
||||
* rows, which changed the alignment requirements and fence programming.
|
||||
@ -3089,6 +3141,8 @@ intel_info(const struct drm_i915_private *dev_priv)
|
||||
#define HAS_RUNTIME_PM(dev_priv) ((dev_priv)->info.has_runtime_pm)
|
||||
#define HAS_64BIT_RELOC(dev_priv) ((dev_priv)->info.has_64bit_reloc)
|
||||
|
||||
#define HAS_IPC(dev_priv) ((dev_priv)->info.has_ipc)
|
||||
|
||||
/*
|
||||
* For now, anything with a GuC requires uCode loading, and then supports
|
||||
* command submission once loaded. But these are logically independent
|
||||
@ -3234,7 +3288,7 @@ static inline void i915_queue_hangcheck(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long delay;
|
||||
|
||||
if (unlikely(!i915.enable_hangcheck))
|
||||
if (unlikely(!i915_modparams.enable_hangcheck))
|
||||
return;
|
||||
|
||||
/* Don't continually defer the hangcheck so that it is always run at
|
||||
@ -3267,6 +3321,8 @@ static inline bool intel_vgpu_active(struct drm_i915_private *dev_priv)
|
||||
return dev_priv->vgpu.active;
|
||||
}
|
||||
|
||||
u32 i915_pipestat_enable_mask(struct drm_i915_private *dev_priv,
|
||||
enum pipe pipe);
|
||||
void
|
||||
i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
|
||||
u32 status_mask);
|
||||
@ -4360,4 +4416,12 @@ int remap_io_mapping(struct vm_area_struct *vma,
|
||||
unsigned long addr, unsigned long pfn, unsigned long size,
|
||||
struct io_mapping *iomap);
|
||||
|
||||
static inline int intel_hws_csb_write_index(struct drm_i915_private *i915)
|
||||
{
|
||||
if (INTEL_GEN(i915) >= 10)
|
||||
return CNL_HWS_CSB_WRITE_INDEX;
|
||||
else
|
||||
return I915_HWS_CSB_WRITE_INDEX;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -179,7 +179,7 @@ i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
|
||||
* the alignment of the buddy allocation will naturally match.
|
||||
*/
|
||||
phys = drm_pci_alloc(obj->base.dev,
|
||||
obj->base.size,
|
||||
roundup_pow_of_two(obj->base.size),
|
||||
roundup_pow_of_two(obj->base.size));
|
||||
if (!phys)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -694,10 +694,10 @@ flush_write_domain(struct drm_i915_gem_object *obj, unsigned int flush_domains)
|
||||
|
||||
switch (obj->base.write_domain) {
|
||||
case I915_GEM_DOMAIN_GTT:
|
||||
if (INTEL_GEN(dev_priv) >= 6 && !HAS_LLC(dev_priv)) {
|
||||
if (!HAS_LLC(dev_priv)) {
|
||||
intel_runtime_pm_get(dev_priv);
|
||||
spin_lock_irq(&dev_priv->uncore.lock);
|
||||
POSTING_READ_FW(RING_ACTHD(dev_priv->engine[RCS]->mmio_base));
|
||||
POSTING_READ_FW(RING_HEAD(dev_priv->engine[RCS]->mmio_base));
|
||||
spin_unlock_irq(&dev_priv->uncore.lock);
|
||||
intel_runtime_pm_put(dev_priv);
|
||||
}
|
||||
@ -2303,7 +2303,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
|
||||
struct sgt_iter sgt_iter;
|
||||
struct page *page;
|
||||
unsigned long last_pfn = 0; /* suppress gcc warning */
|
||||
unsigned int max_segment;
|
||||
unsigned int max_segment = i915_sg_segment_size();
|
||||
gfp_t noreclaim;
|
||||
int ret;
|
||||
|
||||
@ -2314,10 +2314,6 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
|
||||
GEM_BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
|
||||
GEM_BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
|
||||
|
||||
max_segment = swiotlb_max_segment();
|
||||
if (!max_segment)
|
||||
max_segment = rounddown(UINT_MAX, PAGE_SIZE);
|
||||
|
||||
st = kmalloc(sizeof(*st), GFP_KERNEL);
|
||||
if (st == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -2819,8 +2815,8 @@ i915_gem_reset_prepare_engine(struct intel_engine_cs *engine)
|
||||
* Turning off the engine->irq_tasklet until the reset is over
|
||||
* prevents the race.
|
||||
*/
|
||||
tasklet_kill(&engine->irq_tasklet);
|
||||
tasklet_disable(&engine->irq_tasklet);
|
||||
tasklet_kill(&engine->execlists.irq_tasklet);
|
||||
tasklet_disable(&engine->execlists.irq_tasklet);
|
||||
|
||||
if (engine->irq_seqno_barrier)
|
||||
engine->irq_seqno_barrier(engine);
|
||||
@ -2999,7 +2995,7 @@ void i915_gem_reset(struct drm_i915_private *dev_priv)
|
||||
|
||||
void i915_gem_reset_finish_engine(struct intel_engine_cs *engine)
|
||||
{
|
||||
tasklet_enable(&engine->irq_tasklet);
|
||||
tasklet_enable(&engine->execlists.irq_tasklet);
|
||||
kthread_unpark(engine->breadcrumbs.signaler);
|
||||
}
|
||||
|
||||
@ -3026,9 +3022,6 @@ static void nop_submit_request(struct drm_i915_gem_request *request)
|
||||
|
||||
static void engine_set_wedged(struct intel_engine_cs *engine)
|
||||
{
|
||||
struct drm_i915_gem_request *request;
|
||||
unsigned long flags;
|
||||
|
||||
/* We need to be sure that no thread is running the old callback as
|
||||
* we install the nop handler (otherwise we would submit a request
|
||||
* to hardware that will never complete). In order to prevent this
|
||||
@ -3038,40 +3031,7 @@ static void engine_set_wedged(struct intel_engine_cs *engine)
|
||||
engine->submit_request = nop_submit_request;
|
||||
|
||||
/* Mark all executing requests as skipped */
|
||||
spin_lock_irqsave(&engine->timeline->lock, flags);
|
||||
list_for_each_entry(request, &engine->timeline->requests, link)
|
||||
if (!i915_gem_request_completed(request))
|
||||
dma_fence_set_error(&request->fence, -EIO);
|
||||
spin_unlock_irqrestore(&engine->timeline->lock, flags);
|
||||
|
||||
/*
|
||||
* Clear the execlists queue up before freeing the requests, as those
|
||||
* are the ones that keep the context and ringbuffer backing objects
|
||||
* pinned in place.
|
||||
*/
|
||||
|
||||
if (i915.enable_execlists) {
|
||||
struct execlist_port *port = engine->execlist_port;
|
||||
unsigned long flags;
|
||||
unsigned int n;
|
||||
|
||||
spin_lock_irqsave(&engine->timeline->lock, flags);
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++)
|
||||
i915_gem_request_put(port_request(&port[n]));
|
||||
memset(engine->execlist_port, 0, sizeof(engine->execlist_port));
|
||||
engine->execlist_queue = RB_ROOT;
|
||||
engine->execlist_first = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&engine->timeline->lock, flags);
|
||||
|
||||
/* The port is checked prior to scheduling a tasklet, but
|
||||
* just in case we have suspended the tasklet to do the
|
||||
* wedging make sure that when it wakes, it decides there
|
||||
* is no work to do by clearing the irq_posted bit.
|
||||
*/
|
||||
clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
|
||||
}
|
||||
engine->cancel_requests(engine);
|
||||
|
||||
/* Mark all pending requests as complete so that any concurrent
|
||||
* (lockless) lookup doesn't try and wait upon the request as we
|
||||
@ -4778,7 +4738,7 @@ bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value)
|
||||
return false;
|
||||
|
||||
/* TODO: make semaphores and Execlists play nicely together */
|
||||
if (i915.enable_execlists)
|
||||
if (i915_modparams.enable_execlists)
|
||||
return false;
|
||||
|
||||
if (value >= 0)
|
||||
@ -4799,7 +4759,7 @@ int i915_gem_init(struct drm_i915_private *dev_priv)
|
||||
|
||||
dev_priv->mm.unordered_timeline = dma_fence_context_alloc(1);
|
||||
|
||||
if (!i915.enable_execlists) {
|
||||
if (!i915_modparams.enable_execlists) {
|
||||
dev_priv->gt.resume = intel_legacy_submission_resume;
|
||||
dev_priv->gt.cleanup_engine = intel_engine_cleanup;
|
||||
} else {
|
||||
|
@ -314,7 +314,7 @@ __create_hw_context(struct drm_i915_private *dev_priv,
|
||||
* present or not in use we still need a small bias as ring wraparound
|
||||
* at offset 0 sometimes hangs. No idea why.
|
||||
*/
|
||||
if (HAS_GUC(dev_priv) && i915.enable_guc_loading)
|
||||
if (HAS_GUC(dev_priv) && i915_modparams.enable_guc_loading)
|
||||
ctx->ggtt_offset_bias = GUC_WOPCM_TOP;
|
||||
else
|
||||
ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE;
|
||||
@ -407,7 +407,7 @@ i915_gem_context_create_gvt(struct drm_device *dev)
|
||||
i915_gem_context_set_closed(ctx); /* not user accessible */
|
||||
i915_gem_context_clear_bannable(ctx);
|
||||
i915_gem_context_set_force_single_submission(ctx);
|
||||
if (!i915.enable_guc_submission)
|
||||
if (!i915_modparams.enable_guc_submission)
|
||||
ctx->ring_size = 512 * PAGE_SIZE; /* Max ring buffer size */
|
||||
|
||||
GEM_BUG_ON(i915_gem_context_is_kernel(ctx));
|
||||
@ -431,7 +431,7 @@ int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
|
||||
|
||||
if (intel_vgpu_active(dev_priv) &&
|
||||
HAS_LOGICAL_RING_CONTEXTS(dev_priv)) {
|
||||
if (!i915.enable_execlists) {
|
||||
if (!i915_modparams.enable_execlists) {
|
||||
DRM_INFO("Only EXECLIST mode is supported in vgpu.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -483,7 +483,7 @@ void i915_gem_contexts_lost(struct drm_i915_private *dev_priv)
|
||||
}
|
||||
|
||||
/* Force the GPU state to be restored on enabling */
|
||||
if (!i915.enable_execlists) {
|
||||
if (!i915_modparams.enable_execlists) {
|
||||
struct i915_gem_context *ctx;
|
||||
|
||||
list_for_each_entry(ctx, &dev_priv->contexts.list, link) {
|
||||
@ -568,7 +568,7 @@ mi_set_context(struct drm_i915_gem_request *req, u32 flags)
|
||||
enum intel_engine_id id;
|
||||
const int num_rings =
|
||||
/* Use an extended w/a on gen7 if signalling from other rings */
|
||||
(i915.semaphores && INTEL_GEN(dev_priv) == 7) ?
|
||||
(i915_modparams.semaphores && INTEL_GEN(dev_priv) == 7) ?
|
||||
INTEL_INFO(dev_priv)->num_rings - 1 :
|
||||
0;
|
||||
int len;
|
||||
@ -837,7 +837,7 @@ int i915_switch_context(struct drm_i915_gem_request *req)
|
||||
struct intel_engine_cs *engine = req->engine;
|
||||
|
||||
lockdep_assert_held(&req->i915->drm.struct_mutex);
|
||||
if (i915.enable_execlists)
|
||||
if (i915_modparams.enable_execlists)
|
||||
return 0;
|
||||
|
||||
if (!req->ctx->engine[engine->id].state) {
|
||||
|
@ -58,6 +58,7 @@ enum {
|
||||
|
||||
#define __EXEC_HAS_RELOC BIT(31)
|
||||
#define __EXEC_VALIDATED BIT(30)
|
||||
#define __EXEC_INTERNAL_FLAGS (~0u << 30)
|
||||
#define UPDATE PIN_OFFSET_FIXED
|
||||
|
||||
#define BATCH_OFFSET_BIAS (256*1024)
|
||||
@ -679,7 +680,7 @@ static int eb_select_context(struct i915_execbuffer *eb)
|
||||
static int eb_lookup_vmas(struct i915_execbuffer *eb)
|
||||
{
|
||||
struct radix_tree_root *handles_vma = &eb->ctx->handles_vma;
|
||||
struct drm_i915_gem_object *uninitialized_var(obj);
|
||||
struct drm_i915_gem_object *obj;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
@ -725,19 +726,17 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb)
|
||||
goto err_obj;
|
||||
}
|
||||
|
||||
/* transfer ref to ctx */
|
||||
vma->open_count++;
|
||||
list_add(&lut->obj_link, &obj->lut_list);
|
||||
list_add(&lut->ctx_link, &eb->ctx->handles_list);
|
||||
lut->ctx = eb->ctx;
|
||||
lut->handle = handle;
|
||||
|
||||
/* transfer ref to ctx */
|
||||
obj = NULL;
|
||||
|
||||
add_vma:
|
||||
err = eb_add_vma(eb, i, vma);
|
||||
if (unlikely(err))
|
||||
goto err_obj;
|
||||
goto err_vma;
|
||||
|
||||
GEM_BUG_ON(vma != eb->vma[i]);
|
||||
GEM_BUG_ON(vma->exec_flags != &eb->flags[i]);
|
||||
@ -766,8 +765,7 @@ add_vma:
|
||||
return eb_reserve(eb);
|
||||
|
||||
err_obj:
|
||||
if (obj)
|
||||
i915_gem_object_put(obj);
|
||||
i915_gem_object_put(obj);
|
||||
err_vma:
|
||||
eb->vma[i] = NULL;
|
||||
return err;
|
||||
@ -1587,7 +1585,7 @@ static int eb_prefault_relocations(const struct i915_execbuffer *eb)
|
||||
const unsigned int count = eb->buffer_count;
|
||||
unsigned int i;
|
||||
|
||||
if (unlikely(i915.prefault_disable))
|
||||
if (unlikely(i915_modparams.prefault_disable))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
@ -2188,6 +2186,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
|
||||
int out_fence_fd = -1;
|
||||
int err;
|
||||
|
||||
BUILD_BUG_ON(__EXEC_INTERNAL_FLAGS & ~__I915_EXEC_ILLEGAL_FLAGS);
|
||||
BUILD_BUG_ON(__EXEC_OBJECT_INTERNAL_FLAGS &
|
||||
~__EXEC_OBJECT_UNKNOWN_FLAGS);
|
||||
|
||||
|
@ -180,7 +180,7 @@ int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 8 && i915.enable_execlists) {
|
||||
if (INTEL_GEN(dev_priv) >= 8 && i915_modparams.enable_execlists) {
|
||||
if (has_full_48bit_ppgtt)
|
||||
return 3;
|
||||
|
||||
@ -230,13 +230,13 @@ static gen8_pte_t gen8_pte_encode(dma_addr_t addr,
|
||||
|
||||
switch (level) {
|
||||
case I915_CACHE_NONE:
|
||||
pte |= PPAT_UNCACHED_INDEX;
|
||||
pte |= PPAT_UNCACHED;
|
||||
break;
|
||||
case I915_CACHE_WT:
|
||||
pte |= PPAT_DISPLAY_ELLC_INDEX;
|
||||
pte |= PPAT_DISPLAY_ELLC;
|
||||
break;
|
||||
default:
|
||||
pte |= PPAT_CACHED_INDEX;
|
||||
pte |= PPAT_CACHED;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -249,9 +249,9 @@ static gen8_pde_t gen8_pde_encode(const dma_addr_t addr,
|
||||
gen8_pde_t pde = _PAGE_PRESENT | _PAGE_RW;
|
||||
pde |= addr;
|
||||
if (level != I915_CACHE_NONE)
|
||||
pde |= PPAT_CACHED_PDE_INDEX;
|
||||
pde |= PPAT_CACHED_PDE;
|
||||
else
|
||||
pde |= PPAT_UNCACHED_INDEX;
|
||||
pde |= PPAT_UNCACHED;
|
||||
return pde;
|
||||
}
|
||||
|
||||
@ -481,10 +481,8 @@ static void fill_page_dma(struct i915_address_space *vm,
|
||||
const u64 val)
|
||||
{
|
||||
u64 * const vaddr = kmap_atomic(p->page);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 512; i++)
|
||||
vaddr[i] = val;
|
||||
memset64(vaddr, val, PAGE_SIZE / sizeof(val));
|
||||
|
||||
kunmap_atomic(vaddr);
|
||||
}
|
||||
@ -1168,19 +1166,22 @@ static int gen8_ppgtt_alloc_pd(struct i915_address_space *vm,
|
||||
unsigned int pde;
|
||||
|
||||
gen8_for_each_pde(pt, pd, start, length, pde) {
|
||||
int count = gen8_pte_count(start, length);
|
||||
|
||||
if (pt == vm->scratch_pt) {
|
||||
pt = alloc_pt(vm);
|
||||
if (IS_ERR(pt))
|
||||
goto unwind;
|
||||
|
||||
gen8_initialize_pt(vm, pt);
|
||||
if (count < GEN8_PTES)
|
||||
gen8_initialize_pt(vm, pt);
|
||||
|
||||
gen8_ppgtt_set_pde(vm, pd, pt, pde);
|
||||
pd->used_pdes++;
|
||||
GEM_BUG_ON(pd->used_pdes > I915_PDES);
|
||||
}
|
||||
|
||||
pt->used_ptes += gen8_pte_count(start, length);
|
||||
pt->used_ptes += count;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@ -1969,7 +1970,7 @@ int i915_ppgtt_init_hw(struct drm_i915_private *dev_priv)
|
||||
/* In the case of execlists, PPGTT is enabled by the context descriptor
|
||||
* and the PDPs are contained within the context itself. We don't
|
||||
* need to do anything here. */
|
||||
if (i915.enable_execlists)
|
||||
if (i915_modparams.enable_execlists)
|
||||
return 0;
|
||||
|
||||
if (!USES_PPGTT(dev_priv))
|
||||
@ -2816,41 +2817,209 @@ static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cnl_setup_private_ppat(struct drm_i915_private *dev_priv)
|
||||
static struct intel_ppat_entry *
|
||||
__alloc_ppat_entry(struct intel_ppat *ppat, unsigned int index, u8 value)
|
||||
{
|
||||
struct intel_ppat_entry *entry = &ppat->entries[index];
|
||||
|
||||
GEM_BUG_ON(index >= ppat->max_entries);
|
||||
GEM_BUG_ON(test_bit(index, ppat->used));
|
||||
|
||||
entry->ppat = ppat;
|
||||
entry->value = value;
|
||||
kref_init(&entry->ref);
|
||||
set_bit(index, ppat->used);
|
||||
set_bit(index, ppat->dirty);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void __free_ppat_entry(struct intel_ppat_entry *entry)
|
||||
{
|
||||
struct intel_ppat *ppat = entry->ppat;
|
||||
unsigned int index = entry - ppat->entries;
|
||||
|
||||
GEM_BUG_ON(index >= ppat->max_entries);
|
||||
GEM_BUG_ON(!test_bit(index, ppat->used));
|
||||
|
||||
entry->value = ppat->clear_value;
|
||||
clear_bit(index, ppat->used);
|
||||
set_bit(index, ppat->dirty);
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_ppat_get - get a usable PPAT entry
|
||||
* @i915: i915 device instance
|
||||
* @value: the PPAT value required by the caller
|
||||
*
|
||||
* The function tries to search if there is an existing PPAT entry which
|
||||
* matches with the required value. If perfectly matched, the existing PPAT
|
||||
* entry will be used. If only partially matched, it will try to check if
|
||||
* there is any available PPAT index. If yes, it will allocate a new PPAT
|
||||
* index for the required entry and update the HW. If not, the partially
|
||||
* matched entry will be used.
|
||||
*/
|
||||
const struct intel_ppat_entry *
|
||||
intel_ppat_get(struct drm_i915_private *i915, u8 value)
|
||||
{
|
||||
struct intel_ppat *ppat = &i915->ppat;
|
||||
struct intel_ppat_entry *entry;
|
||||
unsigned int scanned, best_score;
|
||||
int i;
|
||||
|
||||
GEM_BUG_ON(!ppat->max_entries);
|
||||
|
||||
scanned = best_score = 0;
|
||||
for_each_set_bit(i, ppat->used, ppat->max_entries) {
|
||||
unsigned int score;
|
||||
|
||||
score = ppat->match(ppat->entries[i].value, value);
|
||||
if (score > best_score) {
|
||||
entry = &ppat->entries[i];
|
||||
if (score == INTEL_PPAT_PERFECT_MATCH) {
|
||||
kref_get(&entry->ref);
|
||||
return entry;
|
||||
}
|
||||
best_score = score;
|
||||
}
|
||||
scanned++;
|
||||
}
|
||||
|
||||
if (scanned == ppat->max_entries) {
|
||||
if (!best_score)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
|
||||
kref_get(&entry->ref);
|
||||
return entry;
|
||||
}
|
||||
|
||||
i = find_first_zero_bit(ppat->used, ppat->max_entries);
|
||||
entry = __alloc_ppat_entry(ppat, i, value);
|
||||
ppat->update_hw(i915);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void release_ppat(struct kref *kref)
|
||||
{
|
||||
struct intel_ppat_entry *entry =
|
||||
container_of(kref, struct intel_ppat_entry, ref);
|
||||
struct drm_i915_private *i915 = entry->ppat->i915;
|
||||
|
||||
__free_ppat_entry(entry);
|
||||
entry->ppat->update_hw(i915);
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_ppat_put - put back the PPAT entry got from intel_ppat_get()
|
||||
* @entry: an intel PPAT entry
|
||||
*
|
||||
* Put back the PPAT entry got from intel_ppat_get(). If the PPAT index of the
|
||||
* entry is dynamically allocated, its reference count will be decreased. Once
|
||||
* the reference count becomes into zero, the PPAT index becomes free again.
|
||||
*/
|
||||
void intel_ppat_put(const struct intel_ppat_entry *entry)
|
||||
{
|
||||
struct intel_ppat *ppat = entry->ppat;
|
||||
unsigned int index = entry - ppat->entries;
|
||||
|
||||
GEM_BUG_ON(!ppat->max_entries);
|
||||
|
||||
kref_put(&ppat->entries[index].ref, release_ppat);
|
||||
}
|
||||
|
||||
static void cnl_private_pat_update_hw(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct intel_ppat *ppat = &dev_priv->ppat;
|
||||
int i;
|
||||
|
||||
for_each_set_bit(i, ppat->dirty, ppat->max_entries) {
|
||||
I915_WRITE(GEN10_PAT_INDEX(i), ppat->entries[i].value);
|
||||
clear_bit(i, ppat->dirty);
|
||||
}
|
||||
}
|
||||
|
||||
static void bdw_private_pat_update_hw(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct intel_ppat *ppat = &dev_priv->ppat;
|
||||
u64 pat = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ppat->max_entries; i++)
|
||||
pat |= GEN8_PPAT(i, ppat->entries[i].value);
|
||||
|
||||
bitmap_clear(ppat->dirty, 0, ppat->max_entries);
|
||||
|
||||
I915_WRITE(GEN8_PRIVATE_PAT_LO, lower_32_bits(pat));
|
||||
I915_WRITE(GEN8_PRIVATE_PAT_HI, upper_32_bits(pat));
|
||||
}
|
||||
|
||||
static unsigned int bdw_private_pat_match(u8 src, u8 dst)
|
||||
{
|
||||
unsigned int score = 0;
|
||||
enum {
|
||||
AGE_MATCH = BIT(0),
|
||||
TC_MATCH = BIT(1),
|
||||
CA_MATCH = BIT(2),
|
||||
};
|
||||
|
||||
/* Cache attribute has to be matched. */
|
||||
if (GEN8_PPAT_GET_CA(src) != GEN8_PPAT_GET_CA(dst))
|
||||
return 0;
|
||||
|
||||
score |= CA_MATCH;
|
||||
|
||||
if (GEN8_PPAT_GET_TC(src) == GEN8_PPAT_GET_TC(dst))
|
||||
score |= TC_MATCH;
|
||||
|
||||
if (GEN8_PPAT_GET_AGE(src) == GEN8_PPAT_GET_AGE(dst))
|
||||
score |= AGE_MATCH;
|
||||
|
||||
if (score == (AGE_MATCH | TC_MATCH | CA_MATCH))
|
||||
return INTEL_PPAT_PERFECT_MATCH;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
static unsigned int chv_private_pat_match(u8 src, u8 dst)
|
||||
{
|
||||
return (CHV_PPAT_GET_SNOOP(src) == CHV_PPAT_GET_SNOOP(dst)) ?
|
||||
INTEL_PPAT_PERFECT_MATCH : 0;
|
||||
}
|
||||
|
||||
static void cnl_setup_private_ppat(struct intel_ppat *ppat)
|
||||
{
|
||||
ppat->max_entries = 8;
|
||||
ppat->update_hw = cnl_private_pat_update_hw;
|
||||
ppat->match = bdw_private_pat_match;
|
||||
ppat->clear_value = GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3);
|
||||
|
||||
/* XXX: spec is unclear if this is still needed for CNL+ */
|
||||
if (!USES_PPGTT(dev_priv)) {
|
||||
I915_WRITE(GEN10_PAT_INDEX(0), GEN8_PPAT_UC);
|
||||
if (!USES_PPGTT(ppat->i915)) {
|
||||
__alloc_ppat_entry(ppat, 0, GEN8_PPAT_UC);
|
||||
return;
|
||||
}
|
||||
|
||||
I915_WRITE(GEN10_PAT_INDEX(0), GEN8_PPAT_WB | GEN8_PPAT_LLC);
|
||||
I915_WRITE(GEN10_PAT_INDEX(1), GEN8_PPAT_WC | GEN8_PPAT_LLCELLC);
|
||||
I915_WRITE(GEN10_PAT_INDEX(2), GEN8_PPAT_WT | GEN8_PPAT_LLCELLC);
|
||||
I915_WRITE(GEN10_PAT_INDEX(3), GEN8_PPAT_UC);
|
||||
I915_WRITE(GEN10_PAT_INDEX(4), GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0));
|
||||
I915_WRITE(GEN10_PAT_INDEX(5), GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1));
|
||||
I915_WRITE(GEN10_PAT_INDEX(6), GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2));
|
||||
I915_WRITE(GEN10_PAT_INDEX(7), GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
|
||||
__alloc_ppat_entry(ppat, 0, GEN8_PPAT_WB | GEN8_PPAT_LLC);
|
||||
__alloc_ppat_entry(ppat, 1, GEN8_PPAT_WC | GEN8_PPAT_LLCELLC);
|
||||
__alloc_ppat_entry(ppat, 2, GEN8_PPAT_WT | GEN8_PPAT_LLCELLC);
|
||||
__alloc_ppat_entry(ppat, 3, GEN8_PPAT_UC);
|
||||
__alloc_ppat_entry(ppat, 4, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0));
|
||||
__alloc_ppat_entry(ppat, 5, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1));
|
||||
__alloc_ppat_entry(ppat, 6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2));
|
||||
__alloc_ppat_entry(ppat, 7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
|
||||
}
|
||||
|
||||
/* The GGTT and PPGTT need a private PPAT setup in order to handle cacheability
|
||||
* bits. When using advanced contexts each context stores its own PAT, but
|
||||
* writing this data shouldn't be harmful even in those cases. */
|
||||
static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
|
||||
static void bdw_setup_private_ppat(struct intel_ppat *ppat)
|
||||
{
|
||||
u64 pat;
|
||||
ppat->max_entries = 8;
|
||||
ppat->update_hw = bdw_private_pat_update_hw;
|
||||
ppat->match = bdw_private_pat_match;
|
||||
ppat->clear_value = GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3);
|
||||
|
||||
pat = GEN8_PPAT(0, GEN8_PPAT_WB | GEN8_PPAT_LLC) | /* for normal objects, no eLLC */
|
||||
GEN8_PPAT(1, GEN8_PPAT_WC | GEN8_PPAT_LLCELLC) | /* for something pointing to ptes? */
|
||||
GEN8_PPAT(2, GEN8_PPAT_WT | GEN8_PPAT_LLCELLC) | /* for scanout with eLLC */
|
||||
GEN8_PPAT(3, GEN8_PPAT_UC) | /* Uncached objects, mostly for scanout */
|
||||
GEN8_PPAT(4, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0)) |
|
||||
GEN8_PPAT(5, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1)) |
|
||||
GEN8_PPAT(6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2)) |
|
||||
GEN8_PPAT(7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
|
||||
|
||||
if (!USES_PPGTT(dev_priv))
|
||||
if (!USES_PPGTT(ppat->i915)) {
|
||||
/* Spec: "For GGTT, there is NO pat_sel[2:0] from the entry,
|
||||
* so RTL will always use the value corresponding to
|
||||
* pat_sel = 000".
|
||||
@ -2864,17 +3033,26 @@ static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
|
||||
* So we can still hold onto all our assumptions wrt cpu
|
||||
* clflushing on LLC machines.
|
||||
*/
|
||||
pat = GEN8_PPAT(0, GEN8_PPAT_UC);
|
||||
__alloc_ppat_entry(ppat, 0, GEN8_PPAT_UC);
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX: spec defines this as 2 distinct registers. It's unclear if a 64b
|
||||
* write would work. */
|
||||
I915_WRITE(GEN8_PRIVATE_PAT_LO, pat);
|
||||
I915_WRITE(GEN8_PRIVATE_PAT_HI, pat >> 32);
|
||||
__alloc_ppat_entry(ppat, 0, GEN8_PPAT_WB | GEN8_PPAT_LLC); /* for normal objects, no eLLC */
|
||||
__alloc_ppat_entry(ppat, 1, GEN8_PPAT_WC | GEN8_PPAT_LLCELLC); /* for something pointing to ptes? */
|
||||
__alloc_ppat_entry(ppat, 2, GEN8_PPAT_WT | GEN8_PPAT_LLCELLC); /* for scanout with eLLC */
|
||||
__alloc_ppat_entry(ppat, 3, GEN8_PPAT_UC); /* Uncached objects, mostly for scanout */
|
||||
__alloc_ppat_entry(ppat, 4, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0));
|
||||
__alloc_ppat_entry(ppat, 5, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1));
|
||||
__alloc_ppat_entry(ppat, 6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2));
|
||||
__alloc_ppat_entry(ppat, 7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
|
||||
}
|
||||
|
||||
static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
|
||||
static void chv_setup_private_ppat(struct intel_ppat *ppat)
|
||||
{
|
||||
u64 pat;
|
||||
ppat->max_entries = 8;
|
||||
ppat->update_hw = bdw_private_pat_update_hw;
|
||||
ppat->match = chv_private_pat_match;
|
||||
ppat->clear_value = CHV_PPAT_SNOOP;
|
||||
|
||||
/*
|
||||
* Map WB on BDW to snooped on CHV.
|
||||
@ -2894,17 +3072,15 @@ static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
|
||||
* Which means we must set the snoop bit in PAT entry 0
|
||||
* in order to keep the global status page working.
|
||||
*/
|
||||
pat = GEN8_PPAT(0, CHV_PPAT_SNOOP) |
|
||||
GEN8_PPAT(1, 0) |
|
||||
GEN8_PPAT(2, 0) |
|
||||
GEN8_PPAT(3, 0) |
|
||||
GEN8_PPAT(4, CHV_PPAT_SNOOP) |
|
||||
GEN8_PPAT(5, CHV_PPAT_SNOOP) |
|
||||
GEN8_PPAT(6, CHV_PPAT_SNOOP) |
|
||||
GEN8_PPAT(7, CHV_PPAT_SNOOP);
|
||||
|
||||
I915_WRITE(GEN8_PRIVATE_PAT_LO, pat);
|
||||
I915_WRITE(GEN8_PRIVATE_PAT_HI, pat >> 32);
|
||||
__alloc_ppat_entry(ppat, 0, CHV_PPAT_SNOOP);
|
||||
__alloc_ppat_entry(ppat, 1, 0);
|
||||
__alloc_ppat_entry(ppat, 2, 0);
|
||||
__alloc_ppat_entry(ppat, 3, 0);
|
||||
__alloc_ppat_entry(ppat, 4, CHV_PPAT_SNOOP);
|
||||
__alloc_ppat_entry(ppat, 5, CHV_PPAT_SNOOP);
|
||||
__alloc_ppat_entry(ppat, 6, CHV_PPAT_SNOOP);
|
||||
__alloc_ppat_entry(ppat, 7, CHV_PPAT_SNOOP);
|
||||
}
|
||||
|
||||
static void gen6_gmch_remove(struct i915_address_space *vm)
|
||||
@ -2915,6 +3091,31 @@ static void gen6_gmch_remove(struct i915_address_space *vm)
|
||||
cleanup_scratch_page(vm);
|
||||
}
|
||||
|
||||
static void setup_private_pat(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct intel_ppat *ppat = &dev_priv->ppat;
|
||||
int i;
|
||||
|
||||
ppat->i915 = dev_priv;
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 10)
|
||||
cnl_setup_private_ppat(ppat);
|
||||
else if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv))
|
||||
chv_setup_private_ppat(ppat);
|
||||
else
|
||||
bdw_setup_private_ppat(ppat);
|
||||
|
||||
GEM_BUG_ON(ppat->max_entries > INTEL_MAX_PPAT_ENTRIES);
|
||||
|
||||
for_each_clear_bit(i, ppat->used, ppat->max_entries) {
|
||||
ppat->entries[i].value = ppat->clear_value;
|
||||
ppat->entries[i].ppat = ppat;
|
||||
set_bit(i, ppat->dirty);
|
||||
}
|
||||
|
||||
ppat->update_hw(dev_priv);
|
||||
}
|
||||
|
||||
static int gen8_gmch_probe(struct i915_ggtt *ggtt)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ggtt->base.i915;
|
||||
@ -2947,14 +3148,6 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
|
||||
}
|
||||
|
||||
ggtt->base.total = (size / sizeof(gen8_pte_t)) << PAGE_SHIFT;
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 10)
|
||||
cnl_setup_private_ppat(dev_priv);
|
||||
else if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv))
|
||||
chv_setup_private_ppat(dev_priv);
|
||||
else
|
||||
bdw_setup_private_ppat(dev_priv);
|
||||
|
||||
ggtt->base.cleanup = gen6_gmch_remove;
|
||||
ggtt->base.bind_vma = ggtt_bind_vma;
|
||||
ggtt->base.unbind_vma = ggtt_unbind_vma;
|
||||
@ -2975,6 +3168,8 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
|
||||
|
||||
ggtt->invalidate = gen6_ggtt_invalidate;
|
||||
|
||||
setup_private_pat(dev_priv);
|
||||
|
||||
return ggtt_probe_common(ggtt, size);
|
||||
}
|
||||
|
||||
@ -3095,7 +3290,7 @@ int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv)
|
||||
* currently don't have any bits spare to pass in this upper
|
||||
* restriction!
|
||||
*/
|
||||
if (HAS_GUC(dev_priv) && i915.enable_guc_loading) {
|
||||
if (HAS_GUC(dev_priv) && i915_modparams.enable_guc_loading) {
|
||||
ggtt->base.total = min_t(u64, ggtt->base.total, GUC_GGTT_TOP);
|
||||
ggtt->mappable_end = min(ggtt->mappable_end, ggtt->base.total);
|
||||
}
|
||||
@ -3232,13 +3427,10 @@ void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv)
|
||||
ggtt->base.closed = false;
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 8) {
|
||||
if (INTEL_GEN(dev_priv) >= 10)
|
||||
cnl_setup_private_ppat(dev_priv);
|
||||
else if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv))
|
||||
chv_setup_private_ppat(dev_priv);
|
||||
else
|
||||
bdw_setup_private_ppat(dev_priv);
|
||||
struct intel_ppat *ppat = &dev_priv->ppat;
|
||||
|
||||
bitmap_set(ppat->dirty, 0, ppat->max_entries);
|
||||
dev_priv->ppat.update_hw(dev_priv);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -126,13 +126,13 @@ typedef u64 gen8_ppgtt_pml4e_t;
|
||||
* tables */
|
||||
#define GEN8_PDPE_MASK 0x1ff
|
||||
|
||||
#define PPAT_UNCACHED_INDEX (_PAGE_PWT | _PAGE_PCD)
|
||||
#define PPAT_CACHED_PDE_INDEX 0 /* WB LLC */
|
||||
#define PPAT_CACHED_INDEX _PAGE_PAT /* WB LLCeLLC */
|
||||
#define PPAT_DISPLAY_ELLC_INDEX _PAGE_PCD /* WT eLLC */
|
||||
#define PPAT_UNCACHED (_PAGE_PWT | _PAGE_PCD)
|
||||
#define PPAT_CACHED_PDE 0 /* WB LLC */
|
||||
#define PPAT_CACHED _PAGE_PAT /* WB LLCeLLC */
|
||||
#define PPAT_DISPLAY_ELLC _PAGE_PCD /* WT eLLC */
|
||||
|
||||
#define CHV_PPAT_SNOOP (1<<6)
|
||||
#define GEN8_PPAT_AGE(x) (x<<4)
|
||||
#define GEN8_PPAT_AGE(x) ((x)<<4)
|
||||
#define GEN8_PPAT_LLCeLLC (3<<2)
|
||||
#define GEN8_PPAT_LLCELLC (2<<2)
|
||||
#define GEN8_PPAT_LLC (1<<2)
|
||||
@ -143,6 +143,11 @@ typedef u64 gen8_ppgtt_pml4e_t;
|
||||
#define GEN8_PPAT_ELLC_OVERRIDE (0<<2)
|
||||
#define GEN8_PPAT(i, x) ((u64)(x) << ((i) * 8))
|
||||
|
||||
#define GEN8_PPAT_GET_CA(x) ((x) & 3)
|
||||
#define GEN8_PPAT_GET_TC(x) ((x) & (3 << 2))
|
||||
#define GEN8_PPAT_GET_AGE(x) ((x) & (3 << 4))
|
||||
#define CHV_PPAT_GET_SNOOP(x) ((x) & (1 << 6))
|
||||
|
||||
struct sg_table;
|
||||
|
||||
struct intel_rotation_info {
|
||||
@ -536,6 +541,37 @@ i915_vm_to_ggtt(struct i915_address_space *vm)
|
||||
return container_of(vm, struct i915_ggtt, base);
|
||||
}
|
||||
|
||||
#define INTEL_MAX_PPAT_ENTRIES 8
|
||||
#define INTEL_PPAT_PERFECT_MATCH (~0U)
|
||||
|
||||
struct intel_ppat;
|
||||
|
||||
struct intel_ppat_entry {
|
||||
struct intel_ppat *ppat;
|
||||
struct kref ref;
|
||||
u8 value;
|
||||
};
|
||||
|
||||
struct intel_ppat {
|
||||
struct intel_ppat_entry entries[INTEL_MAX_PPAT_ENTRIES];
|
||||
DECLARE_BITMAP(used, INTEL_MAX_PPAT_ENTRIES);
|
||||
DECLARE_BITMAP(dirty, INTEL_MAX_PPAT_ENTRIES);
|
||||
unsigned int max_entries;
|
||||
u8 clear_value;
|
||||
/*
|
||||
* Return a score to show how two PPAT values match,
|
||||
* a INTEL_PPAT_PERFECT_MATCH indicates a perfect match
|
||||
*/
|
||||
unsigned int (*match)(u8 src, u8 dst);
|
||||
void (*update_hw)(struct drm_i915_private *i915);
|
||||
|
||||
struct drm_i915_private *i915;
|
||||
};
|
||||
|
||||
const struct intel_ppat_entry *
|
||||
intel_ppat_get(struct drm_i915_private *i915, u8 value);
|
||||
void intel_ppat_put(const struct intel_ppat_entry *entry);
|
||||
|
||||
int i915_gem_init_aliasing_ppgtt(struct drm_i915_private *i915);
|
||||
void i915_gem_fini_aliasing_ppgtt(struct drm_i915_private *i915);
|
||||
|
||||
|
@ -1021,12 +1021,28 @@ static bool busywait_stop(unsigned long timeout, unsigned int cpu)
|
||||
return this_cpu != cpu;
|
||||
}
|
||||
|
||||
bool __i915_spin_request(const struct drm_i915_gem_request *req,
|
||||
u32 seqno, int state, unsigned long timeout_us)
|
||||
static bool __i915_spin_request(const struct drm_i915_gem_request *req,
|
||||
u32 seqno, int state, unsigned long timeout_us)
|
||||
{
|
||||
struct intel_engine_cs *engine = req->engine;
|
||||
unsigned int irq, cpu;
|
||||
|
||||
GEM_BUG_ON(!seqno);
|
||||
|
||||
/*
|
||||
* Only wait for the request if we know it is likely to complete.
|
||||
*
|
||||
* We don't track the timestamps around requests, nor the average
|
||||
* request length, so we do not have a good indicator that this
|
||||
* request will complete within the timeout. What we do know is the
|
||||
* order in which requests are executed by the engine and so we can
|
||||
* tell if the request has started. If the request hasn't started yet,
|
||||
* it is a fair assumption that it will not complete within our
|
||||
* relatively short timeout.
|
||||
*/
|
||||
if (!i915_seqno_passed(intel_engine_get_seqno(engine), seqno - 1))
|
||||
return false;
|
||||
|
||||
/* When waiting for high frequency requests, e.g. during synchronous
|
||||
* rendering split between the CPU and GPU, the finite amount of time
|
||||
* required to set up the irq and wait upon it limits the response
|
||||
@ -1040,12 +1056,8 @@ bool __i915_spin_request(const struct drm_i915_gem_request *req,
|
||||
irq = atomic_read(&engine->irq_count);
|
||||
timeout_us += local_clock_us(&cpu);
|
||||
do {
|
||||
if (seqno != i915_gem_request_global_seqno(req))
|
||||
break;
|
||||
|
||||
if (i915_seqno_passed(intel_engine_get_seqno(req->engine),
|
||||
seqno))
|
||||
return true;
|
||||
if (i915_seqno_passed(intel_engine_get_seqno(engine), seqno))
|
||||
return seqno == i915_gem_request_global_seqno(req);
|
||||
|
||||
/* Seqno are meant to be ordered *before* the interrupt. If
|
||||
* we see an interrupt without a corresponding seqno advance,
|
||||
@ -1156,7 +1168,7 @@ restart:
|
||||
GEM_BUG_ON(!i915_sw_fence_signaled(&req->submit));
|
||||
|
||||
/* Optimistic short spin before touching IRQs */
|
||||
if (i915_spin_request(req, state, 5))
|
||||
if (__i915_spin_request(req, wait.seqno, state, 5))
|
||||
goto complete;
|
||||
|
||||
set_current_state(state);
|
||||
@ -1213,7 +1225,7 @@ wakeup:
|
||||
continue;
|
||||
|
||||
/* Only spin if we know the GPU is processing this request */
|
||||
if (i915_spin_request(req, state, 2))
|
||||
if (__i915_spin_request(req, wait.seqno, state, 2))
|
||||
break;
|
||||
|
||||
if (!intel_wait_check_request(&wait, req)) {
|
||||
|
@ -312,26 +312,6 @@ static inline bool i915_seqno_passed(u32 seq1, u32 seq2)
|
||||
return (s32)(seq1 - seq2) >= 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
__i915_gem_request_started(const struct drm_i915_gem_request *req, u32 seqno)
|
||||
{
|
||||
GEM_BUG_ON(!seqno);
|
||||
return i915_seqno_passed(intel_engine_get_seqno(req->engine),
|
||||
seqno - 1);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
i915_gem_request_started(const struct drm_i915_gem_request *req)
|
||||
{
|
||||
u32 seqno;
|
||||
|
||||
seqno = i915_gem_request_global_seqno(req);
|
||||
if (!seqno)
|
||||
return false;
|
||||
|
||||
return __i915_gem_request_started(req, seqno);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
__i915_gem_request_completed(const struct drm_i915_gem_request *req, u32 seqno)
|
||||
{
|
||||
@ -352,21 +332,6 @@ i915_gem_request_completed(const struct drm_i915_gem_request *req)
|
||||
return __i915_gem_request_completed(req, seqno);
|
||||
}
|
||||
|
||||
bool __i915_spin_request(const struct drm_i915_gem_request *request,
|
||||
u32 seqno, int state, unsigned long timeout_us);
|
||||
static inline bool i915_spin_request(const struct drm_i915_gem_request *request,
|
||||
int state, unsigned long timeout_us)
|
||||
{
|
||||
u32 seqno;
|
||||
|
||||
seqno = i915_gem_request_global_seqno(request);
|
||||
if (!seqno)
|
||||
return 0;
|
||||
|
||||
return (__i915_gem_request_started(request, seqno) &&
|
||||
__i915_spin_request(request, seqno, state, timeout_us));
|
||||
}
|
||||
|
||||
/* We treat requests as fences. This is not be to confused with our
|
||||
* "fence registers" but pipeline synchronisation objects ala GL_ARB_sync.
|
||||
* We use the fences to synchronize access from the CPU with activity on the
|
||||
|
@ -399,64 +399,42 @@ struct get_pages_work {
|
||||
struct task_struct *task;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_SWIOTLB)
|
||||
#define swiotlb_active() swiotlb_nr_tbl()
|
||||
#else
|
||||
#define swiotlb_active() 0
|
||||
#endif
|
||||
|
||||
static int
|
||||
st_set_pages(struct sg_table **st, struct page **pvec, int num_pages)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int ret, n;
|
||||
|
||||
*st = kmalloc(sizeof(**st), GFP_KERNEL);
|
||||
if (*st == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (swiotlb_active()) {
|
||||
ret = sg_alloc_table(*st, num_pages, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for_each_sg((*st)->sgl, sg, num_pages, n)
|
||||
sg_set_page(sg, pvec[n], PAGE_SIZE, 0);
|
||||
} else {
|
||||
ret = sg_alloc_table_from_pages(*st, pvec, num_pages,
|
||||
0, num_pages << PAGE_SHIFT,
|
||||
GFP_KERNEL);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(*st);
|
||||
*st = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct sg_table *
|
||||
__i915_gem_userptr_set_pages(struct drm_i915_gem_object *obj,
|
||||
struct page **pvec, int num_pages)
|
||||
__i915_gem_userptr_alloc_pages(struct drm_i915_gem_object *obj,
|
||||
struct page **pvec, int num_pages)
|
||||
{
|
||||
struct sg_table *pages;
|
||||
unsigned int max_segment = i915_sg_segment_size();
|
||||
struct sg_table *st;
|
||||
int ret;
|
||||
|
||||
ret = st_set_pages(&pages, pvec, num_pages);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
st = kmalloc(sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = i915_gem_gtt_prepare_pages(obj, pages);
|
||||
alloc_table:
|
||||
ret = __sg_alloc_table_from_pages(st, pvec, num_pages,
|
||||
0, num_pages << PAGE_SHIFT,
|
||||
max_segment,
|
||||
GFP_KERNEL);
|
||||
if (ret) {
|
||||
sg_free_table(pages);
|
||||
kfree(pages);
|
||||
kfree(st);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return pages;
|
||||
ret = i915_gem_gtt_prepare_pages(obj, st);
|
||||
if (ret) {
|
||||
sg_free_table(st);
|
||||
|
||||
if (max_segment > PAGE_SIZE) {
|
||||
max_segment = PAGE_SIZE;
|
||||
goto alloc_table;
|
||||
}
|
||||
|
||||
kfree(st);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -540,7 +518,8 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
|
||||
struct sg_table *pages = ERR_PTR(ret);
|
||||
|
||||
if (pinned == npages) {
|
||||
pages = __i915_gem_userptr_set_pages(obj, pvec, npages);
|
||||
pages = __i915_gem_userptr_alloc_pages(obj, pvec,
|
||||
npages);
|
||||
if (!IS_ERR(pages)) {
|
||||
__i915_gem_object_set_pages(obj, pages);
|
||||
pinned = 0;
|
||||
@ -661,7 +640,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
|
||||
pages = __i915_gem_userptr_get_pages_schedule(obj);
|
||||
active = pages == ERR_PTR(-EAGAIN);
|
||||
} else {
|
||||
pages = __i915_gem_userptr_set_pages(obj, pvec, num_pages);
|
||||
pages = __i915_gem_userptr_alloc_pages(obj, pvec, num_pages);
|
||||
active = !IS_ERR(pages);
|
||||
}
|
||||
if (active)
|
||||
@ -834,7 +813,9 @@ int i915_gem_init_userptr(struct drm_i915_private *dev_priv)
|
||||
hash_init(dev_priv->mm_structs);
|
||||
|
||||
dev_priv->mm.userptr_wq =
|
||||
alloc_workqueue("i915-userptr-acquire", WQ_HIGHPRI, 0);
|
||||
alloc_workqueue("i915-userptr-acquire",
|
||||
WQ_HIGHPRI | WQ_MEM_RECLAIM,
|
||||
0);
|
||||
if (!dev_priv->mm.userptr_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -396,6 +396,8 @@ static void error_print_context(struct drm_i915_error_state_buf *m,
|
||||
static void error_print_engine(struct drm_i915_error_state_buf *m,
|
||||
const struct drm_i915_error_engine *ee)
|
||||
{
|
||||
int n;
|
||||
|
||||
err_printf(m, "%s command stream:\n", engine_str(ee->engine_id));
|
||||
err_printf(m, " START: 0x%08x\n", ee->start);
|
||||
err_printf(m, " HEAD: 0x%08x [0x%08x]\n", ee->head, ee->rq_head);
|
||||
@ -465,8 +467,11 @@ static void error_print_engine(struct drm_i915_error_state_buf *m,
|
||||
jiffies_to_msecs(jiffies - ee->hangcheck_timestamp));
|
||||
err_printf(m, " engine reset count: %u\n", ee->reset_count);
|
||||
|
||||
error_print_request(m, " ELSP[0]: ", &ee->execlist[0]);
|
||||
error_print_request(m, " ELSP[1]: ", &ee->execlist[1]);
|
||||
for (n = 0; n < ee->num_ports; n++) {
|
||||
err_printf(m, " ELSP[%d]:", n);
|
||||
error_print_request(m, " ", &ee->execlist[n]);
|
||||
}
|
||||
|
||||
error_print_context(m, " Active context: ", &ee->context);
|
||||
}
|
||||
|
||||
@ -567,7 +572,7 @@ static __always_inline void err_print_param(struct drm_i915_error_state_buf *m,
|
||||
static void err_print_params(struct drm_i915_error_state_buf *m,
|
||||
const struct i915_params *p)
|
||||
{
|
||||
#define PRINT(T, x) err_print_param(m, #x, #T, &p->x);
|
||||
#define PRINT(T, x, ...) err_print_param(m, #x, #T, &p->x);
|
||||
I915_PARAMS_FOR_EACH(PRINT);
|
||||
#undef PRINT
|
||||
}
|
||||
@ -861,7 +866,7 @@ void __i915_gpu_state_free(struct kref *error_ref)
|
||||
kfree(error->overlay);
|
||||
kfree(error->display);
|
||||
|
||||
#define FREE(T, x) free_param(#T, &error->params.x);
|
||||
#define FREE(T, x, ...) free_param(#T, &error->params.x);
|
||||
I915_PARAMS_FOR_EACH(FREE);
|
||||
#undef FREE
|
||||
|
||||
@ -1327,17 +1332,19 @@ static void engine_record_requests(struct intel_engine_cs *engine,
|
||||
static void error_record_engine_execlists(struct intel_engine_cs *engine,
|
||||
struct drm_i915_error_engine *ee)
|
||||
{
|
||||
const struct execlist_port *port = engine->execlist_port;
|
||||
const struct intel_engine_execlists * const execlists = &engine->execlists;
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++) {
|
||||
struct drm_i915_gem_request *rq = port_request(&port[n]);
|
||||
for (n = 0; n < execlists_num_ports(execlists); n++) {
|
||||
struct drm_i915_gem_request *rq = port_request(&execlists->port[n]);
|
||||
|
||||
if (!rq)
|
||||
break;
|
||||
|
||||
record_request(rq, &ee->execlist[n]);
|
||||
}
|
||||
|
||||
ee->num_ports = n;
|
||||
}
|
||||
|
||||
static void record_context(struct drm_i915_error_context *e,
|
||||
@ -1554,7 +1561,7 @@ static void i915_gem_capture_guc_log_buffer(struct drm_i915_private *dev_priv,
|
||||
struct i915_gpu_state *error)
|
||||
{
|
||||
/* Capturing log buf contents won't be useful if logging was disabled */
|
||||
if (!dev_priv->guc.log.vma || (i915.guc_log_level < 0))
|
||||
if (!dev_priv->guc.log.vma || (i915_modparams.guc_log_level < 0))
|
||||
return;
|
||||
|
||||
error->guc_log = i915_error_object_create(dev_priv,
|
||||
@ -1696,8 +1703,8 @@ static int capture(void *data)
|
||||
ktime_to_timeval(ktime_sub(ktime_get(),
|
||||
error->i915->gt.last_init_time));
|
||||
|
||||
error->params = i915;
|
||||
#define DUP(T, x) dup_param(#T, &error->params.x);
|
||||
error->params = i915_modparams;
|
||||
#define DUP(T, x, ...) dup_param(#T, &error->params.x);
|
||||
I915_PARAMS_FOR_EACH(DUP);
|
||||
#undef DUP
|
||||
|
||||
@ -1751,7 +1758,7 @@ void i915_capture_error_state(struct drm_i915_private *dev_priv,
|
||||
struct i915_gpu_state *error;
|
||||
unsigned long flags;
|
||||
|
||||
if (!i915.error_capture)
|
||||
if (!i915_modparams.error_capture)
|
||||
return;
|
||||
|
||||
if (READ_ONCE(dev_priv->gpu_error.first_error))
|
||||
|
@ -192,13 +192,12 @@ static int __create_doorbell(struct i915_guc_client *client)
|
||||
|
||||
doorbell = __get_doorbell(client);
|
||||
doorbell->db_status = GUC_DOORBELL_ENABLED;
|
||||
doorbell->cookie = client->doorbell_cookie;
|
||||
doorbell->cookie = 0;
|
||||
|
||||
err = __guc_allocate_doorbell(client->guc, client->stage_id);
|
||||
if (err) {
|
||||
if (err)
|
||||
doorbell->db_status = GUC_DOORBELL_DISABLED;
|
||||
doorbell->cookie = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -306,7 +305,7 @@ static void guc_proc_desc_init(struct intel_guc *guc,
|
||||
desc->db_base_addr = 0;
|
||||
|
||||
desc->stage_id = client->stage_id;
|
||||
desc->wq_size_bytes = client->wq_size;
|
||||
desc->wq_size_bytes = GUC_WQ_SIZE;
|
||||
desc->wq_status = WQ_STATUS_ACTIVE;
|
||||
desc->priority = client->priority;
|
||||
}
|
||||
@ -391,8 +390,8 @@ static void guc_stage_desc_init(struct intel_guc *guc,
|
||||
desc->db_trigger_cpu = (uintptr_t)__get_doorbell(client);
|
||||
desc->db_trigger_uk = gfx_addr + client->doorbell_offset;
|
||||
desc->process_desc = gfx_addr + client->proc_desc_offset;
|
||||
desc->wq_addr = gfx_addr + client->wq_offset;
|
||||
desc->wq_size = client->wq_size;
|
||||
desc->wq_addr = gfx_addr + GUC_DB_SIZE;
|
||||
desc->wq_size = GUC_WQ_SIZE;
|
||||
|
||||
desc->desc_private = (uintptr_t)client;
|
||||
}
|
||||
@ -406,82 +405,23 @@ static void guc_stage_desc_fini(struct intel_guc *guc,
|
||||
memset(desc, 0, sizeof(*desc));
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_guc_wq_reserve() - reserve space in the GuC's workqueue
|
||||
* @request: request associated with the commands
|
||||
*
|
||||
* Return: 0 if space is available
|
||||
* -EAGAIN if space is not currently available
|
||||
*
|
||||
* This function must be called (and must return 0) before a request
|
||||
* is submitted to the GuC via i915_guc_submit() below. Once a result
|
||||
* of 0 has been returned, it must be balanced by a corresponding
|
||||
* call to submit().
|
||||
*
|
||||
* Reservation allows the caller to determine in advance that space
|
||||
* will be available for the next submission before committing resources
|
||||
* to it, and helps avoid late failures with complicated recovery paths.
|
||||
*/
|
||||
int i915_guc_wq_reserve(struct drm_i915_gem_request *request)
|
||||
{
|
||||
const size_t wqi_size = sizeof(struct guc_wq_item);
|
||||
struct i915_guc_client *client = request->i915->guc.execbuf_client;
|
||||
struct guc_process_desc *desc = __get_process_desc(client);
|
||||
u32 freespace;
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(&client->wq_lock);
|
||||
freespace = CIRC_SPACE(client->wq_tail, desc->head, client->wq_size);
|
||||
freespace -= client->wq_rsvd;
|
||||
if (likely(freespace >= wqi_size)) {
|
||||
client->wq_rsvd += wqi_size;
|
||||
ret = 0;
|
||||
} else {
|
||||
client->no_wq_space++;
|
||||
ret = -EAGAIN;
|
||||
}
|
||||
spin_unlock_irq(&client->wq_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void guc_client_update_wq_rsvd(struct i915_guc_client *client, int size)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&client->wq_lock, flags);
|
||||
client->wq_rsvd += size;
|
||||
spin_unlock_irqrestore(&client->wq_lock, flags);
|
||||
}
|
||||
|
||||
void i915_guc_wq_unreserve(struct drm_i915_gem_request *request)
|
||||
{
|
||||
const int wqi_size = sizeof(struct guc_wq_item);
|
||||
struct i915_guc_client *client = request->i915->guc.execbuf_client;
|
||||
|
||||
GEM_BUG_ON(READ_ONCE(client->wq_rsvd) < wqi_size);
|
||||
guc_client_update_wq_rsvd(client, -wqi_size);
|
||||
}
|
||||
|
||||
/* Construct a Work Item and append it to the GuC's Work Queue */
|
||||
static void guc_wq_item_append(struct i915_guc_client *client,
|
||||
struct drm_i915_gem_request *rq)
|
||||
{
|
||||
/* wqi_len is in DWords, and does not include the one-word header */
|
||||
const size_t wqi_size = sizeof(struct guc_wq_item);
|
||||
const u32 wqi_len = wqi_size/sizeof(u32) - 1;
|
||||
const u32 wqi_len = wqi_size / sizeof(u32) - 1;
|
||||
struct intel_engine_cs *engine = rq->engine;
|
||||
struct i915_gem_context *ctx = rq->ctx;
|
||||
struct guc_process_desc *desc = __get_process_desc(client);
|
||||
struct guc_wq_item *wqi;
|
||||
u32 freespace, tail, wq_off;
|
||||
u32 ring_tail, wq_off;
|
||||
|
||||
/* Free space is guaranteed, see i915_guc_wq_reserve() above */
|
||||
freespace = CIRC_SPACE(client->wq_tail, desc->head, client->wq_size);
|
||||
GEM_BUG_ON(freespace < wqi_size);
|
||||
lockdep_assert_held(&client->wq_lock);
|
||||
|
||||
/* The GuC firmware wants the tail index in QWords, not bytes */
|
||||
tail = intel_ring_set_tail(rq->ring, rq->tail) >> 3;
|
||||
GEM_BUG_ON(tail > WQ_RING_TAIL_MAX);
|
||||
ring_tail = intel_ring_set_tail(rq->ring, rq->tail) / sizeof(u64);
|
||||
GEM_BUG_ON(ring_tail > WQ_RING_TAIL_MAX);
|
||||
|
||||
/* For now workqueue item is 4 DWs; workqueue buffer is 2 pages. So we
|
||||
* should not have the case where structure wqi is across page, neither
|
||||
@ -491,29 +431,29 @@ static void guc_wq_item_append(struct i915_guc_client *client,
|
||||
* workqueue buffer dw by dw.
|
||||
*/
|
||||
BUILD_BUG_ON(wqi_size != 16);
|
||||
GEM_BUG_ON(client->wq_rsvd < wqi_size);
|
||||
|
||||
/* postincrement WQ tail for next time */
|
||||
wq_off = client->wq_tail;
|
||||
/* Free space is guaranteed. */
|
||||
wq_off = READ_ONCE(desc->tail);
|
||||
GEM_BUG_ON(CIRC_SPACE(wq_off, READ_ONCE(desc->head),
|
||||
GUC_WQ_SIZE) < wqi_size);
|
||||
GEM_BUG_ON(wq_off & (wqi_size - 1));
|
||||
client->wq_tail += wqi_size;
|
||||
client->wq_tail &= client->wq_size - 1;
|
||||
client->wq_rsvd -= wqi_size;
|
||||
|
||||
/* WQ starts from the page after doorbell / process_desc */
|
||||
wqi = client->vaddr + wq_off + GUC_DB_SIZE;
|
||||
|
||||
/* Now fill in the 4-word work queue item */
|
||||
wqi->header = WQ_TYPE_INORDER |
|
||||
(wqi_len << WQ_LEN_SHIFT) |
|
||||
(engine->guc_id << WQ_TARGET_SHIFT) |
|
||||
WQ_NO_WCFLUSH_WAIT;
|
||||
(wqi_len << WQ_LEN_SHIFT) |
|
||||
(engine->guc_id << WQ_TARGET_SHIFT) |
|
||||
WQ_NO_WCFLUSH_WAIT;
|
||||
|
||||
/* The GuC wants only the low-order word of the context descriptor */
|
||||
wqi->context_desc = (u32)intel_lr_context_descriptor(rq->ctx, engine);
|
||||
wqi->context_desc = lower_32_bits(intel_lr_context_descriptor(ctx, engine));
|
||||
|
||||
wqi->submit_element_info = tail << WQ_RING_TAIL_SHIFT;
|
||||
wqi->submit_element_info = ring_tail << WQ_RING_TAIL_SHIFT;
|
||||
wqi->fence_id = rq->global_seqno;
|
||||
|
||||
/* Postincrement WQ tail for next time. */
|
||||
WRITE_ONCE(desc->tail, (wq_off + wqi_size) & (GUC_WQ_SIZE - 1));
|
||||
}
|
||||
|
||||
static void guc_reset_wq(struct i915_guc_client *client)
|
||||
@ -522,106 +462,64 @@ static void guc_reset_wq(struct i915_guc_client *client)
|
||||
|
||||
desc->head = 0;
|
||||
desc->tail = 0;
|
||||
|
||||
client->wq_tail = 0;
|
||||
}
|
||||
|
||||
static int guc_ring_doorbell(struct i915_guc_client *client)
|
||||
static void guc_ring_doorbell(struct i915_guc_client *client)
|
||||
{
|
||||
struct guc_process_desc *desc = __get_process_desc(client);
|
||||
union guc_doorbell_qw db_cmp, db_exc, db_ret;
|
||||
union guc_doorbell_qw *db;
|
||||
int attempt = 2, ret = -EAGAIN;
|
||||
struct guc_doorbell_info *db;
|
||||
u32 cookie;
|
||||
|
||||
/* Update the tail so it is visible to GuC */
|
||||
desc->tail = client->wq_tail;
|
||||
|
||||
/* current cookie */
|
||||
db_cmp.db_status = GUC_DOORBELL_ENABLED;
|
||||
db_cmp.cookie = client->doorbell_cookie;
|
||||
|
||||
/* cookie to be updated */
|
||||
db_exc.db_status = GUC_DOORBELL_ENABLED;
|
||||
db_exc.cookie = client->doorbell_cookie + 1;
|
||||
if (db_exc.cookie == 0)
|
||||
db_exc.cookie = 1;
|
||||
lockdep_assert_held(&client->wq_lock);
|
||||
|
||||
/* pointer of current doorbell cacheline */
|
||||
db = (union guc_doorbell_qw *)__get_doorbell(client);
|
||||
db = __get_doorbell(client);
|
||||
|
||||
while (attempt--) {
|
||||
/* lets ring the doorbell */
|
||||
db_ret.value_qw = atomic64_cmpxchg((atomic64_t *)db,
|
||||
db_cmp.value_qw, db_exc.value_qw);
|
||||
/* we're not expecting the doorbell cookie to change behind our back */
|
||||
cookie = READ_ONCE(db->cookie);
|
||||
WARN_ON_ONCE(xchg(&db->cookie, cookie + 1) != cookie);
|
||||
|
||||
/* if the exchange was successfully executed */
|
||||
if (db_ret.value_qw == db_cmp.value_qw) {
|
||||
/* db was successfully rung */
|
||||
client->doorbell_cookie = db_exc.cookie;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* XXX: doorbell was lost and need to acquire it again */
|
||||
if (db_ret.db_status == GUC_DOORBELL_DISABLED)
|
||||
break;
|
||||
|
||||
DRM_WARN("Cookie mismatch. Expected %d, found %d\n",
|
||||
db_cmp.cookie, db_ret.cookie);
|
||||
|
||||
/* update the cookie to newly read cookie from GuC */
|
||||
db_cmp.cookie = db_ret.cookie;
|
||||
db_exc.cookie = db_ret.cookie + 1;
|
||||
if (db_exc.cookie == 0)
|
||||
db_exc.cookie = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
/* XXX: doorbell was lost and need to acquire it again */
|
||||
GEM_BUG_ON(db->db_status != GUC_DOORBELL_ENABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* __i915_guc_submit() - Submit commands through GuC
|
||||
* @rq: request associated with the commands
|
||||
*
|
||||
* The caller must have already called i915_guc_wq_reserve() above with
|
||||
* a result of 0 (success), guaranteeing that there is space in the work
|
||||
* queue for the new request, so enqueuing the item cannot fail.
|
||||
*
|
||||
* Bad Things Will Happen if the caller violates this protocol e.g. calls
|
||||
* submit() when _reserve() says there's no space, or calls _submit()
|
||||
* a different number of times from (successful) calls to _reserve().
|
||||
* i915_guc_submit() - Submit commands through GuC
|
||||
* @engine: engine associated with the commands
|
||||
*
|
||||
* The only error here arises if the doorbell hardware isn't functioning
|
||||
* as expected, which really shouln't happen.
|
||||
*/
|
||||
static void __i915_guc_submit(struct drm_i915_gem_request *rq)
|
||||
static void i915_guc_submit(struct intel_engine_cs *engine)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = rq->i915;
|
||||
struct intel_engine_cs *engine = rq->engine;
|
||||
unsigned int engine_id = engine->id;
|
||||
struct intel_guc *guc = &rq->i915->guc;
|
||||
struct drm_i915_private *dev_priv = engine->i915;
|
||||
struct intel_guc *guc = &dev_priv->guc;
|
||||
struct i915_guc_client *client = guc->execbuf_client;
|
||||
unsigned long flags;
|
||||
int b_ret;
|
||||
struct intel_engine_execlists * const execlists = &engine->execlists;
|
||||
struct execlist_port *port = execlists->port;
|
||||
const unsigned int engine_id = engine->id;
|
||||
unsigned int n;
|
||||
|
||||
/* WA to flush out the pending GMADR writes to ring buffer. */
|
||||
if (i915_vma_is_map_and_fenceable(rq->ring->vma))
|
||||
POSTING_READ_FW(GUC_STATUS);
|
||||
for (n = 0; n < ARRAY_SIZE(execlists->port); n++) {
|
||||
struct drm_i915_gem_request *rq;
|
||||
unsigned int count;
|
||||
|
||||
spin_lock_irqsave(&client->wq_lock, flags);
|
||||
rq = port_unpack(&port[n], &count);
|
||||
if (rq && count == 0) {
|
||||
port_set(&port[n], port_pack(rq, ++count));
|
||||
|
||||
guc_wq_item_append(client, rq);
|
||||
b_ret = guc_ring_doorbell(client);
|
||||
if (i915_vma_is_map_and_fenceable(rq->ring->vma))
|
||||
POSTING_READ_FW(GUC_STATUS);
|
||||
|
||||
client->submissions[engine_id] += 1;
|
||||
spin_lock(&client->wq_lock);
|
||||
|
||||
spin_unlock_irqrestore(&client->wq_lock, flags);
|
||||
}
|
||||
guc_wq_item_append(client, rq);
|
||||
guc_ring_doorbell(client);
|
||||
|
||||
static void i915_guc_submit(struct drm_i915_gem_request *rq)
|
||||
{
|
||||
__i915_gem_request_submit(rq);
|
||||
__i915_guc_submit(rq);
|
||||
client->submissions[engine_id] += 1;
|
||||
|
||||
spin_unlock(&client->wq_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nested_enable_signaling(struct drm_i915_gem_request *rq)
|
||||
@ -655,27 +553,33 @@ static void port_assign(struct execlist_port *port,
|
||||
if (port_isset(port))
|
||||
i915_gem_request_put(port_request(port));
|
||||
|
||||
port_set(port, i915_gem_request_get(rq));
|
||||
port_set(port, port_pack(i915_gem_request_get(rq), port_count(port)));
|
||||
nested_enable_signaling(rq);
|
||||
}
|
||||
|
||||
static bool i915_guc_dequeue(struct intel_engine_cs *engine)
|
||||
static void i915_guc_dequeue(struct intel_engine_cs *engine)
|
||||
{
|
||||
struct execlist_port *port = engine->execlist_port;
|
||||
struct drm_i915_gem_request *last = port_request(port);
|
||||
struct rb_node *rb;
|
||||
struct intel_engine_execlists * const execlists = &engine->execlists;
|
||||
struct execlist_port *port = execlists->port;
|
||||
struct drm_i915_gem_request *last = NULL;
|
||||
const struct execlist_port * const last_port =
|
||||
&execlists->port[execlists->port_mask];
|
||||
bool submit = false;
|
||||
struct rb_node *rb;
|
||||
|
||||
if (port_isset(port))
|
||||
port++;
|
||||
|
||||
spin_lock_irq(&engine->timeline->lock);
|
||||
rb = engine->execlist_first;
|
||||
GEM_BUG_ON(rb_first(&engine->execlist_queue) != rb);
|
||||
rb = execlists->first;
|
||||
GEM_BUG_ON(rb_first(&execlists->queue) != rb);
|
||||
while (rb) {
|
||||
struct i915_priolist *p = rb_entry(rb, typeof(*p), node);
|
||||
struct drm_i915_gem_request *rq, *rn;
|
||||
|
||||
list_for_each_entry_safe(rq, rn, &p->requests, priotree.link) {
|
||||
if (last && rq->ctx != last->ctx) {
|
||||
if (port != engine->execlist_port) {
|
||||
if (port == last_port) {
|
||||
__list_del_many(&p->requests,
|
||||
&rq->priotree.link);
|
||||
goto done;
|
||||
@ -689,50 +593,48 @@ static bool i915_guc_dequeue(struct intel_engine_cs *engine)
|
||||
INIT_LIST_HEAD(&rq->priotree.link);
|
||||
rq->priotree.priority = INT_MAX;
|
||||
|
||||
i915_guc_submit(rq);
|
||||
trace_i915_gem_request_in(rq, port_index(port, engine));
|
||||
__i915_gem_request_submit(rq);
|
||||
trace_i915_gem_request_in(rq, port_index(port, execlists));
|
||||
last = rq;
|
||||
submit = true;
|
||||
}
|
||||
|
||||
rb = rb_next(rb);
|
||||
rb_erase(&p->node, &engine->execlist_queue);
|
||||
rb_erase(&p->node, &execlists->queue);
|
||||
INIT_LIST_HEAD(&p->requests);
|
||||
if (p->priority != I915_PRIORITY_NORMAL)
|
||||
kmem_cache_free(engine->i915->priorities, p);
|
||||
}
|
||||
done:
|
||||
engine->execlist_first = rb;
|
||||
if (submit)
|
||||
execlists->first = rb;
|
||||
if (submit) {
|
||||
port_assign(port, last);
|
||||
i915_guc_submit(engine);
|
||||
}
|
||||
spin_unlock_irq(&engine->timeline->lock);
|
||||
|
||||
return submit;
|
||||
}
|
||||
|
||||
static void i915_guc_irq_handler(unsigned long data)
|
||||
{
|
||||
struct intel_engine_cs *engine = (struct intel_engine_cs *)data;
|
||||
struct execlist_port *port = engine->execlist_port;
|
||||
struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
|
||||
struct intel_engine_execlists * const execlists = &engine->execlists;
|
||||
struct execlist_port *port = execlists->port;
|
||||
const struct execlist_port * const last_port =
|
||||
&execlists->port[execlists->port_mask];
|
||||
struct drm_i915_gem_request *rq;
|
||||
bool submit;
|
||||
|
||||
do {
|
||||
rq = port_request(&port[0]);
|
||||
while (rq && i915_gem_request_completed(rq)) {
|
||||
trace_i915_gem_request_out(rq);
|
||||
i915_gem_request_put(rq);
|
||||
|
||||
execlists_port_complete(execlists, port);
|
||||
|
||||
rq = port_request(&port[0]);
|
||||
while (rq && i915_gem_request_completed(rq)) {
|
||||
trace_i915_gem_request_out(rq);
|
||||
i915_gem_request_put(rq);
|
||||
}
|
||||
|
||||
port[0] = port[1];
|
||||
memset(&port[1], 0, sizeof(port[1]));
|
||||
|
||||
rq = port_request(&port[0]);
|
||||
}
|
||||
|
||||
submit = false;
|
||||
if (!port_count(&port[1]))
|
||||
submit = i915_guc_dequeue(engine);
|
||||
} while (submit);
|
||||
if (!port_isset(last_port))
|
||||
i915_guc_dequeue(engine);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -913,8 +815,6 @@ guc_client_alloc(struct drm_i915_private *dev_priv,
|
||||
client->engines = engines;
|
||||
client->priority = priority;
|
||||
client->doorbell_id = GUC_DOORBELL_INVALID;
|
||||
client->wq_offset = GUC_DB_SIZE;
|
||||
client->wq_size = GUC_WQ_SIZE;
|
||||
spin_lock_init(&client->wq_lock);
|
||||
|
||||
ret = ida_simple_get(&guc->stage_ids, 0, GUC_MAX_STAGE_DESCRIPTORS,
|
||||
@ -996,28 +896,39 @@ static void guc_client_free(struct i915_guc_client *client)
|
||||
kfree(client);
|
||||
}
|
||||
|
||||
static void guc_policy_init(struct guc_policy *policy)
|
||||
{
|
||||
policy->execution_quantum = POLICY_DEFAULT_EXECUTION_QUANTUM_US;
|
||||
policy->preemption_time = POLICY_DEFAULT_PREEMPTION_TIME_US;
|
||||
policy->fault_time = POLICY_DEFAULT_FAULT_TIME_US;
|
||||
policy->policy_flags = 0;
|
||||
}
|
||||
|
||||
static void guc_policies_init(struct guc_policies *policies)
|
||||
{
|
||||
struct guc_policy *policy;
|
||||
u32 p, i;
|
||||
|
||||
policies->dpc_promote_time = 500000;
|
||||
policies->dpc_promote_time = POLICY_DEFAULT_DPC_PROMOTE_TIME_US;
|
||||
policies->max_num_work_items = POLICY_MAX_NUM_WI;
|
||||
|
||||
for (p = 0; p < GUC_CLIENT_PRIORITY_NUM; p++) {
|
||||
for (i = GUC_RENDER_ENGINE; i < GUC_MAX_ENGINES_NUM; i++) {
|
||||
policy = &policies->policy[p][i];
|
||||
|
||||
policy->execution_quantum = 1000000;
|
||||
policy->preemption_time = 500000;
|
||||
policy->fault_time = 250000;
|
||||
policy->policy_flags = 0;
|
||||
guc_policy_init(policy);
|
||||
}
|
||||
}
|
||||
|
||||
policies->is_valid = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The first 80 dwords of the register state context, containing the
|
||||
* execlists and ppgtt registers.
|
||||
*/
|
||||
#define LR_HW_CONTEXT_SIZE (80 * sizeof(u32))
|
||||
|
||||
static int guc_ads_create(struct intel_guc *guc)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = guc_to_i915(guc);
|
||||
@ -1032,6 +943,8 @@ static int guc_ads_create(struct intel_guc *guc)
|
||||
} __packed *blob;
|
||||
struct intel_engine_cs *engine;
|
||||
enum intel_engine_id id;
|
||||
const u32 skipped_offset = LRC_HEADER_PAGES * PAGE_SIZE;
|
||||
const u32 skipped_size = LRC_PPHWSP_SZ * PAGE_SIZE + LR_HW_CONTEXT_SIZE;
|
||||
u32 base;
|
||||
|
||||
GEM_BUG_ON(guc->ads_vma);
|
||||
@ -1062,13 +975,20 @@ static int guc_ads_create(struct intel_guc *guc)
|
||||
* engines after a reset. Here we use the Render ring default
|
||||
* context, which must already exist and be pinned in the GGTT,
|
||||
* so its address won't change after we've told the GuC where
|
||||
* to find it.
|
||||
* to find it. Note that we have to skip our header (1 page),
|
||||
* because our GuC shared data is there.
|
||||
*/
|
||||
blob->ads.golden_context_lrca =
|
||||
dev_priv->engine[RCS]->status_page.ggtt_offset;
|
||||
guc_ggtt_offset(dev_priv->kernel_context->engine[RCS].state) + skipped_offset;
|
||||
|
||||
/*
|
||||
* The GuC expects us to exclude the portion of the context image that
|
||||
* it skips from the size it is to read. It starts reading from after
|
||||
* the execlist context (so skipping the first page [PPHWSP] and 80
|
||||
* dwords). Weird guc is weird.
|
||||
*/
|
||||
for_each_engine(engine, dev_priv, id)
|
||||
blob->ads.eng_state_size[engine->guc_id] = engine->context_size;
|
||||
blob->ads.eng_state_size[engine->guc_id] = engine->context_size - skipped_size;
|
||||
|
||||
base = guc_ggtt_offset(vma);
|
||||
blob->ads.scheduler_policies = base + ptr_offset(blob, policies);
|
||||
@ -1221,6 +1141,19 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv)
|
||||
enum intel_engine_id id;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* We're using GuC work items for submitting work through GuC. Since
|
||||
* we're coalescing multiple requests from a single context into a
|
||||
* single work item prior to assigning it to execlist_port, we can
|
||||
* never have more work items than the total number of ports (for all
|
||||
* engines). The GuC firmware is controlling the HEAD of work queue,
|
||||
* and it is guaranteed that it will remove the work item from the
|
||||
* queue before our request is completed.
|
||||
*/
|
||||
BUILD_BUG_ON(ARRAY_SIZE(engine->execlists.port) *
|
||||
sizeof(struct guc_wq_item) *
|
||||
I915_NUM_ENGINES > GUC_WQ_SIZE);
|
||||
|
||||
if (!client) {
|
||||
client = guc_client_alloc(dev_priv,
|
||||
INTEL_INFO(dev_priv)->ring_mask,
|
||||
@ -1248,24 +1181,15 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv)
|
||||
guc_interrupts_capture(dev_priv);
|
||||
|
||||
for_each_engine(engine, dev_priv, id) {
|
||||
const int wqi_size = sizeof(struct guc_wq_item);
|
||||
struct drm_i915_gem_request *rq;
|
||||
|
||||
struct intel_engine_execlists * const execlists = &engine->execlists;
|
||||
/* The tasklet was initialised by execlists, and may be in
|
||||
* a state of flux (across a reset) and so we just want to
|
||||
* take over the callback without changing any other state
|
||||
* in the tasklet.
|
||||
*/
|
||||
engine->irq_tasklet.func = i915_guc_irq_handler;
|
||||
execlists->irq_tasklet.func = i915_guc_irq_handler;
|
||||
clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
|
||||
|
||||
/* Replay the current set of previously submitted requests */
|
||||
spin_lock_irq(&engine->timeline->lock);
|
||||
list_for_each_entry(rq, &engine->timeline->requests, link) {
|
||||
guc_client_update_wq_rsvd(client, wqi_size);
|
||||
__i915_guc_submit(rq);
|
||||
}
|
||||
spin_unlock_irq(&engine->timeline->lock);
|
||||
tasklet_schedule(&execlists->irq_tasklet);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1310,7 +1234,7 @@ int intel_guc_suspend(struct drm_i915_private *dev_priv)
|
||||
/* any value greater than GUC_POWER_D0 */
|
||||
data[1] = GUC_POWER_D1;
|
||||
/* first page is shared data with GuC */
|
||||
data[2] = guc_ggtt_offset(ctx->engine[RCS].state);
|
||||
data[2] = guc_ggtt_offset(ctx->engine[RCS].state) + LRC_GUCSHR_PN * PAGE_SIZE;
|
||||
|
||||
return intel_guc_send(guc, data, ARRAY_SIZE(data));
|
||||
}
|
||||
@ -1328,7 +1252,7 @@ int intel_guc_resume(struct drm_i915_private *dev_priv)
|
||||
if (guc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS)
|
||||
return 0;
|
||||
|
||||
if (i915.guc_log_level >= 0)
|
||||
if (i915_modparams.guc_log_level >= 0)
|
||||
gen9_enable_guc_interrupts(dev_priv);
|
||||
|
||||
ctx = dev_priv->kernel_context;
|
||||
@ -1336,7 +1260,7 @@ int intel_guc_resume(struct drm_i915_private *dev_priv)
|
||||
data[0] = INTEL_GUC_ACTION_EXIT_S_STATE;
|
||||
data[1] = GUC_POWER_D0;
|
||||
/* first page is shared data with GuC */
|
||||
data[2] = guc_ggtt_offset(ctx->engine[RCS].state);
|
||||
data[2] = guc_ggtt_offset(ctx->engine[RCS].state) + LRC_GUCSHR_PN * PAGE_SIZE;
|
||||
|
||||
return intel_guc_send(guc, data, ARRAY_SIZE(data));
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
109
drivers/gpu/drm/i915/i915_oa_cflgt2.c
Normal file
109
drivers/gpu/drm/i915/i915_oa_cflgt2.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Autogenerated file by GPU Top : https://github.com/rib/gputop
|
||||
* DO NOT EDIT manually!
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* 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 (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_oa_cflgt2.h"
|
||||
|
||||
static const struct i915_oa_reg b_counter_config_test_oa[] = {
|
||||
{ _MMIO(0x2740), 0x00000000 },
|
||||
{ _MMIO(0x2744), 0x00800000 },
|
||||
{ _MMIO(0x2714), 0xf0800000 },
|
||||
{ _MMIO(0x2710), 0x00000000 },
|
||||
{ _MMIO(0x2724), 0xf0800000 },
|
||||
{ _MMIO(0x2720), 0x00000000 },
|
||||
{ _MMIO(0x2770), 0x00000004 },
|
||||
{ _MMIO(0x2774), 0x00000000 },
|
||||
{ _MMIO(0x2778), 0x00000003 },
|
||||
{ _MMIO(0x277c), 0x00000000 },
|
||||
{ _MMIO(0x2780), 0x00000007 },
|
||||
{ _MMIO(0x2784), 0x00000000 },
|
||||
{ _MMIO(0x2788), 0x00100002 },
|
||||
{ _MMIO(0x278c), 0x0000fff7 },
|
||||
{ _MMIO(0x2790), 0x00100002 },
|
||||
{ _MMIO(0x2794), 0x0000ffcf },
|
||||
{ _MMIO(0x2798), 0x00100082 },
|
||||
{ _MMIO(0x279c), 0x0000ffef },
|
||||
{ _MMIO(0x27a0), 0x001000c2 },
|
||||
{ _MMIO(0x27a4), 0x0000ffe7 },
|
||||
{ _MMIO(0x27a8), 0x00100001 },
|
||||
{ _MMIO(0x27ac), 0x0000ffe7 },
|
||||
};
|
||||
|
||||
static const struct i915_oa_reg flex_eu_config_test_oa[] = {
|
||||
};
|
||||
|
||||
static const struct i915_oa_reg mux_config_test_oa[] = {
|
||||
{ _MMIO(0x9840), 0x00000080 },
|
||||
{ _MMIO(0x9888), 0x11810000 },
|
||||
{ _MMIO(0x9888), 0x07810013 },
|
||||
{ _MMIO(0x9888), 0x1f810000 },
|
||||
{ _MMIO(0x9888), 0x1d810000 },
|
||||
{ _MMIO(0x9888), 0x1b930040 },
|
||||
{ _MMIO(0x9888), 0x07e54000 },
|
||||
{ _MMIO(0x9888), 0x1f908000 },
|
||||
{ _MMIO(0x9888), 0x11900000 },
|
||||
{ _MMIO(0x9888), 0x37900000 },
|
||||
{ _MMIO(0x9888), 0x53900000 },
|
||||
{ _MMIO(0x9888), 0x45900000 },
|
||||
{ _MMIO(0x9888), 0x33900000 },
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
show_test_oa_id(struct device *kdev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "1\n");
|
||||
}
|
||||
|
||||
void
|
||||
i915_perf_load_test_config_cflgt2(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
strncpy(dev_priv->perf.oa.test_config.uuid,
|
||||
"74fb4902-d3d3-4237-9e90-cbdc68d0a446",
|
||||
UUID_STRING_LEN);
|
||||
dev_priv->perf.oa.test_config.id = 1;
|
||||
|
||||
dev_priv->perf.oa.test_config.mux_regs = mux_config_test_oa;
|
||||
dev_priv->perf.oa.test_config.mux_regs_len = ARRAY_SIZE(mux_config_test_oa);
|
||||
|
||||
dev_priv->perf.oa.test_config.b_counter_regs = b_counter_config_test_oa;
|
||||
dev_priv->perf.oa.test_config.b_counter_regs_len = ARRAY_SIZE(b_counter_config_test_oa);
|
||||
|
||||
dev_priv->perf.oa.test_config.flex_regs = flex_eu_config_test_oa;
|
||||
dev_priv->perf.oa.test_config.flex_regs_len = ARRAY_SIZE(flex_eu_config_test_oa);
|
||||
|
||||
dev_priv->perf.oa.test_config.sysfs_metric.name = "74fb4902-d3d3-4237-9e90-cbdc68d0a446";
|
||||
dev_priv->perf.oa.test_config.sysfs_metric.attrs = dev_priv->perf.oa.test_config.attrs;
|
||||
|
||||
dev_priv->perf.oa.test_config.attrs[0] = &dev_priv->perf.oa.test_config.sysfs_metric_id.attr;
|
||||
|
||||
dev_priv->perf.oa.test_config.sysfs_metric_id.attr.name = "id";
|
||||
dev_priv->perf.oa.test_config.sysfs_metric_id.attr.mode = 0444;
|
||||
dev_priv->perf.oa.test_config.sysfs_metric_id.show = show_test_oa_id;
|
||||
}
|
34
drivers/gpu/drm/i915/i915_oa_cflgt2.h
Normal file
34
drivers/gpu/drm/i915/i915_oa_cflgt2.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Autogenerated file by GPU Top : https://github.com/rib/gputop
|
||||
* DO NOT EDIT manually!
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* 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 (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __I915_OA_CFLGT2_H__
|
||||
#define __I915_OA_CFLGT2_H__
|
||||
|
||||
extern void i915_perf_load_test_config_cflgt2(struct drm_i915_private *dev_priv);
|
||||
|
||||
#endif
|
@ -25,235 +25,171 @@
|
||||
#include "i915_params.h"
|
||||
#include "i915_drv.h"
|
||||
|
||||
struct i915_params i915 __read_mostly = {
|
||||
.modeset = -1,
|
||||
.panel_ignore_lid = 1,
|
||||
.semaphores = -1,
|
||||
.lvds_channel_mode = 0,
|
||||
.panel_use_ssc = -1,
|
||||
.vbt_sdvo_panel_type = -1,
|
||||
.enable_rc6 = -1,
|
||||
.enable_dc = -1,
|
||||
.enable_fbc = -1,
|
||||
.enable_execlists = -1,
|
||||
.enable_hangcheck = true,
|
||||
.enable_ppgtt = -1,
|
||||
.enable_psr = -1,
|
||||
.alpha_support = IS_ENABLED(CONFIG_DRM_I915_ALPHA_SUPPORT),
|
||||
.disable_power_well = -1,
|
||||
.enable_ips = 1,
|
||||
.fastboot = 0,
|
||||
.prefault_disable = 0,
|
||||
.load_detect_test = 0,
|
||||
.force_reset_modeset_test = 0,
|
||||
.reset = 2,
|
||||
.error_capture = true,
|
||||
.invert_brightness = 0,
|
||||
.disable_display = 0,
|
||||
.enable_cmd_parser = true,
|
||||
.use_mmio_flip = 0,
|
||||
.mmio_debug = 0,
|
||||
.verbose_state_checks = 1,
|
||||
.nuclear_pageflip = 0,
|
||||
.edp_vswing = 0,
|
||||
.enable_guc_loading = 0,
|
||||
.enable_guc_submission = 0,
|
||||
.guc_log_level = -1,
|
||||
.guc_firmware_path = NULL,
|
||||
.huc_firmware_path = NULL,
|
||||
.enable_dp_mst = true,
|
||||
.inject_load_failure = 0,
|
||||
.enable_dpcd_backlight = false,
|
||||
.enable_gvt = false,
|
||||
#define i915_param_named(name, T, perm, desc) \
|
||||
module_param_named(name, i915_modparams.name, T, perm); \
|
||||
MODULE_PARM_DESC(name, desc)
|
||||
#define i915_param_named_unsafe(name, T, perm, desc) \
|
||||
module_param_named_unsafe(name, i915_modparams.name, T, perm); \
|
||||
MODULE_PARM_DESC(name, desc)
|
||||
|
||||
struct i915_params i915_modparams __read_mostly = {
|
||||
#define MEMBER(T, member, value) .member = (value),
|
||||
I915_PARAMS_FOR_EACH(MEMBER)
|
||||
#undef MEMBER
|
||||
};
|
||||
|
||||
module_param_named(modeset, i915.modeset, int, 0400);
|
||||
MODULE_PARM_DESC(modeset,
|
||||
i915_param_named(modeset, int, 0400,
|
||||
"Use kernel modesetting [KMS] (0=disable, "
|
||||
"1=on, -1=force vga console preference [default])");
|
||||
|
||||
module_param_named_unsafe(panel_ignore_lid, i915.panel_ignore_lid, int, 0600);
|
||||
MODULE_PARM_DESC(panel_ignore_lid,
|
||||
i915_param_named_unsafe(panel_ignore_lid, int, 0600,
|
||||
"Override lid status (0=autodetect, 1=autodetect disabled [default], "
|
||||
"-1=force lid closed, -2=force lid open)");
|
||||
|
||||
module_param_named_unsafe(semaphores, i915.semaphores, int, 0400);
|
||||
MODULE_PARM_DESC(semaphores,
|
||||
i915_param_named_unsafe(semaphores, int, 0400,
|
||||
"Use semaphores for inter-ring sync "
|
||||
"(default: -1 (use per-chip defaults))");
|
||||
|
||||
module_param_named_unsafe(enable_rc6, i915.enable_rc6, int, 0400);
|
||||
MODULE_PARM_DESC(enable_rc6,
|
||||
i915_param_named_unsafe(enable_rc6, int, 0400,
|
||||
"Enable power-saving render C-state 6. "
|
||||
"Different stages can be selected via bitmask values "
|
||||
"(0 = disable; 1 = enable rc6; 2 = enable deep rc6; 4 = enable deepest rc6). "
|
||||
"For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. "
|
||||
"default: -1 (use per-chip default)");
|
||||
|
||||
module_param_named_unsafe(enable_dc, i915.enable_dc, int, 0400);
|
||||
MODULE_PARM_DESC(enable_dc,
|
||||
i915_param_named_unsafe(enable_dc, int, 0400,
|
||||
"Enable power-saving display C-states. "
|
||||
"(-1=auto [default]; 0=disable; 1=up to DC5; 2=up to DC6)");
|
||||
|
||||
module_param_named_unsafe(enable_fbc, i915.enable_fbc, int, 0600);
|
||||
MODULE_PARM_DESC(enable_fbc,
|
||||
i915_param_named_unsafe(enable_fbc, int, 0600,
|
||||
"Enable frame buffer compression for power savings "
|
||||
"(default: -1 (use per-chip default))");
|
||||
|
||||
module_param_named_unsafe(lvds_channel_mode, i915.lvds_channel_mode, int, 0400);
|
||||
MODULE_PARM_DESC(lvds_channel_mode,
|
||||
i915_param_named_unsafe(lvds_channel_mode, int, 0400,
|
||||
"Specify LVDS channel mode "
|
||||
"(0=probe BIOS [default], 1=single-channel, 2=dual-channel)");
|
||||
|
||||
module_param_named_unsafe(lvds_use_ssc, i915.panel_use_ssc, int, 0600);
|
||||
MODULE_PARM_DESC(lvds_use_ssc,
|
||||
i915_param_named_unsafe(panel_use_ssc, int, 0600,
|
||||
"Use Spread Spectrum Clock with panels [LVDS/eDP] "
|
||||
"(default: auto from VBT)");
|
||||
|
||||
module_param_named_unsafe(vbt_sdvo_panel_type, i915.vbt_sdvo_panel_type, int, 0400);
|
||||
MODULE_PARM_DESC(vbt_sdvo_panel_type,
|
||||
i915_param_named_unsafe(vbt_sdvo_panel_type, int, 0400,
|
||||
"Override/Ignore selection of SDVO panel mode in the VBT "
|
||||
"(-2=ignore, -1=auto [default], index in VBT BIOS table)");
|
||||
|
||||
module_param_named_unsafe(reset, i915.reset, int, 0600);
|
||||
MODULE_PARM_DESC(reset, "Attempt GPU resets (0=disabled, 1=full gpu reset, 2=engine reset [default])");
|
||||
i915_param_named_unsafe(reset, int, 0600,
|
||||
"Attempt GPU resets (0=disabled, 1=full gpu reset, 2=engine reset [default])");
|
||||
|
||||
module_param_named_unsafe(vbt_firmware, i915.vbt_firmware, charp, 0400);
|
||||
MODULE_PARM_DESC(vbt_firmware,
|
||||
"Load VBT from specified file under /lib/firmware");
|
||||
i915_param_named_unsafe(vbt_firmware, charp, 0400,
|
||||
"Load VBT from specified file under /lib/firmware");
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
|
||||
module_param_named(error_capture, i915.error_capture, bool, 0600);
|
||||
MODULE_PARM_DESC(error_capture,
|
||||
i915_param_named(error_capture, bool, 0600,
|
||||
"Record the GPU state following a hang. "
|
||||
"This information in /sys/class/drm/card<N>/error is vital for "
|
||||
"triaging and debugging hangs.");
|
||||
#endif
|
||||
|
||||
module_param_named_unsafe(enable_hangcheck, i915.enable_hangcheck, bool, 0644);
|
||||
MODULE_PARM_DESC(enable_hangcheck,
|
||||
i915_param_named_unsafe(enable_hangcheck, bool, 0644,
|
||||
"Periodically check GPU activity for detecting hangs. "
|
||||
"WARNING: Disabling this can cause system wide hangs. "
|
||||
"(default: true)");
|
||||
|
||||
module_param_named_unsafe(enable_ppgtt, i915.enable_ppgtt, int, 0400);
|
||||
MODULE_PARM_DESC(enable_ppgtt,
|
||||
i915_param_named_unsafe(enable_ppgtt, int, 0400,
|
||||
"Override PPGTT usage. "
|
||||
"(-1=auto [default], 0=disabled, 1=aliasing, 2=full, 3=full with extended address space)");
|
||||
|
||||
module_param_named_unsafe(enable_execlists, i915.enable_execlists, int, 0400);
|
||||
MODULE_PARM_DESC(enable_execlists,
|
||||
i915_param_named_unsafe(enable_execlists, int, 0400,
|
||||
"Override execlists usage. "
|
||||
"(-1=auto [default], 0=disabled, 1=enabled)");
|
||||
|
||||
module_param_named_unsafe(enable_psr, i915.enable_psr, int, 0600);
|
||||
MODULE_PARM_DESC(enable_psr, "Enable PSR "
|
||||
"(0=disabled, 1=enabled - link mode chosen per-platform, 2=force link-standby mode, 3=force link-off mode) "
|
||||
"Default: -1 (use per-chip default)");
|
||||
i915_param_named_unsafe(enable_psr, int, 0600,
|
||||
"Enable PSR "
|
||||
"(0=disabled, 1=enabled - link mode chosen per-platform, 2=force link-standby mode, 3=force link-off mode) "
|
||||
"Default: -1 (use per-chip default)");
|
||||
|
||||
module_param_named_unsafe(alpha_support, i915.alpha_support, bool, 0400);
|
||||
MODULE_PARM_DESC(alpha_support,
|
||||
i915_param_named_unsafe(alpha_support, bool, 0400,
|
||||
"Enable alpha quality driver support for latest hardware. "
|
||||
"See also CONFIG_DRM_I915_ALPHA_SUPPORT.");
|
||||
|
||||
module_param_named_unsafe(disable_power_well, i915.disable_power_well, int, 0400);
|
||||
MODULE_PARM_DESC(disable_power_well,
|
||||
i915_param_named_unsafe(disable_power_well, int, 0400,
|
||||
"Disable display power wells when possible "
|
||||
"(-1=auto [default], 0=power wells always on, 1=power wells disabled when possible)");
|
||||
|
||||
module_param_named_unsafe(enable_ips, i915.enable_ips, int, 0600);
|
||||
MODULE_PARM_DESC(enable_ips, "Enable IPS (default: true)");
|
||||
i915_param_named_unsafe(enable_ips, int, 0600, "Enable IPS (default: true)");
|
||||
|
||||
module_param_named(fastboot, i915.fastboot, bool, 0600);
|
||||
MODULE_PARM_DESC(fastboot,
|
||||
i915_param_named(fastboot, bool, 0600,
|
||||
"Try to skip unnecessary mode sets at boot time (default: false)");
|
||||
|
||||
module_param_named_unsafe(prefault_disable, i915.prefault_disable, bool, 0600);
|
||||
MODULE_PARM_DESC(prefault_disable,
|
||||
i915_param_named_unsafe(prefault_disable, bool, 0600,
|
||||
"Disable page prefaulting for pread/pwrite/reloc (default:false). "
|
||||
"For developers only.");
|
||||
|
||||
module_param_named_unsafe(load_detect_test, i915.load_detect_test, bool, 0600);
|
||||
MODULE_PARM_DESC(load_detect_test,
|
||||
i915_param_named_unsafe(load_detect_test, bool, 0600,
|
||||
"Force-enable the VGA load detect code for testing (default:false). "
|
||||
"For developers only.");
|
||||
|
||||
module_param_named_unsafe(force_reset_modeset_test, i915.force_reset_modeset_test, bool, 0600);
|
||||
MODULE_PARM_DESC(force_reset_modeset_test,
|
||||
i915_param_named_unsafe(force_reset_modeset_test, bool, 0600,
|
||||
"Force a modeset during gpu reset for testing (default:false). "
|
||||
"For developers only.");
|
||||
|
||||
module_param_named_unsafe(invert_brightness, i915.invert_brightness, int, 0600);
|
||||
MODULE_PARM_DESC(invert_brightness,
|
||||
i915_param_named_unsafe(invert_brightness, int, 0600,
|
||||
"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(disable_display, i915.disable_display, bool, 0400);
|
||||
MODULE_PARM_DESC(disable_display, "Disable display (default: false)");
|
||||
i915_param_named(disable_display, bool, 0400,
|
||||
"Disable display (default: false)");
|
||||
|
||||
module_param_named_unsafe(enable_cmd_parser, i915.enable_cmd_parser, bool, 0400);
|
||||
MODULE_PARM_DESC(enable_cmd_parser,
|
||||
"Enable command parsing (true=enabled [default], false=disabled)");
|
||||
i915_param_named_unsafe(enable_cmd_parser, bool, 0400,
|
||||
"Enable command parsing (true=enabled [default], false=disabled)");
|
||||
|
||||
module_param_named_unsafe(use_mmio_flip, i915.use_mmio_flip, int, 0600);
|
||||
MODULE_PARM_DESC(use_mmio_flip,
|
||||
"use MMIO flips (-1=never, 0=driver discretion [default], 1=always)");
|
||||
i915_param_named_unsafe(use_mmio_flip, int, 0600,
|
||||
"use MMIO flips (-1=never, 0=driver discretion [default], 1=always)");
|
||||
|
||||
module_param_named(mmio_debug, i915.mmio_debug, int, 0600);
|
||||
MODULE_PARM_DESC(mmio_debug,
|
||||
i915_param_named(mmio_debug, int, 0600,
|
||||
"Enable the MMIO debug code for the first N failures (default: off). "
|
||||
"This may negatively affect performance.");
|
||||
|
||||
module_param_named(verbose_state_checks, i915.verbose_state_checks, bool, 0600);
|
||||
MODULE_PARM_DESC(verbose_state_checks,
|
||||
i915_param_named(verbose_state_checks, bool, 0600,
|
||||
"Enable verbose logs (ie. WARN_ON()) in case of unexpected hw state conditions.");
|
||||
|
||||
module_param_named_unsafe(nuclear_pageflip, i915.nuclear_pageflip, bool, 0400);
|
||||
MODULE_PARM_DESC(nuclear_pageflip,
|
||||
"Force enable atomic functionality on platforms that don't have full support yet.");
|
||||
i915_param_named_unsafe(nuclear_pageflip, bool, 0400,
|
||||
"Force enable atomic functionality on platforms that don't have full support yet.");
|
||||
|
||||
/* WA to get away with the default setting in VBT for early platforms.Will be removed */
|
||||
module_param_named_unsafe(edp_vswing, i915.edp_vswing, int, 0400);
|
||||
MODULE_PARM_DESC(edp_vswing,
|
||||
"Ignore/Override vswing pre-emph table selection from VBT "
|
||||
"(0=use value from vbt [default], 1=low power swing(200mV),"
|
||||
"2=default swing(400mV))");
|
||||
i915_param_named_unsafe(edp_vswing, int, 0400,
|
||||
"Ignore/Override vswing pre-emph table selection from VBT "
|
||||
"(0=use value from vbt [default], 1=low power swing(200mV),"
|
||||
"2=default swing(400mV))");
|
||||
|
||||
module_param_named_unsafe(enable_guc_loading, i915.enable_guc_loading, int, 0400);
|
||||
MODULE_PARM_DESC(enable_guc_loading,
|
||||
"Enable GuC firmware loading "
|
||||
"(-1=auto, 0=never [default], 1=if available, 2=required)");
|
||||
i915_param_named_unsafe(enable_guc_loading, int, 0400,
|
||||
"Enable GuC firmware loading "
|
||||
"(-1=auto, 0=never [default], 1=if available, 2=required)");
|
||||
|
||||
module_param_named_unsafe(enable_guc_submission, i915.enable_guc_submission, int, 0400);
|
||||
MODULE_PARM_DESC(enable_guc_submission,
|
||||
"Enable GuC submission "
|
||||
"(-1=auto, 0=never [default], 1=if available, 2=required)");
|
||||
i915_param_named_unsafe(enable_guc_submission, int, 0400,
|
||||
"Enable GuC submission "
|
||||
"(-1=auto, 0=never [default], 1=if available, 2=required)");
|
||||
|
||||
module_param_named(guc_log_level, i915.guc_log_level, int, 0400);
|
||||
MODULE_PARM_DESC(guc_log_level,
|
||||
i915_param_named(guc_log_level, int, 0400,
|
||||
"GuC firmware logging level (-1:disabled (default), 0-3:enabled)");
|
||||
|
||||
module_param_named_unsafe(guc_firmware_path, i915.guc_firmware_path, charp, 0400);
|
||||
MODULE_PARM_DESC(guc_firmware_path,
|
||||
i915_param_named_unsafe(guc_firmware_path, charp, 0400,
|
||||
"GuC firmware path to use instead of the default one");
|
||||
|
||||
module_param_named_unsafe(huc_firmware_path, i915.huc_firmware_path, charp, 0400);
|
||||
MODULE_PARM_DESC(huc_firmware_path,
|
||||
i915_param_named_unsafe(huc_firmware_path, charp, 0400,
|
||||
"HuC firmware path to use instead of the default one");
|
||||
|
||||
module_param_named_unsafe(enable_dp_mst, i915.enable_dp_mst, bool, 0600);
|
||||
MODULE_PARM_DESC(enable_dp_mst,
|
||||
i915_param_named_unsafe(enable_dp_mst, bool, 0600,
|
||||
"Enable multi-stream transport (MST) for new DisplayPort sinks. (default: true)");
|
||||
module_param_named_unsafe(inject_load_failure, i915.inject_load_failure, uint, 0400);
|
||||
MODULE_PARM_DESC(inject_load_failure,
|
||||
|
||||
i915_param_named_unsafe(inject_load_failure, uint, 0400,
|
||||
"Force an error after a number of failure check points (0:disabled (default), N:force failure at the Nth failure check point)");
|
||||
module_param_named(enable_dpcd_backlight, i915.enable_dpcd_backlight, bool, 0600);
|
||||
MODULE_PARM_DESC(enable_dpcd_backlight,
|
||||
|
||||
i915_param_named(enable_dpcd_backlight, bool, 0600,
|
||||
"Enable support for DPCD backlight control (default:false)");
|
||||
|
||||
module_param_named(enable_gvt, i915.enable_gvt, bool, 0400);
|
||||
MODULE_PARM_DESC(enable_gvt,
|
||||
i915_param_named(enable_gvt, bool, 0400,
|
||||
"Enable support for Intel GVT-g graphics virtualization host support(default:false)");
|
||||
|
@ -27,56 +27,56 @@
|
||||
|
||||
#include <linux/cache.h> /* for __read_mostly */
|
||||
|
||||
#define I915_PARAMS_FOR_EACH(func) \
|
||||
func(char *, vbt_firmware); \
|
||||
func(int, modeset); \
|
||||
func(int, panel_ignore_lid); \
|
||||
func(int, semaphores); \
|
||||
func(int, lvds_channel_mode); \
|
||||
func(int, panel_use_ssc); \
|
||||
func(int, vbt_sdvo_panel_type); \
|
||||
func(int, enable_rc6); \
|
||||
func(int, enable_dc); \
|
||||
func(int, enable_fbc); \
|
||||
func(int, enable_ppgtt); \
|
||||
func(int, enable_execlists); \
|
||||
func(int, enable_psr); \
|
||||
func(int, disable_power_well); \
|
||||
func(int, enable_ips); \
|
||||
func(int, invert_brightness); \
|
||||
func(int, enable_guc_loading); \
|
||||
func(int, enable_guc_submission); \
|
||||
func(int, guc_log_level); \
|
||||
func(char *, guc_firmware_path); \
|
||||
func(char *, huc_firmware_path); \
|
||||
func(int, use_mmio_flip); \
|
||||
func(int, mmio_debug); \
|
||||
func(int, edp_vswing); \
|
||||
func(int, reset); \
|
||||
func(unsigned int, inject_load_failure); \
|
||||
#define I915_PARAMS_FOR_EACH(param) \
|
||||
param(char *, vbt_firmware, NULL) \
|
||||
param(int, modeset, -1) \
|
||||
param(int, panel_ignore_lid, 1) \
|
||||
param(int, semaphores, -1) \
|
||||
param(int, lvds_channel_mode, 0) \
|
||||
param(int, panel_use_ssc, -1) \
|
||||
param(int, vbt_sdvo_panel_type, -1) \
|
||||
param(int, enable_rc6, -1) \
|
||||
param(int, enable_dc, -1) \
|
||||
param(int, enable_fbc, -1) \
|
||||
param(int, enable_ppgtt, -1) \
|
||||
param(int, enable_execlists, -1) \
|
||||
param(int, enable_psr, -1) \
|
||||
param(int, disable_power_well, -1) \
|
||||
param(int, enable_ips, 1) \
|
||||
param(int, invert_brightness, 0) \
|
||||
param(int, enable_guc_loading, 0) \
|
||||
param(int, enable_guc_submission, 0) \
|
||||
param(int, guc_log_level, -1) \
|
||||
param(char *, guc_firmware_path, NULL) \
|
||||
param(char *, huc_firmware_path, NULL) \
|
||||
param(int, use_mmio_flip, 0) \
|
||||
param(int, mmio_debug, 0) \
|
||||
param(int, edp_vswing, 0) \
|
||||
param(int, reset, 2) \
|
||||
param(unsigned int, inject_load_failure, 0) \
|
||||
/* leave bools at the end to not create holes */ \
|
||||
func(bool, alpha_support); \
|
||||
func(bool, enable_cmd_parser); \
|
||||
func(bool, enable_hangcheck); \
|
||||
func(bool, fastboot); \
|
||||
func(bool, prefault_disable); \
|
||||
func(bool, load_detect_test); \
|
||||
func(bool, force_reset_modeset_test); \
|
||||
func(bool, error_capture); \
|
||||
func(bool, disable_display); \
|
||||
func(bool, verbose_state_checks); \
|
||||
func(bool, nuclear_pageflip); \
|
||||
func(bool, enable_dp_mst); \
|
||||
func(bool, enable_dpcd_backlight); \
|
||||
func(bool, enable_gvt)
|
||||
param(bool, alpha_support, IS_ENABLED(CONFIG_DRM_I915_ALPHA_SUPPORT)) \
|
||||
param(bool, enable_cmd_parser, true) \
|
||||
param(bool, enable_hangcheck, true) \
|
||||
param(bool, fastboot, false) \
|
||||
param(bool, prefault_disable, false) \
|
||||
param(bool, load_detect_test, false) \
|
||||
param(bool, force_reset_modeset_test, false) \
|
||||
param(bool, error_capture, true) \
|
||||
param(bool, disable_display, false) \
|
||||
param(bool, verbose_state_checks, true) \
|
||||
param(bool, nuclear_pageflip, false) \
|
||||
param(bool, enable_dp_mst, true) \
|
||||
param(bool, enable_dpcd_backlight, false) \
|
||||
param(bool, enable_gvt, false)
|
||||
|
||||
#define MEMBER(T, member) T member
|
||||
#define MEMBER(T, member, ...) T member;
|
||||
struct i915_params {
|
||||
I915_PARAMS_FOR_EACH(MEMBER);
|
||||
};
|
||||
#undef MEMBER
|
||||
|
||||
extern struct i915_params i915 __read_mostly;
|
||||
extern struct i915_params i915_modparams __read_mostly;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -168,6 +168,7 @@ static const struct intel_device_info intel_i965g_info __initconst = {
|
||||
.platform = INTEL_I965G,
|
||||
.has_overlay = 1,
|
||||
.hws_needs_physical = 1,
|
||||
.has_snoop = false,
|
||||
};
|
||||
|
||||
static const struct intel_device_info intel_i965gm_info __initconst = {
|
||||
@ -177,6 +178,7 @@ static const struct intel_device_info intel_i965gm_info __initconst = {
|
||||
.has_overlay = 1,
|
||||
.supports_tv = 1,
|
||||
.hws_needs_physical = 1,
|
||||
.has_snoop = false,
|
||||
};
|
||||
|
||||
static const struct intel_device_info intel_g45_info __initconst = {
|
||||
@ -198,7 +200,6 @@ static const struct intel_device_info intel_gm45_info __initconst = {
|
||||
#define GEN5_FEATURES \
|
||||
.gen = 5, .num_pipes = 2, \
|
||||
.has_hotplug = 1, \
|
||||
.has_gmbus_irq = 1, \
|
||||
.ring_mask = RENDER_RING | BSD_RING, \
|
||||
.has_snoop = true, \
|
||||
GEN_DEFAULT_PIPEOFFSETS, \
|
||||
@ -223,7 +224,6 @@ static const struct intel_device_info intel_ironlake_m_info __initconst = {
|
||||
.has_llc = 1, \
|
||||
.has_rc6 = 1, \
|
||||
.has_rc6p = 1, \
|
||||
.has_gmbus_irq = 1, \
|
||||
.has_aliasing_ppgtt = 1, \
|
||||
GEN_DEFAULT_PIPEOFFSETS, \
|
||||
CURSOR_OFFSETS
|
||||
@ -266,7 +266,6 @@ static const struct intel_device_info intel_sandybridge_m_gt2_info __initconst =
|
||||
.has_llc = 1, \
|
||||
.has_rc6 = 1, \
|
||||
.has_rc6p = 1, \
|
||||
.has_gmbus_irq = 1, \
|
||||
.has_aliasing_ppgtt = 1, \
|
||||
.has_full_ppgtt = 1, \
|
||||
GEN_DEFAULT_PIPEOFFSETS, \
|
||||
@ -319,7 +318,6 @@ static const struct intel_device_info intel_valleyview_info __initconst = {
|
||||
.has_psr = 1,
|
||||
.has_runtime_pm = 1,
|
||||
.has_rc6 = 1,
|
||||
.has_gmbus_irq = 1,
|
||||
.has_gmch_display = 1,
|
||||
.has_hotplug = 1,
|
||||
.has_aliasing_ppgtt = 1,
|
||||
@ -410,7 +408,6 @@ static const struct intel_device_info intel_cherryview_info __initconst = {
|
||||
.has_runtime_pm = 1,
|
||||
.has_resource_streamer = 1,
|
||||
.has_rc6 = 1,
|
||||
.has_gmbus_irq = 1,
|
||||
.has_logical_ring_contexts = 1,
|
||||
.has_gmch_display = 1,
|
||||
.has_aliasing_ppgtt = 1,
|
||||
@ -472,7 +469,6 @@ static const struct intel_device_info intel_skylake_gt4_info __initconst = {
|
||||
.has_resource_streamer = 1, \
|
||||
.has_rc6 = 1, \
|
||||
.has_dp_mst = 1, \
|
||||
.has_gmbus_irq = 1, \
|
||||
.has_logical_ring_contexts = 1, \
|
||||
.has_guc = 1, \
|
||||
.has_aliasing_ppgtt = 1, \
|
||||
@ -480,6 +476,7 @@ static const struct intel_device_info intel_skylake_gt4_info __initconst = {
|
||||
.has_full_48bit_ppgtt = 1, \
|
||||
.has_reset_engine = 1, \
|
||||
.has_snoop = true, \
|
||||
.has_ipc = 1, \
|
||||
GEN_DEFAULT_PIPEOFFSETS, \
|
||||
IVB_CURSOR_OFFSETS, \
|
||||
BDW_COLORS
|
||||
@ -503,6 +500,7 @@ static const struct intel_device_info intel_geminilake_info __initconst = {
|
||||
.platform = INTEL_KABYLAKE, \
|
||||
.has_csr = 1, \
|
||||
.has_guc = 1, \
|
||||
.has_ipc = 1, \
|
||||
.ddb_size = 896
|
||||
|
||||
static const struct intel_device_info intel_kabylake_gt1_info __initconst = {
|
||||
@ -522,12 +520,12 @@ static const struct intel_device_info intel_kabylake_gt3_info __initconst = {
|
||||
};
|
||||
|
||||
#define CFL_PLATFORM \
|
||||
.is_alpha_support = 1, \
|
||||
BDW_FEATURES, \
|
||||
.gen = 9, \
|
||||
.platform = INTEL_COFFEELAKE, \
|
||||
.has_csr = 1, \
|
||||
.has_guc = 1, \
|
||||
.has_ipc = 1, \
|
||||
.ddb_size = 896
|
||||
|
||||
static const struct intel_device_info intel_coffeelake_gt1_info __initconst = {
|
||||
@ -554,6 +552,7 @@ static const struct intel_device_info intel_cannonlake_gt2_info __initconst = {
|
||||
.gt = 2,
|
||||
.ddb_size = 1024,
|
||||
.has_csr = 1,
|
||||
.has_ipc = 1,
|
||||
.color = { .degamma_lut_size = 0, .gamma_lut_size = 1024 }
|
||||
};
|
||||
|
||||
@ -632,7 +631,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
(struct intel_device_info *) ent->driver_data;
|
||||
int err;
|
||||
|
||||
if (IS_ALPHA_SUPPORT(intel_info) && !i915.alpha_support) {
|
||||
if (IS_ALPHA_SUPPORT(intel_info) && !i915_modparams.alpha_support) {
|
||||
DRM_INFO("The driver support for your hardware in this kernel version is alpha quality\n"
|
||||
"See CONFIG_DRM_I915_ALPHA_SUPPORT or i915.alpha_support module parameter\n"
|
||||
"to enable support in this kernel version, or check for kernel updates.\n");
|
||||
@ -690,10 +689,10 @@ static int __init i915_init(void)
|
||||
* vga_text_mode_force boot option.
|
||||
*/
|
||||
|
||||
if (i915.modeset == 0)
|
||||
if (i915_modparams.modeset == 0)
|
||||
use_kms = false;
|
||||
|
||||
if (vgacon_text_force() && i915.modeset == -1)
|
||||
if (vgacon_text_force() && i915_modparams.modeset == -1)
|
||||
use_kms = false;
|
||||
|
||||
if (!use_kms) {
|
||||
|
@ -206,6 +206,7 @@
|
||||
#include "i915_oa_kblgt2.h"
|
||||
#include "i915_oa_kblgt3.h"
|
||||
#include "i915_oa_glk.h"
|
||||
#include "i915_oa_cflgt2.h"
|
||||
|
||||
/* HW requires this to be a power of two, between 128k and 16M, though driver
|
||||
* is currently generally designed assuming the largest 16M size is used such
|
||||
@ -1213,7 +1214,7 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = stream->dev_priv;
|
||||
|
||||
if (i915.enable_execlists)
|
||||
if (i915_modparams.enable_execlists)
|
||||
dev_priv->perf.oa.specific_ctx_id = stream->ctx->hw_id;
|
||||
else {
|
||||
struct intel_engine_cs *engine = dev_priv->engine[RCS];
|
||||
@ -1259,7 +1260,7 @@ static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = stream->dev_priv;
|
||||
|
||||
if (i915.enable_execlists) {
|
||||
if (i915_modparams.enable_execlists) {
|
||||
dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID;
|
||||
} else {
|
||||
struct intel_engine_cs *engine = dev_priv->engine[RCS];
|
||||
@ -1850,8 +1851,7 @@ static int gen8_enable_metric_set(struct drm_i915_private *dev_priv,
|
||||
* be read back from automatically triggered reports, as part of the
|
||||
* RPT_ID field.
|
||||
*/
|
||||
if (IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv) ||
|
||||
IS_KABYLAKE(dev_priv) || IS_GEMINILAKE(dev_priv)) {
|
||||
if (IS_GEN9(dev_priv)) {
|
||||
I915_WRITE(GEN8_OA_DEBUG,
|
||||
_MASKED_BIT_ENABLE(GEN9_OA_DEBUG_DISABLE_CLK_RATIO_REPORTS |
|
||||
GEN9_OA_DEBUG_INCLUDE_CLK_RATIO));
|
||||
@ -2927,6 +2927,9 @@ void i915_perf_register(struct drm_i915_private *dev_priv)
|
||||
i915_perf_load_test_config_kblgt3(dev_priv);
|
||||
} else if (IS_GEMINILAKE(dev_priv)) {
|
||||
i915_perf_load_test_config_glk(dev_priv);
|
||||
} else if (IS_COFFEELAKE(dev_priv)) {
|
||||
if (IS_CFL_GT2(dev_priv))
|
||||
i915_perf_load_test_config_cflgt2(dev_priv);
|
||||
}
|
||||
|
||||
if (dev_priv->perf.oa.test_config.id == 0)
|
||||
@ -3405,7 +3408,7 @@ void i915_perf_init(struct drm_i915_private *dev_priv)
|
||||
dev_priv->perf.oa.timestamp_frequency = 12500000;
|
||||
|
||||
dev_priv->perf.oa.oa_formats = hsw_oa_formats;
|
||||
} else if (i915.enable_execlists) {
|
||||
} else if (i915_modparams.enable_execlists) {
|
||||
/* Note: that although we could theoretically also support the
|
||||
* legacy ringbuffer mode on BDW (and earlier iterations of
|
||||
* this driver, before upstreaming did this) it didn't seem
|
||||
@ -3453,6 +3456,7 @@ void i915_perf_init(struct drm_i915_private *dev_priv)
|
||||
break;
|
||||
case INTEL_SKYLAKE:
|
||||
case INTEL_KABYLAKE:
|
||||
case INTEL_COFFEELAKE:
|
||||
dev_priv->perf.oa.timestamp_frequency = 12000000;
|
||||
break;
|
||||
default:
|
||||
|
@ -2336,7 +2336,7 @@ enum i915_power_well_id {
|
||||
#define DONE_REG _MMIO(0x40b0)
|
||||
#define GEN8_PRIVATE_PAT_LO _MMIO(0x40e0)
|
||||
#define GEN8_PRIVATE_PAT_HI _MMIO(0x40e0 + 4)
|
||||
#define GEN10_PAT_INDEX(index) _MMIO(0x40e0 + index*4)
|
||||
#define GEN10_PAT_INDEX(index) _MMIO(0x40e0 + (index)*4)
|
||||
#define BSD_HWS_PGA_GEN7 _MMIO(0x04180)
|
||||
#define BLT_HWS_PGA_GEN7 _MMIO(0x04280)
|
||||
#define VEBOX_HWS_PGA_GEN7 _MMIO(0x04380)
|
||||
@ -2730,6 +2730,11 @@ enum i915_power_well_id {
|
||||
#define GEN9_F2_SS_DIS_SHIFT 20
|
||||
#define GEN9_F2_SS_DIS_MASK (0xf << GEN9_F2_SS_DIS_SHIFT)
|
||||
|
||||
#define GEN10_F2_S_ENA_SHIFT 22
|
||||
#define GEN10_F2_S_ENA_MASK (0x3f << GEN10_F2_S_ENA_SHIFT)
|
||||
#define GEN10_F2_SS_DIS_SHIFT 18
|
||||
#define GEN10_F2_SS_DIS_MASK (0xf << GEN10_F2_SS_DIS_SHIFT)
|
||||
|
||||
#define GEN8_EU_DISABLE0 _MMIO(0x9134)
|
||||
#define GEN8_EU_DIS0_S0_MASK 0xffffff
|
||||
#define GEN8_EU_DIS0_S1_SHIFT 24
|
||||
@ -2745,6 +2750,9 @@ enum i915_power_well_id {
|
||||
|
||||
#define GEN9_EU_DISABLE(slice) _MMIO(0x9134 + (slice)*0x4)
|
||||
|
||||
#define GEN10_EU_DISABLE3 _MMIO(0x9140)
|
||||
#define GEN10_EU_DIS_SS_MASK 0xff
|
||||
|
||||
#define GEN6_BSD_SLEEP_PSMI_CONTROL _MMIO(0x12050)
|
||||
#define GEN6_BSD_SLEEP_MSG_DISABLE (1 << 0)
|
||||
#define GEN6_BSD_SLEEP_FLUSH_DISABLE (1 << 2)
|
||||
@ -4047,7 +4055,7 @@ enum {
|
||||
#define EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4
|
||||
#define EDP_PSR2_FRAME_BEFORE_SU_MASK (0xf<<4)
|
||||
#define EDP_PSR2_IDLE_MASK 0xf
|
||||
#define EDP_FRAMES_BEFORE_SU_ENTRY (1<<4)
|
||||
#define EDP_PSR2_FRAME_BEFORE_SU(a) ((a)<<4)
|
||||
|
||||
#define EDP_PSR2_STATUS_CTL _MMIO(0x6f940)
|
||||
#define EDP_PSR2_STATUS_STATE_MASK (0xf<<28)
|
||||
@ -6913,7 +6921,7 @@ enum {
|
||||
# define CHICKEN3_DGMG_DONE_FIX_DISABLE (1 << 2)
|
||||
|
||||
#define CHICKEN_PAR1_1 _MMIO(0x42080)
|
||||
#define SKL_RC_HASH_OUTSIDE (1 << 15)
|
||||
#define SKL_DE_COMPRESSED_HASH_MODE (1 << 15)
|
||||
#define DPA_MASK_VBLANK_SRD (1 << 15)
|
||||
#define FORCE_ARB_IDLE_PLANES (1 << 14)
|
||||
#define SKL_EDP_PSR_FIX_RDWRAP (1 << 3)
|
||||
@ -6949,6 +6957,7 @@ enum {
|
||||
#define DISP_FBC_WM_DIS (1<<15)
|
||||
#define DISP_ARB_CTL2 _MMIO(0x45004)
|
||||
#define DISP_DATA_PARTITION_5_6 (1<<6)
|
||||
#define DISP_IPC_ENABLE (1<<3)
|
||||
#define DBUF_CTL _MMIO(0x45008)
|
||||
#define DBUF_POWER_REQUEST (1<<31)
|
||||
#define DBUF_POWER_STATE (1<<30)
|
||||
@ -6990,6 +6999,7 @@ enum {
|
||||
# define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC ((1<<10) | (1<<26))
|
||||
# define GEN9_RHWO_OPTIMIZATION_DISABLE (1<<14)
|
||||
#define COMMON_SLICE_CHICKEN2 _MMIO(0x7014)
|
||||
# define GEN9_PBE_COMPRESSED_HASH_SELECTION (1<<13)
|
||||
# define GEN9_DISABLE_GATHER_AT_SET_SHADER_COMMON_SLICE (1<<12)
|
||||
# define GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION (1<<8)
|
||||
# define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE (1<<0)
|
||||
@ -7469,6 +7479,8 @@ enum {
|
||||
#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 FDI_BC_BIFURCATION_SELECT (1 << 12)
|
||||
#define CHASSIS_CLK_REQ_DURATION_MASK (0xf << 8)
|
||||
#define CHASSIS_CLK_REQ_DURATION(x) ((x) << 8)
|
||||
#define SPT_PWM_GRANULARITY (1<<0)
|
||||
#define SOUTH_CHICKEN2 _MMIO(0xc2004)
|
||||
#define FDI_MPHY_IOSFSB_RESET_STATUS (1<<13)
|
||||
@ -7953,8 +7965,8 @@ enum {
|
||||
#define GEN7_PCODE_TIMEOUT 0x2
|
||||
#define GEN7_PCODE_ILLEGAL_DATA 0x3
|
||||
#define GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE 0x10
|
||||
#define GEN6_PCODE_WRITE_RC6VIDS 0x4
|
||||
#define GEN6_PCODE_READ_RC6VIDS 0x5
|
||||
#define GEN6_PCODE_WRITE_RC6VIDS 0x4
|
||||
#define GEN6_PCODE_READ_RC6VIDS 0x5
|
||||
#define GEN6_ENCODE_RC6_VID(mv) (((mv) - 245) / 5)
|
||||
#define GEN6_DECODE_RC6_VID(vids) (((vids) * 5) + 245)
|
||||
#define BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ 0x18
|
||||
@ -7973,7 +7985,9 @@ enum {
|
||||
#define GEN6_PCODE_WRITE_D_COMP 0x11
|
||||
#define HSW_PCODE_DE_WRITE_FREQ_REQ 0x17
|
||||
#define DISPLAY_IPS_CONTROL 0x19
|
||||
#define HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL 0x1A
|
||||
/* See also IPS_CTL */
|
||||
#define IPS_PCODE_CONTROL (1 << 30)
|
||||
#define HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL 0x1A
|
||||
#define GEN9_PCODE_SAGV_CONTROL 0x21
|
||||
#define GEN9_SAGV_DISABLE 0x0
|
||||
#define GEN9_SAGV_IS_DISABLED 0x1
|
||||
@ -8082,6 +8096,7 @@ enum {
|
||||
#define GEN8_SAMPLER_POWER_BYPASS_DIS (1<<1)
|
||||
|
||||
#define GEN9_HALF_SLICE_CHICKEN7 _MMIO(0xe194)
|
||||
#define GEN9_SAMPLER_HASH_COMPRESSED_READ_ADDR (1<<8)
|
||||
#define GEN9_ENABLE_YV12_BUGFIX (1<<4)
|
||||
#define GEN9_ENABLE_GPGPU_PREEMPTION (1<<2)
|
||||
|
||||
@ -8594,7 +8609,7 @@ enum skl_power_gate {
|
||||
#define DPLL_CFGCR0_LINK_RATE_3240 (6 << 25)
|
||||
#define DPLL_CFGCR0_LINK_RATE_4050 (7 << 25)
|
||||
#define DPLL_CFGCR0_DCO_FRACTION_MASK (0x7fff << 10)
|
||||
#define DPLL_CFGCR0_DCO_FRAC_SHIFT (10)
|
||||
#define DPLL_CFGCR0_DCO_FRACTION_SHIFT (10)
|
||||
#define DPLL_CFGCR0_DCO_FRACTION(x) ((x) << 10)
|
||||
#define DPLL_CFGCR0_DCO_INTEGER_MASK (0x3ff)
|
||||
#define CNL_DPLL_CFGCR0(pll) _MMIO_PLL(pll, _CNL_DPLL0_CFGCR0, _CNL_DPLL1_CFGCR0)
|
||||
@ -8801,6 +8816,15 @@ enum skl_power_gate {
|
||||
#define MIPIO_TXESC_CLK_DIV2 _MMIO(0x160008)
|
||||
#define GLK_TX_ESC_CLK_DIV2_MASK 0x3FF
|
||||
|
||||
/* Gen4+ Timestamp and Pipe Frame time stamp registers */
|
||||
#define GEN4_TIMESTAMP _MMIO(0x2358)
|
||||
#define ILK_TIMESTAMP_HI _MMIO(0x70070)
|
||||
#define IVB_TIMESTAMP_CTR _MMIO(0x44070)
|
||||
|
||||
#define _PIPE_FRMTMSTMP_A 0x70048
|
||||
#define PIPE_FRMTMSTMP(pipe) \
|
||||
_MMIO_PIPE2(pipe, _PIPE_FRMTMSTMP_A)
|
||||
|
||||
/* BXT MIPI clock controls */
|
||||
#define BXT_MAX_VAR_OUTPUT_KHZ 39500
|
||||
|
||||
@ -9382,4 +9406,8 @@ enum skl_power_gate {
|
||||
#define GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_SKL 0x67F1427F /* " " */
|
||||
#define GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_BXT 0x5FF101FF /* " " */
|
||||
|
||||
#define MMCD_MISC_CTRL _MMIO(0x4ddc) /* skl+ */
|
||||
#define MMCD_PCLA (1 << 31)
|
||||
#define MMCD_HOTSPOT_EN (1 << 27)
|
||||
|
||||
#endif /* _I915_REG_H_ */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user