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:
Dave Airlie 2024-04-22 12:29:17 +10:00
commit 2871ec4099
96 changed files with 3481 additions and 519 deletions

View File

@ -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>;
};
};
};
};
...

View File

@ -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

View File

@ -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.

View File

@ -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
----------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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__ */

View File

@ -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
View 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);
}

View 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__ */

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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"

View File

@ -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,

View File

@ -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)) {

View File

@ -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,

View File

@ -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)) {

View File

@ -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)) {

View File

@ -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)) {

View File

@ -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)) {

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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++;

View File

@ -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)) {

View File

@ -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

View File

@ -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;

View File

@ -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>

View File

@ -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))

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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__ */

View File

@ -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,

View File

@ -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;
}

View File

@ -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
View 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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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)
{

View 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;
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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));

View File

@ -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;
};

View File

@ -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, \

View File

@ -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
*/

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View 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");

View File

@ -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]) {

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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 = {

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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:

View File

@ -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)

View File

@ -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:

View File

@ -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:

View 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);
}

View 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

View File

@ -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__ */

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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
View 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__ */

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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.
*