mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
Merge tag 'drm-misc-next-2024-04-19' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v6.10-rc1: UAPI Changes: - Add SIZE_HINTS property for cursor planes. Cross-subsystem Changes: Core Changes: - Document the requirements and expectations of adding new driver-specific properties. - Assorted small fixes to ttm. - More Kconfig fixes. - Add struct drm_edid_product_id and helpers. - Use drm device based logging in more drm functions. - Fixes for drm-panic, and option to test it. - Assorted small fixes and updates to edid. - Add drm_crtc_vblank_crtc and use it in vkms, nouveau. Driver Changes: - Assorted small fixes and improvements to bridge/imx8mp-hdmi-tx, nouveau, ast, qaic, lima, vc4, bridge/anx7625, mipi-dsi. - Add drm panic to simpledrm, mgag200, imx, ast. - Use dev_err_probe in bridge/panel drivers. - Add Innolux G121X1-L03, LG sw43408 panels. - Use struct drm_edid in i915 bios parsing. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/2dc1b7c6-1743-4ddd-ad42-36f700234fbe@linux.intel.com
This commit is contained in:
commit
2871ec4099
@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/lg,sw43408.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LG SW43408 1080x2160 DSI panel
|
||||
|
||||
maintainers:
|
||||
- Caleb Connolly <caleb.connolly@linaro.org>
|
||||
|
||||
description:
|
||||
This panel is used on the Pixel 3, it is a 60hz OLED panel which
|
||||
required DSC (Display Stream Compression) and has rounded corners.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: lg,sw43408
|
||||
|
||||
reg: true
|
||||
port: true
|
||||
vddi-supply: true
|
||||
vpnl-supply: true
|
||||
reset-gpios: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vddi-supply
|
||||
- vpnl-supply
|
||||
- reset-gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "lg,sw43408";
|
||||
reg = <0>;
|
||||
|
||||
vddi-supply = <&vreg_l14a_1p88>;
|
||||
vpnl-supply = <&vreg_l28a_3p0>;
|
||||
|
||||
reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>;
|
||||
|
||||
port {
|
||||
endpoint {
|
||||
remote-endpoint = <&mdss_dsi0_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -190,6 +190,8 @@ properties:
|
||||
- innolux,g121i1-l01
|
||||
# Innolux Corporation 12.1" G121X1-L03 XGA (1024x768) TFT LCD panel
|
||||
- innolux,g121x1-l03
|
||||
# Innolux Corporation 12.1" G121XCE-L01 XGA (1024x768) TFT LCD panel
|
||||
- innolux,g121xce-l01
|
||||
# Innolux Corporation 11.6" WXGA (1366x768) TFT LCD panel
|
||||
- innolux,n116bca-ea1
|
||||
# Innolux Corporation 11.6" WXGA (1366x768) TFT LCD panel
|
||||
|
@ -77,7 +77,7 @@ consider though:
|
||||
the usual size discover pattern size = SEEK_END(0); SEEK_SET(0). Every other
|
||||
llseek operation will report -EINVAL.
|
||||
|
||||
If llseek on dma-buf FDs isn't support the kernel will report -ESPIPE for all
|
||||
If llseek on dma-buf FDs isn't supported the kernel will report -ESPIPE for all
|
||||
cases. Userspace can use this to detect support for discovering the dma-buf
|
||||
size using llseek.
|
||||
|
||||
|
@ -398,6 +398,21 @@ Plane Damage Tracking Functions Reference
|
||||
.. kernel-doc:: include/drm/drm_damage_helper.h
|
||||
:internal:
|
||||
|
||||
Plane Panic Feature
|
||||
-------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_panic.c
|
||||
:doc: overview
|
||||
|
||||
Plane Panic Functions Reference
|
||||
-------------------------------
|
||||
|
||||
.. kernel-doc:: include/drm/drm_panic.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_panic.c
|
||||
:export:
|
||||
|
||||
Display Modes Function Reference
|
||||
================================
|
||||
|
||||
@ -496,6 +511,13 @@ addition to the one mentioned above:
|
||||
|
||||
* An IGT test must be submitted where reasonable.
|
||||
|
||||
For historical reasons, non-standard, driver-specific properties exist. If a KMS
|
||||
driver wants to add support for one of those properties, the requirements for
|
||||
new properties apply where possible. Additionally, the documented behavior must
|
||||
match the de facto semantics of the existing property to ensure compatibility.
|
||||
Developers of the driver that first added the property should help with those
|
||||
tasks and must ACK the documented behavior if possible.
|
||||
|
||||
Property Types and Blob Property Support
|
||||
----------------------------------------
|
||||
|
||||
|
@ -6764,6 +6764,14 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
|
||||
F: drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
|
||||
|
||||
DRM DRIVER FOR LG SW43408 PANELS
|
||||
M: Sumit Semwal <sumit.semwal@linaro.org>
|
||||
M: Caleb Connolly <caleb.connolly@linaro.org>
|
||||
S: Maintained
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
F: Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
|
||||
F: drivers/gpu/drm/panel/panel-lg-sw43408.c
|
||||
|
||||
DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
|
||||
M: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
S: Supported
|
||||
|
@ -10,6 +10,7 @@ qaic-y := \
|
||||
qaic_control.o \
|
||||
qaic_data.o \
|
||||
qaic_drv.o \
|
||||
qaic_timesync.o
|
||||
qaic_timesync.o \
|
||||
sahara.o
|
||||
|
||||
qaic-$(CONFIG_DEBUG_FS) += qaic_debugfs.o
|
||||
|
@ -13,8 +13,8 @@ int qaic_bootlog_register(void);
|
||||
void qaic_bootlog_unregister(void);
|
||||
void qaic_debugfs_init(struct qaic_drm_device *qddev);
|
||||
#else
|
||||
int qaic_bootlog_register(void) { return 0; }
|
||||
void qaic_bootlog_unregister(void) {}
|
||||
void qaic_debugfs_init(struct qaic_drm_device *qddev) {}
|
||||
static inline int qaic_bootlog_register(void) { return 0; }
|
||||
static inline void qaic_bootlog_unregister(void) {}
|
||||
static inline void qaic_debugfs_init(struct qaic_drm_device *qddev) {}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
#endif /* __QAIC_DEBUGFS_H__ */
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "qaic.h"
|
||||
#include "qaic_debugfs.h"
|
||||
#include "qaic_timesync.h"
|
||||
#include "sahara.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
@ -644,6 +645,12 @@ static int __init qaic_init(void)
|
||||
goto free_pci;
|
||||
}
|
||||
|
||||
ret = sahara_register();
|
||||
if (ret) {
|
||||
pr_debug("qaic: sahara_register failed %d\n", ret);
|
||||
goto free_mhi;
|
||||
}
|
||||
|
||||
ret = qaic_timesync_init();
|
||||
if (ret)
|
||||
pr_debug("qaic: qaic_timesync_init failed %d\n", ret);
|
||||
@ -654,6 +661,8 @@ static int __init qaic_init(void)
|
||||
|
||||
return 0;
|
||||
|
||||
free_mhi:
|
||||
mhi_driver_unregister(&qaic_mhi_driver);
|
||||
free_pci:
|
||||
pci_unregister_driver(&qaic_pci_driver);
|
||||
return ret;
|
||||
@ -679,6 +688,7 @@ static void __exit qaic_exit(void)
|
||||
link_up = true;
|
||||
qaic_bootlog_unregister();
|
||||
qaic_timesync_deinit();
|
||||
sahara_unregister();
|
||||
mhi_driver_unregister(&qaic_mhi_driver);
|
||||
pci_unregister_driver(&qaic_pci_driver);
|
||||
}
|
||||
|
449
drivers/accel/qaic/sahara.c
Normal file
449
drivers/accel/qaic/sahara.c
Normal file
@ -0,0 +1,449 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
|
||||
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/mhi.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "sahara.h"
|
||||
|
||||
#define SAHARA_HELLO_CMD 0x1 /* Min protocol version 1.0 */
|
||||
#define SAHARA_HELLO_RESP_CMD 0x2 /* Min protocol version 1.0 */
|
||||
#define SAHARA_READ_DATA_CMD 0x3 /* Min protocol version 1.0 */
|
||||
#define SAHARA_END_OF_IMAGE_CMD 0x4 /* Min protocol version 1.0 */
|
||||
#define SAHARA_DONE_CMD 0x5 /* Min protocol version 1.0 */
|
||||
#define SAHARA_DONE_RESP_CMD 0x6 /* Min protocol version 1.0 */
|
||||
#define SAHARA_RESET_CMD 0x7 /* Min protocol version 1.0 */
|
||||
#define SAHARA_RESET_RESP_CMD 0x8 /* Min protocol version 1.0 */
|
||||
#define SAHARA_MEM_DEBUG_CMD 0x9 /* Min protocol version 2.0 */
|
||||
#define SAHARA_MEM_READ_CMD 0xa /* Min protocol version 2.0 */
|
||||
#define SAHARA_CMD_READY_CMD 0xb /* Min protocol version 2.1 */
|
||||
#define SAHARA_SWITCH_MODE_CMD 0xc /* Min protocol version 2.1 */
|
||||
#define SAHARA_EXECUTE_CMD 0xd /* Min protocol version 2.1 */
|
||||
#define SAHARA_EXECUTE_RESP_CMD 0xe /* Min protocol version 2.1 */
|
||||
#define SAHARA_EXECUTE_DATA_CMD 0xf /* Min protocol version 2.1 */
|
||||
#define SAHARA_MEM_DEBUG64_CMD 0x10 /* Min protocol version 2.5 */
|
||||
#define SAHARA_MEM_READ64_CMD 0x11 /* Min protocol version 2.5 */
|
||||
#define SAHARA_READ_DATA64_CMD 0x12 /* Min protocol version 2.8 */
|
||||
#define SAHARA_RESET_STATE_CMD 0x13 /* Min protocol version 2.9 */
|
||||
#define SAHARA_WRITE_DATA_CMD 0x14 /* Min protocol version 3.0 */
|
||||
|
||||
#define SAHARA_PACKET_MAX_SIZE 0xffffU /* MHI_MAX_MTU */
|
||||
#define SAHARA_TRANSFER_MAX_SIZE 0x80000
|
||||
#define SAHARA_NUM_TX_BUF DIV_ROUND_UP(SAHARA_TRANSFER_MAX_SIZE,\
|
||||
SAHARA_PACKET_MAX_SIZE)
|
||||
#define SAHARA_IMAGE_ID_NONE U32_MAX
|
||||
|
||||
#define SAHARA_VERSION 2
|
||||
#define SAHARA_SUCCESS 0
|
||||
|
||||
#define SAHARA_MODE_IMAGE_TX_PENDING 0x0
|
||||
#define SAHARA_MODE_IMAGE_TX_COMPLETE 0x1
|
||||
#define SAHARA_MODE_MEMORY_DEBUG 0x2
|
||||
#define SAHARA_MODE_COMMAND 0x3
|
||||
|
||||
#define SAHARA_HELLO_LENGTH 0x30
|
||||
#define SAHARA_READ_DATA_LENGTH 0x14
|
||||
#define SAHARA_END_OF_IMAGE_LENGTH 0x10
|
||||
#define SAHARA_DONE_LENGTH 0x8
|
||||
#define SAHARA_RESET_LENGTH 0x8
|
||||
|
||||
struct sahara_packet {
|
||||
__le32 cmd;
|
||||
__le32 length;
|
||||
|
||||
union {
|
||||
struct {
|
||||
__le32 version;
|
||||
__le32 version_compat;
|
||||
__le32 max_length;
|
||||
__le32 mode;
|
||||
} hello;
|
||||
struct {
|
||||
__le32 version;
|
||||
__le32 version_compat;
|
||||
__le32 status;
|
||||
__le32 mode;
|
||||
} hello_resp;
|
||||
struct {
|
||||
__le32 image;
|
||||
__le32 offset;
|
||||
__le32 length;
|
||||
} read_data;
|
||||
struct {
|
||||
__le32 image;
|
||||
__le32 status;
|
||||
} end_of_image;
|
||||
};
|
||||
};
|
||||
|
||||
struct sahara_context {
|
||||
struct sahara_packet *tx[SAHARA_NUM_TX_BUF];
|
||||
struct sahara_packet *rx;
|
||||
struct work_struct work;
|
||||
struct mhi_device *mhi_dev;
|
||||
const char **image_table;
|
||||
u32 table_size;
|
||||
u32 active_image_id;
|
||||
const struct firmware *firmware;
|
||||
};
|
||||
|
||||
static const char *aic100_image_table[] = {
|
||||
[1] = "qcom/aic100/fw1.bin",
|
||||
[2] = "qcom/aic100/fw2.bin",
|
||||
[4] = "qcom/aic100/fw4.bin",
|
||||
[5] = "qcom/aic100/fw5.bin",
|
||||
[6] = "qcom/aic100/fw6.bin",
|
||||
[8] = "qcom/aic100/fw8.bin",
|
||||
[9] = "qcom/aic100/fw9.bin",
|
||||
[10] = "qcom/aic100/fw10.bin",
|
||||
};
|
||||
|
||||
static int sahara_find_image(struct sahara_context *context, u32 image_id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (image_id == context->active_image_id)
|
||||
return 0;
|
||||
|
||||
if (context->active_image_id != SAHARA_IMAGE_ID_NONE) {
|
||||
dev_err(&context->mhi_dev->dev, "image id %d is not valid as %d is active\n",
|
||||
image_id, context->active_image_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (image_id >= context->table_size || !context->image_table[image_id]) {
|
||||
dev_err(&context->mhi_dev->dev, "request for unknown image: %d\n", image_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This image might be optional. The device may continue without it.
|
||||
* Only the device knows. Suppress error messages that could suggest an
|
||||
* a problem when we were actually able to continue.
|
||||
*/
|
||||
ret = firmware_request_nowarn(&context->firmware,
|
||||
context->image_table[image_id],
|
||||
&context->mhi_dev->dev);
|
||||
if (ret) {
|
||||
dev_dbg(&context->mhi_dev->dev, "request for image id %d / file %s failed %d\n",
|
||||
image_id, context->image_table[image_id], ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
context->active_image_id = image_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sahara_release_image(struct sahara_context *context)
|
||||
{
|
||||
if (context->active_image_id != SAHARA_IMAGE_ID_NONE)
|
||||
release_firmware(context->firmware);
|
||||
context->active_image_id = SAHARA_IMAGE_ID_NONE;
|
||||
}
|
||||
|
||||
static void sahara_send_reset(struct sahara_context *context)
|
||||
{
|
||||
int ret;
|
||||
|
||||
context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD);
|
||||
context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH);
|
||||
|
||||
ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
|
||||
SAHARA_RESET_LENGTH, MHI_EOT);
|
||||
if (ret)
|
||||
dev_err(&context->mhi_dev->dev, "Unable to send reset response %d\n", ret);
|
||||
}
|
||||
|
||||
static void sahara_hello(struct sahara_context *context)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(&context->mhi_dev->dev,
|
||||
"HELLO cmd received. length:%d version:%d version_compat:%d max_length:%d mode:%d\n",
|
||||
le32_to_cpu(context->rx->length),
|
||||
le32_to_cpu(context->rx->hello.version),
|
||||
le32_to_cpu(context->rx->hello.version_compat),
|
||||
le32_to_cpu(context->rx->hello.max_length),
|
||||
le32_to_cpu(context->rx->hello.mode));
|
||||
|
||||
if (le32_to_cpu(context->rx->length) != SAHARA_HELLO_LENGTH) {
|
||||
dev_err(&context->mhi_dev->dev, "Malformed hello packet - length %d\n",
|
||||
le32_to_cpu(context->rx->length));
|
||||
return;
|
||||
}
|
||||
if (le32_to_cpu(context->rx->hello.version) != SAHARA_VERSION) {
|
||||
dev_err(&context->mhi_dev->dev, "Unsupported hello packet - version %d\n",
|
||||
le32_to_cpu(context->rx->hello.version));
|
||||
return;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_PENDING &&
|
||||
le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_COMPLETE) {
|
||||
dev_err(&context->mhi_dev->dev, "Unsupported hello packet - mode %d\n",
|
||||
le32_to_cpu(context->rx->hello.mode));
|
||||
return;
|
||||
}
|
||||
|
||||
context->tx[0]->cmd = cpu_to_le32(SAHARA_HELLO_RESP_CMD);
|
||||
context->tx[0]->length = cpu_to_le32(SAHARA_HELLO_LENGTH);
|
||||
context->tx[0]->hello_resp.version = cpu_to_le32(SAHARA_VERSION);
|
||||
context->tx[0]->hello_resp.version_compat = cpu_to_le32(SAHARA_VERSION);
|
||||
context->tx[0]->hello_resp.status = cpu_to_le32(SAHARA_SUCCESS);
|
||||
context->tx[0]->hello_resp.mode = context->rx->hello_resp.mode;
|
||||
|
||||
ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
|
||||
SAHARA_HELLO_LENGTH, MHI_EOT);
|
||||
if (ret)
|
||||
dev_err(&context->mhi_dev->dev, "Unable to send hello response %d\n", ret);
|
||||
}
|
||||
|
||||
static void sahara_read_data(struct sahara_context *context)
|
||||
{
|
||||
u32 image_id, data_offset, data_len, pkt_data_len;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
dev_dbg(&context->mhi_dev->dev,
|
||||
"READ_DATA cmd received. length:%d image:%d offset:%d data_length:%d\n",
|
||||
le32_to_cpu(context->rx->length),
|
||||
le32_to_cpu(context->rx->read_data.image),
|
||||
le32_to_cpu(context->rx->read_data.offset),
|
||||
le32_to_cpu(context->rx->read_data.length));
|
||||
|
||||
if (le32_to_cpu(context->rx->length) != SAHARA_READ_DATA_LENGTH) {
|
||||
dev_err(&context->mhi_dev->dev, "Malformed read_data packet - length %d\n",
|
||||
le32_to_cpu(context->rx->length));
|
||||
return;
|
||||
}
|
||||
|
||||
image_id = le32_to_cpu(context->rx->read_data.image);
|
||||
data_offset = le32_to_cpu(context->rx->read_data.offset);
|
||||
data_len = le32_to_cpu(context->rx->read_data.length);
|
||||
|
||||
ret = sahara_find_image(context, image_id);
|
||||
if (ret) {
|
||||
sahara_send_reset(context);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Image is released when the device is done with it via
|
||||
* SAHARA_END_OF_IMAGE_CMD. sahara_send_reset() will either cause the
|
||||
* device to retry the operation with a modification, or decide to be
|
||||
* done with the image and trigger SAHARA_END_OF_IMAGE_CMD.
|
||||
* release_image() is called from SAHARA_END_OF_IMAGE_CMD. processing
|
||||
* and is not needed here on error.
|
||||
*/
|
||||
|
||||
if (data_len > SAHARA_TRANSFER_MAX_SIZE) {
|
||||
dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data len %d exceeds max xfer size %d\n",
|
||||
data_len, SAHARA_TRANSFER_MAX_SIZE);
|
||||
sahara_send_reset(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data_offset >= context->firmware->size) {
|
||||
dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data offset %d exceeds file size %zu\n",
|
||||
data_offset, context->firmware->size);
|
||||
sahara_send_reset(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size_add(data_offset, data_len) > context->firmware->size) {
|
||||
dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data offset %d and length %d exceeds file size %zu\n",
|
||||
data_offset, data_len, context->firmware->size);
|
||||
sahara_send_reset(context);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < SAHARA_NUM_TX_BUF && data_len; ++i) {
|
||||
pkt_data_len = min(data_len, SAHARA_PACKET_MAX_SIZE);
|
||||
|
||||
memcpy(context->tx[i], &context->firmware->data[data_offset], pkt_data_len);
|
||||
|
||||
data_offset += pkt_data_len;
|
||||
data_len -= pkt_data_len;
|
||||
|
||||
ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE,
|
||||
context->tx[i], pkt_data_len,
|
||||
!data_len ? MHI_EOT : MHI_CHAIN);
|
||||
if (ret) {
|
||||
dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sahara_end_of_image(struct sahara_context *context)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(&context->mhi_dev->dev,
|
||||
"END_OF_IMAGE cmd received. length:%d image:%d status:%d\n",
|
||||
le32_to_cpu(context->rx->length),
|
||||
le32_to_cpu(context->rx->end_of_image.image),
|
||||
le32_to_cpu(context->rx->end_of_image.status));
|
||||
|
||||
if (le32_to_cpu(context->rx->length) != SAHARA_END_OF_IMAGE_LENGTH) {
|
||||
dev_err(&context->mhi_dev->dev, "Malformed end_of_image packet - length %d\n",
|
||||
le32_to_cpu(context->rx->length));
|
||||
return;
|
||||
}
|
||||
|
||||
if (context->active_image_id != SAHARA_IMAGE_ID_NONE &&
|
||||
le32_to_cpu(context->rx->end_of_image.image) != context->active_image_id) {
|
||||
dev_err(&context->mhi_dev->dev, "Malformed end_of_image packet - image %d is not the active image\n",
|
||||
le32_to_cpu(context->rx->end_of_image.image));
|
||||
return;
|
||||
}
|
||||
|
||||
sahara_release_image(context);
|
||||
|
||||
if (le32_to_cpu(context->rx->end_of_image.status))
|
||||
return;
|
||||
|
||||
context->tx[0]->cmd = cpu_to_le32(SAHARA_DONE_CMD);
|
||||
context->tx[0]->length = cpu_to_le32(SAHARA_DONE_LENGTH);
|
||||
|
||||
ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
|
||||
SAHARA_DONE_LENGTH, MHI_EOT);
|
||||
if (ret)
|
||||
dev_dbg(&context->mhi_dev->dev, "Unable to send done response %d\n", ret);
|
||||
}
|
||||
|
||||
static void sahara_processing(struct work_struct *work)
|
||||
{
|
||||
struct sahara_context *context = container_of(work, struct sahara_context, work);
|
||||
int ret;
|
||||
|
||||
switch (le32_to_cpu(context->rx->cmd)) {
|
||||
case SAHARA_HELLO_CMD:
|
||||
sahara_hello(context);
|
||||
break;
|
||||
case SAHARA_READ_DATA_CMD:
|
||||
sahara_read_data(context);
|
||||
break;
|
||||
case SAHARA_END_OF_IMAGE_CMD:
|
||||
sahara_end_of_image(context);
|
||||
break;
|
||||
case SAHARA_DONE_RESP_CMD:
|
||||
/* Intentional do nothing as we don't need to exit an app */
|
||||
break;
|
||||
default:
|
||||
dev_err(&context->mhi_dev->dev, "Unknown command %d\n",
|
||||
le32_to_cpu(context->rx->cmd));
|
||||
break;
|
||||
}
|
||||
|
||||
ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE, context->rx,
|
||||
SAHARA_PACKET_MAX_SIZE, MHI_EOT);
|
||||
if (ret)
|
||||
dev_err(&context->mhi_dev->dev, "Unable to requeue rx buf %d\n", ret);
|
||||
}
|
||||
|
||||
static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
|
||||
{
|
||||
struct sahara_context *context;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
context = devm_kzalloc(&mhi_dev->dev, sizeof(*context), GFP_KERNEL);
|
||||
if (!context)
|
||||
return -ENOMEM;
|
||||
|
||||
context->rx = devm_kzalloc(&mhi_dev->dev, SAHARA_PACKET_MAX_SIZE, GFP_KERNEL);
|
||||
if (!context->rx)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* AIC100 defines SAHARA_TRANSFER_MAX_SIZE as the largest value it
|
||||
* will request for READ_DATA. This is larger than
|
||||
* SAHARA_PACKET_MAX_SIZE, and we need 9x SAHARA_PACKET_MAX_SIZE to
|
||||
* cover SAHARA_TRANSFER_MAX_SIZE. When the remote side issues a
|
||||
* READ_DATA, it requires a transfer of the exact size requested. We
|
||||
* can use MHI_CHAIN to link multiple buffers into a single transfer
|
||||
* but the remote side will not consume the buffers until it sees an
|
||||
* EOT, thus we need to allocate enough buffers to put in the tx fifo
|
||||
* to cover an entire READ_DATA request of the max size.
|
||||
*/
|
||||
for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) {
|
||||
context->tx[i] = devm_kzalloc(&mhi_dev->dev, SAHARA_PACKET_MAX_SIZE, GFP_KERNEL);
|
||||
if (!context->tx[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
context->mhi_dev = mhi_dev;
|
||||
INIT_WORK(&context->work, sahara_processing);
|
||||
context->image_table = aic100_image_table;
|
||||
context->table_size = ARRAY_SIZE(aic100_image_table);
|
||||
context->active_image_id = SAHARA_IMAGE_ID_NONE;
|
||||
dev_set_drvdata(&mhi_dev->dev, context);
|
||||
|
||||
ret = mhi_prepare_for_transfer(mhi_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, context->rx, SAHARA_PACKET_MAX_SIZE, MHI_EOT);
|
||||
if (ret) {
|
||||
mhi_unprepare_from_transfer(mhi_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sahara_mhi_remove(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
|
||||
|
||||
cancel_work_sync(&context->work);
|
||||
sahara_release_image(context);
|
||||
mhi_unprepare_from_transfer(mhi_dev);
|
||||
}
|
||||
|
||||
static void sahara_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
|
||||
{
|
||||
}
|
||||
|
||||
static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
|
||||
{
|
||||
struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
|
||||
|
||||
if (!mhi_result->transaction_status)
|
||||
schedule_work(&context->work);
|
||||
}
|
||||
|
||||
static const struct mhi_device_id sahara_mhi_match_table[] = {
|
||||
{ .chan = "QAIC_SAHARA", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct mhi_driver sahara_mhi_driver = {
|
||||
.id_table = sahara_mhi_match_table,
|
||||
.remove = sahara_mhi_remove,
|
||||
.probe = sahara_mhi_probe,
|
||||
.ul_xfer_cb = sahara_mhi_ul_xfer_cb,
|
||||
.dl_xfer_cb = sahara_mhi_dl_xfer_cb,
|
||||
.driver = {
|
||||
.name = "sahara",
|
||||
},
|
||||
};
|
||||
|
||||
int sahara_register(void)
|
||||
{
|
||||
return mhi_driver_register(&sahara_mhi_driver);
|
||||
}
|
||||
|
||||
void sahara_unregister(void)
|
||||
{
|
||||
mhi_driver_unregister(&sahara_mhi_driver);
|
||||
}
|
10
drivers/accel/qaic/sahara.h
Normal file
10
drivers/accel/qaic/sahara.h
Normal file
@ -0,0 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
|
||||
|
||||
#ifndef __SAHARA_H__
|
||||
#define __SAHARA_H__
|
||||
|
||||
int sahara_register(void);
|
||||
void sahara_unregister(void);
|
||||
#endif /* __SAHARA_H__ */
|
@ -35,12 +35,35 @@
|
||||
|
||||
static inline int is_dma_buf_file(struct file *);
|
||||
|
||||
struct dma_buf_list {
|
||||
struct list_head head;
|
||||
struct mutex lock;
|
||||
};
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
static DEFINE_MUTEX(debugfs_list_mutex);
|
||||
static LIST_HEAD(debugfs_list);
|
||||
|
||||
static struct dma_buf_list db_list;
|
||||
static void __dma_buf_debugfs_list_add(struct dma_buf *dmabuf)
|
||||
{
|
||||
mutex_lock(&debugfs_list_mutex);
|
||||
list_add(&dmabuf->list_node, &debugfs_list);
|
||||
mutex_unlock(&debugfs_list_mutex);
|
||||
}
|
||||
|
||||
static void __dma_buf_debugfs_list_del(struct dma_buf *dmabuf)
|
||||
{
|
||||
if (!dmabuf)
|
||||
return;
|
||||
|
||||
mutex_lock(&debugfs_list_mutex);
|
||||
list_del(&dmabuf->list_node);
|
||||
mutex_unlock(&debugfs_list_mutex);
|
||||
}
|
||||
#else
|
||||
static void __dma_buf_debugfs_list_add(struct dma_buf *dmabuf)
|
||||
{
|
||||
}
|
||||
|
||||
static void __dma_buf_debugfs_list_del(struct file *file)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
|
||||
{
|
||||
@ -89,17 +112,10 @@ static void dma_buf_release(struct dentry *dentry)
|
||||
|
||||
static int dma_buf_file_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dma_buf *dmabuf;
|
||||
|
||||
if (!is_dma_buf_file(file))
|
||||
return -EINVAL;
|
||||
|
||||
dmabuf = file->private_data;
|
||||
if (dmabuf) {
|
||||
mutex_lock(&db_list.lock);
|
||||
list_del(&dmabuf->list_node);
|
||||
mutex_unlock(&db_list.lock);
|
||||
}
|
||||
__dma_buf_debugfs_list_del(file->private_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -672,9 +688,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
|
||||
file->f_path.dentry->d_fsdata = dmabuf;
|
||||
dmabuf->file = file;
|
||||
|
||||
mutex_lock(&db_list.lock);
|
||||
list_add(&dmabuf->list_node, &db_list.head);
|
||||
mutex_unlock(&db_list.lock);
|
||||
__dma_buf_debugfs_list_add(dmabuf);
|
||||
|
||||
return dmabuf;
|
||||
|
||||
@ -1611,7 +1625,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
|
||||
size_t size = 0;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&db_list.lock);
|
||||
ret = mutex_lock_interruptible(&debugfs_list_mutex);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1620,7 +1634,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
|
||||
seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\tname\n",
|
||||
"size", "flags", "mode", "count", "ino");
|
||||
|
||||
list_for_each_entry(buf_obj, &db_list.head, list_node) {
|
||||
list_for_each_entry(buf_obj, &debugfs_list, list_node) {
|
||||
|
||||
ret = dma_resv_lock_interruptible(buf_obj->resv, NULL);
|
||||
if (ret)
|
||||
@ -1657,11 +1671,11 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
|
||||
|
||||
seq_printf(s, "\nTotal %d objects, %zu bytes\n", count, size);
|
||||
|
||||
mutex_unlock(&db_list.lock);
|
||||
mutex_unlock(&debugfs_list_mutex);
|
||||
return 0;
|
||||
|
||||
error_unlock:
|
||||
mutex_unlock(&db_list.lock);
|
||||
mutex_unlock(&debugfs_list_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1718,8 +1732,6 @@ static int __init dma_buf_init(void)
|
||||
if (IS_ERR(dma_buf_mnt))
|
||||
return PTR_ERR(dma_buf_mnt);
|
||||
|
||||
mutex_init(&db_list.lock);
|
||||
INIT_LIST_HEAD(&db_list.head);
|
||||
dma_buf_init_debugfs();
|
||||
return 0;
|
||||
}
|
||||
|
@ -104,6 +104,38 @@ config DRM_KMS_HELPER
|
||||
help
|
||||
CRTC helpers for KMS drivers.
|
||||
|
||||
config DRM_PANIC
|
||||
bool "Display a user-friendly message when a kernel panic occurs"
|
||||
depends on DRM && !FRAMEBUFFER_CONSOLE
|
||||
select DRM_KMS_HELPER
|
||||
select FONT_SUPPORT
|
||||
help
|
||||
Enable a drm panic handler, which will display a user-friendly message
|
||||
when a kernel panic occurs. It's useful when using a user-space
|
||||
console instead of fbcon.
|
||||
It will only work if your graphic driver supports this feature.
|
||||
To support Hi-DPI Display, you can enable bigger fonts like
|
||||
FONT_TER16x32
|
||||
|
||||
config DRM_PANIC_FOREGROUND_COLOR
|
||||
hex "Drm panic screen foreground color, in RGB"
|
||||
depends on DRM_PANIC
|
||||
default 0xffffff
|
||||
|
||||
config DRM_PANIC_BACKGROUND_COLOR
|
||||
hex "Drm panic screen background color, in RGB"
|
||||
depends on DRM_PANIC
|
||||
default 0x000000
|
||||
|
||||
config DRM_PANIC_DEBUG
|
||||
bool "Add a debug fs entry to trigger drm_panic"
|
||||
depends on DRM_PANIC && DEBUG_FS
|
||||
help
|
||||
Add dri/[device]/drm_panic_plane_x in the kernel debugfs, to force the
|
||||
panic handler to write the panic message to this plane scanout buffer.
|
||||
This is unsafe and should not be enabled on a production build.
|
||||
If in doubt, say "N".
|
||||
|
||||
config DRM_DEBUG_DP_MST_TOPOLOGY_REFS
|
||||
bool "Enable refcount backtrace history in the DP MST helpers"
|
||||
depends on STACKTRACE_SUPPORT
|
||||
|
@ -88,6 +88,7 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \
|
||||
drm_privacy_screen.o \
|
||||
drm_privacy_screen_x86.o
|
||||
drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o
|
||||
drm-$(CONFIG_DRM_PANIC) += drm_panic.o
|
||||
obj-$(CONFIG_DRM) += drm.o
|
||||
|
||||
obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
|
||||
|
@ -72,7 +72,10 @@ static void malidp_mw_connector_reset(struct drm_connector *connector)
|
||||
__drm_atomic_helper_connector_destroy_state(connector->state);
|
||||
|
||||
kfree(connector->state);
|
||||
__drm_atomic_helper_connector_reset(connector, &mw_state->base);
|
||||
connector->state = NULL;
|
||||
|
||||
if (mw_state)
|
||||
__drm_atomic_helper_connector_reset(connector, &mw_state->base);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
|
@ -21,12 +21,22 @@
|
||||
* of the Software.
|
||||
*/
|
||||
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "ast_ddc.h"
|
||||
#include "ast_drv.h"
|
||||
|
||||
struct ast_ddc {
|
||||
struct ast_device *ast;
|
||||
|
||||
struct i2c_algo_bit_data bit;
|
||||
struct i2c_adapter adapter;
|
||||
};
|
||||
|
||||
static void ast_ddc_algo_bit_data_setsda(void *data, int state)
|
||||
{
|
||||
struct ast_ddc *ddc = data;
|
||||
@ -132,7 +142,7 @@ static void ast_ddc_release(struct drm_device *dev, void *res)
|
||||
i2c_del_adapter(&ddc->adapter);
|
||||
}
|
||||
|
||||
struct ast_ddc *ast_ddc_create(struct ast_device *ast)
|
||||
struct i2c_adapter *ast_ddc_create(struct ast_device *ast)
|
||||
{
|
||||
struct drm_device *dev = &ast->base;
|
||||
struct ast_ddc *ddc;
|
||||
@ -145,15 +155,7 @@ struct ast_ddc *ast_ddc_create(struct ast_device *ast)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ddc->ast = ast;
|
||||
|
||||
adapter = &ddc->adapter;
|
||||
adapter->owner = THIS_MODULE;
|
||||
adapter->dev.parent = dev->dev;
|
||||
i2c_set_adapdata(adapter, ddc);
|
||||
snprintf(adapter->name, sizeof(adapter->name), "AST DDC bus");
|
||||
|
||||
bit = &ddc->bit;
|
||||
bit->udelay = 20;
|
||||
bit->timeout = 2;
|
||||
bit->data = ddc;
|
||||
bit->setsda = ast_ddc_algo_bit_data_setsda;
|
||||
bit->setscl = ast_ddc_algo_bit_data_setscl;
|
||||
@ -161,8 +163,16 @@ struct ast_ddc *ast_ddc_create(struct ast_device *ast)
|
||||
bit->getscl = ast_ddc_algo_bit_data_getscl;
|
||||
bit->pre_xfer = ast_ddc_algo_bit_data_pre_xfer;
|
||||
bit->post_xfer = ast_ddc_algo_bit_data_post_xfer;
|
||||
bit->udelay = 20;
|
||||
bit->timeout = usecs_to_jiffies(2200);
|
||||
|
||||
adapter = &ddc->adapter;
|
||||
adapter->owner = THIS_MODULE;
|
||||
adapter->algo_data = bit;
|
||||
adapter->dev.parent = dev->dev;
|
||||
snprintf(adapter->name, sizeof(adapter->name), "AST DDC bus");
|
||||
i2c_set_adapdata(adapter, ddc);
|
||||
|
||||
ret = i2c_bit_add_bus(adapter);
|
||||
if (ret) {
|
||||
drm_err(dev, "Failed to register bit i2c\n");
|
||||
@ -173,5 +183,5 @@ struct ast_ddc *ast_ddc_create(struct ast_device *ast)
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return ddc;
|
||||
return &ddc->adapter;
|
||||
}
|
||||
|
@ -3,18 +3,9 @@
|
||||
#ifndef __AST_DDC_H__
|
||||
#define __AST_DDC_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
|
||||
struct ast_device;
|
||||
struct i2c_adapter;
|
||||
|
||||
struct ast_ddc {
|
||||
struct ast_device *ast;
|
||||
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_algo_bit_data bit;
|
||||
};
|
||||
|
||||
struct ast_ddc *ast_ddc_create(struct ast_device *ast);
|
||||
struct i2c_adapter *ast_ddc_create(struct ast_device *ast);
|
||||
|
||||
#endif
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_gem_shmem_helper.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_panic.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
|
||||
@ -701,12 +702,29 @@ static void ast_primary_plane_helper_atomic_disable(struct drm_plane *plane,
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x1, 0xdf, 0x20);
|
||||
}
|
||||
|
||||
static int ast_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
|
||||
struct drm_scanout_buffer *sb)
|
||||
{
|
||||
struct ast_plane *ast_plane = to_ast_plane(plane);
|
||||
|
||||
if (plane->state && plane->state->fb && ast_plane->vaddr) {
|
||||
sb->format = plane->state->fb->format;
|
||||
sb->width = plane->state->fb->width;
|
||||
sb->height = plane->state->fb->height;
|
||||
sb->pitch[0] = plane->state->fb->pitches[0];
|
||||
iosys_map_set_vaddr_iomem(&sb->map[0], ast_plane->vaddr);
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs ast_primary_plane_helper_funcs = {
|
||||
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
|
||||
.atomic_check = ast_primary_plane_helper_atomic_check,
|
||||
.atomic_update = ast_primary_plane_helper_atomic_update,
|
||||
.atomic_enable = ast_primary_plane_helper_atomic_enable,
|
||||
.atomic_disable = ast_primary_plane_helper_atomic_disable,
|
||||
.get_scanout_buffer = ast_primary_plane_helper_get_scanout_buffer,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs ast_primary_plane_funcs = {
|
||||
@ -1360,7 +1378,7 @@ static const struct drm_connector_funcs ast_vga_connector_funcs = {
|
||||
static int ast_vga_connector_init(struct drm_device *dev, struct drm_connector *connector)
|
||||
{
|
||||
struct ast_device *ast = to_ast_device(dev);
|
||||
struct ast_ddc *ddc;
|
||||
struct i2c_adapter *ddc;
|
||||
int ret;
|
||||
|
||||
ddc = ast_ddc_create(ast);
|
||||
@ -1371,7 +1389,7 @@ static int ast_vga_connector_init(struct drm_device *dev, struct drm_connector *
|
||||
}
|
||||
|
||||
ret = drm_connector_init_with_ddc(dev, connector, &ast_vga_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VGA, &ddc->adapter);
|
||||
DRM_MODE_CONNECTOR_VGA, ddc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1429,7 +1447,7 @@ static const struct drm_connector_funcs ast_sil164_connector_funcs = {
|
||||
static int ast_sil164_connector_init(struct drm_device *dev, struct drm_connector *connector)
|
||||
{
|
||||
struct ast_device *ast = to_ast_device(dev);
|
||||
struct ast_ddc *ddc;
|
||||
struct i2c_adapter *ddc;
|
||||
int ret;
|
||||
|
||||
ddc = ast_ddc_create(ast);
|
||||
@ -1440,7 +1458,7 @@ static int ast_sil164_connector_init(struct drm_device *dev, struct drm_connecto
|
||||
}
|
||||
|
||||
ret = drm_connector_init_with_ddc(dev, connector, &ast_sil164_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DVII, &ddc->adapter);
|
||||
DRM_MODE_CONNECTOR_DVII, ddc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -28,7 +28,7 @@ config DRM_ANALOGIX_ANX78XX
|
||||
|
||||
config DRM_ANALOGIX_DP
|
||||
tristate
|
||||
depends on DRM
|
||||
depends on DRM_DISPLAY_HELPER
|
||||
|
||||
config DRM_ANALOGIX_ANX7625
|
||||
tristate "Analogix Anx7625 MIPI to DP interface support"
|
||||
|
@ -2066,10 +2066,8 @@ static int anx7625_setup_dsi_device(struct anx7625_data *ctx)
|
||||
};
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(ctx->pdata.mipi_host_node);
|
||||
if (!host) {
|
||||
DRM_DEV_ERROR(dev, "fail to find dsi host.\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (!host)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "fail to find dsi host.\n");
|
||||
|
||||
dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
@ -2471,15 +2469,22 @@ static void anx7625_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
mutex_unlock(&ctx->aux_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
anx7625_audio_update_connector_status(struct anx7625_data *ctx,
|
||||
enum drm_connector_status status);
|
||||
|
||||
static enum drm_connector_status
|
||||
anx7625_bridge_detect(struct drm_bridge *bridge)
|
||||
{
|
||||
struct anx7625_data *ctx = bridge_to_anx7625(bridge);
|
||||
struct device *dev = ctx->dev;
|
||||
enum drm_connector_status status;
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "drm bridge detect\n");
|
||||
|
||||
return anx7625_sink_detect(ctx);
|
||||
status = anx7625_sink_detect(ctx);
|
||||
anx7625_audio_update_connector_status(ctx, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct drm_edid *anx7625_bridge_edid_read(struct drm_bridge *bridge,
|
||||
|
@ -563,10 +563,8 @@ static int chipone_dsi_host_attach(struct chipone *icn)
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(host_node);
|
||||
of_node_put(host_node);
|
||||
if (!host) {
|
||||
dev_err(dev, "failed to find dsi host\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (!host)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");
|
||||
|
||||
dsi = mipi_dsi_device_register_full(host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
|
@ -104,13 +104,11 @@ static int imx8mp_dw_hdmi_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mp_dw_hdmi_remove(struct platform_device *pdev)
|
||||
static void imx8mp_dw_hdmi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx8mp_hdmi *hdmi = platform_get_drvdata(pdev);
|
||||
|
||||
dw_hdmi_remove(hdmi->dw_hdmi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx8mp_dw_hdmi_pm_suspend(struct device *dev)
|
||||
@ -140,7 +138,7 @@ MODULE_DEVICE_TABLE(of, imx8mp_dw_hdmi_of_table);
|
||||
|
||||
static struct platform_driver imx8mp_dw_hdmi_platform_driver = {
|
||||
.probe = imx8mp_dw_hdmi_probe,
|
||||
.remove = imx8mp_dw_hdmi_remove,
|
||||
.remove_new = imx8mp_dw_hdmi_remove,
|
||||
.driver = {
|
||||
.name = "imx8mp-dw-hdmi-tx",
|
||||
.of_match_table = imx8mp_dw_hdmi_of_table,
|
||||
|
@ -494,10 +494,8 @@ static int lt8912_attach_dsi(struct lt8912 *lt)
|
||||
};
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(lt->host_node);
|
||||
if (!host) {
|
||||
dev_err(dev, "failed to find dsi host\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (!host)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");
|
||||
|
||||
dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
|
@ -761,10 +761,8 @@ static struct mipi_dsi_device *lt9611_attach_dsi(struct lt9611 *lt9611,
|
||||
int ret;
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(dsi_node);
|
||||
if (!host) {
|
||||
dev_err(lt9611->dev, "failed to find dsi host\n");
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
if (!host)
|
||||
return ERR_PTR(dev_err_probe(lt9611->dev, -EPROBE_DEFER, "failed to find dsi host\n"));
|
||||
|
||||
dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
|
@ -266,10 +266,8 @@ static struct mipi_dsi_device *lt9611uxc_attach_dsi(struct lt9611uxc *lt9611uxc,
|
||||
int ret;
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(dsi_node);
|
||||
if (!host) {
|
||||
dev_err(dev, "failed to find dsi host\n");
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
if (!host)
|
||||
return ERR_PTR(dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n"));
|
||||
|
||||
dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
|
@ -610,10 +610,8 @@ static int tc_attach_host(struct tc_data *tc)
|
||||
};
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(tc->host_node);
|
||||
if (!host) {
|
||||
dev_err(dev, "failed to find dsi host\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (!host)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");
|
||||
|
||||
dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
|
@ -319,12 +319,11 @@ static int dlpc_host_attach(struct dlpc *dlpc)
|
||||
.channel = 0,
|
||||
.node = NULL,
|
||||
};
|
||||
int ret;
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(dlpc->host_node);
|
||||
if (!host) {
|
||||
DRM_DEV_ERROR(dev, "failed to find dsi host\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (!host)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");
|
||||
|
||||
dlpc->dsi = mipi_dsi_device_register_full(host, &info);
|
||||
if (IS_ERR(dlpc->dsi)) {
|
||||
@ -336,7 +335,11 @@ static int dlpc_host_attach(struct dlpc *dlpc)
|
||||
dlpc->dsi->format = MIPI_DSI_FMT_RGB565;
|
||||
dlpc->dsi->lanes = dlpc->dsi_lanes;
|
||||
|
||||
return devm_mipi_dsi_attach(dev, dlpc->dsi);
|
||||
ret = devm_mipi_dsi_attach(dev, dlpc->dsi);
|
||||
if (ret)
|
||||
DRM_DEV_ERROR(dev, "failed to attach dsi host\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dlpc3433_probe(struct i2c_client *client)
|
||||
@ -367,10 +370,8 @@ static int dlpc3433_probe(struct i2c_client *client)
|
||||
drm_bridge_add(&dlpc->bridge);
|
||||
|
||||
ret = dlpc_host_attach(dlpc);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to attach dsi host\n");
|
||||
if (ret)
|
||||
goto err_remove_bridge;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_atomic_helper.h>
|
||||
#include <drm/drm_panic.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_self_refresh_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
@ -3016,6 +3017,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
bool stall)
|
||||
{
|
||||
int i, ret;
|
||||
unsigned long flags;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_state *old_conn_state, *new_conn_state;
|
||||
struct drm_crtc *crtc;
|
||||
@ -3099,6 +3101,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
}
|
||||
}
|
||||
|
||||
drm_panic_lock(state->dev, flags);
|
||||
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
|
||||
WARN_ON(plane->state != old_plane_state);
|
||||
|
||||
@ -3108,6 +3111,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
state->planes[i].state = old_plane_state;
|
||||
plane->state = new_plane_state;
|
||||
}
|
||||
drm_panic_unlock(state->dev, flags);
|
||||
|
||||
for_each_oldnew_private_obj_in_state(state, obj, old_obj_state, new_obj_state, i) {
|
||||
WARN_ON(obj->state != old_obj_state);
|
||||
|
@ -145,10 +145,10 @@ int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state,
|
||||
&state->mode, blob->data);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(crtc->dev,
|
||||
"[CRTC:%d:%s] invalid mode (ret=%d, status=%s):\n",
|
||||
"[CRTC:%d:%s] invalid mode (%s, %pe): " DRM_MODE_FMT "\n",
|
||||
crtc->base.id, crtc->name,
|
||||
ret, drm_get_mode_status_name(state->mode.status));
|
||||
drm_mode_debug_printmodeline(&state->mode);
|
||||
drm_get_mode_status_name(state->mode.status),
|
||||
ERR_PTR(ret), DRM_MODE_ARG(&state->mode));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -242,8 +242,10 @@ static void drm_client_connectors_enabled(struct drm_connector **connectors,
|
||||
for (i = 0; i < connector_count; i++) {
|
||||
connector = connectors[i];
|
||||
enabled[i] = drm_connector_enabled(connector, true);
|
||||
DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
|
||||
connector->display_info.non_desktop ? "non desktop" : str_yes_no(enabled[i]));
|
||||
drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] enabled? %s\n",
|
||||
connector->base.id, connector->name,
|
||||
connector->display_info.non_desktop ?
|
||||
"non desktop" : str_yes_no(enabled[i]));
|
||||
|
||||
any_enabled |= enabled[i];
|
||||
}
|
||||
@ -303,7 +305,7 @@ static bool drm_client_target_cloned(struct drm_device *dev,
|
||||
}
|
||||
|
||||
if (can_clone) {
|
||||
DRM_DEBUG_KMS("can clone using command line\n");
|
||||
drm_dbg_kms(dev, "can clone using command line\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -332,15 +334,16 @@ static bool drm_client_target_cloned(struct drm_device *dev,
|
||||
kfree(dmt_mode);
|
||||
|
||||
if (can_clone) {
|
||||
DRM_DEBUG_KMS("can clone using 1024x768\n");
|
||||
drm_dbg_kms(dev, "can clone using 1024x768\n");
|
||||
return true;
|
||||
}
|
||||
fail:
|
||||
DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
|
||||
drm_info(dev, "kms: can't enable cloning when we probably wanted to.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static int drm_client_get_tile_offsets(struct drm_connector **connectors,
|
||||
static int drm_client_get_tile_offsets(struct drm_device *dev,
|
||||
struct drm_connector **connectors,
|
||||
unsigned int connector_count,
|
||||
struct drm_display_mode **modes,
|
||||
struct drm_client_offset *offsets,
|
||||
@ -357,8 +360,9 @@ static int drm_client_get_tile_offsets(struct drm_connector **connectors,
|
||||
continue;
|
||||
|
||||
if (!modes[i] && (h_idx || v_idx)) {
|
||||
DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
|
||||
connector->base.id);
|
||||
drm_dbg_kms(dev,
|
||||
"[CONNECTOR:%d:%s] no modes for connector tiled %d\n",
|
||||
connector->base.id, connector->name, i);
|
||||
continue;
|
||||
}
|
||||
if (connector->tile_h_loc < h_idx)
|
||||
@ -369,11 +373,12 @@ static int drm_client_get_tile_offsets(struct drm_connector **connectors,
|
||||
}
|
||||
offsets[idx].x = hoffset;
|
||||
offsets[idx].y = voffset;
|
||||
DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
|
||||
drm_dbg_kms(dev, "returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool drm_client_target_preferred(struct drm_connector **connectors,
|
||||
static bool drm_client_target_preferred(struct drm_device *dev,
|
||||
struct drm_connector **connectors,
|
||||
unsigned int connector_count,
|
||||
struct drm_display_mode **modes,
|
||||
struct drm_client_offset *offsets,
|
||||
@ -423,17 +428,19 @@ retry:
|
||||
* find the tile offsets for this pass - need to find
|
||||
* all tiles left and above
|
||||
*/
|
||||
drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i,
|
||||
drm_client_get_tile_offsets(dev, connectors, connector_count,
|
||||
modes, offsets, i,
|
||||
connector->tile_h_loc, connector->tile_v_loc);
|
||||
}
|
||||
DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
|
||||
connector->base.id);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] looking for cmdline mode\n",
|
||||
connector->base.id, connector->name);
|
||||
|
||||
/* got for command line mode first */
|
||||
modes[i] = drm_connector_pick_cmdline_mode(connector);
|
||||
if (!modes[i]) {
|
||||
DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
|
||||
connector->base.id, connector->tile_group ? connector->tile_group->id : 0);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] looking for preferred mode, tile %d\n",
|
||||
connector->base.id, connector->name,
|
||||
connector->tile_group ? connector->tile_group->id : 0);
|
||||
modes[i] = drm_connector_has_preferred_mode(connector, width, height);
|
||||
}
|
||||
/* No preferred modes, pick one off the list */
|
||||
@ -455,16 +462,18 @@ retry:
|
||||
(connector->tile_h_loc == 0 &&
|
||||
connector->tile_v_loc == 0 &&
|
||||
!drm_connector_get_tiled_mode(connector))) {
|
||||
DRM_DEBUG_KMS("Falling back to non tiled mode on Connector %d\n",
|
||||
connector->base.id);
|
||||
drm_dbg_kms(dev,
|
||||
"[CONNECTOR:%d:%s] Falling back to non-tiled mode\n",
|
||||
connector->base.id, connector->name);
|
||||
modes[i] = drm_connector_fallback_non_tiled_mode(connector);
|
||||
} else {
|
||||
modes[i] = drm_connector_get_tiled_mode(connector);
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
|
||||
"none");
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Found mode %s\n",
|
||||
connector->base.id, connector->name,
|
||||
modes[i] ? modes[i]->name : "none");
|
||||
conn_configured |= BIT_ULL(i);
|
||||
}
|
||||
|
||||
@ -585,7 +594,7 @@ static bool drm_client_firmware_config(struct drm_client_dev *client,
|
||||
if (!drm_drv_uses_atomic_modeset(dev))
|
||||
return false;
|
||||
|
||||
if (WARN_ON(count <= 0))
|
||||
if (drm_WARN_ON(dev, count <= 0))
|
||||
return false;
|
||||
|
||||
save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL);
|
||||
@ -624,26 +633,26 @@ retry:
|
||||
num_connectors_detected++;
|
||||
|
||||
if (!enabled[i]) {
|
||||
DRM_DEBUG_KMS("connector %s not enabled, skipping\n",
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] not enabled, skipping\n",
|
||||
connector->base.id, connector->name);
|
||||
conn_configured |= BIT(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connector->force == DRM_FORCE_OFF) {
|
||||
DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n",
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] disabled by user, skipping\n",
|
||||
connector->base.id, connector->name);
|
||||
enabled[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
encoder = connector->state->best_encoder;
|
||||
if (!encoder || WARN_ON(!connector->state->crtc)) {
|
||||
if (!encoder || drm_WARN_ON(dev, !connector->state->crtc)) {
|
||||
if (connector->force > DRM_FORCE_OFF)
|
||||
goto bail;
|
||||
|
||||
DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] has no encoder or crtc, skipping\n",
|
||||
connector->base.id, connector->name);
|
||||
enabled[i] = false;
|
||||
conn_configured |= BIT(i);
|
||||
continue;
|
||||
@ -660,28 +669,30 @@ retry:
|
||||
*/
|
||||
for (j = 0; j < count; j++) {
|
||||
if (crtcs[j] == new_crtc) {
|
||||
DRM_DEBUG_KMS("fallback: cloned configuration\n");
|
||||
drm_dbg_kms(dev, "fallback: cloned configuration\n");
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n",
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] looking for cmdline mode\n",
|
||||
connector->base.id, connector->name);
|
||||
|
||||
/* go for command line mode first */
|
||||
modes[i] = drm_connector_pick_cmdline_mode(connector);
|
||||
|
||||
/* try for preferred next */
|
||||
if (!modes[i]) {
|
||||
DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n",
|
||||
connector->name, connector->has_tile);
|
||||
drm_dbg_kms(dev,
|
||||
"[CONNECTOR:%d:%s] looking for preferred mode, has tile: %s\n",
|
||||
connector->base.id, connector->name,
|
||||
str_yes_no(connector->has_tile));
|
||||
modes[i] = drm_connector_has_preferred_mode(connector, width, height);
|
||||
}
|
||||
|
||||
/* No preferred mode marked by the EDID? Are there any modes? */
|
||||
if (!modes[i] && !list_empty(&connector->modes)) {
|
||||
DRM_DEBUG_KMS("using first mode listed on connector %s\n",
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] using first listed mode\n",
|
||||
connector->base.id, connector->name);
|
||||
modes[i] = list_first_entry(&connector->modes,
|
||||
struct drm_display_mode,
|
||||
head);
|
||||
@ -700,8 +711,8 @@ retry:
|
||||
* This is crtc->mode and not crtc->state->mode for the
|
||||
* fastboot check to work correctly.
|
||||
*/
|
||||
DRM_DEBUG_KMS("looking for current mode on connector %s\n",
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] looking for current mode\n",
|
||||
connector->base.id, connector->name);
|
||||
modes[i] = &connector->state->crtc->mode;
|
||||
}
|
||||
/*
|
||||
@ -710,18 +721,18 @@ retry:
|
||||
*/
|
||||
if (connector->has_tile &&
|
||||
num_tiled_conns < connector->num_h_tile * connector->num_v_tile) {
|
||||
DRM_DEBUG_KMS("Falling back to non tiled mode on Connector %d\n",
|
||||
connector->base.id);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Falling back to non-tiled mode\n",
|
||||
connector->base.id, connector->name);
|
||||
modes[i] = drm_connector_fallback_non_tiled_mode(connector);
|
||||
}
|
||||
crtcs[i] = new_crtc;
|
||||
|
||||
DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n",
|
||||
connector->name,
|
||||
connector->state->crtc->base.id,
|
||||
connector->state->crtc->name,
|
||||
modes[i]->hdisplay, modes[i]->vdisplay,
|
||||
modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "");
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] on [CRTC:%d:%s]: %dx%d%s\n",
|
||||
connector->base.id, connector->name,
|
||||
connector->state->crtc->base.id,
|
||||
connector->state->crtc->name,
|
||||
modes[i]->hdisplay, modes[i]->vdisplay,
|
||||
modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "");
|
||||
|
||||
fallback = false;
|
||||
conn_configured |= BIT(i);
|
||||
@ -737,15 +748,15 @@ retry:
|
||||
*/
|
||||
if (num_connectors_enabled != num_connectors_detected &&
|
||||
num_connectors_enabled < dev->mode_config.num_crtc) {
|
||||
DRM_DEBUG_KMS("fallback: Not all outputs enabled\n");
|
||||
DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled,
|
||||
num_connectors_detected);
|
||||
drm_dbg_kms(dev, "fallback: Not all outputs enabled\n");
|
||||
drm_dbg_kms(dev, "Enabled: %i, detected: %i\n",
|
||||
num_connectors_enabled, num_connectors_detected);
|
||||
fallback = true;
|
||||
}
|
||||
|
||||
if (fallback) {
|
||||
bail:
|
||||
DRM_DEBUG_KMS("Not using firmware configuration\n");
|
||||
drm_dbg_kms(dev, "Not using firmware configuration\n");
|
||||
memcpy(enabled, save_enabled, count);
|
||||
ret = false;
|
||||
}
|
||||
@ -782,7 +793,7 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width,
|
||||
int i, ret = 0;
|
||||
bool *enabled;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
drm_dbg_kms(dev, "\n");
|
||||
|
||||
if (!width)
|
||||
width = dev->mode_config.max_width;
|
||||
@ -813,7 +824,6 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width,
|
||||
offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL);
|
||||
enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL);
|
||||
if (!crtcs || !modes || !enabled || !offsets) {
|
||||
DRM_ERROR("Memory allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
@ -824,7 +834,7 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width,
|
||||
for (i = 0; i < connector_count; i++)
|
||||
total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height);
|
||||
if (!total_modes_count)
|
||||
DRM_DEBUG_KMS("No connectors reported connected with modes\n");
|
||||
drm_dbg_kms(dev, "No connectors reported connected with modes\n");
|
||||
drm_client_connectors_enabled(connectors, connector_count, enabled);
|
||||
|
||||
if (!drm_client_firmware_config(client, connectors, connector_count, crtcs,
|
||||
@ -835,12 +845,12 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width,
|
||||
|
||||
if (!drm_client_target_cloned(dev, connectors, connector_count, modes,
|
||||
offsets, enabled, width, height) &&
|
||||
!drm_client_target_preferred(connectors, connector_count, modes,
|
||||
!drm_client_target_preferred(dev, connectors, connector_count, modes,
|
||||
offsets, enabled, width, height))
|
||||
DRM_ERROR("Unable to find initial modes\n");
|
||||
drm_err(dev, "Unable to find initial modes\n");
|
||||
|
||||
DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
|
||||
width, height);
|
||||
drm_dbg_kms(dev, "picking CRTCs for %dx%d config\n",
|
||||
width, height);
|
||||
|
||||
drm_client_pick_crtcs(client, connectors, connector_count,
|
||||
crtcs, modes, 0, width, height);
|
||||
@ -858,11 +868,12 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width,
|
||||
struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc);
|
||||
struct drm_connector *connector = connectors[i];
|
||||
|
||||
DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
|
||||
mode->name, crtc->base.id, offset->x, offset->y);
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s] desired mode %s set (%d,%d)\n",
|
||||
crtc->base.id, crtc->name,
|
||||
mode->name, offset->x, offset->y);
|
||||
|
||||
if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS ||
|
||||
(dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) {
|
||||
if (drm_WARN_ON_ONCE(dev, modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS ||
|
||||
(dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
@ -716,10 +716,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
|
||||
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);
|
||||
drm_dbg_kms(dev, "Unknown CRTC ID %d\n", crtc_req->crtc_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
|
||||
|
||||
plane = crtc->primary;
|
||||
|
||||
@ -742,7 +742,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
old_fb = plane->fb;
|
||||
|
||||
if (!old_fb) {
|
||||
DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
|
||||
drm_dbg_kms(dev, "CRTC doesn't have current FB\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -753,8 +753,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
} else {
|
||||
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);
|
||||
drm_dbg_kms(dev, "Unknown FB ID%d\n",
|
||||
crtc_req->fb_id);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
@ -767,7 +767,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
}
|
||||
if (!file_priv->aspect_ratio_allowed &&
|
||||
(crtc_req->mode.flags & DRM_MODE_FLAG_PIC_AR_MASK) != DRM_MODE_FLAG_PIC_AR_NONE) {
|
||||
DRM_DEBUG_KMS("Unexpected aspect-ratio flag bits\n");
|
||||
drm_dbg_kms(dev, "Unexpected aspect-ratio flag bits\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -775,9 +775,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
|
||||
ret = drm_mode_convert_umode(dev, mode, &crtc_req->mode);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("Invalid mode (ret=%d, status=%s)\n",
|
||||
ret, drm_get_mode_status_name(mode->status));
|
||||
drm_mode_debug_printmodeline(mode);
|
||||
drm_dbg_kms(dev, "Invalid mode (%s, %pe): " DRM_MODE_FMT "\n",
|
||||
drm_get_mode_status_name(mode->status),
|
||||
ERR_PTR(ret), DRM_MODE_ARG(mode));
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -793,9 +793,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
fb->format->format,
|
||||
fb->modifier);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("Invalid pixel format %p4cc, modifier 0x%llx\n",
|
||||
&fb->format->format,
|
||||
fb->modifier);
|
||||
drm_dbg_kms(dev, "Invalid pixel format %p4cc, modifier 0x%llx\n",
|
||||
&fb->format->format, fb->modifier);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -808,14 +807,14 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
}
|
||||
|
||||
if (crtc_req->count_connectors == 0 && mode) {
|
||||
DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
|
||||
drm_dbg_kms(dev, "Count connectors is 0 but mode set\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
|
||||
DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
|
||||
crtc_req->count_connectors);
|
||||
drm_dbg_kms(dev, "Count connectors is %d but no mode or fb set\n",
|
||||
crtc_req->count_connectors);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -847,14 +846,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
|
||||
connector = drm_connector_lookup(dev, file_priv, out_id);
|
||||
if (!connector) {
|
||||
DRM_DEBUG_KMS("Connector id %d unknown\n",
|
||||
out_id);
|
||||
drm_dbg_kms(dev, "Connector id %d unknown\n",
|
||||
out_id);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
|
||||
connector->base.id,
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s]\n",
|
||||
connector->base.id, connector->name);
|
||||
|
||||
connector_set[i] = connector;
|
||||
num_connectors++;
|
||||
|
@ -110,15 +110,15 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
struct drm_device *dev = encoder->dev;
|
||||
|
||||
WARN_ON(drm_drv_uses_atomic_modeset(dev));
|
||||
drm_WARN_ON(dev, drm_drv_uses_atomic_modeset(dev));
|
||||
|
||||
/*
|
||||
* We can expect this mutex to be locked if we are not panicking.
|
||||
* Locking is currently fubar in the panic handler.
|
||||
*/
|
||||
if (!oops_in_progress) {
|
||||
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
||||
WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
|
||||
drm_WARN_ON(dev, !mutex_is_locked(&dev->mode_config.mutex));
|
||||
drm_WARN_ON(dev, !drm_modeset_is_locked(&dev->mode_config.connection_mutex));
|
||||
}
|
||||
|
||||
|
||||
@ -150,14 +150,14 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
|
||||
WARN_ON(drm_drv_uses_atomic_modeset(dev));
|
||||
drm_WARN_ON(dev, drm_drv_uses_atomic_modeset(dev));
|
||||
|
||||
/*
|
||||
* We can expect this mutex to be locked if we are not panicking.
|
||||
* Locking is currently fubar in the panic handler.
|
||||
*/
|
||||
if (!oops_in_progress)
|
||||
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
||||
drm_WARN_ON(dev, !mutex_is_locked(&dev->mode_config.mutex));
|
||||
|
||||
drm_for_each_encoder(encoder, dev)
|
||||
if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))
|
||||
@ -230,7 +230,7 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev)
|
||||
*/
|
||||
void drm_helper_disable_unused_functions(struct drm_device *dev)
|
||||
{
|
||||
WARN_ON(drm_drv_uses_atomic_modeset(dev));
|
||||
drm_WARN_ON(dev, drm_drv_uses_atomic_modeset(dev));
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
__drm_helper_disable_unused_functions(dev);
|
||||
@ -294,7 +294,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
struct drm_encoder *encoder;
|
||||
bool ret = true;
|
||||
|
||||
WARN_ON(drm_drv_uses_atomic_modeset(dev));
|
||||
drm_WARN_ON(dev, drm_drv_uses_atomic_modeset(dev));
|
||||
|
||||
drm_warn_on_modeset_not_all_locked(dev);
|
||||
|
||||
@ -338,7 +338,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
if (encoder_funcs->mode_fixup) {
|
||||
if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
|
||||
adjusted_mode))) {
|
||||
DRM_DEBUG_KMS("Encoder fixup failed\n");
|
||||
drm_dbg_kms(dev, "[ENCODER:%d:%s] mode fixup failed\n",
|
||||
encoder->base.id, encoder->name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
@ -347,11 +348,12 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
if (crtc_funcs->mode_fixup) {
|
||||
if (!(ret = crtc_funcs->mode_fixup(crtc, mode,
|
||||
adjusted_mode))) {
|
||||
DRM_DEBUG_KMS("CRTC fixup failed\n");
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s] mode fixup failed\n",
|
||||
crtc->base.id, crtc->name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
|
||||
|
||||
drm_mode_copy(&crtc->hwmode, adjusted_mode);
|
||||
|
||||
@ -390,8 +392,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
if (!encoder_funcs)
|
||||
continue;
|
||||
|
||||
DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%s]\n",
|
||||
encoder->base.id, encoder->name, mode->name);
|
||||
drm_dbg_kms(dev, "[ENCODER:%d:%s] set [MODE:%s]\n",
|
||||
encoder->base.id, encoder->name, mode->name);
|
||||
if (encoder_funcs->mode_set)
|
||||
encoder_funcs->mode_set(encoder, mode, adjusted_mode);
|
||||
}
|
||||
@ -503,7 +505,7 @@ drm_connector_get_single_encoder(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
WARN_ON(hweight32(connector->possible_encoders) > 1);
|
||||
drm_WARN_ON(connector->dev, hweight32(connector->possible_encoders) > 1);
|
||||
drm_connector_for_each_possible_encoder(connector, encoder)
|
||||
return encoder;
|
||||
|
||||
@ -564,8 +566,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
BUG_ON(!set);
|
||||
BUG_ON(!set->crtc);
|
||||
BUG_ON(!set->crtc->helper_private);
|
||||
@ -577,19 +577,22 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
crtc_funcs = set->crtc->helper_private;
|
||||
|
||||
dev = set->crtc->dev;
|
||||
WARN_ON(drm_drv_uses_atomic_modeset(dev));
|
||||
|
||||
drm_dbg_kms(dev, "\n");
|
||||
|
||||
drm_WARN_ON(dev, drm_drv_uses_atomic_modeset(dev));
|
||||
|
||||
if (!set->mode)
|
||||
set->fb = NULL;
|
||||
|
||||
if (set->fb) {
|
||||
DRM_DEBUG_KMS("[CRTC:%d:%s] [FB:%d] #connectors=%d (x y) (%i %i)\n",
|
||||
set->crtc->base.id, set->crtc->name,
|
||||
set->fb->base.id,
|
||||
(int)set->num_connectors, set->x, set->y);
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s] [FB:%d] #connectors=%d (x y) (%i %i)\n",
|
||||
set->crtc->base.id, set->crtc->name,
|
||||
set->fb->base.id,
|
||||
(int)set->num_connectors, set->x, set->y);
|
||||
} else {
|
||||
DRM_DEBUG_KMS("[CRTC:%d:%s] [NOFB]\n",
|
||||
set->crtc->base.id, set->crtc->name);
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s] [NOFB]\n",
|
||||
set->crtc->base.id, set->crtc->name);
|
||||
drm_crtc_helper_disable(set->crtc);
|
||||
return 0;
|
||||
}
|
||||
@ -639,7 +642,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
if (set->crtc->primary->fb != set->fb) {
|
||||
/* If we have no fb then treat it as a full mode set */
|
||||
if (set->crtc->primary->fb == NULL) {
|
||||
DRM_DEBUG_KMS("crtc has no fb, full mode set\n");
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s] no fb, full mode set\n",
|
||||
set->crtc->base.id, set->crtc->name);
|
||||
mode_changed = true;
|
||||
} else if (set->fb->format != set->crtc->primary->fb->format) {
|
||||
mode_changed = true;
|
||||
@ -651,9 +655,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
fb_changed = true;
|
||||
|
||||
if (!drm_mode_equal(set->mode, &set->crtc->mode)) {
|
||||
DRM_DEBUG_KMS("modes are different, full mode set\n");
|
||||
drm_mode_debug_printmodeline(&set->crtc->mode);
|
||||
drm_mode_debug_printmodeline(set->mode);
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s] modes are different, full mode set:\n",
|
||||
set->crtc->base.id, set->crtc->name);
|
||||
drm_dbg_kms(dev, DRM_MODE_FMT "\n", DRM_MODE_ARG(&set->crtc->mode));
|
||||
drm_dbg_kms(dev, DRM_MODE_FMT "\n", DRM_MODE_ARG(set->mode));
|
||||
mode_changed = true;
|
||||
}
|
||||
|
||||
@ -687,7 +692,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
fail = 1;
|
||||
|
||||
if (connector->dpms != DRM_MODE_DPMS_ON) {
|
||||
DRM_DEBUG_KMS("connector dpms not on, full mode switch\n");
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] DPMS not on, full mode switch\n",
|
||||
connector->base.id, connector->name);
|
||||
mode_changed = true;
|
||||
}
|
||||
|
||||
@ -696,7 +702,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
}
|
||||
|
||||
if (new_encoder != connector->encoder) {
|
||||
DRM_DEBUG_KMS("encoder changed, full mode switch\n");
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] encoder changed, full mode switch\n",
|
||||
connector->base.id, connector->name);
|
||||
mode_changed = true;
|
||||
/* If the encoder is reused for another connector, then
|
||||
* the appropriate crtc will be set later.
|
||||
@ -737,17 +744,18 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
goto fail;
|
||||
}
|
||||
if (new_crtc != connector->encoder->crtc) {
|
||||
DRM_DEBUG_KMS("crtc changed, full mode switch\n");
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] CRTC changed, full mode switch\n",
|
||||
connector->base.id, connector->name);
|
||||
mode_changed = true;
|
||||
connector->encoder->crtc = new_crtc;
|
||||
}
|
||||
if (new_crtc) {
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d:%s]\n",
|
||||
connector->base.id, connector->name,
|
||||
new_crtc->base.id, new_crtc->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] to [CRTC:%d:%s]\n",
|
||||
connector->base.id, connector->name,
|
||||
new_crtc->base.id, new_crtc->name);
|
||||
} else {
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
|
||||
connector->base.id, connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] to [NOCRTC]\n",
|
||||
connector->base.id, connector->name);
|
||||
}
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
@ -758,23 +766,23 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
|
||||
|
||||
if (mode_changed) {
|
||||
if (drm_helper_crtc_in_use(set->crtc)) {
|
||||
DRM_DEBUG_KMS("attempting to set mode from"
|
||||
" userspace\n");
|
||||
drm_mode_debug_printmodeline(set->mode);
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s] attempting to set mode from userspace: " DRM_MODE_FMT "\n",
|
||||
set->crtc->base.id, set->crtc->name, DRM_MODE_ARG(set->mode));
|
||||
set->crtc->primary->fb = set->fb;
|
||||
if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
|
||||
set->x, set->y,
|
||||
save_set.fb)) {
|
||||
DRM_ERROR("failed to set mode on [CRTC:%d:%s]\n",
|
||||
set->crtc->base.id, set->crtc->name);
|
||||
drm_err(dev, "[CRTC:%d:%s] failed to set mode\n",
|
||||
set->crtc->base.id, set->crtc->name);
|
||||
set->crtc->primary->fb = save_set.fb;
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
|
||||
drm_dbg_kms(dev, "[CRTC:%d:%s] Setting connector DPMS state to on\n",
|
||||
set->crtc->base.id, set->crtc->name);
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
|
||||
set->connectors[i]->name);
|
||||
drm_dbg_kms(dev, "\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
|
||||
set->connectors[i]->name);
|
||||
set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
|
||||
}
|
||||
}
|
||||
@ -823,7 +831,7 @@ fail:
|
||||
if (mode_changed &&
|
||||
!drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x,
|
||||
save_set.y, save_set.fb))
|
||||
DRM_ERROR("failed to restore config after modeset failure\n");
|
||||
drm_err(dev, "failed to restore config after modeset failure\n");
|
||||
|
||||
kfree(save_connector_encoders);
|
||||
kfree(save_encoder_crtcs);
|
||||
@ -905,7 +913,7 @@ int drm_helper_connector_dpms(struct drm_connector *connector, int mode)
|
||||
struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
|
||||
int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
WARN_ON(drm_drv_uses_atomic_modeset(connector->dev));
|
||||
drm_WARN_ON(connector->dev, drm_drv_uses_atomic_modeset(connector->dev));
|
||||
|
||||
if (mode == connector->dpms)
|
||||
return 0;
|
||||
@ -980,7 +988,7 @@ void drm_helper_resume_force_mode(struct drm_device *dev)
|
||||
int encoder_dpms;
|
||||
bool ret;
|
||||
|
||||
WARN_ON(drm_drv_uses_atomic_modeset(dev));
|
||||
drm_WARN_ON(dev, drm_drv_uses_atomic_modeset(dev));
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
drm_for_each_crtc(crtc, dev) {
|
||||
@ -993,7 +1001,7 @@ void drm_helper_resume_force_mode(struct drm_device *dev)
|
||||
|
||||
/* Restoring the old config should never fail! */
|
||||
if (ret == false)
|
||||
DRM_ERROR("failed to set mode on crtc %p\n", crtc);
|
||||
drm_err(dev, "failed to set mode on crtc %p\n", crtc);
|
||||
|
||||
/* Turn off outputs that were already powered off */
|
||||
if (drm_helper_choose_crtc_dpms(crtc)) {
|
||||
|
@ -43,12 +43,14 @@ enum drm_color_range;
|
||||
enum drm_connector_force;
|
||||
enum drm_mode_status;
|
||||
|
||||
struct cea_sad;
|
||||
struct drm_atomic_state;
|
||||
struct drm_bridge;
|
||||
struct drm_connector;
|
||||
struct drm_crtc;
|
||||
struct drm_device;
|
||||
struct drm_display_mode;
|
||||
struct drm_edid;
|
||||
struct drm_file;
|
||||
struct drm_framebuffer;
|
||||
struct drm_mode_create_dumb;
|
||||
@ -297,6 +299,10 @@ void drm_mode_fixup_1366x768(struct drm_display_mode *mode);
|
||||
int drm_edid_override_show(struct drm_connector *connector, struct seq_file *m);
|
||||
int drm_edid_override_set(struct drm_connector *connector, const void *edid, size_t size);
|
||||
int drm_edid_override_reset(struct drm_connector *connector);
|
||||
const u8 *drm_edid_find_extension(const struct drm_edid *drm_edid,
|
||||
int ext_id, int *ext_index);
|
||||
void drm_edid_cta_sad_get(const struct cea_sad *cta_sad, u8 *sad);
|
||||
void drm_edid_cta_sad_set(struct cea_sad *cta_sad, const u8 *sad);
|
||||
|
||||
/* drm_edid_load.c */
|
||||
#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
|
||||
|
@ -3,10 +3,12 @@
|
||||
* Copyright © 2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <drm/drm_displayid.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "drm_crtc_internal.h"
|
||||
#include "drm_displayid_internal.h"
|
||||
|
||||
static const struct displayid_header *
|
||||
displayid_get_header(const u8 *displayid, int length, int index)
|
||||
{
|
||||
@ -53,9 +55,10 @@ static const u8 *drm_find_displayid_extension(const struct drm_edid *drm_edid,
|
||||
int *length, int *idx,
|
||||
int *ext_index)
|
||||
{
|
||||
const u8 *displayid = drm_find_edid_extension(drm_edid, DISPLAYID_EXT, ext_index);
|
||||
const struct displayid_header *base;
|
||||
const u8 *displayid;
|
||||
|
||||
displayid = drm_edid_find_extension(drm_edid, DISPLAYID_EXT, ext_index);
|
||||
if (!displayid)
|
||||
return NULL;
|
||||
|
||||
|
@ -19,8 +19,9 @@
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef DRM_DISPLAYID_H
|
||||
#define DRM_DISPLAYID_H
|
||||
|
||||
#ifndef __DRM_DISPLAYID_INTERNAL_H__
|
||||
#define __DRM_DISPLAYID_INTERNAL_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/bits.h>
|
@ -43,6 +43,7 @@
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_mode_object.h>
|
||||
#include <drm/drm_panic.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_privacy_screen_machine.h>
|
||||
|
||||
@ -638,6 +639,7 @@ static int drm_dev_init(struct drm_device *dev,
|
||||
mutex_init(&dev->filelist_mutex);
|
||||
mutex_init(&dev->clientlist_mutex);
|
||||
mutex_init(&dev->master_mutex);
|
||||
raw_spin_lock_init(&dev->mode_config.panic_lock);
|
||||
|
||||
ret = drmm_add_action_or_reset(dev, drm_dev_init_release, NULL);
|
||||
if (ret)
|
||||
@ -943,6 +945,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
|
||||
if (ret)
|
||||
goto err_unload;
|
||||
}
|
||||
drm_panic_register(dev);
|
||||
|
||||
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
|
||||
driver->name, driver->major, driver->minor,
|
||||
@ -987,6 +990,8 @@ void drm_dev_unregister(struct drm_device *dev)
|
||||
{
|
||||
dev->registered = false;
|
||||
|
||||
drm_panic_unregister(dev);
|
||||
|
||||
drm_client_dev_unregister(dev);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
|
@ -29,16 +29,17 @@
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/cec.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vga_switcheroo.h>
|
||||
|
||||
#include <drm/drm_displayid.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_eld.h>
|
||||
@ -46,6 +47,7 @@
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "drm_crtc_internal.h"
|
||||
#include "drm_displayid_internal.h"
|
||||
#include "drm_internal.h"
|
||||
|
||||
static int oui(u8 first, u8 second, u8 third)
|
||||
@ -1818,36 +1820,25 @@ static bool edid_block_is_zero(const void *edid)
|
||||
return !memchr_inv(edid, 0, EDID_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_edid_are_equal - compare two edid blobs.
|
||||
* @edid1: pointer to first blob
|
||||
* @edid2: pointer to second blob
|
||||
* This helper can be used during probing to determine if
|
||||
* edid had changed.
|
||||
*/
|
||||
bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2)
|
||||
static bool drm_edid_eq(const struct drm_edid *drm_edid,
|
||||
const void *raw_edid, size_t raw_edid_size)
|
||||
{
|
||||
int edid1_len, edid2_len;
|
||||
bool edid1_present = edid1 != NULL;
|
||||
bool edid2_present = edid2 != NULL;
|
||||
bool edid1_present = drm_edid && drm_edid->edid && drm_edid->size;
|
||||
bool edid2_present = raw_edid && raw_edid_size;
|
||||
|
||||
if (edid1_present != edid2_present)
|
||||
return false;
|
||||
|
||||
if (edid1) {
|
||||
edid1_len = edid_size(edid1);
|
||||
edid2_len = edid_size(edid2);
|
||||
|
||||
if (edid1_len != edid2_len)
|
||||
if (edid1_present) {
|
||||
if (drm_edid->size != raw_edid_size)
|
||||
return false;
|
||||
|
||||
if (memcmp(edid1, edid2, edid1_len))
|
||||
if (memcmp(drm_edid->edid, raw_edid, drm_edid->size))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_are_equal);
|
||||
|
||||
enum edid_block_status {
|
||||
EDID_BLOCK_OK = 0,
|
||||
@ -2756,6 +2747,63 @@ const struct drm_edid *drm_edid_read(struct drm_connector *connector)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_read);
|
||||
|
||||
/**
|
||||
* drm_edid_get_product_id - Get the vendor and product identification
|
||||
* @drm_edid: EDID
|
||||
* @id: Where to place the product id
|
||||
*/
|
||||
void drm_edid_get_product_id(const struct drm_edid *drm_edid,
|
||||
struct drm_edid_product_id *id)
|
||||
{
|
||||
if (drm_edid && drm_edid->edid && drm_edid->size >= EDID_LENGTH)
|
||||
memcpy(id, &drm_edid->edid->product_id, sizeof(*id));
|
||||
else
|
||||
memset(id, 0, sizeof(*id));
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_get_product_id);
|
||||
|
||||
static void decode_date(struct seq_buf *s, const struct drm_edid_product_id *id)
|
||||
{
|
||||
int week = id->week_of_manufacture;
|
||||
int year = id->year_of_manufacture + 1990;
|
||||
|
||||
if (week == 0xff)
|
||||
seq_buf_printf(s, "model year: %d", year);
|
||||
else if (!week)
|
||||
seq_buf_printf(s, "year of manufacture: %d", year);
|
||||
else
|
||||
seq_buf_printf(s, "week/year of manufacture: %d/%d", week, year);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_edid_print_product_id - Print decoded product id to printer
|
||||
* @p: drm printer
|
||||
* @id: EDID product id
|
||||
* @raw: If true, also print the raw hex
|
||||
*
|
||||
* See VESA E-EDID 1.4 section 3.4.
|
||||
*/
|
||||
void drm_edid_print_product_id(struct drm_printer *p,
|
||||
const struct drm_edid_product_id *id, bool raw)
|
||||
{
|
||||
DECLARE_SEQ_BUF(date, 40);
|
||||
char vend[4];
|
||||
|
||||
drm_edid_decode_mfg_id(be16_to_cpu(id->manufacturer_name), vend);
|
||||
|
||||
decode_date(&date, id);
|
||||
|
||||
drm_printf(p, "manufacturer name: %s, product code: %u, serial number: %u, %s\n",
|
||||
vend, le16_to_cpu(id->product_code),
|
||||
le32_to_cpu(id->serial_number), seq_buf_str(&date));
|
||||
|
||||
if (raw)
|
||||
drm_printf(p, "raw product id: %*ph\n", (int)sizeof(*id), id);
|
||||
|
||||
WARN_ON(seq_buf_has_overflowed(&date));
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_print_product_id);
|
||||
|
||||
/**
|
||||
* drm_edid_get_panel_id - Get a panel's ID from EDID
|
||||
* @drm_edid: EDID that contains panel ID.
|
||||
@ -4141,7 +4189,7 @@ static int add_detailed_modes(struct drm_connector *connector,
|
||||
*
|
||||
* FIXME: Prefer not returning pointers to raw EDID data.
|
||||
*/
|
||||
const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
|
||||
const u8 *drm_edid_find_extension(const struct drm_edid *drm_edid,
|
||||
int ext_id, int *ext_index)
|
||||
{
|
||||
const u8 *edid_ext = NULL;
|
||||
@ -4171,11 +4219,21 @@ static bool drm_edid_has_cta_extension(const struct drm_edid *drm_edid)
|
||||
{
|
||||
const struct displayid_block *block;
|
||||
struct displayid_iter iter;
|
||||
int ext_index = 0;
|
||||
struct drm_edid_iter edid_iter;
|
||||
const u8 *ext;
|
||||
bool found = false;
|
||||
|
||||
/* Look for a top level CEA extension block */
|
||||
if (drm_find_edid_extension(drm_edid, CEA_EXT, &ext_index))
|
||||
drm_edid_iter_begin(drm_edid, &edid_iter);
|
||||
drm_edid_iter_for_each(ext, &edid_iter) {
|
||||
if (ext[0] == CEA_EXT) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
drm_edid_iter_end(&edid_iter);
|
||||
|
||||
if (found)
|
||||
return true;
|
||||
|
||||
/* CEA blocks can also be found embedded in a DisplayID block */
|
||||
@ -6868,15 +6926,14 @@ static int _drm_edid_connector_property_update(struct drm_connector *connector,
|
||||
int ret;
|
||||
|
||||
if (connector->edid_blob_ptr) {
|
||||
const struct edid *old_edid = connector->edid_blob_ptr->data;
|
||||
const void *old_edid = connector->edid_blob_ptr->data;
|
||||
size_t old_edid_size = connector->edid_blob_ptr->length;
|
||||
|
||||
if (old_edid) {
|
||||
if (!drm_edid_are_equal(drm_edid ? drm_edid->edid : NULL, old_edid)) {
|
||||
connector->epoch_counter++;
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID changed, epoch counter %llu\n",
|
||||
connector->base.id, connector->name,
|
||||
connector->epoch_counter);
|
||||
}
|
||||
if (old_edid && !drm_edid_eq(drm_edid, old_edid, old_edid_size)) {
|
||||
connector->epoch_counter++;
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID changed, epoch counter %llu\n",
|
||||
connector->base.id, connector->name,
|
||||
connector->epoch_counter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,12 @@
|
||||
* Copyright © 2023 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_eld.h>
|
||||
|
||||
#include "drm_internal.h"
|
||||
#include "drm_crtc_internal.h"
|
||||
|
||||
/**
|
||||
* drm_eld_sad_get - get SAD from ELD to struct cea_sad
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_dma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_panic.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
@ -148,3 +149,44 @@ void drm_fb_dma_sync_non_coherent(struct drm_device *drm,
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_dma_sync_non_coherent);
|
||||
|
||||
/**
|
||||
* drm_fb_dma_get_scanout_buffer - Provide a scanout buffer in case of panic
|
||||
* @plane: DRM primary plane
|
||||
* @sb: scanout buffer for the panic handler
|
||||
* Returns: 0 or negative error code
|
||||
*
|
||||
* Generic get_scanout_buffer() implementation, for drivers that uses the
|
||||
* drm_fb_dma_helper. It won't call vmap in the panic context, so the driver
|
||||
* should make sure the primary plane is vmapped, otherwise the panic screen
|
||||
* won't get displayed.
|
||||
*/
|
||||
int drm_fb_dma_get_scanout_buffer(struct drm_plane *plane,
|
||||
struct drm_scanout_buffer *sb)
|
||||
{
|
||||
struct drm_gem_dma_object *dma_obj;
|
||||
struct drm_framebuffer *fb;
|
||||
|
||||
fb = plane->state->fb;
|
||||
/* Only support linear modifier */
|
||||
if (fb->modifier != DRM_FORMAT_MOD_LINEAR)
|
||||
return -ENODEV;
|
||||
|
||||
dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
|
||||
|
||||
/* Buffer should be accessible from the CPU */
|
||||
if (dma_obj->base.import_attach)
|
||||
return -ENODEV;
|
||||
|
||||
/* Buffer should be already mapped to CPU */
|
||||
if (!dma_obj->vaddr)
|
||||
return -ENODEV;
|
||||
|
||||
iosys_map_set_vaddr(&sb->map[0], dma_obj->vaddr);
|
||||
sb->format = fb->format;
|
||||
sb->height = fb->height;
|
||||
sb->width = fb->width;
|
||||
sb->pitch[0] = fb->pitches[0];
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_dma_get_scanout_buffer);
|
||||
|
@ -35,7 +35,6 @@
|
||||
|
||||
#define DRM_IF_VERSION(maj, min) (maj << 16 | min)
|
||||
|
||||
struct cea_sad;
|
||||
struct dentry;
|
||||
struct dma_buf;
|
||||
struct iosys_map;
|
||||
@ -278,8 +277,4 @@ void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
|
||||
const struct drm_framebuffer *fb);
|
||||
void drm_framebuffer_debugfs_init(struct drm_device *dev);
|
||||
|
||||
/* drm_edid.c */
|
||||
void drm_edid_cta_sad_get(const struct cea_sad *cta_sad, u8 *sad);
|
||||
void drm_edid_cta_sad_set(struct cea_sad *cta_sad, const u8 *sad);
|
||||
|
||||
#endif /* __DRM_INTERNAL_H__ */
|
||||
|
@ -644,6 +644,43 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
|
||||
|
||||
/**
|
||||
* mipi_dsi_compression_mode_ext() - enable/disable DSC on the peripheral
|
||||
* @dsi: DSI peripheral device
|
||||
* @enable: Whether to enable or disable the DSC
|
||||
* @algo: Selected compression algorithm
|
||||
* @pps_selector: Select PPS from the table of pre-stored or uploaded PPS entries
|
||||
*
|
||||
* Enable or disable Display Stream Compression on the peripheral.
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
|
||||
enum mipi_dsi_compression_algo algo,
|
||||
unsigned int pps_selector)
|
||||
{
|
||||
u8 tx[2] = { };
|
||||
struct mipi_dsi_msg msg = {
|
||||
.channel = dsi->channel,
|
||||
.type = MIPI_DSI_COMPRESSION_MODE,
|
||||
.tx_len = sizeof(tx),
|
||||
.tx_buf = tx,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (algo > 3 || pps_selector > 3)
|
||||
return -EINVAL;
|
||||
|
||||
tx[0] = (enable << 0) |
|
||||
(algo << 1) |
|
||||
(pps_selector << 4);
|
||||
|
||||
ret = mipi_dsi_device_transfer(dsi, &msg);
|
||||
|
||||
return (ret < 0) ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dsi_compression_mode_ext);
|
||||
|
||||
/**
|
||||
* mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
|
||||
* @dsi: DSI peripheral device
|
||||
@ -654,19 +691,9 @@ EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
ssize_t mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
|
||||
int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
|
||||
{
|
||||
/* Note: Needs updating for non-default PPS or algorithm */
|
||||
u8 tx[2] = { enable << 0, 0 };
|
||||
struct mipi_dsi_msg msg = {
|
||||
.channel = dsi->channel,
|
||||
.type = MIPI_DSI_COMPRESSION_MODE,
|
||||
.tx_len = sizeof(tx),
|
||||
.tx_buf = tx,
|
||||
};
|
||||
int ret = mipi_dsi_device_transfer(dsi, &msg);
|
||||
|
||||
return (ret < 0) ? ret : 0;
|
||||
return mipi_dsi_compression_mode_ext(dsi, enable, MIPI_DSI_COMPRESSION_DSC, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dsi_compression_mode);
|
||||
|
||||
@ -679,8 +706,8 @@ EXPORT_SYMBOL(mipi_dsi_compression_mode);
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
ssize_t mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
|
||||
const struct drm_dsc_picture_parameter_set *pps)
|
||||
int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
|
||||
const struct drm_dsc_picture_parameter_set *pps)
|
||||
{
|
||||
struct mipi_dsi_msg msg = {
|
||||
.channel = dsi->channel,
|
||||
|
@ -372,6 +372,13 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
|
||||
return -ENOMEM;
|
||||
dev->mode_config.modifiers_property = prop;
|
||||
|
||||
prop = drm_property_create(dev,
|
||||
DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_BLOB,
|
||||
"SIZE_HINTS", 0);
|
||||
if (!prop)
|
||||
return -ENOMEM;
|
||||
dev->mode_config.size_hints_property = prop;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -373,8 +373,8 @@ static int fill_analog_mode(struct drm_device *dev,
|
||||
if (!bt601 &&
|
||||
(hact_duration_ns < params->hact_ns.min ||
|
||||
hact_duration_ns > params->hact_ns.max)) {
|
||||
DRM_ERROR("Invalid horizontal active area duration: %uns (min: %u, max %u)\n",
|
||||
hact_duration_ns, params->hact_ns.min, params->hact_ns.max);
|
||||
drm_err(dev, "Invalid horizontal active area duration: %uns (min: %u, max %u)\n",
|
||||
hact_duration_ns, params->hact_ns.min, params->hact_ns.max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -385,8 +385,8 @@ static int fill_analog_mode(struct drm_device *dev,
|
||||
if (!bt601 &&
|
||||
(hblk_duration_ns < params->hblk_ns.min ||
|
||||
hblk_duration_ns > params->hblk_ns.max)) {
|
||||
DRM_ERROR("Invalid horizontal blanking duration: %uns (min: %u, max %u)\n",
|
||||
hblk_duration_ns, params->hblk_ns.min, params->hblk_ns.max);
|
||||
drm_err(dev, "Invalid horizontal blanking duration: %uns (min: %u, max %u)\n",
|
||||
hblk_duration_ns, params->hblk_ns.min, params->hblk_ns.max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -397,8 +397,8 @@ static int fill_analog_mode(struct drm_device *dev,
|
||||
if (!bt601 &&
|
||||
(hslen_duration_ns < params->hslen_ns.min ||
|
||||
hslen_duration_ns > params->hslen_ns.max)) {
|
||||
DRM_ERROR("Invalid horizontal sync duration: %uns (min: %u, max %u)\n",
|
||||
hslen_duration_ns, params->hslen_ns.min, params->hslen_ns.max);
|
||||
drm_err(dev, "Invalid horizontal sync duration: %uns (min: %u, max %u)\n",
|
||||
hslen_duration_ns, params->hslen_ns.min, params->hslen_ns.max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -409,7 +409,8 @@ static int fill_analog_mode(struct drm_device *dev,
|
||||
if (!bt601 &&
|
||||
(porches_duration_ns > (params->hfp_ns.max + params->hbp_ns.max) ||
|
||||
porches_duration_ns < (params->hfp_ns.min + params->hbp_ns.min))) {
|
||||
DRM_ERROR("Invalid horizontal porches duration: %uns\n", porches_duration_ns);
|
||||
drm_err(dev, "Invalid horizontal porches duration: %uns\n",
|
||||
porches_duration_ns);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -431,8 +432,8 @@ static int fill_analog_mode(struct drm_device *dev,
|
||||
if (!bt601 &&
|
||||
(hfp_duration_ns < params->hfp_ns.min ||
|
||||
hfp_duration_ns > params->hfp_ns.max)) {
|
||||
DRM_ERROR("Invalid horizontal front porch duration: %uns (min: %u, max %u)\n",
|
||||
hfp_duration_ns, params->hfp_ns.min, params->hfp_ns.max);
|
||||
drm_err(dev, "Invalid horizontal front porch duration: %uns (min: %u, max %u)\n",
|
||||
hfp_duration_ns, params->hfp_ns.min, params->hfp_ns.max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -443,8 +444,8 @@ static int fill_analog_mode(struct drm_device *dev,
|
||||
if (!bt601 &&
|
||||
(hbp_duration_ns < params->hbp_ns.min ||
|
||||
hbp_duration_ns > params->hbp_ns.max)) {
|
||||
DRM_ERROR("Invalid horizontal back porch duration: %uns (min: %u, max %u)\n",
|
||||
hbp_duration_ns, params->hbp_ns.min, params->hbp_ns.max);
|
||||
drm_err(dev, "Invalid horizontal back porch duration: %uns (min: %u, max %u)\n",
|
||||
hbp_duration_ns, params->hbp_ns.min, params->hbp_ns.max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -495,8 +496,8 @@ static int fill_analog_mode(struct drm_device *dev,
|
||||
|
||||
vtotal = vactive + vfp + vslen + vbp;
|
||||
if (params->num_lines != vtotal) {
|
||||
DRM_ERROR("Invalid vertical total: %upx (expected %upx)\n",
|
||||
vtotal, params->num_lines);
|
||||
drm_err(dev, "Invalid vertical total: %upx (expected %upx)\n",
|
||||
vtotal, params->num_lines);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1200,9 +1201,8 @@ int of_get_drm_display_mode(struct device_node *np,
|
||||
if (bus_flags)
|
||||
drm_bus_flags_from_videomode(&vm, bus_flags);
|
||||
|
||||
pr_debug("%pOF: got %dx%d display mode\n",
|
||||
np, vm.hactive, vm.vactive);
|
||||
drm_mode_debug_printmodeline(dmode);
|
||||
pr_debug("%pOF: got %dx%d display mode: " DRM_MODE_FMT "\n",
|
||||
np, vm.hactive, vm.vactive, DRM_MODE_ARG(dmode));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1250,7 +1250,7 @@ int of_get_drm_panel_display_mode(struct device_node *np,
|
||||
dmode->width_mm = width_mm;
|
||||
dmode->height_mm = height_mm;
|
||||
|
||||
drm_mode_debug_printmodeline(dmode);
|
||||
pr_debug(DRM_MODE_FMT "\n", DRM_MODE_ARG(dmode));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1812,10 +1812,8 @@ void drm_mode_prune_invalid(struct drm_device *dev,
|
||||
DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
|
||||
}
|
||||
if (verbose) {
|
||||
drm_mode_debug_printmodeline(mode);
|
||||
DRM_DEBUG_KMS("Not using %s mode: %s\n",
|
||||
mode->name,
|
||||
drm_get_mode_status_name(mode->status));
|
||||
drm_dbg_kms(dev, "Rejected mode: " DRM_MODE_FMT " (%s)\n",
|
||||
DRM_MODE_ARG(mode), drm_get_mode_status_name(mode->status));
|
||||
}
|
||||
drm_mode_destroy(dev, mode);
|
||||
}
|
||||
|
585
drivers/gpu/drm/drm_panic.c
Normal file
585
drivers/gpu/drm/drm_panic.c
Normal file
@ -0,0 +1,585 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 or MIT
|
||||
/*
|
||||
* Copyright (c) 2023 Red Hat.
|
||||
* Author: Jocelyn Falempe <jfalempe@redhat.com>
|
||||
* inspired by the drm_log driver from David Herrmann <dh.herrmann@gmail.com>
|
||||
* Tux Ascii art taken from cowsay written by Tony Monroe
|
||||
*/
|
||||
|
||||
#include <linux/font.h>
|
||||
#include <linux/iosys-map.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/kmsg_dump.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_format_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_panic.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
MODULE_AUTHOR("Jocelyn Falempe");
|
||||
MODULE_DESCRIPTION("DRM panic handler");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/**
|
||||
* DOC: overview
|
||||
*
|
||||
* To enable DRM panic for a driver, the primary plane must implement a
|
||||
* &drm_plane_helper_funcs.get_scanout_buffer helper function. It is then
|
||||
* automatically registered to the drm panic handler.
|
||||
* When a panic occurs, the &drm_plane_helper_funcs.get_scanout_buffer will be
|
||||
* called, and the driver can provide a framebuffer so the panic handler can
|
||||
* draw the panic screen on it. Currently only linear buffer and a few color
|
||||
* formats are supported.
|
||||
* Optionally the driver can also provide a &drm_plane_helper_funcs.panic_flush
|
||||
* callback, that will be called after that, to send additional commands to the
|
||||
* hardware to make the scanout buffer visible.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module displays a user friendly message on screen when a kernel panic
|
||||
* occurs. This is conflicting with fbcon, so you can only enable it when fbcon
|
||||
* is disabled.
|
||||
* It's intended for end-user, so have minimal technical/debug information.
|
||||
*
|
||||
* Implementation details:
|
||||
*
|
||||
* It is a panic handler, so it can't take lock, allocate memory, run tasks/irq,
|
||||
* or attempt to sleep. It's a best effort, and it may not be able to display
|
||||
* the message in all situations (like if the panic occurs in the middle of a
|
||||
* modesetting).
|
||||
* It will display only one static frame, so performance optimizations are low
|
||||
* priority as the machine is already in an unusable state.
|
||||
*/
|
||||
|
||||
struct drm_panic_line {
|
||||
u32 len;
|
||||
const char *txt;
|
||||
};
|
||||
|
||||
#define PANIC_LINE(s) {.len = sizeof(s) - 1, .txt = s}
|
||||
|
||||
static struct drm_panic_line panic_msg[] = {
|
||||
PANIC_LINE("KERNEL PANIC !"),
|
||||
PANIC_LINE(""),
|
||||
PANIC_LINE("Please reboot your computer."),
|
||||
};
|
||||
|
||||
static const struct drm_panic_line logo[] = {
|
||||
PANIC_LINE(" .--. _"),
|
||||
PANIC_LINE(" |o_o | | |"),
|
||||
PANIC_LINE(" |:_/ | | |"),
|
||||
PANIC_LINE(" // \\ \\ |_|"),
|
||||
PANIC_LINE(" (| | ) _"),
|
||||
PANIC_LINE(" /'\\_ _/`\\ (_)"),
|
||||
PANIC_LINE(" \\___)=(___/"),
|
||||
};
|
||||
|
||||
/*
|
||||
* Color conversion
|
||||
*/
|
||||
|
||||
static u16 convert_xrgb8888_to_rgb565(u32 pix)
|
||||
{
|
||||
return ((pix & 0x00F80000) >> 8) |
|
||||
((pix & 0x0000FC00) >> 5) |
|
||||
((pix & 0x000000F8) >> 3);
|
||||
}
|
||||
|
||||
static u16 convert_xrgb8888_to_rgba5551(u32 pix)
|
||||
{
|
||||
return ((pix & 0x00f80000) >> 8) |
|
||||
((pix & 0x0000f800) >> 5) |
|
||||
((pix & 0x000000f8) >> 2) |
|
||||
BIT(0); /* set alpha bit */
|
||||
}
|
||||
|
||||
static u16 convert_xrgb8888_to_xrgb1555(u32 pix)
|
||||
{
|
||||
return ((pix & 0x00f80000) >> 9) |
|
||||
((pix & 0x0000f800) >> 6) |
|
||||
((pix & 0x000000f8) >> 3);
|
||||
}
|
||||
|
||||
static u16 convert_xrgb8888_to_argb1555(u32 pix)
|
||||
{
|
||||
return BIT(15) | /* set alpha bit */
|
||||
((pix & 0x00f80000) >> 9) |
|
||||
((pix & 0x0000f800) >> 6) |
|
||||
((pix & 0x000000f8) >> 3);
|
||||
}
|
||||
|
||||
static u32 convert_xrgb8888_to_argb8888(u32 pix)
|
||||
{
|
||||
return pix | GENMASK(31, 24); /* fill alpha bits */
|
||||
}
|
||||
|
||||
static u32 convert_xrgb8888_to_xbgr8888(u32 pix)
|
||||
{
|
||||
return ((pix & 0x00ff0000) >> 16) << 0 |
|
||||
((pix & 0x0000ff00) >> 8) << 8 |
|
||||
((pix & 0x000000ff) >> 0) << 16 |
|
||||
((pix & 0xff000000) >> 24) << 24;
|
||||
}
|
||||
|
||||
static u32 convert_xrgb8888_to_abgr8888(u32 pix)
|
||||
{
|
||||
return ((pix & 0x00ff0000) >> 16) << 0 |
|
||||
((pix & 0x0000ff00) >> 8) << 8 |
|
||||
((pix & 0x000000ff) >> 0) << 16 |
|
||||
GENMASK(31, 24); /* fill alpha bits */
|
||||
}
|
||||
|
||||
static u32 convert_xrgb8888_to_xrgb2101010(u32 pix)
|
||||
{
|
||||
pix = ((pix & 0x000000FF) << 2) |
|
||||
((pix & 0x0000FF00) << 4) |
|
||||
((pix & 0x00FF0000) << 6);
|
||||
return pix | ((pix >> 8) & 0x00300C03);
|
||||
}
|
||||
|
||||
static u32 convert_xrgb8888_to_argb2101010(u32 pix)
|
||||
{
|
||||
pix = ((pix & 0x000000FF) << 2) |
|
||||
((pix & 0x0000FF00) << 4) |
|
||||
((pix & 0x00FF0000) << 6);
|
||||
return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03);
|
||||
}
|
||||
|
||||
/*
|
||||
* convert_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format
|
||||
* @color: input color, in xrgb8888 format
|
||||
* @format: output format
|
||||
*
|
||||
* Returns:
|
||||
* Color in the format specified, casted to u32.
|
||||
* Or 0 if the format is not supported.
|
||||
*/
|
||||
static u32 convert_from_xrgb8888(u32 color, u32 format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
return convert_xrgb8888_to_rgb565(color);
|
||||
case DRM_FORMAT_RGBA5551:
|
||||
return convert_xrgb8888_to_rgba5551(color);
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
return convert_xrgb8888_to_xrgb1555(color);
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
return convert_xrgb8888_to_argb1555(color);
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
return color;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
return convert_xrgb8888_to_argb8888(color);
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
return convert_xrgb8888_to_xbgr8888(color);
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
return convert_xrgb8888_to_abgr8888(color);
|
||||
case DRM_FORMAT_XRGB2101010:
|
||||
return convert_xrgb8888_to_xrgb2101010(color);
|
||||
case DRM_FORMAT_ARGB2101010:
|
||||
return convert_xrgb8888_to_argb2101010(color);
|
||||
default:
|
||||
WARN_ONCE(1, "Can't convert to %p4cc\n", &format);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Blit & Fill
|
||||
*/
|
||||
static void drm_panic_blit16(struct iosys_map *dmap, unsigned int dpitch,
|
||||
const u8 *sbuf8, unsigned int spitch,
|
||||
unsigned int height, unsigned int width,
|
||||
u16 fg16, u16 bg16)
|
||||
{
|
||||
unsigned int y, x;
|
||||
u16 val16;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width; x++) {
|
||||
val16 = (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg16 : bg16;
|
||||
iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, val16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch,
|
||||
const u8 *sbuf8, unsigned int spitch,
|
||||
unsigned int height, unsigned int width,
|
||||
u32 fg32, u32 bg32)
|
||||
{
|
||||
unsigned int y, x;
|
||||
u32 val32;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width; x++) {
|
||||
u32 off = y * dpitch + x * 3;
|
||||
|
||||
val32 = (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32;
|
||||
|
||||
/* write blue-green-red to output in little endianness */
|
||||
iosys_map_wr(dmap, off, u8, (val32 & 0x000000FF) >> 0);
|
||||
iosys_map_wr(dmap, off + 1, u8, (val32 & 0x0000FF00) >> 8);
|
||||
iosys_map_wr(dmap, off + 2, u8, (val32 & 0x00FF0000) >> 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch,
|
||||
const u8 *sbuf8, unsigned int spitch,
|
||||
unsigned int height, unsigned int width,
|
||||
u32 fg32, u32 bg32)
|
||||
{
|
||||
unsigned int y, x;
|
||||
u32 val32;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width; x++) {
|
||||
val32 = (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32;
|
||||
iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, val32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_panic_blit - convert a monochrome image to a linear framebuffer
|
||||
* @dmap: destination iosys_map
|
||||
* @dpitch: destination pitch in bytes
|
||||
* @sbuf8: source buffer, in monochrome format, 8 pixels per byte.
|
||||
* @spitch: source pitch in bytes
|
||||
* @height: height of the image to copy, in pixels
|
||||
* @width: width of the image to copy, in pixels
|
||||
* @fg_color: foreground color, in destination format
|
||||
* @bg_color: background color, in destination format
|
||||
* @pixel_width: pixel width in bytes.
|
||||
*
|
||||
* This can be used to draw a font character, which is a monochrome image, to a
|
||||
* framebuffer in other supported format.
|
||||
*/
|
||||
static void drm_panic_blit(struct iosys_map *dmap, unsigned int dpitch,
|
||||
const u8 *sbuf8, unsigned int spitch,
|
||||
unsigned int height, unsigned int width,
|
||||
u32 fg_color, u32 bg_color,
|
||||
unsigned int pixel_width)
|
||||
{
|
||||
switch (pixel_width) {
|
||||
case 2:
|
||||
drm_panic_blit16(dmap, dpitch, sbuf8, spitch,
|
||||
height, width, fg_color, bg_color);
|
||||
break;
|
||||
case 3:
|
||||
drm_panic_blit24(dmap, dpitch, sbuf8, spitch,
|
||||
height, width, fg_color, bg_color);
|
||||
break;
|
||||
case 4:
|
||||
drm_panic_blit32(dmap, dpitch, sbuf8, spitch,
|
||||
height, width, fg_color, bg_color);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Can't blit with pixel width %d\n", pixel_width);
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_panic_fill16(struct iosys_map *dmap, unsigned int dpitch,
|
||||
unsigned int height, unsigned int width,
|
||||
u16 color)
|
||||
{
|
||||
unsigned int y, x;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
for (x = 0; x < width; x++)
|
||||
iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, color);
|
||||
}
|
||||
|
||||
static void drm_panic_fill24(struct iosys_map *dmap, unsigned int dpitch,
|
||||
unsigned int height, unsigned int width,
|
||||
u32 color)
|
||||
{
|
||||
unsigned int y, x;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width; x++) {
|
||||
unsigned int off = y * dpitch + x * 3;
|
||||
|
||||
/* write blue-green-red to output in little endianness */
|
||||
iosys_map_wr(dmap, off, u8, (color & 0x000000FF) >> 0);
|
||||
iosys_map_wr(dmap, off + 1, u8, (color & 0x0000FF00) >> 8);
|
||||
iosys_map_wr(dmap, off + 2, u8, (color & 0x00FF0000) >> 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_panic_fill32(struct iosys_map *dmap, unsigned int dpitch,
|
||||
unsigned int height, unsigned int width,
|
||||
u32 color)
|
||||
{
|
||||
unsigned int y, x;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
for (x = 0; x < width; x++)
|
||||
iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, color);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_panic_fill - Fill a rectangle with a color
|
||||
* @dmap: destination iosys_map, pointing to the top left corner of the rectangle
|
||||
* @dpitch: destination pitch in bytes
|
||||
* @height: height of the rectangle, in pixels
|
||||
* @width: width of the rectangle, in pixels
|
||||
* @color: color to fill the rectangle.
|
||||
* @pixel_width: pixel width in bytes
|
||||
*
|
||||
* Fill a rectangle with a color, in a linear framebuffer.
|
||||
*/
|
||||
static void drm_panic_fill(struct iosys_map *dmap, unsigned int dpitch,
|
||||
unsigned int height, unsigned int width,
|
||||
u32 color, unsigned int pixel_width)
|
||||
{
|
||||
switch (pixel_width) {
|
||||
case 2:
|
||||
drm_panic_fill16(dmap, dpitch, height, width, color);
|
||||
break;
|
||||
case 3:
|
||||
drm_panic_fill24(dmap, dpitch, height, width, color);
|
||||
break;
|
||||
case 4:
|
||||
drm_panic_fill32(dmap, dpitch, height, width, color);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Can't fill with pixel width %d\n", pixel_width);
|
||||
}
|
||||
}
|
||||
|
||||
static const u8 *get_char_bitmap(const struct font_desc *font, char c, size_t font_pitch)
|
||||
{
|
||||
return font->data + (c * font->height) * font_pitch;
|
||||
}
|
||||
|
||||
static unsigned int get_max_line_len(const struct drm_panic_line *lines, int len)
|
||||
{
|
||||
int i;
|
||||
unsigned int max = 0;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
max = max(lines[i].len, max);
|
||||
return max;
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw a text in a rectangle on a framebuffer. The text is truncated if it overflows the rectangle
|
||||
*/
|
||||
static void draw_txt_rectangle(struct drm_scanout_buffer *sb,
|
||||
const struct font_desc *font,
|
||||
const struct drm_panic_line *msg,
|
||||
unsigned int msg_lines,
|
||||
bool centered,
|
||||
struct drm_rect *clip,
|
||||
u32 fg_color,
|
||||
u32 bg_color)
|
||||
{
|
||||
int i, j;
|
||||
const u8 *src;
|
||||
size_t font_pitch = DIV_ROUND_UP(font->width, 8);
|
||||
struct iosys_map dst;
|
||||
unsigned int px_width = sb->format->cpp[0];
|
||||
int left = 0;
|
||||
|
||||
msg_lines = min(msg_lines, drm_rect_height(clip) / font->height);
|
||||
for (i = 0; i < msg_lines; i++) {
|
||||
size_t line_len = min(msg[i].len, drm_rect_width(clip) / font->width);
|
||||
|
||||
if (centered)
|
||||
left = (drm_rect_width(clip) - (line_len * font->width)) / 2;
|
||||
|
||||
dst = sb->map[0];
|
||||
iosys_map_incr(&dst, (clip->y1 + i * font->height) * sb->pitch[0] +
|
||||
(clip->x1 + left) * px_width);
|
||||
for (j = 0; j < line_len; j++) {
|
||||
src = get_char_bitmap(font, msg[i].txt[j], font_pitch);
|
||||
drm_panic_blit(&dst, sb->pitch[0], src, font_pitch,
|
||||
font->height, font->width,
|
||||
fg_color, bg_color, px_width);
|
||||
iosys_map_incr(&dst, font->width * px_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw the panic message at the center of the screen
|
||||
*/
|
||||
static void draw_panic_static(struct drm_scanout_buffer *sb)
|
||||
{
|
||||
size_t msg_lines = ARRAY_SIZE(panic_msg);
|
||||
size_t logo_lines = ARRAY_SIZE(logo);
|
||||
u32 fg_color = CONFIG_DRM_PANIC_FOREGROUND_COLOR;
|
||||
u32 bg_color = CONFIG_DRM_PANIC_BACKGROUND_COLOR;
|
||||
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
|
||||
struct drm_rect r_logo, r_msg;
|
||||
|
||||
if (!font)
|
||||
return;
|
||||
|
||||
fg_color = convert_from_xrgb8888(fg_color, sb->format->format);
|
||||
bg_color = convert_from_xrgb8888(bg_color, sb->format->format);
|
||||
|
||||
r_logo = DRM_RECT_INIT(0, 0,
|
||||
get_max_line_len(logo, logo_lines) * font->width,
|
||||
logo_lines * font->height);
|
||||
r_msg = DRM_RECT_INIT(0, 0,
|
||||
min(get_max_line_len(panic_msg, msg_lines) * font->width, sb->width),
|
||||
min(msg_lines * font->height, sb->height));
|
||||
|
||||
/* Center the panic message */
|
||||
drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, (sb->height - r_msg.y2) / 2);
|
||||
|
||||
/* Fill with the background color, and draw text on top */
|
||||
drm_panic_fill(&sb->map[0], sb->pitch[0], sb->height, sb->width,
|
||||
bg_color, sb->format->cpp[0]);
|
||||
|
||||
if ((r_msg.x1 >= drm_rect_width(&r_logo) || r_msg.y1 >= drm_rect_height(&r_logo)) &&
|
||||
drm_rect_width(&r_logo) < sb->width && drm_rect_height(&r_logo) < sb->height) {
|
||||
draw_txt_rectangle(sb, font, logo, logo_lines, false, &r_logo, fg_color, bg_color);
|
||||
}
|
||||
draw_txt_rectangle(sb, font, panic_msg, msg_lines, true, &r_msg, fg_color, bg_color);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_panic_is_format_supported()
|
||||
* @format: a fourcc color code
|
||||
* Returns: true if supported, false otherwise.
|
||||
*
|
||||
* Check if drm_panic will be able to use this color format.
|
||||
*/
|
||||
static bool drm_panic_is_format_supported(const struct drm_format_info *format)
|
||||
{
|
||||
if (format->num_planes != 1)
|
||||
return false;
|
||||
return convert_from_xrgb8888(0xffffff, format->format) != 0;
|
||||
}
|
||||
|
||||
static void draw_panic_plane(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_scanout_buffer sb;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (!drm_panic_trylock(plane->dev, flags))
|
||||
return;
|
||||
|
||||
ret = plane->helper_private->get_scanout_buffer(plane, &sb);
|
||||
|
||||
if (!ret && drm_panic_is_format_supported(sb.format)) {
|
||||
draw_panic_static(&sb);
|
||||
if (plane->helper_private->panic_flush)
|
||||
plane->helper_private->panic_flush(plane);
|
||||
}
|
||||
drm_panic_unlock(plane->dev, flags);
|
||||
}
|
||||
|
||||
static struct drm_plane *to_drm_plane(struct kmsg_dumper *kd)
|
||||
{
|
||||
return container_of(kd, struct drm_plane, kmsg_panic);
|
||||
}
|
||||
|
||||
static void drm_panic(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason)
|
||||
{
|
||||
struct drm_plane *plane = to_drm_plane(dumper);
|
||||
|
||||
if (reason == KMSG_DUMP_PANIC)
|
||||
draw_panic_plane(plane);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DEBUG FS, This is currently unsafe.
|
||||
* Create one file per plane, so it's possible to debug one plane at a time.
|
||||
* TODO: It would be better to emulate an NMI context.
|
||||
*/
|
||||
#ifdef CONFIG_DRM_PANIC_DEBUG
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
static ssize_t debugfs_trigger_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
bool run;
|
||||
|
||||
if (kstrtobool_from_user(user_buf, count, &run) == 0 && run) {
|
||||
struct drm_plane *plane = file->private_data;
|
||||
|
||||
draw_panic_plane(plane);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations dbg_drm_panic_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = debugfs_trigger_write,
|
||||
.open = simple_open,
|
||||
};
|
||||
|
||||
static void debugfs_register_plane(struct drm_plane *plane, int index)
|
||||
{
|
||||
char fname[32];
|
||||
|
||||
snprintf(fname, 32, "drm_panic_plane_%d", index);
|
||||
debugfs_create_file(fname, 0200, plane->dev->debugfs_root,
|
||||
plane, &dbg_drm_panic_ops);
|
||||
}
|
||||
#else
|
||||
static void debugfs_register_plane(struct drm_plane *plane, int index) {}
|
||||
#endif /* CONFIG_DRM_PANIC_DEBUG */
|
||||
|
||||
/**
|
||||
* drm_panic_register() - Initialize DRM panic for a device
|
||||
* @dev: the drm device on which the panic screen will be displayed.
|
||||
*/
|
||||
void drm_panic_register(struct drm_device *dev)
|
||||
{
|
||||
struct drm_plane *plane;
|
||||
int registered_plane = 0;
|
||||
|
||||
if (!dev->mode_config.num_total_plane)
|
||||
return;
|
||||
|
||||
drm_for_each_plane(plane, dev) {
|
||||
if (!plane->helper_private || !plane->helper_private->get_scanout_buffer)
|
||||
continue;
|
||||
plane->kmsg_panic.dump = drm_panic;
|
||||
plane->kmsg_panic.max_reason = KMSG_DUMP_PANIC;
|
||||
if (kmsg_dump_register(&plane->kmsg_panic))
|
||||
drm_warn(dev, "Failed to register panic handler\n");
|
||||
else {
|
||||
debugfs_register_plane(plane, registered_plane);
|
||||
registered_plane++;
|
||||
}
|
||||
}
|
||||
if (registered_plane)
|
||||
drm_info(dev, "Registered %d planes with drm panic\n", registered_plane);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panic_register);
|
||||
|
||||
/**
|
||||
* drm_panic_unregister()
|
||||
* @dev: the drm device previously registered.
|
||||
*/
|
||||
void drm_panic_unregister(struct drm_device *dev)
|
||||
{
|
||||
struct drm_plane *plane;
|
||||
|
||||
if (!dev->mode_config.num_total_plane)
|
||||
return;
|
||||
|
||||
drm_for_each_plane(plane, dev) {
|
||||
if (!plane->helper_private || !plane->helper_private->get_scanout_buffer)
|
||||
continue;
|
||||
kmsg_dump_unregister(&plane->kmsg_panic);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panic_unregister);
|
@ -140,6 +140,25 @@
|
||||
* DRM_FORMAT_MOD_LINEAR. Before linux kernel release v5.1 there have been
|
||||
* various bugs in this area with inconsistencies between the capability
|
||||
* flag and per-plane properties.
|
||||
*
|
||||
* SIZE_HINTS:
|
||||
* Blob property which contains the set of recommended plane size
|
||||
* which can used for simple "cursor like" use cases (eg. no scaling).
|
||||
* Using these hints frees userspace from extensive probing of
|
||||
* supported plane sizes through atomic/setcursor ioctls.
|
||||
*
|
||||
* The blob contains an array of struct drm_plane_size_hint, in
|
||||
* order of preference. For optimal usage userspace should pick
|
||||
* the first size that satisfies its own requirements.
|
||||
*
|
||||
* Drivers should only attach this property to planes that
|
||||
* support a very limited set of sizes.
|
||||
*
|
||||
* Note that property value 0 (ie. no blob) is reserved for potential
|
||||
* future use. Current userspace is expected to ignore the property
|
||||
* if the value is 0, and fall back to some other means (eg.
|
||||
* &DRM_CAP_CURSOR_WIDTH and &DRM_CAP_CURSOR_HEIGHT) to determine
|
||||
* the appropriate plane size to use.
|
||||
*/
|
||||
|
||||
static unsigned int drm_num_planes(struct drm_device *dev)
|
||||
@ -1729,3 +1748,40 @@ int drm_plane_create_scaling_filter_property(struct drm_plane *plane,
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_plane_create_scaling_filter_property);
|
||||
|
||||
/**
|
||||
* drm_plane_add_size_hint_property - create a size hint property
|
||||
*
|
||||
* @plane: drm plane
|
||||
* @hints: size hints
|
||||
* @num_hints: number of size hints
|
||||
*
|
||||
* Create a size hints property for the plane.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero for success or -errno
|
||||
*/
|
||||
int drm_plane_add_size_hints_property(struct drm_plane *plane,
|
||||
const struct drm_plane_size_hint *hints,
|
||||
int num_hints)
|
||||
{
|
||||
struct drm_device *dev = plane->dev;
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
struct drm_property_blob *blob;
|
||||
|
||||
/* extending to other plane types needs actual thought */
|
||||
if (drm_WARN_ON(dev, plane->type != DRM_PLANE_TYPE_CURSOR))
|
||||
return -EINVAL;
|
||||
|
||||
blob = drm_property_create_blob(dev,
|
||||
array_size(sizeof(hints[0]), num_hints),
|
||||
hints);
|
||||
if (IS_ERR(blob))
|
||||
return PTR_ERR(blob);
|
||||
|
||||
drm_object_attach_property(&plane->base, config->size_hints_property,
|
||||
blob->base.id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_plane_add_size_hints_property);
|
||||
|
@ -567,8 +567,8 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s]\n", connector->base.id,
|
||||
connector->name);
|
||||
|
||||
retry:
|
||||
ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);
|
||||
@ -611,11 +611,10 @@ retry:
|
||||
* check here, and if anything changed start the hotplug code.
|
||||
*/
|
||||
if (old_status != connector->status) {
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
|
||||
connector->base.id,
|
||||
connector->name,
|
||||
drm_get_connector_status_name(old_status),
|
||||
drm_get_connector_status_name(connector->status));
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s\n",
|
||||
connector->base.id, connector->name,
|
||||
drm_get_connector_status_name(old_status),
|
||||
drm_get_connector_status_name(connector->status));
|
||||
|
||||
/*
|
||||
* The hotplug event code might call into the fb
|
||||
@ -638,8 +637,8 @@ retry:
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
|
||||
if (connector->status == connector_status_disconnected) {
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
|
||||
connector->base.id, connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] disconnected\n",
|
||||
connector->base.id, connector->name);
|
||||
drm_connector_update_edid_property(connector, NULL);
|
||||
drm_mode_prune_invalid(dev, &connector->modes, false);
|
||||
goto exit;
|
||||
@ -697,11 +696,13 @@ exit:
|
||||
|
||||
drm_mode_sort(&connector->modes);
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] probed modes:\n",
|
||||
connector->base.id, connector->name);
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
|
||||
drm_mode_debug_printmodeline(mode);
|
||||
drm_dbg_kms(dev, "Probed mode: " DRM_MODE_FMT "\n",
|
||||
DRM_MODE_ARG(mode));
|
||||
}
|
||||
|
||||
return count;
|
||||
@ -834,14 +835,12 @@ static void output_poll_execute(struct work_struct *work)
|
||||
old = drm_get_connector_status_name(old_status);
|
||||
new = drm_get_connector_status_name(connector->status);
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] "
|
||||
"status updated from %s to %s\n",
|
||||
connector->base.id,
|
||||
connector->name,
|
||||
old, new);
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] epoch counter %llu -> %llu\n",
|
||||
connector->base.id, connector->name,
|
||||
old_epoch_counter, connector->epoch_counter);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s\n",
|
||||
connector->base.id, connector->name,
|
||||
old, new);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] epoch counter %llu -> %llu\n",
|
||||
connector->base.id, connector->name,
|
||||
old_epoch_counter, connector->epoch_counter);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
@ -209,10 +209,9 @@ static ssize_t status_store(struct device *device,
|
||||
ret = -EINVAL;
|
||||
|
||||
if (old_force != connector->force || !connector->force) {
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force updated from %d to %d or reprobing\n",
|
||||
connector->base.id,
|
||||
connector->name,
|
||||
old_force, connector->force);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] force updated from %d to %d or reprobing\n",
|
||||
connector->base.id, connector->name,
|
||||
old_force, connector->force);
|
||||
|
||||
connector->funcs->fill_modes(connector,
|
||||
dev->mode_config.max_width,
|
||||
@ -383,8 +382,8 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
|
||||
if (r)
|
||||
goto err_free;
|
||||
|
||||
DRM_DEBUG("adding \"%s\" to sysfs\n",
|
||||
connector->name);
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] adding connector to sysfs\n",
|
||||
connector->base.id, connector->name);
|
||||
|
||||
r = device_add(kdev);
|
||||
if (r) {
|
||||
@ -430,8 +429,9 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
|
||||
if (dev_fwnode(connector->kdev))
|
||||
component_del(connector->kdev, &typec_connector_ops);
|
||||
|
||||
DRM_DEBUG("removing \"%s\" from sysfs\n",
|
||||
connector->name);
|
||||
drm_dbg_kms(connector->dev,
|
||||
"[CONNECTOR:%d:%s] removing connector from sysfs\n",
|
||||
connector->base.id, connector->name);
|
||||
|
||||
device_unregister(connector->kdev);
|
||||
connector->kdev = NULL;
|
||||
@ -442,7 +442,7 @@ void drm_sysfs_lease_event(struct drm_device *dev)
|
||||
char *event_string = "LEASE=1";
|
||||
char *envp[] = { event_string, NULL };
|
||||
|
||||
DRM_DEBUG("generating lease event\n");
|
||||
drm_dbg_lease(dev, "generating lease event\n");
|
||||
|
||||
kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
@ -463,7 +463,7 @@ void drm_sysfs_hotplug_event(struct drm_device *dev)
|
||||
char *event_string = "HOTPLUG=1";
|
||||
char *envp[] = { event_string, NULL };
|
||||
|
||||
DRM_DEBUG("generating hotplug event\n");
|
||||
drm_dbg_kms(dev, "generating hotplug event\n");
|
||||
|
||||
kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
@ -166,11 +166,24 @@ module_param_named(timestamp_precision_usec, drm_timestamp_precision, 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]");
|
||||
|
||||
static struct drm_vblank_crtc *
|
||||
drm_vblank_crtc(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
return &dev->vblank[pipe];
|
||||
}
|
||||
|
||||
struct drm_vblank_crtc *
|
||||
drm_crtc_vblank_crtc(struct drm_crtc *crtc)
|
||||
{
|
||||
return drm_vblank_crtc(crtc->dev, drm_crtc_index(crtc));
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_vblank_crtc);
|
||||
|
||||
static void store_vblank(struct drm_device *dev, unsigned int pipe,
|
||||
u32 vblank_count_inc,
|
||||
ktime_t t_vblank, u32 last)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
|
||||
assert_spin_locked(&dev->vblank_time_lock);
|
||||
|
||||
@ -184,7 +197,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe,
|
||||
|
||||
static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
|
||||
return vblank->max_vblank_count ?: dev->max_vblank_count;
|
||||
}
|
||||
@ -273,7 +286,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
|
||||
static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
bool in_vblank_irq)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
u32 cur_vblank, diff;
|
||||
bool rc;
|
||||
ktime_t t_vblank;
|
||||
@ -364,7 +377,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
|
||||
u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
u64 count;
|
||||
|
||||
if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
|
||||
@ -438,7 +451,7 @@ static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
|
||||
*/
|
||||
void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
unsigned long irqflags;
|
||||
|
||||
assert_spin_locked(&dev->vbl_lock);
|
||||
@ -600,7 +613,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
int linedur_ns = 0, framedur_ns = 0;
|
||||
int dotclock = mode->crtc_clock;
|
||||
|
||||
@ -930,7 +943,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_count);
|
||||
static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
|
||||
ktime_t *vblanktime)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
u64 vblank_count;
|
||||
unsigned int seq;
|
||||
|
||||
@ -985,7 +998,6 @@ EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
|
||||
*/
|
||||
int drm_crtc_next_vblank_start(struct drm_crtc *crtc, ktime_t *vblanktime)
|
||||
{
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank;
|
||||
struct drm_display_mode *mode;
|
||||
u64 vblank_start;
|
||||
@ -993,7 +1005,7 @@ int drm_crtc_next_vblank_start(struct drm_crtc *crtc, ktime_t *vblanktime)
|
||||
if (!drm_dev_has_vblank(crtc->dev))
|
||||
return -EINVAL;
|
||||
|
||||
vblank = &crtc->dev->vblank[pipe];
|
||||
vblank = drm_crtc_vblank_crtc(crtc);
|
||||
mode = &vblank->hwmode;
|
||||
|
||||
if (!vblank->framedur_ns || !vblank->linedur_ns)
|
||||
@ -1147,7 +1159,7 @@ static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
|
||||
|
||||
static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
int ret = 0;
|
||||
|
||||
assert_spin_locked(&dev->vbl_lock);
|
||||
@ -1185,7 +1197,7 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
|
||||
|
||||
int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
unsigned long irqflags;
|
||||
int ret = 0;
|
||||
|
||||
@ -1228,7 +1240,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_get);
|
||||
|
||||
void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
|
||||
if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
|
||||
return;
|
||||
@ -1274,7 +1286,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_put);
|
||||
*/
|
||||
void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
int ret;
|
||||
u64 last;
|
||||
|
||||
@ -1327,7 +1339,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
struct drm_pending_vblank_event *e, *t;
|
||||
ktime_t now;
|
||||
u64 seq;
|
||||
@ -1405,8 +1417,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_off);
|
||||
void drm_crtc_vblank_reset(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
spin_lock_irq(&dev->vbl_lock);
|
||||
/*
|
||||
@ -1445,8 +1456,7 @@ void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
|
||||
u32 max_vblank_count)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
drm_WARN_ON(dev, dev->max_vblank_count);
|
||||
drm_WARN_ON(dev, !READ_ONCE(vblank->inmodeset));
|
||||
@ -1469,7 +1479,7 @@ void drm_crtc_vblank_on(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
|
||||
return;
|
||||
@ -1512,7 +1522,7 @@ static void drm_vblank_restore(struct drm_device *dev, unsigned int pipe)
|
||||
assert_spin_locked(&dev->vbl_lock);
|
||||
assert_spin_locked(&dev->vblank_time_lock);
|
||||
|
||||
vblank = &dev->vblank[pipe];
|
||||
vblank = drm_vblank_crtc(dev, pipe);
|
||||
drm_WARN_ONCE(dev,
|
||||
drm_debug_enabled(DRM_UT_VBL) && !vblank->framedur_ns,
|
||||
"Cannot compute missed vblanks without frame duration\n");
|
||||
@ -1564,7 +1574,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
|
||||
union drm_wait_vblank *vblwait,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
struct drm_pending_vblank_event *e;
|
||||
ktime_t now;
|
||||
u64 seq;
|
||||
@ -1872,7 +1882,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
|
||||
*/
|
||||
bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
|
||||
unsigned long irqflags;
|
||||
bool disable_irq;
|
||||
|
||||
@ -1981,7 +1991,7 @@ int drm_crtc_get_sequence_ioctl(struct drm_device *dev, void *data,
|
||||
|
||||
pipe = drm_crtc_index(crtc);
|
||||
|
||||
vblank = &dev->vblank[pipe];
|
||||
vblank = drm_crtc_vblank_crtc(crtc);
|
||||
vblank_enabled = dev->vblank_disable_immediate && READ_ONCE(vblank->enabled);
|
||||
|
||||
if (!vblank_enabled) {
|
||||
@ -2046,7 +2056,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data,
|
||||
|
||||
pipe = drm_crtc_index(crtc);
|
||||
|
||||
vblank = &dev->vblank[pipe];
|
||||
vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
e = kzalloc(sizeof(*e), GFP_KERNEL);
|
||||
if (e == NULL)
|
||||
|
@ -245,7 +245,7 @@ void drm_vblank_work_init(struct drm_vblank_work *work, struct drm_crtc *crtc,
|
||||
{
|
||||
kthread_init_work(&work->base, func);
|
||||
INIT_LIST_HEAD(&work->node);
|
||||
work->vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
|
||||
work->vblank = drm_crtc_vblank_crtc(crtc);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_vblank_work_init);
|
||||
|
||||
|
@ -595,11 +595,14 @@ get_lvds_fp_timing(const struct bdb_lvds_lfp_data *data,
|
||||
return (const void *)data + ptrs->ptr[index].fp_timing.offset;
|
||||
}
|
||||
|
||||
static const struct lvds_pnp_id *
|
||||
static const struct drm_edid_product_id *
|
||||
get_lvds_pnp_id(const struct bdb_lvds_lfp_data *data,
|
||||
const struct bdb_lvds_lfp_data_ptrs *ptrs,
|
||||
int index)
|
||||
{
|
||||
/* These two are supposed to have the same layout in memory. */
|
||||
BUILD_BUG_ON(sizeof(struct lvds_pnp_id) != sizeof(struct drm_edid_product_id));
|
||||
|
||||
return (const void *)data + ptrs->ptr[index].panel_pnp_id.offset;
|
||||
}
|
||||
|
||||
@ -613,19 +616,6 @@ get_lfp_data_tail(const struct bdb_lvds_lfp_data *data,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dump_pnp_id(struct drm_i915_private *i915,
|
||||
const struct lvds_pnp_id *pnp_id,
|
||||
const char *name)
|
||||
{
|
||||
u16 mfg_name = be16_to_cpu((__force __be16)pnp_id->mfg_name);
|
||||
char vend[4];
|
||||
|
||||
drm_dbg_kms(&i915->drm, "%s PNPID mfg: %s (0x%x), prod: %u, serial: %u, week: %d, year: %d\n",
|
||||
name, drm_edid_decode_mfg_id(mfg_name, vend),
|
||||
pnp_id->mfg_name, pnp_id->product_code, pnp_id->serial,
|
||||
pnp_id->mfg_week, pnp_id->mfg_year + 1990);
|
||||
}
|
||||
|
||||
static int opregion_get_panel_type(struct drm_i915_private *i915,
|
||||
const struct intel_bios_encoder_data *devdata,
|
||||
const struct drm_edid *drm_edid, bool use_fallback)
|
||||
@ -664,21 +654,21 @@ static int pnpid_get_panel_type(struct drm_i915_private *i915,
|
||||
{
|
||||
const struct bdb_lvds_lfp_data *data;
|
||||
const struct bdb_lvds_lfp_data_ptrs *ptrs;
|
||||
const struct lvds_pnp_id *edid_id;
|
||||
struct lvds_pnp_id edid_id_nodate;
|
||||
const struct edid *edid = drm_edid_raw(drm_edid); /* FIXME */
|
||||
struct drm_edid_product_id product_id, product_id_nodate;
|
||||
struct drm_printer p;
|
||||
int i, best = -1;
|
||||
|
||||
if (!edid)
|
||||
if (!drm_edid)
|
||||
return -1;
|
||||
|
||||
edid_id = (const void *)&edid->mfg_id[0];
|
||||
drm_edid_get_product_id(drm_edid, &product_id);
|
||||
|
||||
edid_id_nodate = *edid_id;
|
||||
edid_id_nodate.mfg_week = 0;
|
||||
edid_id_nodate.mfg_year = 0;
|
||||
product_id_nodate = product_id;
|
||||
product_id_nodate.week_of_manufacture = 0;
|
||||
product_id_nodate.year_of_manufacture = 0;
|
||||
|
||||
dump_pnp_id(i915, edid_id, "EDID");
|
||||
p = drm_dbg_printer(&i915->drm, DRM_UT_KMS, "EDID");
|
||||
drm_edid_print_product_id(&p, &product_id, true);
|
||||
|
||||
ptrs = bdb_find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
|
||||
if (!ptrs)
|
||||
@ -689,11 +679,11 @@ static int pnpid_get_panel_type(struct drm_i915_private *i915,
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
const struct lvds_pnp_id *vbt_id =
|
||||
const struct drm_edid_product_id *vbt_id =
|
||||
get_lvds_pnp_id(data, ptrs, i);
|
||||
|
||||
/* full match? */
|
||||
if (!memcmp(vbt_id, edid_id, sizeof(*vbt_id)))
|
||||
if (!memcmp(vbt_id, &product_id, sizeof(*vbt_id)))
|
||||
return i;
|
||||
|
||||
/*
|
||||
@ -701,7 +691,7 @@ static int pnpid_get_panel_type(struct drm_i915_private *i915,
|
||||
* and the VBT entry does not specify a date.
|
||||
*/
|
||||
if (best < 0 &&
|
||||
!memcmp(vbt_id, &edid_id_nodate, sizeof(*vbt_id)))
|
||||
!memcmp(vbt_id, &product_id_nodate, sizeof(*vbt_id)))
|
||||
best = i;
|
||||
}
|
||||
|
||||
@ -887,7 +877,8 @@ parse_lfp_data(struct drm_i915_private *i915,
|
||||
const struct bdb_lvds_lfp_data *data;
|
||||
const struct bdb_lvds_lfp_data_tail *tail;
|
||||
const struct bdb_lvds_lfp_data_ptrs *ptrs;
|
||||
const struct lvds_pnp_id *pnp_id;
|
||||
const struct drm_edid_product_id *pnp_id;
|
||||
struct drm_printer p;
|
||||
int panel_type = panel->vbt.panel_type;
|
||||
|
||||
ptrs = bdb_find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
|
||||
@ -902,7 +893,9 @@ parse_lfp_data(struct drm_i915_private *i915,
|
||||
parse_lfp_panel_dtd(i915, panel, data, ptrs);
|
||||
|
||||
pnp_id = get_lvds_pnp_id(data, ptrs, panel_type);
|
||||
dump_pnp_id(i915, pnp_id, "Panel");
|
||||
|
||||
p = drm_dbg_printer(&i915->drm, DRM_UT_KMS, "Panel");
|
||||
drm_edid_print_product_id(&p, pnp_id, false);
|
||||
|
||||
tail = get_lfp_data_tail(data, ptrs);
|
||||
if (!tail)
|
||||
|
@ -843,6 +843,28 @@ static const struct drm_plane_funcs intel_cursor_plane_funcs = {
|
||||
.format_mod_supported = intel_cursor_format_mod_supported,
|
||||
};
|
||||
|
||||
static void intel_cursor_add_size_hints_property(struct intel_plane *plane)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(plane->base.dev);
|
||||
const struct drm_mode_config *config = &i915->drm.mode_config;
|
||||
struct drm_plane_size_hint hints[4];
|
||||
int size, max_size, num_hints = 0;
|
||||
|
||||
max_size = min(config->cursor_width, config->cursor_height);
|
||||
|
||||
/* for simplicity only enumerate the supported square+POT sizes */
|
||||
for (size = 64; size <= max_size; size *= 2) {
|
||||
if (drm_WARN_ON(&i915->drm, num_hints >= ARRAY_SIZE(hints)))
|
||||
break;
|
||||
|
||||
hints[num_hints].width = size;
|
||||
hints[num_hints].height = size;
|
||||
num_hints++;
|
||||
}
|
||||
|
||||
drm_plane_add_size_hints_property(&plane->base, hints, num_hints);
|
||||
}
|
||||
|
||||
struct intel_plane *
|
||||
intel_cursor_plane_create(struct drm_i915_private *dev_priv,
|
||||
enum pipe pipe)
|
||||
@ -901,6 +923,8 @@ intel_cursor_plane_create(struct drm_i915_private *dev_priv,
|
||||
DRM_MODE_ROTATE_0 |
|
||||
DRM_MODE_ROTATE_180);
|
||||
|
||||
intel_cursor_add_size_hints_property(cursor);
|
||||
|
||||
zpos = DISPLAY_RUNTIME_INFO(dev_priv)->num_sprites[pipe] + 1;
|
||||
drm_plane_create_zpos_immutable_property(&cursor->base, zpos);
|
||||
|
||||
|
@ -773,6 +773,13 @@ static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
|
||||
.atomic_update = ipu_plane_atomic_update,
|
||||
};
|
||||
|
||||
static const struct drm_plane_helper_funcs ipu_primary_plane_helper_funcs = {
|
||||
.atomic_check = ipu_plane_atomic_check,
|
||||
.atomic_disable = ipu_plane_atomic_disable,
|
||||
.atomic_update = ipu_plane_atomic_update,
|
||||
.get_scanout_buffer = drm_fb_dma_get_scanout_buffer,
|
||||
};
|
||||
|
||||
bool ipu_plane_atomic_update_pending(struct drm_plane *plane)
|
||||
{
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
@ -916,7 +923,10 @@ struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
|
||||
ipu_plane->dma = dma;
|
||||
ipu_plane->dp_flow = dp;
|
||||
|
||||
drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs);
|
||||
if (type == DRM_PLANE_TYPE_PRIMARY)
|
||||
drm_plane_helper_add(&ipu_plane->base, &ipu_primary_plane_helper_funcs);
|
||||
else
|
||||
drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs);
|
||||
|
||||
if (dp == IPU_DP_FLOW_SYNC_BG || dp == IPU_DP_FLOW_SYNC_FG)
|
||||
ret = drm_plane_create_zpos_property(&ipu_plane->base, zpos, 0,
|
||||
|
@ -43,6 +43,18 @@ void lima_bcast_suspend(struct lima_ip *ip)
|
||||
|
||||
}
|
||||
|
||||
int lima_bcast_mask_irq(struct lima_ip *ip)
|
||||
{
|
||||
bcast_write(LIMA_BCAST_BROADCAST_MASK, 0);
|
||||
bcast_write(LIMA_BCAST_INTERRUPT_MASK, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lima_bcast_reset(struct lima_ip *ip)
|
||||
{
|
||||
return lima_bcast_hw_init(ip);
|
||||
}
|
||||
|
||||
int lima_bcast_init(struct lima_ip *ip)
|
||||
{
|
||||
int i;
|
||||
|
@ -13,4 +13,7 @@ void lima_bcast_fini(struct lima_ip *ip);
|
||||
|
||||
void lima_bcast_enable(struct lima_device *dev, int num_pp);
|
||||
|
||||
int lima_bcast_mask_irq(struct lima_ip *ip);
|
||||
int lima_bcast_reset(struct lima_ip *ip);
|
||||
|
||||
#endif
|
||||
|
@ -371,6 +371,7 @@ static int lima_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lima_device *ldev;
|
||||
struct drm_device *ddev;
|
||||
const struct lima_compatible *comp;
|
||||
int err;
|
||||
|
||||
err = lima_sched_slab_init();
|
||||
@ -384,7 +385,13 @@ static int lima_pdev_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ldev->dev = &pdev->dev;
|
||||
ldev->id = (enum lima_gpu_id)of_device_get_match_data(&pdev->dev);
|
||||
comp = of_device_get_match_data(&pdev->dev);
|
||||
if (!comp) {
|
||||
err = -ENODEV;
|
||||
goto err_out0;
|
||||
}
|
||||
|
||||
ldev->id = comp->id;
|
||||
|
||||
platform_set_drvdata(pdev, ldev);
|
||||
|
||||
@ -459,9 +466,17 @@ static void lima_pdev_remove(struct platform_device *pdev)
|
||||
lima_sched_slab_fini();
|
||||
}
|
||||
|
||||
static const struct lima_compatible lima_mali400_data = {
|
||||
.id = lima_gpu_mali400,
|
||||
};
|
||||
|
||||
static const struct lima_compatible lima_mali450_data = {
|
||||
.id = lima_gpu_mali450,
|
||||
};
|
||||
|
||||
static const struct of_device_id dt_match[] = {
|
||||
{ .compatible = "arm,mali-400", .data = (void *)lima_gpu_mali400 },
|
||||
{ .compatible = "arm,mali-450", .data = (void *)lima_gpu_mali450 },
|
||||
{ .compatible = "arm,mali-400", .data = &lima_mali400_data },
|
||||
{ .compatible = "arm,mali-450", .data = &lima_mali450_data },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dt_match);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <drm/drm_file.h>
|
||||
|
||||
#include "lima_ctx.h"
|
||||
#include "lima_device.h"
|
||||
|
||||
extern int lima_sched_timeout_ms;
|
||||
extern uint lima_heap_init_nr_pages;
|
||||
@ -39,6 +40,10 @@ struct lima_submit {
|
||||
struct lima_sched_task *task;
|
||||
};
|
||||
|
||||
struct lima_compatible {
|
||||
enum lima_gpu_id id;
|
||||
};
|
||||
|
||||
static inline struct lima_drm_priv *
|
||||
to_lima_drm_priv(struct drm_file *file)
|
||||
{
|
||||
|
@ -233,6 +233,13 @@ static void lima_gp_task_mmu_error(struct lima_sched_pipe *pipe)
|
||||
lima_sched_pipe_task_done(pipe);
|
||||
}
|
||||
|
||||
static void lima_gp_task_mask_irq(struct lima_sched_pipe *pipe)
|
||||
{
|
||||
struct lima_ip *ip = pipe->processor[0];
|
||||
|
||||
gp_write(LIMA_GP_INT_MASK, 0);
|
||||
}
|
||||
|
||||
static int lima_gp_task_recover(struct lima_sched_pipe *pipe)
|
||||
{
|
||||
struct lima_ip *ip = pipe->processor[0];
|
||||
@ -338,7 +345,9 @@ int lima_gp_init(struct lima_ip *ip)
|
||||
|
||||
void lima_gp_fini(struct lima_ip *ip)
|
||||
{
|
||||
struct lima_device *dev = ip->dev;
|
||||
|
||||
devm_free_irq(dev->dev, ip->irq, ip);
|
||||
}
|
||||
|
||||
int lima_gp_pipe_init(struct lima_device *dev)
|
||||
@ -365,6 +374,7 @@ int lima_gp_pipe_init(struct lima_device *dev)
|
||||
pipe->task_error = lima_gp_task_error;
|
||||
pipe->task_mmu_error = lima_gp_task_mmu_error;
|
||||
pipe->task_recover = lima_gp_task_recover;
|
||||
pipe->task_mask_irq = lima_gp_task_mask_irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -118,7 +118,12 @@ int lima_mmu_init(struct lima_ip *ip)
|
||||
|
||||
void lima_mmu_fini(struct lima_ip *ip)
|
||||
{
|
||||
struct lima_device *dev = ip->dev;
|
||||
|
||||
if (ip->id == lima_ip_ppmmu_bcast)
|
||||
return;
|
||||
|
||||
devm_free_irq(dev->dev, ip->irq, ip);
|
||||
}
|
||||
|
||||
void lima_mmu_flush_tlb(struct lima_ip *ip)
|
||||
|
@ -286,7 +286,9 @@ int lima_pp_init(struct lima_ip *ip)
|
||||
|
||||
void lima_pp_fini(struct lima_ip *ip)
|
||||
{
|
||||
struct lima_device *dev = ip->dev;
|
||||
|
||||
devm_free_irq(dev->dev, ip->irq, ip);
|
||||
}
|
||||
|
||||
int lima_pp_bcast_resume(struct lima_ip *ip)
|
||||
@ -319,7 +321,9 @@ int lima_pp_bcast_init(struct lima_ip *ip)
|
||||
|
||||
void lima_pp_bcast_fini(struct lima_ip *ip)
|
||||
{
|
||||
struct lima_device *dev = ip->dev;
|
||||
|
||||
devm_free_irq(dev->dev, ip->irq, ip);
|
||||
}
|
||||
|
||||
static int lima_pp_task_validate(struct lima_sched_pipe *pipe,
|
||||
@ -429,6 +433,9 @@ static void lima_pp_task_error(struct lima_sched_pipe *pipe)
|
||||
|
||||
lima_pp_hard_reset(ip);
|
||||
}
|
||||
|
||||
if (pipe->bcast_processor)
|
||||
lima_bcast_reset(pipe->bcast_processor);
|
||||
}
|
||||
|
||||
static void lima_pp_task_mmu_error(struct lima_sched_pipe *pipe)
|
||||
@ -437,6 +444,20 @@ static void lima_pp_task_mmu_error(struct lima_sched_pipe *pipe)
|
||||
lima_sched_pipe_task_done(pipe);
|
||||
}
|
||||
|
||||
static void lima_pp_task_mask_irq(struct lima_sched_pipe *pipe)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pipe->num_processor; i++) {
|
||||
struct lima_ip *ip = pipe->processor[i];
|
||||
|
||||
pp_write(LIMA_PP_INT_MASK, 0);
|
||||
}
|
||||
|
||||
if (pipe->bcast_processor)
|
||||
lima_bcast_mask_irq(pipe->bcast_processor);
|
||||
}
|
||||
|
||||
static struct kmem_cache *lima_pp_task_slab;
|
||||
static int lima_pp_task_slab_refcnt;
|
||||
|
||||
@ -468,6 +489,7 @@ int lima_pp_pipe_init(struct lima_device *dev)
|
||||
pipe->task_fini = lima_pp_task_fini;
|
||||
pipe->task_error = lima_pp_task_error;
|
||||
pipe->task_mmu_error = lima_pp_task_mmu_error;
|
||||
pipe->task_mask_irq = lima_pp_task_mask_irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -422,12 +422,21 @@ static enum drm_gpu_sched_stat lima_sched_timedout_job(struct drm_sched_job *job
|
||||
*/
|
||||
for (i = 0; i < pipe->num_processor; i++)
|
||||
synchronize_irq(pipe->processor[i]->irq);
|
||||
if (pipe->bcast_processor)
|
||||
synchronize_irq(pipe->bcast_processor->irq);
|
||||
|
||||
if (dma_fence_is_signaled(task->fence)) {
|
||||
DRM_WARN("%s unexpectedly high interrupt latency\n", lima_ip_name(ip));
|
||||
return DRM_GPU_SCHED_STAT_NOMINAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The task might still finish while this timeout handler runs.
|
||||
* To prevent a race condition on its completion, mask all irqs
|
||||
* on the running core until the next hard reset completes.
|
||||
*/
|
||||
pipe->task_mask_irq(pipe);
|
||||
|
||||
if (!pipe->error)
|
||||
DRM_ERROR("%s job timeout\n", lima_ip_name(ip));
|
||||
|
||||
|
@ -80,6 +80,7 @@ struct lima_sched_pipe {
|
||||
void (*task_error)(struct lima_sched_pipe *pipe);
|
||||
void (*task_mmu_error)(struct lima_sched_pipe *pipe);
|
||||
int (*task_recover)(struct lima_sched_pipe *pipe);
|
||||
void (*task_mask_irq)(struct lima_sched_pipe *pipe);
|
||||
|
||||
struct work_struct recover_work;
|
||||
};
|
||||
|
@ -366,6 +366,7 @@ struct drm_crtc_state;
|
||||
struct drm_display_mode;
|
||||
struct drm_plane;
|
||||
struct drm_atomic_state;
|
||||
struct drm_scanout_buffer;
|
||||
|
||||
extern const uint32_t mgag200_primary_plane_formats[];
|
||||
extern const size_t mgag200_primary_plane_formats_size;
|
||||
@ -379,12 +380,16 @@ void mgag200_primary_plane_helper_atomic_enable(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state);
|
||||
void mgag200_primary_plane_helper_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_atomic_state *old_state);
|
||||
int mgag200_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
|
||||
struct drm_scanout_buffer *sb);
|
||||
|
||||
#define MGAG200_PRIMARY_PLANE_HELPER_FUNCS \
|
||||
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, \
|
||||
.atomic_check = mgag200_primary_plane_helper_atomic_check, \
|
||||
.atomic_update = mgag200_primary_plane_helper_atomic_update, \
|
||||
.atomic_enable = mgag200_primary_plane_helper_atomic_enable, \
|
||||
.atomic_disable = mgag200_primary_plane_helper_atomic_disable
|
||||
.atomic_disable = mgag200_primary_plane_helper_atomic_disable, \
|
||||
.get_scanout_buffer = mgag200_primary_plane_helper_get_scanout_buffer
|
||||
|
||||
#define MGAG200_PRIMARY_PLANE_FUNCS \
|
||||
.update_plane = drm_atomic_helper_update_plane, \
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_atomic_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_panic.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "mgag200_drv.h"
|
||||
@ -546,6 +547,23 @@ void mgag200_primary_plane_helper_atomic_disable(struct drm_plane *plane,
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
int mgag200_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
|
||||
struct drm_scanout_buffer *sb)
|
||||
{
|
||||
struct mga_device *mdev = to_mga_device(plane->dev);
|
||||
struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(mdev->vram);
|
||||
|
||||
if (plane->state && plane->state->fb) {
|
||||
sb->format = plane->state->fb->format;
|
||||
sb->width = plane->state->fb->width;
|
||||
sb->height = plane->state->fb->height;
|
||||
sb->pitch[0] = plane->state->fb->pitches[0];
|
||||
sb->map[0] = map;
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* CRTC
|
||||
*/
|
||||
|
@ -83,7 +83,7 @@ static bool
|
||||
nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
struct nvif_head *head = &nouveau_crtc(crtc)->head;
|
||||
struct nvif_head_scanoutpos_v0 args;
|
||||
int retry = 20;
|
||||
|
@ -225,12 +225,18 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
|
||||
u8 *dpcd = nv_encoder->dp.dpcd;
|
||||
int ret = NOUVEAU_DP_NONE, hpd;
|
||||
|
||||
/* If we've already read the DPCD on an eDP device, we don't need to
|
||||
* reread it as it won't change
|
||||
/* eDP ports don't support hotplugging - so there's no point in probing eDP ports unless we
|
||||
* haven't probed them once before.
|
||||
*/
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP &&
|
||||
dpcd[DP_DPCD_REV] != 0)
|
||||
return NOUVEAU_DP_SST;
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
|
||||
if (connector->status == connector_status_connected)
|
||||
return NOUVEAU_DP_SST;
|
||||
else if (connector->status == connector_status_disconnected)
|
||||
return NOUVEAU_DP_NONE;
|
||||
}
|
||||
|
||||
// Ensure that the aux bus is enabled for probing
|
||||
drm_dp_dpcd_set_powered(&nv_connector->aux, true);
|
||||
|
||||
mutex_lock(&nv_encoder->dp.hpd_irq_lock);
|
||||
if (mstm) {
|
||||
@ -293,6 +299,13 @@ out:
|
||||
if (mstm && !mstm->suspended && ret != NOUVEAU_DP_MST)
|
||||
nv50_mstm_remove(mstm);
|
||||
|
||||
/* GSP doesn't like when we try to do aux transactions on a port it considers disconnected,
|
||||
* and since we don't really have a usecase for that anyway - just disable the aux bus here
|
||||
* if we've decided the connector is disconnected
|
||||
*/
|
||||
if (ret == NOUVEAU_DP_NONE)
|
||||
drm_dp_dpcd_set_powered(&nv_connector->aux, false);
|
||||
|
||||
mutex_unlock(&nv_encoder->dp.hpd_irq_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -335,6 +335,17 @@ config DRM_PANEL_LG_LG4573
|
||||
Say Y here if you want to enable support for LG4573 RGB panel.
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config DRM_PANEL_LG_SW43408
|
||||
tristate "LG SW43408 panel"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Say Y here if you want to enable support for LG sw43408 panel.
|
||||
The panel has a 1080x2160@60Hz resolution and uses 24 bit RGB per
|
||||
pixel. It provides a MIPI DSI interface to the host and has a
|
||||
built-in LED backlight.
|
||||
|
||||
config DRM_PANEL_MAGNACHIP_D53E6EA8966
|
||||
tristate "Magnachip D53E6EA8966 DSI panel"
|
||||
depends on OF && SPI
|
||||
|
@ -34,6 +34,7 @@ obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W) += panel-leadtek-ltk050h3146w.o
|
||||
obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o
|
||||
obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
|
||||
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
|
||||
obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o
|
||||
obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o
|
||||
obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
|
||||
obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
|
||||
|
320
drivers/gpu/drm/panel/panel-lg-sw43408.c
Normal file
320
drivers/gpu/drm/panel/panel-lg-sw43408.c
Normal file
@ -0,0 +1,320 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019-2024 Linaro Ltd
|
||||
* Author: Sumit Semwal <sumit.semwal@linaro.org>
|
||||
* Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/display/drm_dsc.h>
|
||||
#include <drm/display/drm_dsc_helper.h>
|
||||
|
||||
#define NUM_SUPPLIES 2
|
||||
|
||||
struct sw43408_panel {
|
||||
struct drm_panel base;
|
||||
struct mipi_dsi_device *link;
|
||||
|
||||
struct regulator_bulk_data supplies[NUM_SUPPLIES];
|
||||
|
||||
struct gpio_desc *reset_gpio;
|
||||
|
||||
struct drm_dsc_config dsc;
|
||||
};
|
||||
|
||||
static inline struct sw43408_panel *to_panel_info(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct sw43408_panel, base);
|
||||
}
|
||||
|
||||
static int sw43408_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct sw43408_panel *ctx = to_panel_info(panel);
|
||||
int ret;
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_off(ctx->link);
|
||||
if (ret < 0)
|
||||
dev_err(panel->dev, "set_display_off cmd failed ret = %d\n", ret);
|
||||
|
||||
ret = mipi_dsi_dcs_enter_sleep_mode(ctx->link);
|
||||
if (ret < 0)
|
||||
dev_err(panel->dev, "enter_sleep cmd failed ret = %d\n", ret);
|
||||
|
||||
msleep(100);
|
||||
|
||||
gpiod_set_value(ctx->reset_gpio, 1);
|
||||
|
||||
return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
}
|
||||
|
||||
static int sw43408_program(struct drm_panel *panel)
|
||||
{
|
||||
struct sw43408_panel *ctx = to_panel_info(panel);
|
||||
struct drm_dsc_picture_parameter_set pps;
|
||||
|
||||
mipi_dsi_dcs_write_seq(ctx->link, MIPI_DCS_SET_GAMMA_CURVE, 0x02);
|
||||
|
||||
mipi_dsi_dcs_set_tear_on(ctx->link, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
|
||||
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0x53, 0x0c, 0x30);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x00, 0x70, 0xdf, 0x00, 0x70, 0xdf);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0xf7, 0x01, 0x49, 0x0c);
|
||||
|
||||
mipi_dsi_dcs_exit_sleep_mode(ctx->link);
|
||||
|
||||
msleep(135);
|
||||
|
||||
/* COMPRESSION_MODE moved after setting the PPS */
|
||||
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xac);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0xe5,
|
||||
0x00, 0x3a, 0x00, 0x3a, 0x00, 0x0e, 0x10);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0xb5,
|
||||
0x75, 0x60, 0x2d, 0x5d, 0x80, 0x00, 0x0a, 0x0b,
|
||||
0x00, 0x05, 0x0b, 0x00, 0x80, 0x0d, 0x0e, 0x40,
|
||||
0x00, 0x0c, 0x00, 0x16, 0x00, 0xb8, 0x00, 0x80,
|
||||
0x0d, 0x0e, 0x40, 0x00, 0x0c, 0x00, 0x16, 0x00,
|
||||
0xb8, 0x00, 0x81, 0x00, 0x03, 0x03, 0x03, 0x01,
|
||||
0x01);
|
||||
msleep(85);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0xcd,
|
||||
0x00, 0x00, 0x00, 0x19, 0x19, 0x19, 0x19, 0x19,
|
||||
0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
|
||||
0x16, 0x16);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0xcb, 0x80, 0x5c, 0x07, 0x03, 0x28);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0xc0, 0x02, 0x02, 0x0f);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x04, 0x61, 0xdb, 0x04, 0x70, 0xdb);
|
||||
mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xca);
|
||||
|
||||
mipi_dsi_dcs_set_display_on(ctx->link);
|
||||
|
||||
msleep(50);
|
||||
|
||||
ctx->link->mode_flags &= ~MIPI_DSI_MODE_LPM;
|
||||
|
||||
drm_dsc_pps_payload_pack(&pps, ctx->link->dsc);
|
||||
mipi_dsi_picture_parameter_set(ctx->link, &pps);
|
||||
|
||||
ctx->link->mode_flags |= MIPI_DSI_MODE_LPM;
|
||||
|
||||
/*
|
||||
* This panel uses PPS selectors with offset:
|
||||
* PPS 1 if pps_identifier is 0
|
||||
* PPS 2 if pps_identifier is 1
|
||||
*/
|
||||
mipi_dsi_compression_mode_ext(ctx->link, true,
|
||||
MIPI_DSI_COMPRESSION_DSC, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sw43408_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct sw43408_panel *ctx = to_panel_info(panel);
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
gpiod_set_value(ctx->reset_gpio, 0);
|
||||
usleep_range(9000, 10000);
|
||||
gpiod_set_value(ctx->reset_gpio, 1);
|
||||
usleep_range(1000, 2000);
|
||||
gpiod_set_value(ctx->reset_gpio, 0);
|
||||
usleep_range(9000, 10000);
|
||||
|
||||
ret = sw43408_program(panel);
|
||||
if (ret)
|
||||
goto poweroff;
|
||||
|
||||
return 0;
|
||||
|
||||
poweroff:
|
||||
gpiod_set_value(ctx->reset_gpio, 1);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode sw43408_mode = {
|
||||
.clock = (1080 + 20 + 32 + 20) * (2160 + 20 + 4 + 20) * 60 / 1000,
|
||||
|
||||
.hdisplay = 1080,
|
||||
.hsync_start = 1080 + 20,
|
||||
.hsync_end = 1080 + 20 + 32,
|
||||
.htotal = 1080 + 20 + 32 + 20,
|
||||
|
||||
.vdisplay = 2160,
|
||||
.vsync_start = 2160 + 20,
|
||||
.vsync_end = 2160 + 20 + 4,
|
||||
.vtotal = 2160 + 20 + 4 + 20,
|
||||
|
||||
.width_mm = 62,
|
||||
.height_mm = 124,
|
||||
|
||||
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
|
||||
};
|
||||
|
||||
static int sw43408_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
return drm_connector_helper_get_modes_fixed(connector, &sw43408_mode);
|
||||
}
|
||||
|
||||
static int sw43408_backlight_update_status(struct backlight_device *bl)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = bl_get_data(bl);
|
||||
u16 brightness = backlight_get_brightness(bl);
|
||||
|
||||
return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
|
||||
}
|
||||
|
||||
const struct backlight_ops sw43408_backlight_ops = {
|
||||
.update_status = sw43408_backlight_update_status,
|
||||
};
|
||||
|
||||
static int sw43408_backlight_init(struct sw43408_panel *ctx)
|
||||
{
|
||||
struct device *dev = &ctx->link->dev;
|
||||
const struct backlight_properties props = {
|
||||
.type = BACKLIGHT_PLATFORM,
|
||||
.brightness = 255,
|
||||
.max_brightness = 255,
|
||||
};
|
||||
|
||||
ctx->base.backlight = devm_backlight_device_register(dev, dev_name(dev), dev,
|
||||
ctx->link,
|
||||
&sw43408_backlight_ops,
|
||||
&props);
|
||||
|
||||
if (IS_ERR(ctx->base.backlight))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->base.backlight),
|
||||
"Failed to create backlight\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs sw43408_funcs = {
|
||||
.unprepare = sw43408_unprepare,
|
||||
.prepare = sw43408_prepare,
|
||||
.get_modes = sw43408_get_modes,
|
||||
};
|
||||
|
||||
static const struct of_device_id sw43408_of_match[] = {
|
||||
{ .compatible = "lg,sw43408", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sw43408_of_match);
|
||||
|
||||
static int sw43408_add(struct sw43408_panel *ctx)
|
||||
{
|
||||
struct device *dev = &ctx->link->dev;
|
||||
int ret;
|
||||
|
||||
ctx->supplies[0].supply = "vddi"; /* 1.88 V */
|
||||
ctx->supplies[0].init_load_uA = 62000;
|
||||
ctx->supplies[1].supply = "vpnl"; /* 3.0 V */
|
||||
ctx->supplies[1].init_load_uA = 857000;
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
|
||||
ctx->supplies);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->reset_gpio)) {
|
||||
ret = PTR_ERR(ctx->reset_gpio);
|
||||
return dev_err_probe(dev, ret, "cannot get reset gpio\n");
|
||||
}
|
||||
|
||||
ret = sw43408_backlight_init(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->base.prepare_prev_first = true;
|
||||
|
||||
drm_panel_init(&ctx->base, dev, &sw43408_funcs, DRM_MODE_CONNECTOR_DSI);
|
||||
|
||||
drm_panel_add(&ctx->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sw43408_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct sw43408_panel *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
dsi->mode_flags = MIPI_DSI_MODE_LPM;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->lanes = 4;
|
||||
|
||||
ctx->link = dsi;
|
||||
mipi_dsi_set_drvdata(dsi, ctx);
|
||||
|
||||
ret = sw43408_add(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* The panel works only in the DSC mode. Set DSC params. */
|
||||
ctx->dsc.dsc_version_major = 0x1;
|
||||
ctx->dsc.dsc_version_minor = 0x1;
|
||||
|
||||
/* slice_count * slice_width == width */
|
||||
ctx->dsc.slice_height = 16;
|
||||
ctx->dsc.slice_width = 540;
|
||||
ctx->dsc.slice_count = 2;
|
||||
ctx->dsc.bits_per_component = 8;
|
||||
ctx->dsc.bits_per_pixel = 8 << 4;
|
||||
ctx->dsc.block_pred_enable = true;
|
||||
|
||||
dsi->dsc = &ctx->dsc;
|
||||
|
||||
return mipi_dsi_attach(dsi);
|
||||
}
|
||||
|
||||
static void sw43408_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct sw43408_panel *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
int ret;
|
||||
|
||||
ret = sw43408_unprepare(&ctx->base);
|
||||
if (ret < 0)
|
||||
dev_err(&dsi->dev, "failed to unprepare panel: %d\n", ret);
|
||||
|
||||
ret = mipi_dsi_detach(dsi);
|
||||
if (ret < 0)
|
||||
dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
|
||||
|
||||
drm_panel_remove(&ctx->base);
|
||||
}
|
||||
|
||||
static struct mipi_dsi_driver sw43408_driver = {
|
||||
.driver = {
|
||||
.name = "panel-lg-sw43408",
|
||||
.of_match_table = sw43408_of_match,
|
||||
},
|
||||
.probe = sw43408_probe,
|
||||
.remove = sw43408_remove,
|
||||
};
|
||||
module_mipi_dsi_driver(sw43408_driver);
|
||||
|
||||
MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
|
||||
MODULE_DESCRIPTION("LG SW436408 MIPI-DSI LED panel");
|
||||
MODULE_LICENSE("GPL");
|
@ -556,10 +556,8 @@ static int nt35950_probe(struct mipi_dsi_device *dsi)
|
||||
}
|
||||
dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r);
|
||||
of_node_put(dsi_r);
|
||||
if (!dsi_r_host) {
|
||||
dev_err(dev, "Cannot get secondary DSI host\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (!dsi_r_host)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "Cannot get secondary DSI host\n");
|
||||
|
||||
nt->dsi[1] = mipi_dsi_device_register_full(dsi_r_host, info);
|
||||
if (!nt->dsi[1]) {
|
||||
|
@ -2591,22 +2591,22 @@ static const struct panel_desc innolux_g121i1_l01 = {
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode innolux_g121x1_l03_mode = {
|
||||
.clock = 65000,
|
||||
.hdisplay = 1024,
|
||||
.hsync_start = 1024 + 0,
|
||||
.hsync_end = 1024 + 1,
|
||||
.htotal = 1024 + 0 + 1 + 320,
|
||||
.vdisplay = 768,
|
||||
.vsync_start = 768 + 38,
|
||||
.vsync_end = 768 + 38 + 1,
|
||||
.vtotal = 768 + 38 + 1 + 0,
|
||||
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
|
||||
static const struct display_timing innolux_g121x1_l03_timings = {
|
||||
.pixelclock = { 57500000, 64900000, 74400000 },
|
||||
.hactive = { 1024, 1024, 1024 },
|
||||
.hfront_porch = { 90, 140, 190 },
|
||||
.hback_porch = { 90, 140, 190 },
|
||||
.hsync_len = { 36, 40, 60 },
|
||||
.vactive = { 768, 768, 768 },
|
||||
.vfront_porch = { 2, 15, 30 },
|
||||
.vback_porch = { 2, 15, 30 },
|
||||
.vsync_len = { 2, 8, 20 },
|
||||
.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
|
||||
};
|
||||
|
||||
static const struct panel_desc innolux_g121x1_l03 = {
|
||||
.modes = &innolux_g121x1_l03_mode,
|
||||
.num_modes = 1,
|
||||
.timings = &innolux_g121x1_l03_timings,
|
||||
.num_timings = 1,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 246,
|
||||
@ -2617,6 +2617,27 @@ static const struct panel_desc innolux_g121x1_l03 = {
|
||||
.unprepare = 200,
|
||||
.disable = 400,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
static const struct panel_desc innolux_g121xce_l01 = {
|
||||
.timings = &innolux_g121x1_l03_timings,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 246,
|
||||
.height = 185,
|
||||
},
|
||||
.delay = {
|
||||
.enable = 200,
|
||||
.unprepare = 200,
|
||||
.disable = 400,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
static const struct display_timing innolux_g156hce_l01_timings = {
|
||||
@ -4592,6 +4613,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "innolux,g121x1-l03",
|
||||
.data = &innolux_g121x1_l03,
|
||||
}, {
|
||||
.compatible = "innolux,g121xce-l01",
|
||||
.data = &innolux_g121xce_l01,
|
||||
}, {
|
||||
.compatible = "innolux,g156hce-l01",
|
||||
.data = &innolux_g156hce_l01,
|
||||
|
@ -550,10 +550,8 @@ static int truly_nt35597_probe(struct mipi_dsi_device *dsi)
|
||||
|
||||
dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);
|
||||
of_node_put(dsi1);
|
||||
if (!dsi1_host) {
|
||||
dev_err(dev, "failed to find dsi host\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (!dsi1_host)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");
|
||||
|
||||
/* register the second DSI device */
|
||||
dsi1_device = mipi_dsi_device_register_full(dsi1_host, &info);
|
||||
|
@ -36,7 +36,7 @@ config ROCKCHIP_VOP2
|
||||
config ROCKCHIP_ANALOGIX_DP
|
||||
bool "Rockchip specific extensions for Analogix DP driver"
|
||||
depends on DRM_DISPLAY_DP_HELPER
|
||||
depends on DRM_DISPLAY_HELPER
|
||||
depends on DRM_DISPLAY_HELPER=y || (DRM_DISPLAY_HELPER=m && DRM_ROCKCHIP=m)
|
||||
depends on ROCKCHIP_VOP
|
||||
help
|
||||
This selects support for Rockchip SoC specific extensions
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <drm/drm_gem_shmem_helper.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_panic.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#define DRIVER_NAME "simpledrm"
|
||||
@ -671,11 +672,26 @@ static void simpledrm_primary_plane_helper_atomic_disable(struct drm_plane *plan
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static int simpledrm_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
|
||||
struct drm_scanout_buffer *sb)
|
||||
{
|
||||
struct simpledrm_device *sdev = simpledrm_device_of_dev(plane->dev);
|
||||
|
||||
sb->width = sdev->mode.hdisplay;
|
||||
sb->height = sdev->mode.vdisplay;
|
||||
sb->format = sdev->format;
|
||||
sb->pitch[0] = sdev->pitch;
|
||||
sb->map[0] = sdev->screen_base;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs simpledrm_primary_plane_helper_funcs = {
|
||||
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
|
||||
.atomic_check = simpledrm_primary_plane_helper_atomic_check,
|
||||
.atomic_update = simpledrm_primary_plane_helper_atomic_update,
|
||||
.atomic_disable = simpledrm_primary_plane_helper_atomic_disable,
|
||||
.get_scanout_buffer = simpledrm_primary_plane_helper_get_scanout_buffer,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs simpledrm_primary_plane_funcs = {
|
||||
|
@ -402,7 +402,6 @@ void ttm_bo_put(struct ttm_buffer_object *bo)
|
||||
EXPORT_SYMBOL(ttm_bo_put);
|
||||
|
||||
static int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo,
|
||||
struct ttm_resource **mem,
|
||||
struct ttm_operation_ctx *ctx,
|
||||
struct ttm_place *hop)
|
||||
{
|
||||
@ -469,7 +468,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo,
|
||||
if (ret != -EMULTIHOP)
|
||||
break;
|
||||
|
||||
ret = ttm_bo_bounce_temp_buffer(bo, &evict_mem, ctx, &hop);
|
||||
ret = ttm_bo_bounce_temp_buffer(bo, ctx, &hop);
|
||||
} while (!ret);
|
||||
|
||||
if (ret) {
|
||||
@ -698,7 +697,6 @@ EXPORT_SYMBOL(ttm_bo_unpin);
|
||||
*/
|
||||
static int ttm_bo_add_move_fence(struct ttm_buffer_object *bo,
|
||||
struct ttm_resource_manager *man,
|
||||
struct ttm_resource *mem,
|
||||
bool no_wait_gpu)
|
||||
{
|
||||
struct dma_fence *fence;
|
||||
@ -787,7 +785,7 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ret = ttm_bo_add_move_fence(bo, man, *res, ctx->no_wait_gpu);
|
||||
ret = ttm_bo_add_move_fence(bo, man, ctx->no_wait_gpu);
|
||||
if (unlikely(ret)) {
|
||||
ttm_resource_free(bo, res);
|
||||
if (ret == -EBUSY)
|
||||
@ -894,7 +892,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
|
||||
bounce:
|
||||
ret = ttm_bo_handle_move_mem(bo, res, false, ctx, &hop);
|
||||
if (ret == -EMULTIHOP) {
|
||||
ret = ttm_bo_bounce_temp_buffer(bo, &res, ctx, &hop);
|
||||
ret = ttm_bo_bounce_temp_buffer(bo, ctx, &hop);
|
||||
/* try and move to final place now. */
|
||||
if (!ret)
|
||||
goto bounce;
|
||||
|
@ -2740,6 +2740,8 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
|
||||
index = 1;
|
||||
|
||||
addr = of_get_address(dev->of_node, index, NULL, NULL);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
||||
vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + mai_data->offset;
|
||||
vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
@ -61,9 +61,7 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
|
||||
|
||||
static int vkms_enable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
|
||||
|
||||
drm_calc_timestamping_constants(crtc, &crtc->mode);
|
||||
@ -88,10 +86,9 @@ static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
|
||||
bool in_vblank_irq)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned int pipe = crtc->index;
|
||||
struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
|
||||
struct vkms_output *output = &vkmsdev->output;
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
if (!READ_ONCE(vblank->enabled)) {
|
||||
*vblank_time = ktime_get();
|
||||
|
@ -10,6 +10,6 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
|
||||
vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \
|
||||
vmwgfx_validation.o vmwgfx_page_dirty.o vmwgfx_streamoutput.o \
|
||||
vmwgfx_devcaps.o ttm_object.o vmwgfx_system_manager.o \
|
||||
vmwgfx_gem.o
|
||||
vmwgfx_gem.o vmwgfx_vkms.o
|
||||
|
||||
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "vmwgfx_binding.h"
|
||||
#include "vmwgfx_devcaps.h"
|
||||
#include "vmwgfx_mksstat.h"
|
||||
#include "vmwgfx_vkms.h"
|
||||
#include "ttm_object.h"
|
||||
|
||||
#include <drm/drm_aperture.h>
|
||||
@ -910,6 +911,8 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
|
||||
"Please switch to a supported graphics device to avoid problems.");
|
||||
}
|
||||
|
||||
vmw_vkms_init(dev_priv);
|
||||
|
||||
ret = vmw_dma_select_mode(dev_priv);
|
||||
if (unlikely(ret != 0)) {
|
||||
drm_info(&dev_priv->drm,
|
||||
@ -1195,6 +1198,7 @@ static void vmw_driver_unload(struct drm_device *dev)
|
||||
|
||||
vmw_svga_disable(dev_priv);
|
||||
|
||||
vmw_vkms_cleanup(dev_priv);
|
||||
vmw_kms_close(dev_priv);
|
||||
vmw_overlay_close(dev_priv);
|
||||
|
||||
|
@ -615,6 +615,9 @@ struct vmw_private {
|
||||
|
||||
uint32 *devcaps;
|
||||
|
||||
bool vkms_enabled;
|
||||
struct workqueue_struct *crc_workq;
|
||||
|
||||
/*
|
||||
* mksGuestStat instance-descriptor and pid arrays
|
||||
*/
|
||||
@ -809,6 +812,7 @@ void vmw_resource_mob_attach(struct vmw_resource *res);
|
||||
void vmw_resource_mob_detach(struct vmw_resource *res);
|
||||
void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
|
||||
pgoff_t end);
|
||||
int vmw_resource_clean(struct vmw_resource *res);
|
||||
int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start,
|
||||
pgoff_t end, pgoff_t *num_prefault);
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "vmwgfx_kms.h"
|
||||
|
||||
#include "vmwgfx_bo.h"
|
||||
#include "vmwgfx_vkms.h"
|
||||
#include "vmw_surface_cache.h"
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
@ -37,9 +38,16 @@
|
||||
#include <drm/drm_sysfs.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
void vmw_du_init(struct vmw_display_unit *du)
|
||||
{
|
||||
vmw_vkms_crtc_init(&du->crtc);
|
||||
}
|
||||
|
||||
void vmw_du_cleanup(struct vmw_display_unit *du)
|
||||
{
|
||||
struct vmw_private *dev_priv = vmw_priv(du->primary.dev);
|
||||
|
||||
vmw_vkms_crtc_cleanup(&du->crtc);
|
||||
drm_plane_cleanup(&du->primary);
|
||||
if (vmw_cmd_supported(dev_priv))
|
||||
drm_plane_cleanup(&du->cursor.base);
|
||||
@ -955,15 +963,9 @@ int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
vmw_vkms_crtc_atomic_begin(crtc, state);
|
||||
}
|
||||
|
||||
|
||||
void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* vmw_du_crtc_duplicate_state - duplicate crtc state
|
||||
* @crtc: DRM crtc
|
||||
@ -2028,6 +2030,29 @@ vmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv)
|
||||
"hotplug_mode_update", 0, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
vmw_atomic_commit_tail(struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct vmw_private *vmw = vmw_priv(old_state->dev);
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *old_crtc_state;
|
||||
int i;
|
||||
|
||||
drm_atomic_helper_commit_tail(old_state);
|
||||
|
||||
if (vmw->vkms_enabled) {
|
||||
for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
(void)old_crtc_state;
|
||||
flush_work(&du->vkms.crc_generator_work);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_helper_funcs vmw_mode_config_helpers = {
|
||||
.atomic_commit_tail = vmw_atomic_commit_tail,
|
||||
};
|
||||
|
||||
int vmw_kms_init(struct vmw_private *dev_priv)
|
||||
{
|
||||
struct drm_device *dev = &dev_priv->drm;
|
||||
@ -2047,6 +2072,7 @@ int vmw_kms_init(struct vmw_private *dev_priv)
|
||||
dev->mode_config.max_width = dev_priv->texture_max_width;
|
||||
dev->mode_config.max_height = dev_priv->texture_max_height;
|
||||
dev->mode_config.preferred_depth = dev_priv->assume_16bpp ? 16 : 32;
|
||||
dev->mode_config.helper_private = &vmw_mode_config_helpers;
|
||||
|
||||
drm_mode_create_suggested_offset_properties(dev);
|
||||
vmw_kms_create_hotplug_mode_update_property(dev_priv);
|
||||
|
@ -376,6 +376,25 @@ struct vmw_display_unit {
|
||||
bool is_implicit;
|
||||
int set_gui_x;
|
||||
int set_gui_y;
|
||||
|
||||
struct {
|
||||
struct work_struct crc_generator_work;
|
||||
struct hrtimer timer;
|
||||
ktime_t period_ns;
|
||||
|
||||
/* protects concurrent access to the vblank handler */
|
||||
atomic_t atomic_lock;
|
||||
/* protected by @atomic_lock */
|
||||
bool crc_enabled;
|
||||
struct vmw_surface *surface;
|
||||
|
||||
/* protects concurrent access to the crc worker */
|
||||
spinlock_t crc_state_lock;
|
||||
/* protected by @crc_state_lock */
|
||||
bool crc_pending;
|
||||
u64 frame_start;
|
||||
u64 frame_end;
|
||||
} vkms;
|
||||
};
|
||||
|
||||
#define vmw_crtc_to_du(x) \
|
||||
@ -387,6 +406,7 @@ struct vmw_display_unit {
|
||||
/*
|
||||
* Shared display unit functions - vmwgfx_kms.c
|
||||
*/
|
||||
void vmw_du_init(struct vmw_display_unit *du);
|
||||
void vmw_du_cleanup(struct vmw_display_unit *du);
|
||||
void vmw_du_crtc_save(struct drm_crtc *crtc);
|
||||
void vmw_du_crtc_restore(struct drm_crtc *crtc);
|
||||
@ -473,8 +493,6 @@ int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state);
|
||||
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state);
|
||||
void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state);
|
||||
void vmw_du_crtc_reset(struct drm_crtc *crtc);
|
||||
struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc);
|
||||
void vmw_du_crtc_destroy_state(struct drm_crtc *crtc,
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "vmwgfx_bo.h"
|
||||
#include "vmwgfx_kms.h"
|
||||
#include "vmwgfx_vkms.h"
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
@ -241,33 +242,6 @@ static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_ldu_crtc_atomic_enable - Noop
|
||||
*
|
||||
* @crtc: CRTC associated with the new screen
|
||||
* @state: Unused
|
||||
*
|
||||
* This is called after a mode set has been completed. Here's
|
||||
* usually a good place to call vmw_ldu_add_active/vmw_ldu_del_active
|
||||
* but since for LDU the display plane is closely tied to the
|
||||
* CRTC, it makes more sense to do those at plane update time.
|
||||
*/
|
||||
static void vmw_ldu_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_ldu_crtc_atomic_disable - Turns off CRTC
|
||||
*
|
||||
* @crtc: CRTC to be turned off
|
||||
* @state: Unused
|
||||
*/
|
||||
static void vmw_ldu_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
|
||||
.gamma_set = vmw_du_crtc_gamma_set,
|
||||
.destroy = vmw_ldu_crtc_destroy,
|
||||
@ -276,6 +250,9 @@ static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
|
||||
.atomic_destroy_state = vmw_du_crtc_destroy_state,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.enable_vblank = vmw_vkms_enable_vblank,
|
||||
.disable_vblank = vmw_vkms_disable_vblank,
|
||||
.get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
|
||||
@ -418,9 +395,9 @@ static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = {
|
||||
.mode_set_nofb = vmw_ldu_crtc_mode_set_nofb,
|
||||
.atomic_check = vmw_du_crtc_atomic_check,
|
||||
.atomic_begin = vmw_du_crtc_atomic_begin,
|
||||
.atomic_flush = vmw_du_crtc_atomic_flush,
|
||||
.atomic_enable = vmw_ldu_crtc_atomic_enable,
|
||||
.atomic_disable = vmw_ldu_crtc_atomic_disable,
|
||||
.atomic_flush = vmw_vkms_crtc_atomic_flush,
|
||||
.atomic_enable = vmw_vkms_crtc_atomic_enable,
|
||||
.atomic_disable = vmw_vkms_crtc_atomic_disable,
|
||||
};
|
||||
|
||||
|
||||
@ -541,6 +518,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
|
||||
dev_priv->implicit_placement_property,
|
||||
1);
|
||||
|
||||
vmw_du_init(&ldu->base);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_unregister:
|
||||
|
@ -1064,6 +1064,22 @@ void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
|
||||
end << PAGE_SHIFT);
|
||||
}
|
||||
|
||||
int vmw_resource_clean(struct vmw_resource *res)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (res->res_dirty) {
|
||||
if (!res->func->clean)
|
||||
return -EINVAL;
|
||||
|
||||
ret = res->func->clean(res);
|
||||
if (ret)
|
||||
return ret;
|
||||
res->res_dirty = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_resources_clean - Clean resources intersecting a mob range
|
||||
* @vbo: The mob buffer object
|
||||
@ -1080,6 +1096,7 @@ int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start,
|
||||
unsigned long res_start = start << PAGE_SHIFT;
|
||||
unsigned long res_end = end << PAGE_SHIFT;
|
||||
unsigned long last_cleaned = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Find the resource with lowest backup_offset that intersects the
|
||||
@ -1106,18 +1123,9 @@ int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start,
|
||||
* intersecting the range.
|
||||
*/
|
||||
while (found) {
|
||||
if (found->res_dirty) {
|
||||
int ret;
|
||||
|
||||
if (!found->func->clean)
|
||||
return -EINVAL;
|
||||
|
||||
ret = found->func->clean(found);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
found->res_dirty = false;
|
||||
}
|
||||
ret = vmw_resource_clean(found);
|
||||
if (ret)
|
||||
return ret;
|
||||
last_cleaned = found->guest_memory_offset + found->guest_memory_size;
|
||||
cur = rb_next(&found->mob_node);
|
||||
if (!cur)
|
||||
|
@ -27,11 +27,13 @@
|
||||
|
||||
#include "vmwgfx_bo.h"
|
||||
#include "vmwgfx_kms.h"
|
||||
#include "vmwgfx_vkms.h"
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#define vmw_crtc_to_sou(x) \
|
||||
container_of(x, struct vmw_screen_object_unit, base.crtc)
|
||||
@ -267,19 +269,6 @@ static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_sou_crtc_atomic_enable - Noop
|
||||
*
|
||||
* @crtc: CRTC associated with the new screen
|
||||
* @state: Unused
|
||||
*
|
||||
* This is called after a mode set has been completed.
|
||||
*/
|
||||
static void vmw_sou_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_sou_crtc_atomic_disable - Turns off CRTC
|
||||
*
|
||||
@ -302,6 +291,9 @@ static void vmw_sou_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
sou = vmw_crtc_to_sou(crtc);
|
||||
dev_priv = vmw_priv(crtc->dev);
|
||||
|
||||
if (dev_priv->vkms_enabled)
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
if (sou->defined) {
|
||||
ret = vmw_sou_fifo_destroy(dev_priv, sou);
|
||||
if (ret)
|
||||
@ -317,6 +309,9 @@ static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
|
||||
.atomic_destroy_state = vmw_du_crtc_destroy_state,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.enable_vblank = vmw_vkms_enable_vblank,
|
||||
.disable_vblank = vmw_vkms_disable_vblank,
|
||||
.get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -794,8 +789,8 @@ static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = {
|
||||
.mode_set_nofb = vmw_sou_crtc_mode_set_nofb,
|
||||
.atomic_check = vmw_du_crtc_atomic_check,
|
||||
.atomic_begin = vmw_du_crtc_atomic_begin,
|
||||
.atomic_flush = vmw_du_crtc_atomic_flush,
|
||||
.atomic_enable = vmw_sou_crtc_atomic_enable,
|
||||
.atomic_flush = vmw_vkms_crtc_atomic_flush,
|
||||
.atomic_enable = vmw_vkms_crtc_atomic_enable,
|
||||
.atomic_disable = vmw_sou_crtc_atomic_disable,
|
||||
};
|
||||
|
||||
@ -905,6 +900,9 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
|
||||
dev->mode_config.suggested_x_property, 0);
|
||||
drm_object_attach_property(&connector->base,
|
||||
dev->mode_config.suggested_y_property, 0);
|
||||
|
||||
vmw_du_init(&sou->base);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_unregister:
|
||||
|
@ -27,12 +27,14 @@
|
||||
|
||||
#include "vmwgfx_bo.h"
|
||||
#include "vmwgfx_kms.h"
|
||||
#include "vmwgfx_vkms.h"
|
||||
#include "vmw_surface_cache.h"
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#define vmw_crtc_to_stdu(x) \
|
||||
container_of(x, struct vmw_screen_target_display_unit, base.crtc)
|
||||
@ -407,16 +409,6 @@ static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
crtc->x, crtc->y);
|
||||
}
|
||||
|
||||
|
||||
static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
}
|
||||
|
||||
static void vmw_stdu_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
@ -424,7 +416,6 @@ static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct vmw_screen_target_display_unit *stdu;
|
||||
int ret;
|
||||
|
||||
|
||||
if (!crtc) {
|
||||
DRM_ERROR("CRTC is NULL\n");
|
||||
return;
|
||||
@ -433,6 +424,9 @@ static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
stdu = vmw_crtc_to_stdu(crtc);
|
||||
dev_priv = vmw_priv(crtc->dev);
|
||||
|
||||
if (dev_priv->vkms_enabled)
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
if (stdu->defined) {
|
||||
ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
|
||||
if (ret)
|
||||
@ -770,7 +764,6 @@ out_unref:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Screen Target CRTC dispatch table
|
||||
*/
|
||||
@ -782,6 +775,12 @@ static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = {
|
||||
.atomic_destroy_state = vmw_du_crtc_destroy_state,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.enable_vblank = vmw_vkms_enable_vblank,
|
||||
.disable_vblank = vmw_vkms_disable_vblank,
|
||||
.get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,
|
||||
.get_crc_sources = vmw_vkms_get_crc_sources,
|
||||
.set_crc_source = vmw_vkms_set_crc_source,
|
||||
.verify_crc_source = vmw_vkms_verify_crc_source,
|
||||
};
|
||||
|
||||
|
||||
@ -1413,6 +1412,17 @@ vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,
|
||||
vmw_fence_obj_unreference(&fence);
|
||||
}
|
||||
|
||||
static void
|
||||
vmw_stdu_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc);
|
||||
|
||||
if (vmw->vkms_enabled)
|
||||
vmw_vkms_set_crc_surface(crtc, stdu->display_srf);
|
||||
vmw_vkms_crtc_atomic_flush(crtc, state);
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs vmw_stdu_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
@ -1453,12 +1463,11 @@ drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = {
|
||||
};
|
||||
|
||||
static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {
|
||||
.prepare = vmw_stdu_crtc_helper_prepare,
|
||||
.mode_set_nofb = vmw_stdu_crtc_mode_set_nofb,
|
||||
.atomic_check = vmw_du_crtc_atomic_check,
|
||||
.atomic_begin = vmw_du_crtc_atomic_begin,
|
||||
.atomic_flush = vmw_du_crtc_atomic_flush,
|
||||
.atomic_enable = vmw_stdu_crtc_atomic_enable,
|
||||
.atomic_flush = vmw_stdu_crtc_atomic_flush,
|
||||
.atomic_enable = vmw_vkms_crtc_atomic_enable,
|
||||
.atomic_disable = vmw_stdu_crtc_atomic_disable,
|
||||
};
|
||||
|
||||
@ -1575,6 +1584,9 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
|
||||
dev->mode_config.suggested_x_property, 0);
|
||||
drm_object_attach_property(&connector->base,
|
||||
dev->mode_config.suggested_y_property, 0);
|
||||
|
||||
vmw_du_init(&stdu->base);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_unregister:
|
||||
|
632
drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
Normal file
632
drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
Normal file
@ -0,0 +1,632 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 Broadcom. All Rights Reserved. The term
|
||||
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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 "vmwgfx_vkms.h"
|
||||
|
||||
#include "vmwgfx_bo.h"
|
||||
#include "vmwgfx_drv.h"
|
||||
#include "vmwgfx_kms.h"
|
||||
#include "vmwgfx_vkms.h"
|
||||
|
||||
#include "vmw_surface_cache.h"
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_debugfs_crc.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define GUESTINFO_VBLANK "guestinfo.vmwgfx.vkms_enable"
|
||||
|
||||
static int
|
||||
vmw_surface_sync(struct vmw_private *vmw,
|
||||
struct vmw_surface *surf)
|
||||
{
|
||||
int ret;
|
||||
struct vmw_fence_obj *fence = NULL;
|
||||
struct vmw_bo *bo = surf->res.guest_memory_bo;
|
||||
|
||||
vmw_resource_clean(&surf->res);
|
||||
|
||||
ret = ttm_bo_reserve(&bo->tbo, false, false, NULL);
|
||||
if (ret != 0) {
|
||||
drm_warn(&vmw->drm, "%s: failed reserve\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = vmw_execbuf_fence_commands(NULL, vmw, &fence, NULL);
|
||||
if (ret != 0) {
|
||||
drm_warn(&vmw->drm, "%s: failed execbuf\n", __func__);
|
||||
ttm_bo_unreserve(&bo->tbo);
|
||||
goto done;
|
||||
}
|
||||
|
||||
dma_fence_wait(&fence->base, false);
|
||||
dma_fence_put(&fence->base);
|
||||
|
||||
ttm_bo_unreserve(&bo->tbo);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
compute_crc(struct drm_crtc *crtc,
|
||||
struct vmw_surface *surf,
|
||||
u32 *crc)
|
||||
{
|
||||
u8 *mapped_surface;
|
||||
struct vmw_bo *bo = surf->res.guest_memory_bo;
|
||||
const struct SVGA3dSurfaceDesc *desc =
|
||||
vmw_surface_get_desc(surf->metadata.format);
|
||||
u32 row_pitch_bytes;
|
||||
SVGA3dSize blocks;
|
||||
u32 y;
|
||||
|
||||
*crc = 0;
|
||||
|
||||
vmw_surface_get_size_in_blocks(desc, &surf->metadata.base_size, &blocks);
|
||||
row_pitch_bytes = blocks.width * desc->pitchBytesPerBlock;
|
||||
WARN_ON(!bo);
|
||||
mapped_surface = vmw_bo_map_and_cache(bo);
|
||||
|
||||
for (y = 0; y < blocks.height; y++) {
|
||||
*crc = crc32_le(*crc, mapped_surface, row_pitch_bytes);
|
||||
mapped_surface += row_pitch_bytes;
|
||||
}
|
||||
|
||||
vmw_bo_unmap(bo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
crc_generate_worker(struct work_struct *work)
|
||||
{
|
||||
struct vmw_display_unit *du =
|
||||
container_of(work, struct vmw_display_unit, vkms.crc_generator_work);
|
||||
struct drm_crtc *crtc = &du->crtc;
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
bool crc_pending;
|
||||
u64 frame_start, frame_end;
|
||||
u32 crc32 = 0;
|
||||
struct vmw_surface *surf = 0;
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(&du->vkms.crc_state_lock);
|
||||
crc_pending = du->vkms.crc_pending;
|
||||
spin_unlock_irq(&du->vkms.crc_state_lock);
|
||||
|
||||
/*
|
||||
* We raced with the vblank hrtimer and previous work already computed
|
||||
* the crc, nothing to do.
|
||||
*/
|
||||
if (!crc_pending)
|
||||
return;
|
||||
|
||||
spin_lock_irq(&du->vkms.crc_state_lock);
|
||||
surf = du->vkms.surface;
|
||||
spin_unlock_irq(&du->vkms.crc_state_lock);
|
||||
|
||||
if (vmw_surface_sync(vmw, surf)) {
|
||||
drm_warn(crtc->dev, "CRC worker wasn't able to sync the crc surface!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = compute_crc(crtc, surf, &crc32);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
spin_lock_irq(&du->vkms.crc_state_lock);
|
||||
frame_start = du->vkms.frame_start;
|
||||
frame_end = du->vkms.frame_end;
|
||||
crc_pending = du->vkms.crc_pending;
|
||||
du->vkms.frame_start = 0;
|
||||
du->vkms.frame_end = 0;
|
||||
du->vkms.crc_pending = false;
|
||||
spin_unlock_irq(&du->vkms.crc_state_lock);
|
||||
|
||||
/*
|
||||
* The worker can fall behind the vblank hrtimer, make sure we catch up.
|
||||
*/
|
||||
while (frame_start <= frame_end)
|
||||
drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart
|
||||
vmw_vkms_vblank_simulate(struct hrtimer *timer)
|
||||
{
|
||||
struct vmw_display_unit *du = container_of(timer, struct vmw_display_unit, vkms.timer);
|
||||
struct drm_crtc *crtc = &du->crtc;
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
struct vmw_surface *surf = NULL;
|
||||
u64 ret_overrun;
|
||||
bool locked, ret;
|
||||
|
||||
ret_overrun = hrtimer_forward_now(&du->vkms.timer,
|
||||
du->vkms.period_ns);
|
||||
if (ret_overrun != 1)
|
||||
drm_dbg_driver(crtc->dev, "vblank timer missed %lld frames.\n",
|
||||
ret_overrun - 1);
|
||||
|
||||
locked = vmw_vkms_vblank_trylock(crtc);
|
||||
ret = drm_crtc_handle_vblank(crtc);
|
||||
WARN_ON(!ret);
|
||||
if (!locked)
|
||||
return HRTIMER_RESTART;
|
||||
surf = du->vkms.surface;
|
||||
vmw_vkms_unlock(crtc);
|
||||
|
||||
if (du->vkms.crc_enabled && surf) {
|
||||
u64 frame = drm_crtc_accurate_vblank_count(crtc);
|
||||
|
||||
spin_lock(&du->vkms.crc_state_lock);
|
||||
if (!du->vkms.crc_pending)
|
||||
du->vkms.frame_start = frame;
|
||||
else
|
||||
drm_dbg_driver(crtc->dev,
|
||||
"crc worker falling behind, frame_start: %llu, frame_end: %llu\n",
|
||||
du->vkms.frame_start, frame);
|
||||
du->vkms.frame_end = frame;
|
||||
du->vkms.crc_pending = true;
|
||||
spin_unlock(&du->vkms.crc_state_lock);
|
||||
|
||||
ret = queue_work(vmw->crc_workq, &du->vkms.crc_generator_work);
|
||||
if (!ret)
|
||||
drm_dbg_driver(crtc->dev, "Composer worker already queued\n");
|
||||
}
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_init(struct vmw_private *vmw)
|
||||
{
|
||||
char buffer[64];
|
||||
const size_t max_buf_len = sizeof(buffer) - 1;
|
||||
size_t buf_len = max_buf_len;
|
||||
int ret;
|
||||
|
||||
vmw->vkms_enabled = false;
|
||||
|
||||
ret = vmw_host_get_guestinfo(GUESTINFO_VBLANK, buffer, &buf_len);
|
||||
if (ret || buf_len > max_buf_len)
|
||||
return;
|
||||
buffer[buf_len] = '\0';
|
||||
|
||||
ret = kstrtobool(buffer, &vmw->vkms_enabled);
|
||||
if (!ret && vmw->vkms_enabled) {
|
||||
ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS);
|
||||
vmw->vkms_enabled = (ret == 0);
|
||||
}
|
||||
|
||||
vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0);
|
||||
if (!vmw->crc_workq) {
|
||||
drm_warn(&vmw->drm, "crc workqueue allocation failed. Disabling vkms.");
|
||||
vmw->vkms_enabled = false;
|
||||
}
|
||||
if (vmw->vkms_enabled)
|
||||
drm_info(&vmw->drm, "VKMS enabled\n");
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_cleanup(struct vmw_private *vmw)
|
||||
{
|
||||
destroy_workqueue(vmw->crc_workq);
|
||||
}
|
||||
|
||||
bool
|
||||
vmw_vkms_get_vblank_timestamp(struct drm_crtc *crtc,
|
||||
int *max_error,
|
||||
ktime_t *vblank_time,
|
||||
bool in_vblank_irq)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vmw_private *vmw = vmw_priv(dev);
|
||||
unsigned int pipe = crtc->index;
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
|
||||
if (!vmw->vkms_enabled)
|
||||
return false;
|
||||
|
||||
if (!READ_ONCE(vblank->enabled)) {
|
||||
*vblank_time = ktime_get();
|
||||
return true;
|
||||
}
|
||||
|
||||
*vblank_time = READ_ONCE(du->vkms.timer.node.expires);
|
||||
|
||||
if (WARN_ON(*vblank_time == vblank->time))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* To prevent races we roll the hrtimer forward before we do any
|
||||
* interrupt processing - this is how real hw works (the interrupt is
|
||||
* only generated after all the vblank registers are updated) and what
|
||||
* the vblank core expects. Therefore we need to always correct the
|
||||
* timestampe by one frame.
|
||||
*/
|
||||
*vblank_time -= du->vkms.period_ns;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
vmw_vkms_enable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vmw_private *vmw = vmw_priv(dev);
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
|
||||
if (!vmw->vkms_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
drm_calc_timestamping_constants(crtc, &crtc->mode);
|
||||
|
||||
hrtimer_init(&du->vkms.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
du->vkms.timer.function = &vmw_vkms_vblank_simulate;
|
||||
du->vkms.period_ns = ktime_set(0, vblank->framedur_ns);
|
||||
hrtimer_start(&du->vkms.timer, du->vkms.period_ns, HRTIMER_MODE_REL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_disable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
|
||||
if (!vmw->vkms_enabled)
|
||||
return;
|
||||
|
||||
hrtimer_cancel(&du->vkms.timer);
|
||||
du->vkms.surface = NULL;
|
||||
du->vkms.period_ns = ktime_set(0, 0);
|
||||
}
|
||||
|
||||
enum vmw_vkms_lock_state {
|
||||
VMW_VKMS_LOCK_UNLOCKED = 0,
|
||||
VMW_VKMS_LOCK_MODESET = 1,
|
||||
VMW_VKMS_LOCK_VBLANK = 2
|
||||
};
|
||||
|
||||
void
|
||||
vmw_vkms_crtc_init(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
|
||||
atomic_set(&du->vkms.atomic_lock, VMW_VKMS_LOCK_UNLOCKED);
|
||||
spin_lock_init(&du->vkms.crc_state_lock);
|
||||
|
||||
INIT_WORK(&du->vkms.crc_generator_work, crc_generate_worker);
|
||||
du->vkms.surface = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_crtc_cleanup(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
|
||||
WARN_ON(work_pending(&du->vkms.crc_generator_work));
|
||||
hrtimer_cancel(&du->vkms.timer);
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
|
||||
if (vmw->vkms_enabled)
|
||||
vmw_vkms_modeset_lock(crtc);
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
|
||||
if (!vmw->vkms_enabled)
|
||||
return;
|
||||
|
||||
if (crtc->state->event) {
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
|
||||
if (drm_crtc_vblank_get(crtc) != 0)
|
||||
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
||||
else
|
||||
drm_crtc_arm_vblank_event(crtc, crtc->state->event);
|
||||
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
|
||||
vmw_vkms_unlock(crtc);
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
|
||||
if (vmw->vkms_enabled)
|
||||
drm_crtc_vblank_on(crtc);
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
|
||||
if (vmw->vkms_enabled)
|
||||
drm_crtc_vblank_off(crtc);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_crc_supported(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
|
||||
if (!vmw->vkms_enabled)
|
||||
return false;
|
||||
|
||||
if (vmw->active_display_unit != vmw_du_screen_target)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char * const pipe_crc_sources[] = {"auto"};
|
||||
|
||||
static int
|
||||
crc_parse_source(const char *src_name,
|
||||
bool *enabled)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!src_name) {
|
||||
*enabled = false;
|
||||
} else if (strcmp(src_name, "auto") == 0) {
|
||||
*enabled = true;
|
||||
} else {
|
||||
*enabled = false;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *const *
|
||||
vmw_vkms_get_crc_sources(struct drm_crtc *crtc,
|
||||
size_t *count)
|
||||
{
|
||||
*count = 0;
|
||||
if (!is_crc_supported(crtc))
|
||||
return NULL;
|
||||
|
||||
*count = ARRAY_SIZE(pipe_crc_sources);
|
||||
return pipe_crc_sources;
|
||||
}
|
||||
|
||||
int
|
||||
vmw_vkms_verify_crc_source(struct drm_crtc *crtc,
|
||||
const char *src_name,
|
||||
size_t *values_cnt)
|
||||
{
|
||||
bool enabled;
|
||||
|
||||
if (!is_crc_supported(crtc))
|
||||
return -EINVAL;
|
||||
|
||||
if (crc_parse_source(src_name, &enabled) < 0) {
|
||||
drm_dbg_driver(crtc->dev, "unknown source '%s'\n", src_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*values_cnt = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
vmw_vkms_set_crc_source(struct drm_crtc *crtc,
|
||||
const char *src_name)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
bool enabled, prev_enabled, locked;
|
||||
int ret;
|
||||
|
||||
if (!is_crc_supported(crtc))
|
||||
return -EINVAL;
|
||||
|
||||
ret = crc_parse_source(src_name, &enabled);
|
||||
|
||||
if (enabled)
|
||||
drm_crtc_vblank_get(crtc);
|
||||
|
||||
locked = vmw_vkms_modeset_lock_relaxed(crtc);
|
||||
prev_enabled = du->vkms.crc_enabled;
|
||||
du->vkms.crc_enabled = enabled;
|
||||
if (locked)
|
||||
vmw_vkms_unlock(crtc);
|
||||
|
||||
if (prev_enabled)
|
||||
drm_crtc_vblank_put(crtc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_set_crc_surface(struct drm_crtc *crtc,
|
||||
struct vmw_surface *surf)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
struct vmw_private *vmw = vmw_priv(crtc->dev);
|
||||
|
||||
if (vmw->vkms_enabled) {
|
||||
WARN_ON(atomic_read(&du->vkms.atomic_lock) != VMW_VKMS_LOCK_MODESET);
|
||||
du->vkms.surface = surf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_vkms_lock_max_wait_ns - Return the max wait for the vkms lock
|
||||
* @du: The vmw_display_unit from which to grab the vblank timings
|
||||
*
|
||||
* Returns the maximum wait time used to acquire the vkms lock. By
|
||||
* default uses a time of a single frame and in case where vblank
|
||||
* was not initialized for the display unit 1/60th of a second.
|
||||
*/
|
||||
static inline u64
|
||||
vmw_vkms_lock_max_wait_ns(struct vmw_display_unit *du)
|
||||
{
|
||||
s64 nsecs = ktime_to_ns(du->vkms.period_ns);
|
||||
|
||||
return (nsecs > 0) ? nsecs : 16666666;
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_vkms_modeset_lock - Protects access to crtc during modeset
|
||||
* @crtc: The crtc to lock for vkms
|
||||
*
|
||||
* This function prevents the VKMS timers/callbacks from being called
|
||||
* while a modeset operation is in process. We don't want the callbacks
|
||||
* e.g. the vblank simulator to be trying to access incomplete state
|
||||
* so we need to make sure they execute only when the modeset has
|
||||
* finished.
|
||||
*
|
||||
* Normally this would have been done with a spinlock but locking the
|
||||
* entire atomic modeset with vmwgfx is impossible because kms prepare
|
||||
* executes non-atomic ops (e.g. vmw_validation_prepare holds a mutex to
|
||||
* guard various bits of state). Which means that we need to synchronize
|
||||
* atomic context (the vblank handler) with the non-atomic entirity
|
||||
* of kms - so use an atomic_t to track which part of vkms has access
|
||||
* to the basic vkms state.
|
||||
*/
|
||||
void
|
||||
vmw_vkms_modeset_lock(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
const u64 nsecs_delay = 10;
|
||||
const u64 MAX_NSECS_DELAY = vmw_vkms_lock_max_wait_ns(du);
|
||||
u64 total_delay = 0;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = atomic_cmpxchg(&du->vkms.atomic_lock,
|
||||
VMW_VKMS_LOCK_UNLOCKED,
|
||||
VMW_VKMS_LOCK_MODESET);
|
||||
if (ret == VMW_VKMS_LOCK_UNLOCKED || total_delay >= MAX_NSECS_DELAY)
|
||||
break;
|
||||
ndelay(nsecs_delay);
|
||||
total_delay += nsecs_delay;
|
||||
} while (1);
|
||||
|
||||
if (total_delay >= MAX_NSECS_DELAY) {
|
||||
drm_warn(crtc->dev, "VKMS lock expired! total_delay = %lld, ret = %d, cur = %d\n",
|
||||
total_delay, ret, atomic_read(&du->vkms.atomic_lock));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_vkms_modeset_lock_relaxed - Protects access to crtc during modeset
|
||||
* @crtc: The crtc to lock for vkms
|
||||
*
|
||||
* Much like vmw_vkms_modeset_lock except that when the crtc is currently
|
||||
* in a modeset it will return immediately.
|
||||
*
|
||||
* Returns true if actually locked vkms to modeset or false otherwise.
|
||||
*/
|
||||
bool
|
||||
vmw_vkms_modeset_lock_relaxed(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
const u64 nsecs_delay = 10;
|
||||
const u64 MAX_NSECS_DELAY = vmw_vkms_lock_max_wait_ns(du);
|
||||
u64 total_delay = 0;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = atomic_cmpxchg(&du->vkms.atomic_lock,
|
||||
VMW_VKMS_LOCK_UNLOCKED,
|
||||
VMW_VKMS_LOCK_MODESET);
|
||||
if (ret == VMW_VKMS_LOCK_UNLOCKED ||
|
||||
ret == VMW_VKMS_LOCK_MODESET ||
|
||||
total_delay >= MAX_NSECS_DELAY)
|
||||
break;
|
||||
ndelay(nsecs_delay);
|
||||
total_delay += nsecs_delay;
|
||||
} while (1);
|
||||
|
||||
if (total_delay >= MAX_NSECS_DELAY) {
|
||||
drm_warn(crtc->dev, "VKMS relaxed lock expired!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ret == VMW_VKMS_LOCK_UNLOCKED;
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_vkms_vblank_trylock - Protects access to crtc during vblank
|
||||
* @crtc: The crtc to lock for vkms
|
||||
*
|
||||
* Tries to lock vkms for vblank, returns immediately.
|
||||
*
|
||||
* Returns true if locked vkms to vblank or false otherwise.
|
||||
*/
|
||||
bool
|
||||
vmw_vkms_vblank_trylock(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
u32 ret;
|
||||
|
||||
ret = atomic_cmpxchg(&du->vkms.atomic_lock,
|
||||
VMW_VKMS_LOCK_UNLOCKED,
|
||||
VMW_VKMS_LOCK_VBLANK);
|
||||
|
||||
return ret == VMW_VKMS_LOCK_UNLOCKED;
|
||||
}
|
||||
|
||||
void
|
||||
vmw_vkms_unlock(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
|
||||
/* Release flag; mark it as unlocked. */
|
||||
atomic_set(&du->vkms.atomic_lock, VMW_VKMS_LOCK_UNLOCKED);
|
||||
}
|
75
drivers/gpu/drm/vmwgfx/vmwgfx_vkms.h
Normal file
75
drivers/gpu/drm/vmwgfx/vmwgfx_vkms.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 Broadcom. All Rights Reserved. The term
|
||||
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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 VMWGFX_VKMS_H_
|
||||
#define VMWGFX_VKMS_H_
|
||||
|
||||
#include <linux/hrtimer_types.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct drm_atomic_state;
|
||||
struct drm_crtc;
|
||||
struct vmw_private;
|
||||
struct vmw_surface;
|
||||
|
||||
void vmw_vkms_init(struct vmw_private *vmw);
|
||||
void vmw_vkms_cleanup(struct vmw_private *vmw);
|
||||
|
||||
void vmw_vkms_modeset_lock(struct drm_crtc *crtc);
|
||||
bool vmw_vkms_modeset_lock_relaxed(struct drm_crtc *crtc);
|
||||
bool vmw_vkms_vblank_trylock(struct drm_crtc *crtc);
|
||||
void vmw_vkms_unlock(struct drm_crtc *crtc);
|
||||
|
||||
bool vmw_vkms_get_vblank_timestamp(struct drm_crtc *crtc,
|
||||
int *max_error,
|
||||
ktime_t *vblank_time,
|
||||
bool in_vblank_irq);
|
||||
int vmw_vkms_enable_vblank(struct drm_crtc *crtc);
|
||||
void vmw_vkms_disable_vblank(struct drm_crtc *crtc);
|
||||
|
||||
void vmw_vkms_crtc_init(struct drm_crtc *crtc);
|
||||
void vmw_vkms_crtc_cleanup(struct drm_crtc *crtc);
|
||||
void vmw_vkms_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state);
|
||||
void vmw_vkms_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state);
|
||||
void vmw_vkms_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state);
|
||||
void vmw_vkms_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state);
|
||||
|
||||
const char *const *vmw_vkms_get_crc_sources(struct drm_crtc *crtc,
|
||||
size_t *count);
|
||||
int vmw_vkms_verify_crc_source(struct drm_crtc *crtc,
|
||||
const char *src_name,
|
||||
size_t *values_cnt);
|
||||
int vmw_vkms_set_crc_source(struct drm_crtc *crtc,
|
||||
const char *src_name);
|
||||
void vmw_vkms_set_crc_surface(struct drm_crtc *crtc,
|
||||
struct vmw_surface *surf);
|
||||
|
||||
#endif
|
@ -30,6 +30,7 @@ struct drm_connector;
|
||||
struct drm_device;
|
||||
struct drm_display_mode;
|
||||
struct drm_edid;
|
||||
struct drm_printer;
|
||||
struct hdmi_avi_infoframe;
|
||||
struct hdmi_vendor_infoframe;
|
||||
struct i2c_adapter;
|
||||
@ -272,14 +273,27 @@ struct detailed_timing {
|
||||
#define DRM_EDID_DSC_MAX_SLICES 0xf
|
||||
#define DRM_EDID_DSC_TOTAL_CHUNK_KBYTES 0x3f
|
||||
|
||||
struct drm_edid_product_id {
|
||||
__be16 manufacturer_name;
|
||||
__le16 product_code;
|
||||
__le32 serial_number;
|
||||
u8 week_of_manufacture;
|
||||
u8 year_of_manufacture;
|
||||
} __packed;
|
||||
|
||||
struct edid {
|
||||
u8 header[8];
|
||||
/* Vendor & product info */
|
||||
u8 mfg_id[2];
|
||||
u8 prod_code[2];
|
||||
u32 serial; /* FIXME: byte order */
|
||||
u8 mfg_week;
|
||||
u8 mfg_year;
|
||||
union {
|
||||
struct drm_edid_product_id product_id;
|
||||
struct {
|
||||
u8 mfg_id[2];
|
||||
u8 prod_code[2];
|
||||
u32 serial; /* FIXME: byte order */
|
||||
u8 mfg_week;
|
||||
u8 mfg_year;
|
||||
} __packed;
|
||||
} __packed;
|
||||
/* EDID version */
|
||||
u8 version;
|
||||
u8 revision;
|
||||
@ -334,8 +348,6 @@ int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb);
|
||||
int drm_av_sync_delay(struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode);
|
||||
|
||||
bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2);
|
||||
|
||||
int
|
||||
drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
|
||||
const struct drm_connector *connector,
|
||||
@ -417,10 +429,6 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
|
||||
void *data);
|
||||
struct edid *drm_get_edid(struct drm_connector *connector,
|
||||
struct i2c_adapter *adapter);
|
||||
const struct drm_edid *drm_edid_read_base_block(struct i2c_adapter *adapter);
|
||||
u32 drm_edid_get_panel_id(const struct drm_edid *drm_edid);
|
||||
bool drm_edid_match(const struct drm_edid *drm_edid,
|
||||
const struct drm_edid_ident *ident);
|
||||
struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
|
||||
struct i2c_adapter *adapter);
|
||||
struct edid *drm_edid_duplicate(const struct edid *edid);
|
||||
@ -460,14 +468,19 @@ const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector,
|
||||
const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector,
|
||||
int (*read_block)(void *context, u8 *buf, unsigned int block, size_t len),
|
||||
void *context);
|
||||
const struct drm_edid *drm_edid_read_base_block(struct i2c_adapter *adapter);
|
||||
const struct drm_edid *drm_edid_read_switcheroo(struct drm_connector *connector,
|
||||
struct i2c_adapter *adapter);
|
||||
int drm_edid_connector_update(struct drm_connector *connector,
|
||||
const struct drm_edid *edid);
|
||||
int drm_edid_connector_add_modes(struct drm_connector *connector);
|
||||
bool drm_edid_is_digital(const struct drm_edid *drm_edid);
|
||||
|
||||
const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
|
||||
int ext_id, int *ext_index);
|
||||
void drm_edid_get_product_id(const struct drm_edid *drm_edid,
|
||||
struct drm_edid_product_id *id);
|
||||
void drm_edid_print_product_id(struct drm_printer *p,
|
||||
const struct drm_edid_product_id *id, bool raw);
|
||||
u32 drm_edid_get_panel_id(const struct drm_edid *drm_edid);
|
||||
bool drm_edid_match(const struct drm_edid *drm_edid,
|
||||
const struct drm_edid_ident *ident);
|
||||
|
||||
#endif /* __DRM_EDID_H__ */
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
struct drm_device;
|
||||
struct drm_framebuffer;
|
||||
struct drm_plane;
|
||||
struct drm_plane_state;
|
||||
struct drm_scanout_buffer;
|
||||
|
||||
struct drm_gem_dma_object *drm_fb_dma_get_gem_obj(struct drm_framebuffer *fb,
|
||||
unsigned int plane);
|
||||
@ -19,5 +21,8 @@ void drm_fb_dma_sync_non_coherent(struct drm_device *drm,
|
||||
struct drm_plane_state *old_state,
|
||||
struct drm_plane_state *state);
|
||||
|
||||
int drm_fb_dma_get_scanout_buffer(struct drm_plane *plane,
|
||||
struct drm_scanout_buffer *sb);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -226,6 +226,12 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
enum mipi_dsi_compression_algo {
|
||||
MIPI_DSI_COMPRESSION_DSC = 0,
|
||||
MIPI_DSI_COMPRESSION_VENDOR = 3,
|
||||
/* other two values are reserved, DSI 1.3 */
|
||||
};
|
||||
|
||||
struct mipi_dsi_device *
|
||||
mipi_dsi_device_register_full(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_device_info *info);
|
||||
@ -241,9 +247,12 @@ int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi);
|
||||
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
|
||||
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
|
||||
u16 value);
|
||||
ssize_t mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
|
||||
ssize_t mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
|
||||
const struct drm_dsc_picture_parameter_set *pps);
|
||||
int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
|
||||
int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
|
||||
enum mipi_dsi_compression_algo algo,
|
||||
unsigned int pps_selector);
|
||||
int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
|
||||
const struct drm_dsc_picture_parameter_set *pps);
|
||||
|
||||
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
|
||||
size_t size);
|
||||
|
@ -505,6 +505,16 @@ struct drm_mode_config {
|
||||
*/
|
||||
struct list_head plane_list;
|
||||
|
||||
/**
|
||||
* @panic_lock:
|
||||
*
|
||||
* Raw spinlock used to protect critical sections of code that access
|
||||
* the display hardware or modeset software state, which the panic
|
||||
* printing code must be protected against. See drm_panic_trylock(),
|
||||
* drm_panic_lock() and drm_panic_unlock().
|
||||
*/
|
||||
struct raw_spinlock panic_lock;
|
||||
|
||||
/**
|
||||
* @num_crtc:
|
||||
*
|
||||
@ -942,6 +952,11 @@ struct drm_mode_config {
|
||||
*/
|
||||
struct drm_property *modifiers_property;
|
||||
|
||||
/**
|
||||
* @size_hints_propertty: Plane SIZE_HINTS property.
|
||||
*/
|
||||
struct drm_property *size_hints_property;
|
||||
|
||||
/* cursor size */
|
||||
uint32_t cursor_width, cursor_height;
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
* To make this clear all the helper vtables are pulled together in this location here.
|
||||
*/
|
||||
|
||||
struct drm_scanout_buffer;
|
||||
struct drm_writeback_connector;
|
||||
struct drm_writeback_job;
|
||||
|
||||
@ -1443,6 +1444,44 @@ struct drm_plane_helper_funcs {
|
||||
*/
|
||||
void (*atomic_async_update)(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state);
|
||||
|
||||
/**
|
||||
* @get_scanout_buffer:
|
||||
*
|
||||
* Get the current scanout buffer, to display a message with drm_panic.
|
||||
* The driver should do the minimum changes to provide a buffer,
|
||||
* that can be used to display the panic screen. Currently only linear
|
||||
* buffers are supported. Non-linear buffer support is on the TODO list.
|
||||
* The device &dev.mode_config.panic_lock is taken before calling this
|
||||
* function, so you can safely access the &plane.state
|
||||
* It is called from a panic callback, and must follow its restrictions.
|
||||
* Please look the documentation at drm_panic_trylock() for an in-depth
|
||||
* discussions of what's safe and what is not allowed.
|
||||
* It's a best effort mode, so it's expected that in some complex cases
|
||||
* the panic screen won't be displayed.
|
||||
* The returned &drm_scanout_buffer.map must be valid if no error code is
|
||||
* returned.
|
||||
*
|
||||
* Return:
|
||||
* %0 on success, negative errno on failure.
|
||||
*/
|
||||
int (*get_scanout_buffer)(struct drm_plane *plane,
|
||||
struct drm_scanout_buffer *sb);
|
||||
|
||||
/**
|
||||
* @panic_flush:
|
||||
*
|
||||
* It is used by drm_panic, and is called after the panic screen is
|
||||
* drawn to the scanout buffer. In this function, the driver
|
||||
* can send additional commands to the hardware, to make the scanout
|
||||
* buffer visible.
|
||||
* It is only called if get_scanout_buffer() returned successfully, and
|
||||
* the &dev.mode_config.panic_lock is held during the entire sequence.
|
||||
* It is called from a panic callback, and must follow its restrictions.
|
||||
* Please look the documentation at drm_panic_trylock() for an in-depth
|
||||
* discussions of what's safe and what is not allowed.
|
||||
*/
|
||||
void (*panic_flush)(struct drm_plane *plane);
|
||||
};
|
||||
|
||||
/**
|
||||
|
152
include/drm/drm_panic.h
Normal file
152
include/drm/drm_panic.h
Normal file
@ -0,0 +1,152 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 or MIT */
|
||||
#ifndef __DRM_PANIC_H__
|
||||
#define __DRM_PANIC_H__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/iosys-map.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
/*
|
||||
* Copyright (c) 2024 Intel
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct drm_scanout_buffer - DRM scanout buffer
|
||||
*
|
||||
* This structure holds the information necessary for drm_panic to draw the
|
||||
* panic screen, and display it.
|
||||
*/
|
||||
struct drm_scanout_buffer {
|
||||
/**
|
||||
* @format:
|
||||
*
|
||||
* drm format of the scanout buffer.
|
||||
*/
|
||||
const struct drm_format_info *format;
|
||||
|
||||
/**
|
||||
* @map:
|
||||
*
|
||||
* Virtual address of the scanout buffer, either in memory or iomem.
|
||||
* The scanout buffer should be in linear format, and can be directly
|
||||
* sent to the display hardware. Tearing is not an issue for the panic
|
||||
* screen.
|
||||
*/
|
||||
struct iosys_map map[DRM_FORMAT_MAX_PLANES];
|
||||
|
||||
/**
|
||||
* @width: Width of the scanout buffer, in pixels.
|
||||
*/
|
||||
unsigned int width;
|
||||
|
||||
/**
|
||||
* @height: Height of the scanout buffer, in pixels.
|
||||
*/
|
||||
unsigned int height;
|
||||
|
||||
/**
|
||||
* @pitch: Length in bytes between the start of two consecutive lines.
|
||||
*/
|
||||
unsigned int pitch[DRM_FORMAT_MAX_PLANES];
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_panic_trylock - try to enter the panic printing critical section
|
||||
* @dev: struct drm_device
|
||||
* @flags: unsigned long irq flags you need to pass to the unlock() counterpart
|
||||
*
|
||||
* This function must be called by any panic printing code. The panic printing
|
||||
* attempt must be aborted if the trylock fails.
|
||||
*
|
||||
* Panic printing code can make the following assumptions while holding the
|
||||
* panic lock:
|
||||
*
|
||||
* - Anything protected by drm_panic_lock() and drm_panic_unlock() pairs is safe
|
||||
* to access.
|
||||
*
|
||||
* - Furthermore the panic printing code only registers in drm_dev_unregister()
|
||||
* and gets removed in drm_dev_unregister(). This allows the panic code to
|
||||
* safely access any state which is invariant in between these two function
|
||||
* calls, like the list of planes &drm_mode_config.plane_list or most of the
|
||||
* struct drm_plane structure.
|
||||
*
|
||||
* Specifically thanks to the protection around plane updates in
|
||||
* drm_atomic_helper_swap_state() the following additional guarantees hold:
|
||||
*
|
||||
* - It is safe to deference the drm_plane.state pointer.
|
||||
*
|
||||
* - Anything in struct drm_plane_state or the driver's subclass thereof which
|
||||
* stays invariant after the atomic check code has finished is safe to access.
|
||||
* Specifically this includes the reference counted pointers to framebuffer
|
||||
* and buffer objects.
|
||||
*
|
||||
* - Anything set up by &drm_plane_helper_funcs.fb_prepare and cleaned up
|
||||
* &drm_plane_helper_funcs.fb_cleanup is safe to access, as long as it stays
|
||||
* invariant between these two calls. This also means that for drivers using
|
||||
* dynamic buffer management the framebuffer is pinned, and therefer all
|
||||
* relevant datastructures can be accessed without taking any further locks
|
||||
* (which would be impossible in panic context anyway).
|
||||
*
|
||||
* - Importantly, software and hardware state set up by
|
||||
* &drm_plane_helper_funcs.begin_fb_access and
|
||||
* &drm_plane_helper_funcs.end_fb_access is not safe to access.
|
||||
*
|
||||
* Drivers must not make any assumptions about the actual state of the hardware,
|
||||
* unless they explicitly protected these hardware access with drm_panic_lock()
|
||||
* and drm_panic_unlock().
|
||||
*
|
||||
* Return:
|
||||
* %0 when failing to acquire the raw spinlock, nonzero on success.
|
||||
*/
|
||||
#define drm_panic_trylock(dev, flags) \
|
||||
raw_spin_trylock_irqsave(&(dev)->mode_config.panic_lock, flags)
|
||||
|
||||
/**
|
||||
* drm_panic_lock - protect panic printing relevant state
|
||||
* @dev: struct drm_device
|
||||
* @flags: unsigned long irq flags you need to pass to the unlock() counterpart
|
||||
*
|
||||
* This function must be called to protect software and hardware state that the
|
||||
* panic printing code must be able to rely on. The protected sections must be
|
||||
* as small as possible. It uses the irqsave/irqrestore variant, and can be
|
||||
* called from irq handler. Examples include:
|
||||
*
|
||||
* - Access to peek/poke or other similar registers, if that is the way the
|
||||
* driver prints the pixels into the scanout buffer at panic time.
|
||||
*
|
||||
* - Updates to pointers like &drm_plane.state, allowing the panic handler to
|
||||
* safely deference these. This is done in drm_atomic_helper_swap_state().
|
||||
*
|
||||
* - An state that isn't invariant and that the driver must be able to access
|
||||
* during panic printing.
|
||||
*/
|
||||
|
||||
#define drm_panic_lock(dev, flags) \
|
||||
raw_spin_lock_irqsave(&(dev)->mode_config.panic_lock, flags)
|
||||
|
||||
/**
|
||||
* drm_panic_unlock - end of the panic printing critical section
|
||||
* @dev: struct drm_device
|
||||
* @flags: irq flags that were returned when acquiring the lock
|
||||
*
|
||||
* Unlocks the raw spinlock acquired by either drm_panic_lock() or
|
||||
* drm_panic_trylock().
|
||||
*/
|
||||
#define drm_panic_unlock(dev, flags) \
|
||||
raw_spin_unlock_irqrestore(&(dev)->mode_config.panic_lock, flags)
|
||||
|
||||
#ifdef CONFIG_DRM_PANIC
|
||||
|
||||
void drm_panic_register(struct drm_device *dev);
|
||||
void drm_panic_unregister(struct drm_device *dev);
|
||||
|
||||
#else
|
||||
|
||||
static inline void drm_panic_register(struct drm_device *dev) {}
|
||||
static inline void drm_panic_unregister(struct drm_device *dev) {}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __DRM_PANIC_H__ */
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kmsg_dump.h>
|
||||
#include <drm/drm_mode_object.h>
|
||||
#include <drm/drm_color_mgmt.h>
|
||||
#include <drm/drm_rect.h>
|
||||
@ -32,6 +33,7 @@
|
||||
#include <drm/drm_util.h>
|
||||
|
||||
struct drm_crtc;
|
||||
struct drm_plane_size_hint;
|
||||
struct drm_printer;
|
||||
struct drm_modeset_acquire_ctx;
|
||||
|
||||
@ -779,6 +781,11 @@ struct drm_plane {
|
||||
* @hotspot_y_property: property to set mouse hotspot y offset.
|
||||
*/
|
||||
struct drm_property *hotspot_y_property;
|
||||
|
||||
/**
|
||||
* @kmsg_panic: Used to register a panic notifier for this plane
|
||||
*/
|
||||
struct kmsg_dumper kmsg_panic;
|
||||
};
|
||||
|
||||
#define obj_to_plane(x) container_of(x, struct drm_plane, base)
|
||||
@ -976,5 +983,8 @@ drm_plane_get_damage_clips(const struct drm_plane_state *state);
|
||||
|
||||
int drm_plane_create_scaling_filter_property(struct drm_plane *plane,
|
||||
unsigned int supported_filters);
|
||||
int drm_plane_add_size_hints_property(struct drm_plane *plane,
|
||||
const struct drm_plane_size_hint *hints,
|
||||
int num_hints);
|
||||
|
||||
#endif
|
||||
|
@ -225,6 +225,7 @@ struct drm_vblank_crtc {
|
||||
wait_queue_head_t work_wait_queue;
|
||||
};
|
||||
|
||||
struct drm_vblank_crtc *drm_crtc_vblank_crtc(struct drm_crtc *crtc);
|
||||
int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
|
||||
bool drm_dev_has_vblank(const struct drm_device *dev);
|
||||
u64 drm_crtc_vblank_count(struct drm_crtc *crtc);
|
||||
|
@ -370,8 +370,10 @@ struct dma_buf {
|
||||
*/
|
||||
struct module *owner;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
/** @list_node: node for dma_buf accounting and debugging. */
|
||||
struct list_head list_node;
|
||||
#endif
|
||||
|
||||
/** @priv: exporter specific private data for this buffer object. */
|
||||
void *priv;
|
||||
|
@ -865,6 +865,17 @@ struct drm_color_lut {
|
||||
__u16 reserved;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_plane_size_hint - Plane size hints
|
||||
*
|
||||
* The plane SIZE_HINTS property blob contains an
|
||||
* array of struct drm_plane_size_hint.
|
||||
*/
|
||||
struct drm_plane_size_hint {
|
||||
__u16 width;
|
||||
__u16 height;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hdr_metadata_infoframe - HDR Metadata Infoframe Data.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user