Merge tag 'drm-misc-next-2026-01-22' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for 6.20:

Core Changes:
 - buddy: Fix free_trees memory leak, prevent a BUG_ON
 - dma-buf: Start to introduce cgroup memory accounting in heaps, Remove
   sysfs stats, add new tracepoints
 - hdmi: Limit infoframes exposure to userspace based on driver
   capabilities
 - property: Account for property blobs in memcg

Driver Changes:
 - atmel-hlcdc: Switch to drmm resources, Support nomodeset parameter,
   various patches to use newish helpers and fix memory safety bugs
 - hisilicon: Fix various DisplayPort related bugs
 - imagination: Introduce hardware version checks
 - renesas: Fix kernel panic on reboot
 - rockchip: Fix RK3576 HPD interrupt handling, Improve RK3588 HPD
   interrupt handling
 - v3d: Convert to drm logging helpers

 - bridge:
   - Continuation of the refcounting effort
   - new bridge: Algoltek AG6311

 - panel:
   - new panel: Anbernic RG-DS

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maxime Ripard <mripard@redhat.com>
Link: https://patch.msgid.link/20260122-antique-sexy-junglefowl-1bc5a8@houat
This commit is contained in:
Dave Airlie
2026-01-23 12:44:59 +10:00
94 changed files with 3108 additions and 1489 deletions

View File

@@ -373,7 +373,9 @@ Jesper Dangaard Brouer <hawk@kernel.org> <hawk@comx.dk>
Jesper Dangaard Brouer <hawk@kernel.org> <jbrouer@redhat.com>
Jesper Dangaard Brouer <hawk@kernel.org> <jdb@comx.dk>
Jesper Dangaard Brouer <hawk@kernel.org> <netoptimizer@brouer.com>
Jessica Zhang <quic_jesszhan@quicinc.com> <jesszhan@codeaurora.org>
Jessica Zhang <jesszhan0024@gmail.com> <jesszhan@codeaurora.org>
Jessica Zhang <jesszhan0024@gmail.com> <quic_jesszhan@quicinc.com>
Jessica Zhang <jesszhan0024@gmail.com> <jessica.zhang@oss.qualcomm.com>
Jilai Wang <quic_jilaiw@quicinc.com> <jilaiw@codeaurora.org>
Jiri Kosina <jikos@kernel.org> <jikos@jikos.cz>
Jiri Kosina <jikos@kernel.org> <jkosina@suse.cz>

View File

@@ -1,24 +0,0 @@
What: /sys/kernel/dmabuf/buffers
Date: May 2021
KernelVersion: v5.13
Contact: Hridya Valsaraju <hridya@google.com>
Description: The /sys/kernel/dmabuf/buffers directory contains a
snapshot of the internal state of every DMA-BUF.
/sys/kernel/dmabuf/buffers/<inode_number> will contain the
statistics for the DMA-BUF with the unique inode number
<inode_number>
Users: kernel memory tuning/debugging tools
What: /sys/kernel/dmabuf/buffers/<inode_number>/exporter_name
Date: May 2021
KernelVersion: v5.13
Contact: Hridya Valsaraju <hridya@google.com>
Description: This file is read-only and contains the name of the exporter of
the DMA-BUF.
What: /sys/kernel/dmabuf/buffers/<inode_number>/size
Date: May 2021
KernelVersion: v5.13
Contact: Hridya Valsaraju <hridya@google.com>
Description: This file is read-only and specifies the size of the DMA-BUF in
bytes.

View File

@@ -27,6 +27,7 @@ properties:
- const: adi,adv7123
- enum:
- adi,adv7123
- algoltek,ag6311
- asl-tek,cs5263
- dumb-vga-dac
- parade,ps185hdm

View File

@@ -16,6 +16,8 @@ properties:
compatible:
items:
- enum:
- anbernic,rg-ds-display-bottom
- anbernic,rg-ds-display-top
- chongzhou,cz101b4001
- kingdisplay,kd101ne3-40ti
- melfas,lmfbx101117480

View File

@@ -86,6 +86,8 @@ patternProperties:
description: Aldec, Inc.
"^alfa-network,.*":
description: ALFA Network Inc.
"^algoltek,.*":
description: AlgolTek, Inc.
"^allegro,.*":
description: Allegro DVT
"^allegromicro,.*":

View File

@@ -125,11 +125,6 @@ Implicit Fence Poll Support
.. kernel-doc:: drivers/dma-buf/dma-buf.c
:doc: implicit fence polling
DMA-BUF statistics
~~~~~~~~~~~~~~~~~~
.. kernel-doc:: drivers/dma-buf/dma-buf-sysfs-stats.c
:doc: overview
DMA Buffer ioctls
~~~~~~~~~~~~~~~~~

View File

@@ -75,21 +75,6 @@ menuconfig DMABUF_HEAPS
allows userspace to allocate dma-bufs that can be shared
between drivers.
menuconfig DMABUF_SYSFS_STATS
bool "DMA-BUF sysfs statistics (DEPRECATED)"
depends on DMA_SHARED_BUFFER
help
Choose this option to enable DMA-BUF sysfs statistics
in location /sys/kernel/dmabuf/buffers.
/sys/kernel/dmabuf/buffers/<inode_number> will contain
statistics for the DMA-BUF with the unique inode number
<inode_number>.
This option is deprecated and should sooner or later be removed.
Android is the only user of this and it turned out that this resulted
in quite some performance problems.
source "drivers/dma-buf/heaps/Kconfig"
endmenu

View File

@@ -6,7 +6,6 @@ obj-$(CONFIG_DMABUF_HEAPS) += heaps/
obj-$(CONFIG_SYNC_FILE) += sync_file.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
obj-$(CONFIG_UDMABUF) += udmabuf.o
obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o
dmabuf_selftests-y := \
selftest.o \

View File

@@ -1,202 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* DMA-BUF sysfs statistics.
*
* Copyright (C) 2021 Google LLC.
*/
#include <linux/dma-buf.h>
#include <linux/dma-resv.h>
#include <linux/kobject.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "dma-buf-sysfs-stats.h"
#define to_dma_buf_entry_from_kobj(x) container_of(x, struct dma_buf_sysfs_entry, kobj)
/**
* DOC: overview
*
* ``/sys/kernel/debug/dma_buf/bufinfo`` provides an overview of every DMA-BUF
* in the system. However, since debugfs is not safe to be mounted in
* production, procfs and sysfs can be used to gather DMA-BUF statistics on
* production systems.
*
* The ``/proc/<pid>/fdinfo/<fd>`` files in procfs can be used to gather
* information about DMA-BUF fds. Detailed documentation about the interface
* is present in Documentation/filesystems/proc.rst.
*
* Unfortunately, the existing procfs interfaces can only provide information
* about the DMA-BUFs for which processes hold fds or have the buffers mmapped
* into their address space. This necessitated the creation of the DMA-BUF sysfs
* statistics interface to provide per-buffer information on production systems.
*
* The interface at ``/sys/kernel/dmabuf/buffers`` exposes information about
* every DMA-BUF when ``CONFIG_DMABUF_SYSFS_STATS`` is enabled.
*
* The following stats are exposed by the interface:
*
* * ``/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name``
* * ``/sys/kernel/dmabuf/buffers/<inode_number>/size``
*
* The information in the interface can also be used to derive per-exporter
* statistics. The data from the interface can be gathered on error conditions
* or other important events to provide a snapshot of DMA-BUF usage.
* It can also be collected periodically by telemetry to monitor various metrics.
*
* Detailed documentation about the interface is present in
* Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers.
*/
struct dma_buf_stats_attribute {
struct attribute attr;
ssize_t (*show)(struct dma_buf *dmabuf,
struct dma_buf_stats_attribute *attr, char *buf);
};
#define to_dma_buf_stats_attr(x) container_of(x, struct dma_buf_stats_attribute, attr)
static ssize_t dma_buf_stats_attribute_show(struct kobject *kobj,
struct attribute *attr,
char *buf)
{
struct dma_buf_stats_attribute *attribute;
struct dma_buf_sysfs_entry *sysfs_entry;
struct dma_buf *dmabuf;
attribute = to_dma_buf_stats_attr(attr);
sysfs_entry = to_dma_buf_entry_from_kobj(kobj);
dmabuf = sysfs_entry->dmabuf;
if (!dmabuf || !attribute->show)
return -EIO;
return attribute->show(dmabuf, attribute, buf);
}
static const struct sysfs_ops dma_buf_stats_sysfs_ops = {
.show = dma_buf_stats_attribute_show,
};
static ssize_t exporter_name_show(struct dma_buf *dmabuf,
struct dma_buf_stats_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%s\n", dmabuf->exp_name);
}
static ssize_t size_show(struct dma_buf *dmabuf,
struct dma_buf_stats_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%zu\n", dmabuf->size);
}
static struct dma_buf_stats_attribute exporter_name_attribute =
__ATTR_RO(exporter_name);
static struct dma_buf_stats_attribute size_attribute = __ATTR_RO(size);
static struct attribute *dma_buf_stats_default_attrs[] = {
&exporter_name_attribute.attr,
&size_attribute.attr,
NULL,
};
ATTRIBUTE_GROUPS(dma_buf_stats_default);
static void dma_buf_sysfs_release(struct kobject *kobj)
{
struct dma_buf_sysfs_entry *sysfs_entry;
sysfs_entry = to_dma_buf_entry_from_kobj(kobj);
kfree(sysfs_entry);
}
static const struct kobj_type dma_buf_ktype = {
.sysfs_ops = &dma_buf_stats_sysfs_ops,
.release = dma_buf_sysfs_release,
.default_groups = dma_buf_stats_default_groups,
};
void dma_buf_stats_teardown(struct dma_buf *dmabuf)
{
struct dma_buf_sysfs_entry *sysfs_entry;
sysfs_entry = dmabuf->sysfs_entry;
if (!sysfs_entry)
return;
kobject_del(&sysfs_entry->kobj);
kobject_put(&sysfs_entry->kobj);
}
/* Statistics files do not need to send uevents. */
static int dmabuf_sysfs_uevent_filter(const struct kobject *kobj)
{
return 0;
}
static const struct kset_uevent_ops dmabuf_sysfs_no_uevent_ops = {
.filter = dmabuf_sysfs_uevent_filter,
};
static struct kset *dma_buf_stats_kset;
static struct kset *dma_buf_per_buffer_stats_kset;
int dma_buf_init_sysfs_statistics(void)
{
dma_buf_stats_kset = kset_create_and_add("dmabuf",
&dmabuf_sysfs_no_uevent_ops,
kernel_kobj);
if (!dma_buf_stats_kset)
return -ENOMEM;
dma_buf_per_buffer_stats_kset = kset_create_and_add("buffers",
&dmabuf_sysfs_no_uevent_ops,
&dma_buf_stats_kset->kobj);
if (!dma_buf_per_buffer_stats_kset) {
kset_unregister(dma_buf_stats_kset);
return -ENOMEM;
}
return 0;
}
void dma_buf_uninit_sysfs_statistics(void)
{
kset_unregister(dma_buf_per_buffer_stats_kset);
kset_unregister(dma_buf_stats_kset);
}
int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file)
{
struct dma_buf_sysfs_entry *sysfs_entry;
int ret;
if (!dmabuf->exp_name) {
pr_err("exporter name must not be empty if stats needed\n");
return -EINVAL;
}
sysfs_entry = kzalloc(sizeof(struct dma_buf_sysfs_entry), GFP_KERNEL);
if (!sysfs_entry)
return -ENOMEM;
sysfs_entry->kobj.kset = dma_buf_per_buffer_stats_kset;
sysfs_entry->dmabuf = dmabuf;
dmabuf->sysfs_entry = sysfs_entry;
/* create the directory for buffer stats */
ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_ktype, NULL,
"%lu", file_inode(file)->i_ino);
if (ret)
goto err_sysfs_dmabuf;
return 0;
err_sysfs_dmabuf:
kobject_put(&sysfs_entry->kobj);
dmabuf->sysfs_entry = NULL;
return ret;
}

View File

@@ -1,35 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* DMA-BUF sysfs statistics.
*
* Copyright (C) 2021 Google LLC.
*/
#ifndef _DMA_BUF_SYSFS_STATS_H
#define _DMA_BUF_SYSFS_STATS_H
#ifdef CONFIG_DMABUF_SYSFS_STATS
int dma_buf_init_sysfs_statistics(void);
void dma_buf_uninit_sysfs_statistics(void);
int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file);
void dma_buf_stats_teardown(struct dma_buf *dmabuf);
#else
static inline int dma_buf_init_sysfs_statistics(void)
{
return 0;
}
static inline void dma_buf_uninit_sysfs_statistics(void) {}
static inline int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file)
{
return 0;
}
static inline void dma_buf_stats_teardown(struct dma_buf *dmabuf) {}
#endif
#endif // _DMA_BUF_SYSFS_STATS_H

View File

@@ -33,8 +33,6 @@
#include <uapi/linux/dma-buf.h>
#include <uapi/linux/magic.h>
#include "dma-buf-sysfs-stats.h"
#define CREATE_TRACE_POINTS
#include <trace/events/dma_buf.h>
@@ -48,12 +46,10 @@
*/
#define DMA_BUF_TRACE(FUNC, ...) \
do { \
if (FUNC##_enabled()) { \
/* Always expose lock if lockdep is enabled */ \
if (IS_ENABLED(CONFIG_LOCKDEP) || FUNC##_enabled()) { \
guard(spinlock)(&dmabuf->name_lock); \
FUNC(__VA_ARGS__); \
} else if (IS_ENABLED(CONFIG_LOCKDEP)) { \
/* Expose this lock when lockdep is enabled */ \
guard(spinlock)(&dmabuf->name_lock); \
} \
} while (0)
@@ -184,7 +180,6 @@ static void dma_buf_release(struct dentry *dentry)
*/
BUG_ON(dmabuf->cb_in.active || dmabuf->cb_out.active);
dma_buf_stats_teardown(dmabuf);
dmabuf->ops->release(dmabuf);
if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
@@ -765,10 +760,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
dmabuf->resv = resv;
}
ret = dma_buf_stats_setup(dmabuf, file);
if (ret)
goto err_dmabuf;
file->private_data = dmabuf;
file->f_path.dentry->d_fsdata = dmabuf;
dmabuf->file = file;
@@ -779,10 +770,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
return dmabuf;
err_dmabuf:
if (!resv)
dma_resv_fini(dmabuf->resv);
kfree(dmabuf);
err_file:
fput(file);
err_module:
@@ -806,8 +793,7 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags)
return -EINVAL;
fd = FD_ADD(flags, dmabuf->file);
if (fd >= 0)
DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd);
DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd);
return fd;
}
@@ -1802,12 +1788,6 @@ static inline void dma_buf_uninit_debugfs(void)
static int __init dma_buf_init(void)
{
int ret;
ret = dma_buf_init_sysfs_statistics();
if (ret)
return ret;
dma_buf_mnt = kern_mount(&dma_buf_fs_type);
if (IS_ERR(dma_buf_mnt))
return PTR_ERR(dma_buf_mnt);
@@ -1821,6 +1801,5 @@ static void __exit dma_buf_deinit(void)
{
dma_buf_uninit_debugfs();
kern_unmount(dma_buf_mnt);
dma_buf_uninit_sysfs_statistics();
}
__exitcall(dma_buf_deinit);

View File

@@ -49,6 +49,11 @@ static dev_t dma_heap_devt;
static struct class *dma_heap_class;
static DEFINE_XARRAY_ALLOC(dma_heap_minors);
bool __read_mostly mem_accounting;
module_param(mem_accounting, bool, 0444);
MODULE_PARM_DESC(mem_accounting,
"Enable cgroup-based memory accounting for dma-buf heap allocations (default=false).");
static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
u32 fd_flags,
u64 heap_flags)

View File

@@ -320,14 +320,17 @@ static struct page *alloc_largest_available(unsigned long size,
{
struct page *page;
int i;
gfp_t flags;
for (i = 0; i < NUM_ORDERS; i++) {
if (size < (PAGE_SIZE << orders[i]))
continue;
if (max_order < orders[i])
continue;
page = alloc_pages(order_flags[i], orders[i]);
flags = order_flags[i];
if (mem_accounting)
flags |= __GFP_ACCOUNT;
page = alloc_pages(flags, orders[i]);
if (!page)
continue;
return page;

View File

@@ -509,14 +509,6 @@ static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
.atomic_disable = atmel_hlcdc_crtc_atomic_disable,
};
static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
{
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
drm_crtc_cleanup(c);
kfree(crtc);
}
static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
@@ -607,7 +599,6 @@ static void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c)
static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.set_config = drm_atomic_helper_set_config,
.destroy = atmel_hlcdc_crtc_destroy,
.reset = atmel_hlcdc_crtc_reset,
.atomic_duplicate_state = atmel_hlcdc_crtc_duplicate_state,
.atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
@@ -620,15 +611,8 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL;
struct atmel_hlcdc_dc *dc = dev->dev_private;
struct atmel_hlcdc_crtc *crtc;
int ret;
int i;
crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
if (!crtc)
return -ENOMEM;
crtc->dc = dc;
for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
if (!dc->layers[i])
continue;
@@ -646,13 +630,13 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
break;
}
}
crtc = drmm_crtc_alloc_with_planes(dev, struct atmel_hlcdc_crtc, base,
&primary->base, &cursor->base, &atmel_hlcdc_crtc_funcs,
NULL);
if (IS_ERR(crtc))
return PTR_ERR(crtc);
ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base,
&cursor->base, &atmel_hlcdc_crtc_funcs,
NULL);
if (ret < 0)
goto fail;
crtc->dc = dc;
crtc->id = drm_crtc_index(&crtc->base);
for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
@@ -674,8 +658,4 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
dc->crtc = &crtc->base;
return 0;
fail:
atmel_hlcdc_crtc_destroy(&crtc->base);
return ret;
}

View File

@@ -723,12 +723,6 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
drm_mode_config_init(dev);
ret = atmel_hlcdc_create_outputs(dev);
if (ret) {
drm_err(dev, "failed to create HLCDC outputs: %d\n", ret);
return ret;
}
ret = atmel_hlcdc_create_planes(dev);
if (ret) {
drm_err(dev, "failed to create planes: %d\n", ret);
@@ -741,6 +735,12 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
return ret;
}
ret = atmel_hlcdc_create_outputs(dev);
if (ret) {
drm_err(dev, "failed to create HLCDC outputs: %d\n", ret);
return ret;
}
dev->mode_config.min_width = dc->desc->min_width;
dev->mode_config.min_height = dc->desc->min_height;
dev->mode_config.max_width = dc->desc->max_width;
@@ -751,11 +751,16 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
return 0;
}
static struct atmel_hlcdc_dc *atmel_hlcdc_dc_of_dev(struct drm_device *dev)
{
return container_of(dev, struct atmel_hlcdc_dc, dev);
}
static int atmel_hlcdc_dc_load(struct drm_device *dev)
{
struct platform_device *pdev = to_platform_device(dev->dev);
const struct of_device_id *match;
struct atmel_hlcdc_dc *dc;
struct atmel_hlcdc_dc *dc = atmel_hlcdc_dc_of_dev(dev);
int ret;
match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
@@ -769,10 +774,6 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
return -EINVAL;
}
dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
if (!dc)
return -ENOMEM;
dc->desc = match->data;
dc->hlcdc = dev_get_drvdata(dev->dev->parent);
dev->dev_private = dc;
@@ -853,16 +854,21 @@ static const struct drm_driver atmel_hlcdc_dc_driver = {
static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
{
struct atmel_hlcdc_dc *dc;
struct drm_device *ddev;
int ret;
ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev);
if (IS_ERR(ddev))
return PTR_ERR(ddev);
if (drm_firmware_drivers_only())
return -ENODEV;
dc = devm_drm_dev_alloc(&pdev->dev, &atmel_hlcdc_dc_driver, struct atmel_hlcdc_dc, dev);
if (IS_ERR(dc))
return PTR_ERR(dc);
ddev = &dc->dev;
ret = atmel_hlcdc_dc_load(ddev);
if (ret)
goto err_put;
return ret;
ret = drm_dev_register(ddev, 0);
if (ret)
@@ -875,9 +881,6 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
err_unload:
atmel_hlcdc_dc_unload(ddev);
err_put:
drm_dev_put(ddev);
return ret;
}
@@ -887,7 +890,6 @@ static void atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
drm_dev_unregister(ddev);
atmel_hlcdc_dc_unload(ddev);
drm_dev_put(ddev);
}
static void atmel_hlcdc_dc_drm_shutdown(struct platform_device *pdev)

View File

@@ -350,6 +350,7 @@ struct atmel_hlcdc_dc {
struct dma_pool *dscrpool;
struct atmel_hlcdc *hlcdc;
struct drm_crtc *crtc;
struct drm_device dev;
struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
struct {
u32 imr;

View File

@@ -69,27 +69,24 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
{
struct atmel_hlcdc_rgb_output *output;
struct device_node *ep;
struct drm_panel *panel;
struct drm_bridge *bridge;
int ret;
struct atmel_hlcdc_dc *dc = dev->dev_private;
struct drm_crtc *crtc = dc->crtc;
int ret = 0;
bridge = devm_drm_of_get_bridge(dev->dev, dev->dev->of_node, 0, endpoint);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
output = drmm_simple_encoder_alloc(dev, struct atmel_hlcdc_rgb_output,
encoder, DRM_MODE_ENCODER_NONE);
if (IS_ERR(output))
return PTR_ERR(output);
ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint);
if (!ep)
return -ENODEV;
ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint,
&panel, &bridge);
if (ret) {
of_node_put(ep);
return ret;
}
output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);
if (!output) {
of_node_put(ep);
return -ENOMEM;
}
output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);
of_node_put(ep);
if (output->bus_fmt < 0) {
@@ -97,30 +94,11 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
return -EINVAL;
}
ret = drm_simple_encoder_init(dev, &output->encoder,
DRM_MODE_ENCODER_NONE);
if (ret)
return ret;
output->encoder.possible_crtcs = 0x1;
output->encoder.possible_crtcs = drm_crtc_mask(crtc);
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_Unknown);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
}
if (bridge) {
if (bridge)
ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0);
if (!ret)
return 0;
if (panel)
drm_panel_bridge_remove(bridge);
}
drm_encoder_cleanup(&output->encoder);
return ret;
}

View File

@@ -79,8 +79,6 @@ drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
return container_of(s, struct atmel_hlcdc_plane_state, base);
}
#define SUBPIXEL_MASK 0xffff
static uint32_t rgb_formats[] = {
DRM_FORMAT_C8,
DRM_FORMAT_XRGB4444,
@@ -745,24 +743,15 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
if (ret || !s->visible)
return ret;
hstate->src_x = s->src.x1;
hstate->src_y = s->src.y1;
hstate->src_w = drm_rect_width(&s->src);
hstate->src_h = drm_rect_height(&s->src);
hstate->src_x = s->src.x1 >> 16;
hstate->src_y = s->src.y1 >> 16;
hstate->src_w = drm_rect_width(&s->src) >> 16;
hstate->src_h = drm_rect_height(&s->src) >> 16;
hstate->crtc_x = s->dst.x1;
hstate->crtc_y = s->dst.y1;
hstate->crtc_w = drm_rect_width(&s->dst);
hstate->crtc_h = drm_rect_height(&s->dst);
if ((hstate->src_x | hstate->src_y | hstate->src_w | hstate->src_h) &
SUBPIXEL_MASK)
return -EINVAL;
hstate->src_x >>= 16;
hstate->src_y >>= 16;
hstate->src_w >>= 16;
hstate->src_h >>= 16;
hstate->nplanes = fb->format->num_planes;
if (hstate->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
return -EINVAL;
@@ -1155,32 +1144,6 @@ err:
return -ENOMEM;
}
static void atmel_hlcdc_plane_reset(struct drm_plane *p)
{
struct atmel_hlcdc_plane_state *state;
if (p->state) {
state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
if (state->base.fb)
drm_framebuffer_put(state->base.fb);
kfree(state);
p->state = NULL;
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state) {
if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
kfree(state);
drm_err(p->dev,
"Failed to allocate initial plane state\n");
return;
}
__drm_atomic_helper_plane_reset(p, &state->base);
}
}
static struct drm_plane_state *
atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
{
@@ -1197,8 +1160,7 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
return NULL;
}
if (copy->base.fb)
drm_framebuffer_get(copy->base.fb);
__drm_atomic_helper_plane_duplicate_state(p, &copy->base);
return &copy->base;
}
@@ -1216,16 +1178,40 @@ static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
state->dscrs[i]->self);
}
if (s->fb)
drm_framebuffer_put(s->fb);
__drm_atomic_helper_plane_destroy_state(s);
kfree(state);
}
static void atmel_hlcdc_plane_reset(struct drm_plane *p)
{
struct atmel_hlcdc_plane_state *state;
struct atmel_hlcdc_dc *dc = p->dev->dev_private;
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
if (p->state) {
atmel_hlcdc_plane_atomic_destroy_state(p, p->state);
p->state = NULL;
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state) {
if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
kfree(state);
drm_err(p->dev,
"Failed to allocate initial plane state\n");
return;
}
__drm_atomic_helper_plane_reset(p, &state->base);
}
if (plane->layer.desc->layout.csc)
dc->desc->ops->lcdc_csc_init(plane, plane->layer.desc);
}
static const struct drm_plane_funcs layer_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = atmel_hlcdc_plane_reset,
.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
@@ -1239,12 +1225,6 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
enum drm_plane_type type;
int ret;
plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
if (!plane)
return -ENOMEM;
atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
if (desc->type == ATMEL_HLCDC_BASE_LAYER)
type = DRM_PLANE_TYPE_PRIMARY;
else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
@@ -1252,13 +1232,13 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
else
type = DRM_PLANE_TYPE_OVERLAY;
ret = drm_universal_plane_init(dev, &plane->base, 0,
&layer_plane_funcs,
desc->formats->formats,
desc->formats->nformats,
NULL, type, NULL);
if (ret)
return ret;
plane = drmm_universal_plane_alloc(dev, struct atmel_hlcdc_plane, base, 0,
&layer_plane_funcs, desc->formats->formats,
desc->formats->nformats, NULL, type, NULL);
if (IS_ERR(plane))
return PTR_ERR(plane);
atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
drm_plane_helper_add(&plane->base,
&atmel_hlcdc_layer_plane_helper_funcs);

View File

@@ -887,88 +887,111 @@ static const struct drm_edid *adv7511_bridge_edid_read(struct drm_bridge *bridge
return adv7511_edid_read(adv, connector);
}
static int adv7511_bridge_hdmi_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
static int adv7511_bridge_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
switch (type) {
case HDMI_INFOFRAME_TYPE_AUDIO:
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
break;
case HDMI_INFOFRAME_TYPE_AVI:
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
break;
case HDMI_INFOFRAME_TYPE_SPD:
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
break;
case HDMI_INFOFRAME_TYPE_VENDOR:
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
break;
default:
drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
break;
}
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
return 0;
}
static int adv7511_bridge_hdmi_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
static int adv7511_bridge_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
switch (type) {
case HDMI_INFOFRAME_TYPE_AUDIO:
/* send current Audio infoframe values while updating */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(5), BIT(5));
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
/* The Audio infoframe id is not configurable */
regmap_bulk_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_VERSION,
buffer + 1, len - 1);
return 0;
}
/* use Audio infoframe updated info */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(5), 0);
static int adv7511_bridge_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
break;
case HDMI_INFOFRAME_TYPE_AVI:
/* send current AVI infoframe values while updating */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(6), BIT(6));
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
/* The AVI infoframe id is not configurable */
regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION,
buffer + 1, len - 1);
return 0;
}
regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_LENGTH, 0x2);
regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME(1), 0x1);
static int adv7511_bridge_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
/* use AVI infoframe updated info */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(6), 0);
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
break;
case HDMI_INFOFRAME_TYPE_SPD:
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPD(0),
buffer, len);
adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPD);
break;
case HDMI_INFOFRAME_TYPE_VENDOR:
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPARE1(0),
buffer, len);
adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
break;
default:
drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
break;
}
return 0;
}
static int adv7511_bridge_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
/* send current Audio infoframe values while updating */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(5), BIT(5));
/* The Audio infoframe id is not configurable */
regmap_bulk_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_VERSION,
buffer + 1, len - 1);
/* use Audio infoframe updated info */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(5), 0);
adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
return 0;
}
static int adv7511_bridge_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
/* send current AVI infoframe values while updating */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(6), BIT(6));
/* The AVI infoframe id is not configurable */
regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION,
buffer + 1, len - 1);
regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_LENGTH, 0x2);
regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME(1), 0x1);
/* use AVI infoframe updated info */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(6), 0);
adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
return 0;
}
static int adv7511_bridge_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPD(0),
buffer, len);
adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPD);
return 0;
}
static int adv7511_bridge_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPARE1(0),
buffer, len);
adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
return 0;
}
@@ -986,8 +1009,14 @@ static const struct drm_bridge_funcs adv7511_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
.hdmi_tmds_char_rate_valid = adv7511_bridge_hdmi_tmds_char_rate_valid,
.hdmi_clear_infoframe = adv7511_bridge_hdmi_clear_infoframe,
.hdmi_write_infoframe = adv7511_bridge_hdmi_write_infoframe,
.hdmi_clear_audio_infoframe = adv7511_bridge_hdmi_clear_audio_infoframe,
.hdmi_write_audio_infoframe = adv7511_bridge_hdmi_write_audio_infoframe,
.hdmi_clear_avi_infoframe = adv7511_bridge_hdmi_clear_avi_infoframe,
.hdmi_write_avi_infoframe = adv7511_bridge_hdmi_write_avi_infoframe,
.hdmi_clear_spd_infoframe = adv7511_bridge_hdmi_clear_spd_infoframe,
.hdmi_write_spd_infoframe = adv7511_bridge_hdmi_write_spd_infoframe,
.hdmi_clear_hdmi_infoframe = adv7511_bridge_hdmi_clear_hdmi_infoframe,
.hdmi_write_hdmi_infoframe = adv7511_bridge_hdmi_write_hdmi_infoframe,
.hdmi_audio_startup = adv7511_hdmi_audio_startup,
.hdmi_audio_prepare = adv7511_hdmi_audio_prepare,
@@ -1322,7 +1351,8 @@ static int adv7511_probe(struct i2c_client *i2c)
adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_HDMI;
DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
if (adv7511->i2c_main->irq)
adv7511->bridge.ops |= DRM_BRIDGE_OP_HPD;

View File

@@ -1801,7 +1801,7 @@ static const struct drm_edid *anx7625_edid_read(struct anx7625_data *ctx)
return NULL;
}
ctx->cached_drm_edid = drm_edid_alloc(edid_buf, FOUR_BLOCK_SIZE);
ctx->cached_drm_edid = drm_edid_alloc(edid_buf, edid_num * ONE_BLOCK_SIZE);
kfree(edid_buf);
out:

View File

@@ -29,7 +29,6 @@
struct imx8mp_hdmi_pvi {
struct drm_bridge bridge;
struct device *dev;
struct drm_bridge *next_bridge;
void __iomem *regs;
};
@@ -45,7 +44,7 @@ static int imx8mp_hdmi_pvi_bridge_attach(struct drm_bridge *bridge,
{
struct imx8mp_hdmi_pvi *pvi = to_imx8mp_hdmi_pvi(bridge);
return drm_bridge_attach(encoder, pvi->next_bridge,
return drm_bridge_attach(encoder, pvi->bridge.next_bridge,
bridge, flags);
}
@@ -78,8 +77,8 @@ static void imx8mp_hdmi_pvi_bridge_enable(struct drm_bridge *bridge,
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
val |= PVI_CTRL_OP_HSYNC_POL | PVI_CTRL_INP_HSYNC_POL;
if (pvi->next_bridge->timings)
bus_flags = pvi->next_bridge->timings->input_bus_flags;
if (pvi->bridge.next_bridge->timings)
bus_flags = pvi->bridge.next_bridge->timings->input_bus_flags;
else if (bridge_state)
bus_flags = bridge_state->input_bus_cfg.flags;
@@ -108,7 +107,7 @@ imx8mp_hdmi_pvi_bridge_get_input_bus_fmts(struct drm_bridge *bridge,
unsigned int *num_input_fmts)
{
struct imx8mp_hdmi_pvi *pvi = to_imx8mp_hdmi_pvi(bridge);
struct drm_bridge *next_bridge = pvi->next_bridge;
struct drm_bridge *next_bridge = pvi->bridge.next_bridge;
struct drm_bridge_state *next_state;
if (!next_bridge->funcs->atomic_get_input_bus_fmts)
@@ -157,10 +156,10 @@ static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev)
if (!remote)
return -EINVAL;
pvi->next_bridge = of_drm_find_bridge(remote);
pvi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
if (!pvi->next_bridge)
if (!pvi->bridge.next_bridge)
return dev_err_probe(&pdev->dev, -EPROBE_DEFER,
"could not find next bridge\n");
@@ -168,7 +167,7 @@ static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev)
/* Register the bridge. */
pvi->bridge.of_node = pdev->dev.of_node;
pvi->bridge.timings = pvi->next_bridge->timings;
pvi->bridge.timings = pvi->bridge.next_bridge->timings;
drm_bridge_add(&pvi->bridge);

View File

@@ -62,6 +62,15 @@ static inline struct imx8qxp_ldb *base_to_imx8qxp_ldb(struct ldb *base)
return container_of(base, struct imx8qxp_ldb, base);
}
static void imx8qxp_ldb_bridge_destroy(struct drm_bridge *bridge)
{
struct ldb_channel *ldb_ch = bridge->driver_private;
struct ldb *ldb = ldb_ch->ldb;
struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);
drm_bridge_put(imx8qxp_ldb->companion);
}
static void imx8qxp_ldb_set_phy_cfg(struct imx8qxp_ldb *imx8qxp_ldb,
unsigned long di_clk, bool is_split,
struct phy_configure_opts_lvds *phy_cfg)
@@ -391,6 +400,7 @@ imx8qxp_ldb_bridge_mode_valid(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs imx8qxp_ldb_bridge_funcs = {
.destroy = imx8qxp_ldb_bridge_destroy,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
@@ -552,7 +562,7 @@ static int imx8qxp_ldb_parse_dt_companion(struct imx8qxp_ldb *imx8qxp_ldb)
goto out;
}
imx8qxp_ldb->companion = of_drm_find_bridge(companion_port);
imx8qxp_ldb->companion = of_drm_find_and_get_bridge(companion_port);
if (!imx8qxp_ldb->companion) {
ret = -EPROBE_DEFER;
DRM_DEV_DEBUG_DRIVER(dev,

View File

@@ -584,34 +584,22 @@ static void inno_hdmi_init_hw(struct inno_hdmi *hdmi)
hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
}
static int inno_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
static int inno_hdmi_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
return 0;
}
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
return 0;
}
static int inno_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
static int inno_hdmi_bridge_write_avi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
ssize_t i;
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
return 0;
}
inno_hdmi_bridge_clear_infoframe(bridge, type);
inno_hdmi_bridge_clear_avi_infoframe(bridge);
for (i = 0; i < len; i++)
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, buffer[i]);
@@ -619,6 +607,21 @@ static int inno_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
return 0;
}
static int inno_hdmi_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
drm_warn_once(bridge->encoder->dev, "HDMI VSI not implemented\n");
return 0;
}
static int inno_hdmi_bridge_write_hdmi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
drm_warn_once(bridge->encoder->dev, "HDMI VSI not implemented\n");
return 0;
}
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi,
struct drm_connector *connector,
struct drm_display_mode *mode)
@@ -883,8 +886,10 @@ static const struct drm_bridge_funcs inno_hdmi_bridge_funcs = {
.atomic_disable = inno_hdmi_bridge_atomic_disable,
.detect = inno_hdmi_bridge_detect,
.edid_read = inno_hdmi_bridge_edid_read,
.hdmi_clear_infoframe = inno_hdmi_bridge_clear_infoframe,
.hdmi_write_infoframe = inno_hdmi_bridge_write_infoframe,
.hdmi_clear_avi_infoframe = inno_hdmi_bridge_clear_avi_infoframe,
.hdmi_write_avi_infoframe = inno_hdmi_bridge_write_avi_infoframe,
.hdmi_clear_hdmi_infoframe = inno_hdmi_bridge_clear_hdmi_infoframe,
.hdmi_write_hdmi_infoframe = inno_hdmi_bridge_write_hdmi_infoframe,
.mode_valid = inno_hdmi_bridge_mode_valid,
};

View File

@@ -759,61 +759,62 @@ it6263_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
return MODE_OK;
}
static int it6263_hdmi_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
static int it6263_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct it6263 *it = bridge_to_it6263(bridge);
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
regmap_write(it->hdmi_regmap, HDMI_REG_AVI_INFOFRM_CTRL, 0);
break;
case HDMI_INFOFRAME_TYPE_VENDOR:
regmap_write(it->hdmi_regmap, HDMI_REG_PKT_NULL_CTRL, 0);
break;
default:
dev_dbg(it->dev, "unsupported HDMI infoframe 0x%x\n", type);
}
regmap_write(it->hdmi_regmap, HDMI_REG_AVI_INFOFRM_CTRL, 0);
return 0;
}
static int it6263_hdmi_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
static int it6263_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
struct it6263 *it = bridge_to_it6263(bridge);
regmap_write(it->hdmi_regmap, HDMI_REG_PKT_NULL_CTRL, 0);
return 0;
}
static int it6263_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct it6263 *it = bridge_to_it6263(bridge);
struct regmap *regmap = it->hdmi_regmap;
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
/* write the first AVI infoframe data byte chunk(DB1-DB5) */
regmap_bulk_write(regmap, HDMI_REG_AVI_DB1,
&buffer[HDMI_INFOFRAME_HEADER_SIZE],
HDMI_AVI_DB_CHUNK1_SIZE);
/* write the first AVI infoframe data byte chunk(DB1-DB5) */
regmap_bulk_write(regmap, HDMI_REG_AVI_DB1,
&buffer[HDMI_INFOFRAME_HEADER_SIZE],
HDMI_AVI_DB_CHUNK1_SIZE);
/* write the second AVI infoframe data byte chunk(DB6-DB13) */
regmap_bulk_write(regmap, HDMI_REG_AVI_DB6,
&buffer[HDMI_INFOFRAME_HEADER_SIZE +
HDMI_AVI_DB_CHUNK1_SIZE],
HDMI_AVI_DB_CHUNK2_SIZE);
/* write the second AVI infoframe data byte chunk(DB6-DB13) */
regmap_bulk_write(regmap, HDMI_REG_AVI_DB6,
&buffer[HDMI_INFOFRAME_HEADER_SIZE +
HDMI_AVI_DB_CHUNK1_SIZE],
HDMI_AVI_DB_CHUNK2_SIZE);
/* write checksum */
regmap_write(regmap, HDMI_REG_AVI_CSUM, buffer[3]);
/* write checksum */
regmap_write(regmap, HDMI_REG_AVI_CSUM, buffer[3]);
regmap_write(regmap, HDMI_REG_AVI_INFOFRM_CTRL,
ENABLE_PKT | REPEAT_PKT);
break;
case HDMI_INFOFRAME_TYPE_VENDOR:
/* write header and payload */
regmap_bulk_write(regmap, HDMI_REG_PKT_HB(0), buffer, len);
regmap_write(regmap, HDMI_REG_AVI_INFOFRM_CTRL,
ENABLE_PKT | REPEAT_PKT);
return 0;
}
static int it6263_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct it6263 *it = bridge_to_it6263(bridge);
struct regmap *regmap = it->hdmi_regmap;
/* write header and payload */
regmap_bulk_write(regmap, HDMI_REG_PKT_HB(0), buffer, len);
regmap_write(regmap, HDMI_REG_PKT_NULL_CTRL,
ENABLE_PKT | REPEAT_PKT);
regmap_write(regmap, HDMI_REG_PKT_NULL_CTRL,
ENABLE_PKT | REPEAT_PKT);
break;
default:
dev_dbg(it->dev, "unsupported HDMI infoframe 0x%x\n", type);
}
return 0;
}
@@ -830,8 +831,10 @@ static const struct drm_bridge_funcs it6263_bridge_funcs = {
.edid_read = it6263_bridge_edid_read,
.atomic_get_input_bus_fmts = it6263_bridge_atomic_get_input_bus_fmts,
.hdmi_tmds_char_rate_valid = it6263_hdmi_tmds_char_rate_valid,
.hdmi_clear_infoframe = it6263_hdmi_clear_infoframe,
.hdmi_write_infoframe = it6263_hdmi_write_infoframe,
.hdmi_clear_avi_infoframe = it6263_hdmi_clear_avi_infoframe,
.hdmi_write_avi_infoframe = it6263_hdmi_write_avi_infoframe,
.hdmi_clear_hdmi_infoframe = it6263_hdmi_clear_hdmi_infoframe,
.hdmi_write_hdmi_infoframe = it6263_hdmi_write_hdmi_infoframe,
};
static int it6263_probe(struct i2c_client *client)

View File

@@ -35,7 +35,6 @@ struct lt8912 {
struct regmap *regmap[I2C_MAX_IDX];
struct device_node *host_node;
struct drm_bridge *hdmi_port;
struct mipi_dsi_device *dsi;
@@ -407,8 +406,8 @@ lt8912_connector_detect(struct drm_connector *connector, bool force)
{
struct lt8912 *lt = connector_to_lt8912(connector);
if (lt->hdmi_port->ops & DRM_BRIDGE_OP_DETECT)
return drm_bridge_detect(lt->hdmi_port, connector);
if (lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_DETECT)
return drm_bridge_detect(lt->bridge.next_bridge, connector);
return lt8912_check_cable_status(lt);
}
@@ -429,7 +428,7 @@ static int lt8912_connector_get_modes(struct drm_connector *connector)
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
int ret, num;
drm_edid = drm_bridge_edid_read(lt->hdmi_port, connector);
drm_edid = drm_bridge_edid_read(lt->bridge.next_bridge, connector);
drm_edid_connector_update(connector, drm_edid);
if (!drm_edid)
return 0;
@@ -519,8 +518,8 @@ static int lt8912_bridge_connector_init(struct drm_bridge *bridge)
struct lt8912 *lt = bridge_to_lt8912(bridge);
struct drm_connector *connector = &lt->connector;
if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) {
drm_bridge_hpd_enable(lt->hdmi_port, lt8912_bridge_hpd_cb, lt);
if (lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD) {
drm_bridge_hpd_enable(lt->bridge.next_bridge, lt8912_bridge_hpd_cb, lt);
connector->polled = DRM_CONNECTOR_POLL_HPD;
} else {
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
@@ -529,7 +528,7 @@ static int lt8912_bridge_connector_init(struct drm_bridge *bridge)
ret = drm_connector_init(bridge->dev, connector,
&lt8912_connector_funcs,
lt->hdmi_port->type);
lt->bridge.next_bridge->type);
if (ret)
goto exit;
@@ -549,7 +548,7 @@ static int lt8912_bridge_attach(struct drm_bridge *bridge,
struct lt8912 *lt = bridge_to_lt8912(bridge);
int ret;
ret = drm_bridge_attach(encoder, lt->hdmi_port, bridge,
ret = drm_bridge_attach(encoder, lt->bridge.next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0) {
dev_err(lt->dev, "Failed to attach next bridge (%d)\n", ret);
@@ -585,8 +584,8 @@ static void lt8912_bridge_detach(struct drm_bridge *bridge)
lt8912_hard_power_off(lt);
if (lt->connector.dev && lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD)
drm_bridge_hpd_disable(lt->hdmi_port);
if (lt->connector.dev && lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD)
drm_bridge_hpd_disable(lt->bridge.next_bridge);
}
static enum drm_mode_status
@@ -611,8 +610,8 @@ lt8912_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
{
struct lt8912 *lt = bridge_to_lt8912(bridge);
if (lt->hdmi_port->ops & DRM_BRIDGE_OP_DETECT)
return drm_bridge_detect(lt->hdmi_port, connector);
if (lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_DETECT)
return drm_bridge_detect(lt->bridge.next_bridge, connector);
return lt8912_check_cable_status(lt);
}
@@ -626,8 +625,8 @@ static const struct drm_edid *lt8912_bridge_edid_read(struct drm_bridge *bridge,
* edid must be read through the ddc bus but it must be
* given to the hdmi connector node.
*/
if (lt->hdmi_port->ops & DRM_BRIDGE_OP_EDID)
return drm_bridge_edid_read(lt->hdmi_port, connector);
if (lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_EDID)
return drm_bridge_edid_read(lt->bridge.next_bridge, connector);
dev_warn(lt->dev, "The connected bridge does not supports DRM_BRIDGE_OP_EDID\n");
return NULL;
@@ -723,8 +722,8 @@ static int lt8912_parse_dt(struct lt8912 *lt)
goto err_free_host_node;
}
lt->hdmi_port = of_drm_find_bridge(port_node);
if (!lt->hdmi_port) {
lt->bridge.next_bridge = of_drm_find_and_get_bridge(port_node);
if (!lt->bridge.next_bridge) {
ret = -EPROBE_DEFER;
dev_err_probe(lt->dev, ret, "%s: Failed to get hdmi port\n", __func__);
goto err_free_host_node;

View File

@@ -843,84 +843,96 @@ lt9611_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
#define LT9611_INFOFRAME_AUDIO 0x02
#define LT9611_INFOFRAME_AVI 0x08
#define LT9611_INFOFRAME_SPD 0x10
#define LT9611_INFOFRAME_VENDOR 0x20
#define LT9611_INFOFRAME_HDMI 0x20
static int lt9611_hdmi_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
static int lt9611_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
unsigned int mask;
switch (type) {
case HDMI_INFOFRAME_TYPE_AUDIO:
mask = LT9611_INFOFRAME_AUDIO;
break;
case HDMI_INFOFRAME_TYPE_AVI:
mask = LT9611_INFOFRAME_AVI;
break;
case HDMI_INFOFRAME_TYPE_SPD:
mask = LT9611_INFOFRAME_SPD;
break;
case HDMI_INFOFRAME_TYPE_VENDOR:
mask = LT9611_INFOFRAME_VENDOR;
break;
default:
drm_dbg_driver(lt9611->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
mask = 0;
break;
}
if (mask)
regmap_update_bits(lt9611->regmap, 0x843d, mask, 0);
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, 0);
return 0;
}
static int lt9611_hdmi_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
static int lt9611_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AVI, 0);
return 0;
}
static int lt9611_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_SPD, 0);
return 0;
}
static int lt9611_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_HDMI, 0);
return 0;
}
static int lt9611_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
unsigned int mask, addr;
int i;
switch (type) {
case HDMI_INFOFRAME_TYPE_AUDIO:
mask = LT9611_INFOFRAME_AUDIO;
addr = 0x84b2;
break;
for (i = 0; i < len; i++)
regmap_write(lt9611->regmap, 0x84b2 + i, buffer[i]);
case HDMI_INFOFRAME_TYPE_AVI:
mask = LT9611_INFOFRAME_AVI;
addr = 0x8440;
break;
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, LT9611_INFOFRAME_AUDIO);
case HDMI_INFOFRAME_TYPE_SPD:
mask = LT9611_INFOFRAME_SPD;
addr = 0x8493;
break;
return 0;
}
case HDMI_INFOFRAME_TYPE_VENDOR:
mask = LT9611_INFOFRAME_VENDOR;
addr = 0x8474;
break;
static int lt9611_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
int i;
default:
drm_dbg_driver(lt9611->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
mask = 0;
break;
}
for (i = 0; i < len; i++)
regmap_write(lt9611->regmap, 0x8440 + i, buffer[i]);
if (mask) {
for (i = 0; i < len; i++)
regmap_write(lt9611->regmap, addr + i, buffer[i]);
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AVI, LT9611_INFOFRAME_AVI);
regmap_update_bits(lt9611->regmap, 0x843d, mask, mask);
}
return 0;
}
static int lt9611_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
int i;
for (i = 0; i < len; i++)
regmap_write(lt9611->regmap, 0x8493 + i, buffer[i]);
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_SPD, LT9611_INFOFRAME_SPD);
return 0;
}
static int lt9611_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
int i;
for (i = 0; i < len; i++)
regmap_write(lt9611->regmap, 0x8474 + i, buffer[i]);
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_HDMI, LT9611_INFOFRAME_HDMI);
return 0;
}
@@ -1003,8 +1015,14 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
.atomic_get_input_bus_fmts = lt9611_atomic_get_input_bus_fmts,
.hdmi_tmds_char_rate_valid = lt9611_hdmi_tmds_char_rate_valid,
.hdmi_write_infoframe = lt9611_hdmi_write_infoframe,
.hdmi_clear_infoframe = lt9611_hdmi_clear_infoframe,
.hdmi_write_audio_infoframe = lt9611_hdmi_write_audio_infoframe,
.hdmi_clear_audio_infoframe = lt9611_hdmi_clear_audio_infoframe,
.hdmi_write_avi_infoframe = lt9611_hdmi_write_avi_infoframe,
.hdmi_clear_avi_infoframe = lt9611_hdmi_clear_avi_infoframe,
.hdmi_write_spd_infoframe = lt9611_hdmi_write_spd_infoframe,
.hdmi_clear_spd_infoframe = lt9611_hdmi_clear_spd_infoframe,
.hdmi_write_hdmi_infoframe = lt9611_hdmi_write_hdmi_infoframe,
.hdmi_clear_hdmi_infoframe = lt9611_hdmi_clear_hdmi_infoframe,
.hdmi_audio_startup = lt9611_hdmi_audio_startup,
.hdmi_audio_prepare = lt9611_hdmi_audio_prepare,
@@ -1132,7 +1150,8 @@ static int lt9611_probe(struct i2c_client *client)
lt9611->bridge.of_node = client->dev.of_node;
lt9611->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_MODES |
DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_HDMI_AUDIO;
DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_HDMI_AUDIO |
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
lt9611->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
lt9611->bridge.vendor = "Lontium";
lt9611->bridge.product = "LT9611";

View File

@@ -1828,7 +1828,7 @@ static int samsung_dsim_attach(struct drm_bridge *bridge,
{
struct samsung_dsim *dsi = bridge_to_dsi(bridge);
return drm_bridge_attach(encoder, dsi->out_bridge, bridge,
return drm_bridge_attach(encoder, dsi->bridge.next_bridge, bridge,
flags);
}
@@ -1886,11 +1886,12 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
{
struct samsung_dsim *dsi = host_to_dsi(host);
const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
struct drm_bridge *next_bridge __free(drm_bridge_put) = NULL;
struct device *dev = dsi->dev;
struct device_node *np = dev->of_node;
struct device_node *remote;
struct drm_panel *panel;
int ret;
int ret = 0;
/*
* Devices can also be child nodes when we also control that device
@@ -1924,17 +1925,22 @@ of_find_panel_or_bridge:
panel = of_drm_find_panel(remote);
if (!IS_ERR(panel)) {
dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel);
next_bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(next_bridge)) {
ret = PTR_ERR(next_bridge);
next_bridge = NULL; // Inhibit the cleanup action on an ERR_PTR
} else {
drm_bridge_get(next_bridge);
}
} else {
dsi->out_bridge = of_drm_find_bridge(remote);
if (!dsi->out_bridge)
dsi->out_bridge = ERR_PTR(-EINVAL);
next_bridge = of_drm_find_and_get_bridge(remote);
if (!next_bridge)
ret = -EINVAL;
}
of_node_put(remote);
if (IS_ERR(dsi->out_bridge)) {
ret = PTR_ERR(dsi->out_bridge);
if (ret) {
DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret);
return ret;
}
@@ -1958,10 +1964,13 @@ of_find_panel_or_bridge:
return ret;
}
// The next bridge can be used by host_ops->attach
dsi->bridge.next_bridge = drm_bridge_get(next_bridge);
if (pdata->host_ops && pdata->host_ops->attach) {
ret = pdata->host_ops->attach(dsi, device);
if (ret)
return ret;
goto err_release_next_bridge;
}
dsi->lanes = device->lanes;
@@ -1969,6 +1978,11 @@ of_find_panel_or_bridge:
dsi->mode_flags = device->mode_flags;
return 0;
err_release_next_bridge:
drm_bridge_put(dsi->bridge.next_bridge);
dsi->bridge.next_bridge = NULL;
return ret;
}
static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi)
@@ -1985,11 +1999,12 @@ static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
struct samsung_dsim *dsi = host_to_dsi(host);
const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
dsi->out_bridge = NULL;
if (pdata->host_ops && pdata->host_ops->detach)
pdata->host_ops->detach(dsi, device);
drm_bridge_put(dsi->bridge.next_bridge);
dsi->bridge.next_bridge = NULL;
samsung_dsim_unregister_te_irq(dsi);
drm_bridge_remove(&dsi->bridge);

View File

@@ -175,7 +175,6 @@ struct sii902x {
struct i2c_client *i2c;
struct regmap *regmap;
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
struct drm_connector connector;
struct gpio_desc *reset_gpio;
struct i2c_mux_core *i2cmux;
@@ -421,7 +420,7 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge,
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return drm_bridge_attach(encoder, sii902x->next_bridge,
return drm_bridge_attach(encoder, sii902x->bridge.next_bridge,
bridge, flags);
drm_connector_helper_add(&sii902x->connector,
@@ -1204,9 +1203,9 @@ static int sii902x_probe(struct i2c_client *client)
return -ENODEV;
}
sii902x->next_bridge = of_drm_find_bridge(remote);
sii902x->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
if (!sii902x->next_bridge)
if (!sii902x->bridge.next_bridge)
return dev_err_probe(dev, -EPROBE_DEFER,
"Failed to find remote bridge\n");
}

View File

@@ -260,6 +260,11 @@ static const struct of_device_id simple_bridge_match[] = {
.timings = &default_bridge_timings,
.connector_type = DRM_MODE_CONNECTOR_VGA,
},
}, {
.compatible = "algoltek,ag6311",
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
},
}, {
.compatible = "asl-tek,cs5263",
.data = &(const struct simple_bridge_info) {

View File

@@ -26,6 +26,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_modes.h>
#include <drm/drm_print.h>
#include <media/cec.h>
@@ -956,57 +957,85 @@ dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge,
return MODE_OK;
}
static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
static int dw_hdmi_qp_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
PKTSCHED_PKT_EN);
break;
case HDMI_INFOFRAME_TYPE_DRM:
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
break;
case HDMI_INFOFRAME_TYPE_AUDIO:
dw_hdmi_qp_mod(hdmi, 0,
PKTSCHED_ACR_TX_EN |
PKTSCHED_AUDS_TX_EN |
PKTSCHED_AUDI_TX_EN,
PKTSCHED_PKT_EN);
break;
default:
dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
}
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
PKTSCHED_PKT_EN);
return 0;
}
static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
static int dw_hdmi_qp_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
/* FIXME: add support for this InfoFrame */
drm_warn_once(bridge->encoder->dev, "HDMI VSI not supported\n");
return 0;
}
static int dw_hdmi_qp_bridge_clear_hdr_drm_infoframe(struct drm_bridge *bridge)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
dw_hdmi_qp_bridge_clear_infoframe(bridge, type);
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len);
return 0;
}
case HDMI_INFOFRAME_TYPE_DRM:
return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
static int dw_hdmi_qp_bridge_clear_audio_infoframe(struct drm_bridge *bridge)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
case HDMI_INFOFRAME_TYPE_AUDIO:
return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len);
dw_hdmi_qp_mod(hdmi, 0,
PKTSCHED_ACR_TX_EN |
PKTSCHED_AUDS_TX_EN |
PKTSCHED_AUDI_TX_EN,
PKTSCHED_PKT_EN);
default:
dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
return 0;
}
return 0;
}
static int dw_hdmi_qp_bridge_write_avi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
dw_hdmi_qp_bridge_clear_avi_infoframe(bridge);
return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len);
}
static int dw_hdmi_qp_bridge_write_hdmi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
dw_hdmi_qp_bridge_clear_hdmi_infoframe(bridge);
/* FIXME: add support for the HDMI VSI */
return 0;
}
static int dw_hdmi_qp_bridge_write_hdr_drm_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
dw_hdmi_qp_bridge_clear_hdr_drm_infoframe(bridge);
return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
}
static int dw_hdmi_qp_bridge_write_audio_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
dw_hdmi_qp_bridge_clear_audio_infoframe(bridge);
return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len);
}
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
@@ -1191,8 +1220,14 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.detect = dw_hdmi_qp_bridge_detect,
.edid_read = dw_hdmi_qp_bridge_edid_read,
.hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid,
.hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe,
.hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe,
.hdmi_clear_avi_infoframe = dw_hdmi_qp_bridge_clear_avi_infoframe,
.hdmi_write_avi_infoframe = dw_hdmi_qp_bridge_write_avi_infoframe,
.hdmi_clear_hdmi_infoframe = dw_hdmi_qp_bridge_clear_hdmi_infoframe,
.hdmi_write_hdmi_infoframe = dw_hdmi_qp_bridge_write_hdmi_infoframe,
.hdmi_clear_hdr_drm_infoframe = dw_hdmi_qp_bridge_clear_hdr_drm_infoframe,
.hdmi_write_hdr_drm_infoframe = dw_hdmi_qp_bridge_write_hdr_drm_infoframe,
.hdmi_clear_audio_infoframe = dw_hdmi_qp_bridge_clear_audio_infoframe,
.hdmi_write_audio_infoframe = dw_hdmi_qp_bridge_write_audio_infoframe,
.hdmi_audio_startup = dw_hdmi_qp_audio_enable,
.hdmi_audio_shutdown = dw_hdmi_qp_audio_disable,
.hdmi_audio_prepare = dw_hdmi_qp_audio_prepare,
@@ -1306,7 +1341,8 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_HDMI_AUDIO;
DRM_BRIDGE_OP_HDMI_AUDIO |
DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME;
if (!hdmi->no_hpd)
hdmi->bridge.ops |= DRM_BRIDGE_OP_HPD;
hdmi->bridge.of_node = pdev->dev.of_node;

View File

@@ -132,7 +132,6 @@ struct dw_hdmi_phy_data {
struct dw_hdmi {
struct drm_connector connector;
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
unsigned int version;
@@ -2912,7 +2911,7 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
struct dw_hdmi *hdmi = bridge->driver_private;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return drm_bridge_attach(encoder, hdmi->next_bridge,
return drm_bridge_attach(encoder, hdmi->bridge.next_bridge,
bridge, flags);
return dw_hdmi_connector_create(hdmi);
@@ -3318,9 +3317,9 @@ static int dw_hdmi_parse_dt(struct dw_hdmi *hdmi)
if (!remote)
return -ENODEV;
hdmi->next_bridge = of_drm_find_bridge(remote);
hdmi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
if (!hdmi->next_bridge)
if (!hdmi->bridge.next_bridge)
return -EPROBE_DEFER;
return 0;

View File

@@ -32,7 +32,6 @@ struct thc63_dev {
struct gpio_desc *oe;
struct drm_bridge bridge;
struct drm_bridge *next;
struct drm_bridge_timings timings;
};
@@ -48,7 +47,7 @@ static int thc63_attach(struct drm_bridge *bridge,
{
struct thc63_dev *thc63 = to_thc63(bridge);
return drm_bridge_attach(encoder, thc63->next, bridge, flags);
return drm_bridge_attach(encoder, thc63->bridge.next_bridge, bridge, flags);
}
static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
@@ -132,9 +131,9 @@ static int thc63_parse_dt(struct thc63_dev *thc63)
return -ENODEV;
}
thc63->next = of_drm_find_bridge(remote);
thc63->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
if (!thc63->next)
if (!thc63->bridge.next_bridge)
return -EPROBE_DEFER;
endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node,

View File

@@ -30,7 +30,6 @@ struct tfp410 {
struct gpio_desc *powerdown;
struct drm_bridge_timings timings;
struct drm_bridge *next_bridge;
struct device *dev;
};
@@ -53,8 +52,8 @@ static int tfp410_get_modes(struct drm_connector *connector)
const struct drm_edid *drm_edid;
int ret;
if (dvi->next_bridge->ops & DRM_BRIDGE_OP_EDID) {
drm_edid = drm_bridge_edid_read(dvi->next_bridge, connector);
if (dvi->bridge.next_bridge->ops & DRM_BRIDGE_OP_EDID) {
drm_edid = drm_bridge_edid_read(dvi->bridge.next_bridge, connector);
if (!drm_edid)
DRM_INFO("EDID read failed. Fallback to standard modes\n");
} else {
@@ -89,7 +88,7 @@ tfp410_connector_detect(struct drm_connector *connector, bool force)
{
struct tfp410 *dvi = drm_connector_to_tfp410(connector);
return drm_bridge_detect(dvi->next_bridge, connector);
return drm_bridge_detect(dvi->bridge.next_bridge, connector);
}
static const struct drm_connector_funcs tfp410_con_funcs = {
@@ -126,7 +125,7 @@ static int tfp410_attach(struct drm_bridge *bridge,
struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
int ret;
ret = drm_bridge_attach(encoder, dvi->next_bridge, bridge,
ret = drm_bridge_attach(encoder, dvi->bridge.next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0)
return ret;
@@ -134,14 +133,14 @@ static int tfp410_attach(struct drm_bridge *bridge,
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
if (dvi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)
if (dvi->bridge.next_bridge->ops & DRM_BRIDGE_OP_DETECT)
dvi->connector.polled = DRM_CONNECTOR_POLL_HPD;
else
dvi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
if (dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
if (dvi->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD) {
INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func);
drm_bridge_hpd_enable(dvi->next_bridge, tfp410_hpd_callback,
drm_bridge_hpd_enable(dvi->bridge.next_bridge, tfp410_hpd_callback,
dvi);
}
@@ -149,8 +148,8 @@ static int tfp410_attach(struct drm_bridge *bridge,
&tfp410_con_helper_funcs);
ret = drm_connector_init_with_ddc(bridge->dev, &dvi->connector,
&tfp410_con_funcs,
dvi->next_bridge->type,
dvi->next_bridge->ddc);
dvi->bridge.next_bridge->type,
dvi->bridge.next_bridge->ddc);
if (ret) {
dev_err(dvi->dev, "drm_connector_init_with_ddc() failed: %d\n",
ret);
@@ -169,8 +168,8 @@ static void tfp410_detach(struct drm_bridge *bridge)
{
struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
if (dvi->connector.dev && dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
drm_bridge_hpd_disable(dvi->next_bridge);
if (dvi->connector.dev && dvi->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD) {
drm_bridge_hpd_disable(dvi->bridge.next_bridge);
cancel_delayed_work_sync(&dvi->hpd_work);
}
}
@@ -362,10 +361,10 @@ static int tfp410_init(struct device *dev, bool i2c)
if (!node)
return -ENODEV;
dvi->next_bridge = of_drm_find_bridge(node);
dvi->bridge.next_bridge = of_drm_find_and_get_bridge(node);
of_node_put(node);
if (!dvi->next_bridge)
if (!dvi->bridge.next_bridge)
return -EPROBE_DEFER;
/* Get the powerdown GPIO. */

View File

@@ -28,8 +28,6 @@ struct tpd12s015_device {
struct gpio_desc *ls_oe_gpio;
struct gpio_desc *hpd_gpio;
int hpd_irq;
struct drm_bridge *next_bridge;
};
static inline struct tpd12s015_device *to_tpd12s015(struct drm_bridge *bridge)
@@ -47,7 +45,7 @@ static int tpd12s015_attach(struct drm_bridge *bridge,
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
ret = drm_bridge_attach(encoder, tpd->next_bridge,
ret = drm_bridge_attach(encoder, tpd->bridge.next_bridge,
bridge, flags);
if (ret < 0)
return ret;
@@ -138,10 +136,10 @@ static int tpd12s015_probe(struct platform_device *pdev)
if (!node)
return -ENODEV;
tpd->next_bridge = of_drm_find_bridge(node);
tpd->bridge.next_bridge = of_drm_find_and_get_bridge(node);
of_node_put(node);
if (!tpd->next_bridge)
if (!tpd->bridge.next_bridge)
return -EPROBE_DEFER;
/* Get the control and HPD GPIOs. */

View File

@@ -123,6 +123,14 @@ struct drm_bridge_connector {
* DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER).
*/
struct drm_bridge *bridge_hdmi_cec;
/**
* @hdmi_funcs:
*
* The particular &drm_connector_hdmi_funcs implementation for this
* bridge connector.
*/
struct drm_connector_hdmi_funcs hdmi_funcs;
};
#define to_drm_bridge_connector(x) \
@@ -401,8 +409,7 @@ drm_bridge_connector_tmds_char_rate_valid(const struct drm_connector *connector,
return MODE_OK;
}
static int drm_bridge_connector_clear_infoframe(struct drm_connector *connector,
enum hdmi_infoframe_type type)
static int drm_bridge_connector_clear_avi_infoframe(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
@@ -412,12 +419,11 @@ static int drm_bridge_connector_clear_infoframe(struct drm_connector *connector,
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_clear_infoframe(bridge, type);
return bridge->funcs->hdmi_clear_avi_infoframe(bridge);
}
static int drm_bridge_connector_write_infoframe(struct drm_connector *connector,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
static int drm_bridge_connector_write_avi_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
@@ -427,7 +433,115 @@ static int drm_bridge_connector_write_infoframe(struct drm_connector *connector,
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_write_infoframe(bridge, type, buffer, len);
return bridge->funcs->hdmi_write_avi_infoframe(bridge, buffer, len);
}
static int drm_bridge_connector_clear_hdmi_infoframe(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_clear_hdmi_infoframe(bridge);
}
static int drm_bridge_connector_write_hdmi_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_write_hdmi_infoframe(bridge, buffer, len);
}
static int drm_bridge_connector_clear_audio_infoframe(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_clear_audio_infoframe(bridge);
}
static int drm_bridge_connector_write_audio_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_write_audio_infoframe(bridge, buffer, len);
}
static int drm_bridge_connector_clear_hdr_drm_infoframe(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_clear_hdr_drm_infoframe(bridge);
}
static int drm_bridge_connector_write_hdr_drm_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_write_hdr_drm_infoframe(bridge, buffer, len);
}
static int drm_bridge_connector_clear_spd_infoframe(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_clear_spd_infoframe(bridge);
}
static int drm_bridge_connector_write_spd_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
bridge = bridge_connector->bridge_hdmi;
if (!bridge)
return -EINVAL;
return bridge->funcs->hdmi_write_spd_infoframe(bridge, buffer, len);
}
static const struct drm_edid *
@@ -446,9 +560,31 @@ drm_bridge_connector_read_edid(struct drm_connector *connector)
static const struct drm_connector_hdmi_funcs drm_bridge_connector_hdmi_funcs = {
.tmds_char_rate_valid = drm_bridge_connector_tmds_char_rate_valid,
.clear_infoframe = drm_bridge_connector_clear_infoframe,
.write_infoframe = drm_bridge_connector_write_infoframe,
.read_edid = drm_bridge_connector_read_edid,
.avi = {
.clear_infoframe = drm_bridge_connector_clear_avi_infoframe,
.write_infoframe = drm_bridge_connector_write_avi_infoframe,
},
.hdmi = {
.clear_infoframe = drm_bridge_connector_clear_hdmi_infoframe,
.write_infoframe = drm_bridge_connector_write_hdmi_infoframe,
},
/* audio, hdr_drm and spd are set dynamically during init */
};
static const struct drm_connector_infoframe_funcs drm_bridge_connector_hdmi_audio_infoframe = {
.clear_infoframe = drm_bridge_connector_clear_audio_infoframe,
.write_infoframe = drm_bridge_connector_write_audio_infoframe,
};
static const struct drm_connector_infoframe_funcs drm_bridge_connector_hdmi_hdr_drm_infoframe = {
.clear_infoframe = drm_bridge_connector_clear_hdr_drm_infoframe,
.write_infoframe = drm_bridge_connector_write_hdr_drm_infoframe,
};
static const struct drm_connector_infoframe_funcs drm_bridge_connector_hdmi_spd_infoframe = {
.clear_infoframe = drm_bridge_connector_clear_spd_infoframe,
.write_infoframe = drm_bridge_connector_write_spd_infoframe,
};
static int drm_bridge_connector_audio_startup(struct drm_connector *connector)
@@ -709,8 +845,20 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
if (bridge_connector->bridge_hdmi)
return ERR_PTR(-EBUSY);
if (!bridge->funcs->hdmi_write_infoframe ||
!bridge->funcs->hdmi_clear_infoframe)
if (!bridge->funcs->hdmi_write_avi_infoframe ||
!bridge->funcs->hdmi_clear_avi_infoframe ||
!bridge->funcs->hdmi_write_hdmi_infoframe ||
!bridge->funcs->hdmi_clear_hdmi_infoframe)
return ERR_PTR(-EINVAL);
if (bridge->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME &&
(!bridge->funcs->hdmi_write_hdr_drm_infoframe ||
!bridge->funcs->hdmi_clear_hdr_drm_infoframe))
return ERR_PTR(-EINVAL);
if (bridge->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME &&
(!bridge->funcs->hdmi_write_spd_infoframe ||
!bridge->funcs->hdmi_clear_spd_infoframe))
return ERR_PTR(-EINVAL);
bridge_connector->bridge_hdmi = drm_bridge_get(bridge);
@@ -732,7 +880,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
!bridge->hdmi_audio_spdif_playback)
return ERR_PTR(-EINVAL);
if (!bridge->funcs->hdmi_audio_prepare ||
if (!bridge->funcs->hdmi_write_audio_infoframe ||
!bridge->funcs->hdmi_clear_audio_infoframe ||
!bridge->funcs->hdmi_audio_prepare ||
!bridge->funcs->hdmi_audio_shutdown)
return ERR_PTR(-EINVAL);
@@ -803,11 +953,25 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (!connector->ycbcr_420_allowed)
supported_formats &= ~BIT(HDMI_COLORSPACE_YUV420);
bridge_connector->hdmi_funcs = drm_bridge_connector_hdmi_funcs;
if (bridge_connector->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_AUDIO)
bridge_connector->hdmi_funcs.audio =
drm_bridge_connector_hdmi_audio_infoframe;
if (bridge_connector->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME)
bridge_connector->hdmi_funcs.hdr_drm =
drm_bridge_connector_hdmi_hdr_drm_infoframe;
if (bridge_connector->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME)
bridge_connector->hdmi_funcs.spd =
drm_bridge_connector_hdmi_spd_infoframe;
ret = drmm_connector_hdmi_init(drm, connector,
bridge_connector->bridge_hdmi->vendor,
bridge_connector->bridge_hdmi->product,
&drm_bridge_connector_funcs,
&drm_bridge_connector_hdmi_funcs,
&bridge_connector->hdmi_funcs,
connector_type, ddc,
supported_formats,
max_bpc);

View File

@@ -718,6 +718,9 @@ static int hdmi_generate_spd_infoframe(const struct drm_connector *connector,
infoframe->set = false;
if (!connector->hdmi.funcs->spd.write_infoframe)
return 0;
ret = hdmi_spd_infoframe_init(frame,
connector->hdmi.vendor,
connector->hdmi.product);
@@ -742,6 +745,9 @@ static int hdmi_generate_hdr_infoframe(const struct drm_connector *connector,
infoframe->set = false;
if (!connector->hdmi.funcs->hdr_drm.write_infoframe)
return 0;
if (connector->max_bpc < 10)
return 0;
@@ -891,62 +897,21 @@ drm_hdmi_connector_mode_valid(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_hdmi_connector_mode_valid);
static int clear_device_infoframe(struct drm_connector *connector,
enum hdmi_infoframe_type type)
{
const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
struct drm_device *dev = connector->dev;
int ret;
drm_dbg_kms(dev, "Clearing infoframe type 0x%x\n", type);
if (!funcs || !funcs->clear_infoframe) {
drm_dbg_kms(dev, "Function not implemented, bailing.\n");
return 0;
}
ret = funcs->clear_infoframe(connector, type);
if (ret) {
drm_dbg_kms(dev, "Call failed: %d\n", ret);
return ret;
}
return 0;
}
static int clear_infoframe(struct drm_connector *connector,
struct drm_connector_hdmi_infoframe *old_frame)
const struct drm_connector_infoframe_funcs *funcs,
const char *type)
{
int ret;
ret = clear_device_infoframe(connector, old_frame->data.any.type);
if (ret)
return ret;
return 0;
}
static int write_device_infoframe(struct drm_connector *connector,
union hdmi_infoframe *frame)
{
const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
struct drm_device *dev = connector->dev;
u8 buffer[HDMI_INFOFRAME_SIZE(MAX)];
int ret;
int len;
drm_dbg_kms(dev, "Writing infoframe type %x\n", frame->any.type);
drm_dbg_kms(dev, "Clearing %s InfoFrame\n", type);
if (!funcs || !funcs->write_infoframe) {
if (!funcs->clear_infoframe) {
drm_dbg_kms(dev, "Function not implemented, bailing.\n");
return -EINVAL;
return -EOPNOTSUPP;
}
len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
if (len < 0)
return len;
ret = funcs->write_infoframe(connector, frame->any.type, buffer, len);
ret = funcs->clear_infoframe(connector);
if (ret) {
drm_dbg_kms(dev, "Call failed: %d\n", ret);
return ret;
@@ -956,26 +921,46 @@ static int write_device_infoframe(struct drm_connector *connector,
}
static int write_infoframe(struct drm_connector *connector,
const struct drm_connector_infoframe_funcs *funcs,
const char *type,
struct drm_connector_hdmi_infoframe *new_frame)
{
struct drm_device *dev = connector->dev;
u8 buffer[HDMI_INFOFRAME_SIZE(MAX)];
int ret;
int len;
ret = write_device_infoframe(connector, &new_frame->data);
if (ret)
drm_dbg_kms(dev, "Writing %s InfoFrame\n", type);
if (!funcs->write_infoframe) {
drm_dbg_kms(dev, "Function not implemented, bailing.\n");
return -EOPNOTSUPP;
}
len = hdmi_infoframe_pack(&new_frame->data, buffer, sizeof(buffer));
if (len < 0)
return len;
ret = funcs->write_infoframe(connector, buffer, len);
if (ret) {
drm_dbg_kms(dev, "Call failed: %d\n", ret);
return ret;
}
return 0;
}
static int write_or_clear_infoframe(struct drm_connector *connector,
const struct drm_connector_infoframe_funcs *funcs,
const char *type,
struct drm_connector_hdmi_infoframe *old_frame,
struct drm_connector_hdmi_infoframe *new_frame)
{
if (new_frame->set)
return write_infoframe(connector, new_frame);
return write_infoframe(connector, funcs, type, new_frame);
if (old_frame->set && !new_frame->set)
return clear_infoframe(connector, old_frame);
return clear_infoframe(connector, funcs, type);
return 0;
}
@@ -995,6 +980,7 @@ static int write_or_clear_infoframe(struct drm_connector *connector,
int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector,
struct drm_atomic_state *state)
{
const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
struct drm_connector_state *old_conn_state =
drm_atomic_get_old_connector_state(state, connector);
struct drm_connector_state *new_conn_state =
@@ -1005,9 +991,15 @@ int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *con
if (!info->is_hdmi)
return 0;
if (!funcs) {
drm_dbg_kms(connector->dev, "Function not implemented, bailing.\n");
return -EINVAL;
}
mutex_lock(&connector->hdmi.infoframes.lock);
ret = write_or_clear_infoframe(connector,
&funcs->avi, "AVI",
&old_conn_state->hdmi.infoframes.avi,
&new_conn_state->hdmi.infoframes.avi);
if (ret)
@@ -1015,18 +1007,21 @@ int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *con
if (connector->hdmi.infoframes.audio.set) {
ret = write_infoframe(connector,
&funcs->audio, "Audio",
&connector->hdmi.infoframes.audio);
if (ret)
goto out;
}
ret = write_or_clear_infoframe(connector,
&funcs->hdr_drm, "HDR DRM",
&old_conn_state->hdmi.infoframes.hdr_drm,
&new_conn_state->hdmi.infoframes.hdr_drm);
if (ret)
goto out;
ret = write_or_clear_infoframe(connector,
&funcs->spd, "SPD",
&old_conn_state->hdmi.infoframes.spd,
&new_conn_state->hdmi.infoframes.spd);
if (ret)
@@ -1034,6 +1029,7 @@ int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *con
if (info->has_hdmi_infoframe) {
ret = write_or_clear_infoframe(connector,
&funcs->hdmi, "HDMI-VS",
&old_conn_state->hdmi.infoframes.hdmi,
&new_conn_state->hdmi.infoframes.hdmi);
if (ret)
@@ -1062,6 +1058,7 @@ int
drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector,
struct hdmi_audio_infoframe *frame)
{
const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
struct drm_connector_hdmi_infoframe *infoframe =
&connector->hdmi.infoframes.audio;
struct drm_display_info *info = &connector->display_info;
@@ -1070,12 +1067,17 @@ drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *co
if (!info->is_hdmi)
return 0;
if (!funcs || !funcs->audio.write_infoframe) {
drm_dbg_kms(connector->dev, "Function not implemented, bailing.\n");
return -EINVAL;
}
mutex_lock(&connector->hdmi.infoframes.lock);
memcpy(&infoframe->data, frame, sizeof(infoframe->data));
infoframe->set = true;
ret = write_infoframe(connector, infoframe);
ret = write_infoframe(connector, &funcs->audio, "Audio", infoframe);
mutex_unlock(&connector->hdmi.infoframes.lock);
@@ -1097,6 +1099,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_audio_infoframe);
int
drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *connector)
{
const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
struct drm_connector_hdmi_infoframe *infoframe =
&connector->hdmi.infoframes.audio;
struct drm_display_info *info = &connector->display_info;
@@ -1105,11 +1108,16 @@ drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *con
if (!info->is_hdmi)
return 0;
if (!funcs || !funcs->audio.write_infoframe) {
drm_dbg_kms(connector->dev, "Function not implemented, bailing.\n");
return -EINVAL;
}
mutex_lock(&connector->hdmi.infoframes.lock);
infoframe->set = false;
ret = clear_infoframe(connector, infoframe);
ret = clear_infoframe(connector, &funcs->audio, "Audio");
memset(&infoframe->data, 0, sizeof(infoframe->data));

View File

@@ -1518,11 +1518,14 @@ EXPORT_SYMBOL(of_drm_find_and_get_bridge);
* The bridge returned by this function is not refcounted. This is
* dangerous because the bridge might be deallocated even before the caller
* has a chance to use it. To use this function you have to do one of:
*
* - get a reference with drm_bridge_get() as soon as possible to
* minimize the race window, and then drm_bridge_put() when no longer
* using the pointer
*
* - not call drm_bridge_get() or drm_bridge_put() at all, which used to
* be the correct practice before dynamic bridge lifetime was introduced
*
* - again, convert to of_drm_find_and_get_bridge(), which is the only safe
* thing to do
*

View File

@@ -420,6 +420,7 @@ void drm_buddy_fini(struct drm_buddy *mm)
for_each_free_tree(i)
kfree(mm->free_trees[i]);
kfree(mm->free_trees);
kfree(mm->roots);
}
EXPORT_SYMBOL(drm_buddy_fini);
@@ -1155,6 +1156,15 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
order = fls(pages) - 1;
min_order = ilog2(min_block_size) - ilog2(mm->chunk_size);
if (order > mm->max_order || size > mm->size) {
if ((flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION) &&
!(flags & DRM_BUDDY_RANGE_ALLOCATION))
return __alloc_contig_try_harder(mm, original_size,
original_min_size, blocks);
return -EINVAL;
}
do {
order = min(order, (unsigned int)fls(pages) - 1);
BUG_ON(order > mm->max_order);

View File

@@ -600,6 +600,12 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
return -EINVAL;
if (!hdmi_funcs->avi.clear_infoframe ||
!hdmi_funcs->avi.write_infoframe ||
!hdmi_funcs->hdmi.clear_infoframe ||
!hdmi_funcs->hdmi.write_infoframe)
return -EINVAL;
ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
if (ret)
return ret;

View File

@@ -672,6 +672,10 @@ static int create_hdmi_audio_infoframe_file(struct drm_connector *connector,
{
struct dentry *file;
if (!connector->hdmi.funcs ||
!connector->hdmi.funcs->audio.write_infoframe)
return 0;
file = debugfs_create_file("audio", 0400, parent, connector, &audio_infoframe_fops);
if (IS_ERR(file))
return PTR_ERR(file);
@@ -726,6 +730,9 @@ static int create_hdmi_## _f ## _infoframe_file(struct drm_connector *connector,
{ \
struct dentry *file; \
\
if (!connector->hdmi.funcs || \
!connector->hdmi.funcs->_f.write_infoframe) \
return 0; \
file = debugfs_create_file(#_f, 0400, parent, connector, &_f ## _infoframe_fops); \
if (IS_ERR(file)) \
return PTR_ERR(file); \

View File

@@ -562,7 +562,7 @@ drm_property_create_blob(struct drm_device *dev, size_t length,
if (!length || length > INT_MAX - sizeof(struct drm_property_blob))
return ERR_PTR(-EINVAL);
blob = kvzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
blob = kvzalloc(sizeof(struct drm_property_blob) + length, GFP_KERNEL_ACCOUNT);
if (!blob)
return ERR_PTR(-ENOMEM);

View File

@@ -1779,7 +1779,7 @@ static int hdmi_bridge_init(struct hdmi_context *hdata)
return -EINVAL;
}
hdata->bridge = of_drm_find_bridge(np);
hdata->bridge = of_drm_find_and_get_bridge(np);
of_node_put(np);
if (!hdata->bridge)
@@ -2096,6 +2096,8 @@ static void hdmi_remove(struct platform_device *pdev)
put_device(&hdata->ddc_adpt->dev);
drm_bridge_put(hdata->bridge);
mutex_destroy(&hdata->mutex);
}

View File

@@ -40,6 +40,10 @@ struct hibmc_dp_dev {
struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */
struct hibmc_dp_link link;
u8 dpcd[DP_RECEIVER_CAP_SIZE];
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
struct drm_dp_desc desc;
bool is_branch;
int hpd_status;
void __iomem *serdes_base;
};

View File

@@ -17,5 +17,7 @@
#define HIBMC_DP_LINK_RATE_CAL 27
#define HIBMC_DP_SYNC_DELAY(lanes) ((lanes) == 0x2 ? 86 : 46)
#define HIBMC_DP_INT_ENABLE 0xc
/* HIBMC_DP_LINK_RATE_CAL * 10000 * 80% = 216000 */
#define DP_MODE_VALI_CAL 216000
#endif

View File

@@ -2,6 +2,7 @@
// Copyright (c) 2024 Hisilicon Limited.
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
#include "dp_config.h"
#include "dp_comm.h"
@@ -176,13 +177,18 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp)
dp_dev->link.cap.lanes = 0x2;
dp_dev->link.cap.link_rate = DP_LINK_BW_8_1;
/* hdcp data */
writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG);
/* int init */
writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE);
writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS);
/* clr colorbar */
writel(0, dp_dev->base + HIBMC_DP_COLOR_BAR_CTRL);
/* rst */
writel(0, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL);
usleep_range(30, 50);
/* de-rst */
writel(HIBMC_DP_DPTX_RST, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL);
/* hdcp data */
writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG);
/* clock enable */
writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL);
@@ -263,6 +269,16 @@ void hibmc_dp_reset_link(struct hibmc_dp *dp)
dp->dp_dev->link.status.channel_equalized = false;
}
u8 hibmc_dp_get_link_rate(struct hibmc_dp *dp)
{
return dp->dp_dev->link.cap.link_rate;
}
u8 hibmc_dp_get_lanes(struct hibmc_dp *dp)
{
return dp->dp_dev->link.cap.lanes;
}
static const struct hibmc_dp_color_raw g_rgb_raw[] = {
{CBAR_COLOR_BAR, 0x000, 0x000, 0x000},
{CBAR_WHITE, 0xfff, 0xfff, 0xfff},
@@ -305,3 +321,21 @@ void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg)
hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(0), cfg->enable);
writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
}
bool hibmc_dp_check_hpd_status(struct hibmc_dp *dp, int exp_status)
{
u32 status;
int ret;
ret = readl_poll_timeout(dp->dp_dev->base + HIBMC_DP_HPD_STATUS, status,
FIELD_GET(HIBMC_DP_HPD_CUR_STATE, status) == exp_status,
1000, 100000); /* DP spec says 100ms */
if (ret) {
drm_dbg_dp(dp->drm_dev, "wait hpd status timeout");
return false;
}
dp->dp_dev->hpd_status = exp_status;
return true;
}

View File

@@ -14,6 +14,11 @@
struct hibmc_dp_dev;
enum hibmc_hpd_status {
HIBMC_HPD_OUT,
HIBMC_HPD_IN,
};
enum hibmc_dp_cbar_pattern {
CBAR_COLOR_BAR,
CBAR_WHITE,
@@ -60,5 +65,8 @@ void hibmc_dp_reset_link(struct hibmc_dp *dp);
void hibmc_dp_hpd_cfg(struct hibmc_dp *dp);
void hibmc_dp_enable_int(struct hibmc_dp *dp);
void hibmc_dp_disable_int(struct hibmc_dp *dp);
bool hibmc_dp_check_hpd_status(struct hibmc_dp *dp, int exp_status);
u8 hibmc_dp_get_link_rate(struct hibmc_dp *dp);
u8 hibmc_dp_get_lanes(struct hibmc_dp *dp);
#endif

View File

@@ -24,6 +24,9 @@
#define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12)
#define HIBMC_DP_CFG_AUX GENMASK(24, 17)
#define HIBMC_DP_HPD_STATUS 0x98
#define HIBMC_DP_HPD_CUR_STATE GENMASK(7, 4)
#define HIBMC_DP_PHYIF_CTRL0 0xa0
#define HIBMC_DP_CFG_SCRAMBLE_EN BIT(0)
#define HIBMC_DP_CFG_PAT_SEL GENMASK(7, 4)

View File

@@ -12,6 +12,8 @@
#include "hibmc_drm_drv.h"
#include "dp/dp_hw.h"
#include "dp/dp_comm.h"
#include "dp/dp_config.h"
#define DP_MASKED_SINK_HPD_PLUG_INT BIT(2)
@@ -31,17 +33,76 @@ static int hibmc_dp_connector_get_modes(struct drm_connector *connector)
return count;
}
static bool hibmc_dp_get_dpcd(struct hibmc_dp_dev *dp_dev)
{
int ret;
ret = drm_dp_read_dpcd_caps(dp_dev->aux, dp_dev->dpcd);
if (ret)
return false;
dp_dev->is_branch = drm_dp_is_branch(dp_dev->dpcd);
ret = drm_dp_read_desc(dp_dev->aux, &dp_dev->desc, dp_dev->is_branch);
if (ret)
return false;
ret = drm_dp_read_downstream_info(dp_dev->aux, dp_dev->dpcd, dp_dev->downstream_ports);
if (ret)
return false;
return true;
}
static int hibmc_dp_detect(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx, bool force)
{
mdelay(200);
struct hibmc_dp *dp = to_hibmc_dp(connector);
struct hibmc_dp_dev *dp_dev = dp->dp_dev;
int ret;
return drm_connector_helper_detect_from_ddc(connector, ctx, force);
if (dp->irq_status) {
if (dp_dev->hpd_status != HIBMC_HPD_IN)
return connector_status_disconnected;
}
if (!hibmc_dp_get_dpcd(dp_dev))
return connector_status_disconnected;
if (!dp_dev->is_branch)
return connector_status_connected;
if (drm_dp_read_sink_count_cap(connector, dp_dev->dpcd, &dp_dev->desc) &&
dp_dev->downstream_ports[0] & DP_DS_PORT_HPD) {
ret = drm_dp_read_sink_count(dp_dev->aux);
if (ret > 0)
return connector_status_connected;
}
return connector_status_disconnected;
}
static int hibmc_dp_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_modeset_acquire_ctx *ctx,
enum drm_mode_status *status)
{
struct hibmc_dp *dp = to_hibmc_dp(connector);
u64 cur_val, max_val;
/* check DP link BW */
cur_val = (u64)mode->clock * HIBMC_DP_BPP;
max_val = (u64)hibmc_dp_get_link_rate(dp) * DP_MODE_VALI_CAL * hibmc_dp_get_lanes(dp);
*status = cur_val > max_val ? MODE_CLOCK_HIGH : MODE_OK;
return 0;
}
static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = {
.get_modes = hibmc_dp_connector_get_modes,
.detect_ctx = hibmc_dp_detect,
.mode_valid_ctx = hibmc_dp_mode_valid,
};
static int hibmc_dp_late_register(struct drm_connector *connector)
@@ -115,7 +176,7 @@ irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *)arg;
struct hibmc_drm_private *priv = to_hibmc_drm_private(dev);
int idx;
int idx, exp_status;
if (!drm_dev_enter(dev, &idx))
return -ENODEV;
@@ -123,12 +184,14 @@ irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg)
if (priv->dp.irq_status & DP_MASKED_SINK_HPD_PLUG_INT) {
drm_dbg_dp(&priv->dev, "HPD IN isr occur!\n");
hibmc_dp_hpd_cfg(&priv->dp);
exp_status = HIBMC_HPD_IN;
} else {
drm_dbg_dp(&priv->dev, "HPD OUT isr occur!\n");
hibmc_dp_reset_link(&priv->dp);
exp_status = HIBMC_HPD_OUT;
}
if (dev->registered)
if (hibmc_dp_check_hpd_status(&priv->dp, exp_status))
drm_connector_helper_hpd_irq_event(&priv->dp.connector);
drm_dev_exit(idx);

View File

@@ -18,3 +18,15 @@ config DRM_POWERVR
Technologies PowerVR (Series 6 or later) or IMG GPU.
If "M" is selected, the module will be called powervr.
config DRM_POWERVR_KUNIT_TEST
tristate "KUnit tests for the drm powervr driver" if !KUNIT_ALL_TESTS
depends on DRM_POWERVR && KUNIT
default KUNIT_ALL_TESTS
help
Choose this option to allow the driver to perform selftests under
the kunit framework
Recommended for driver developers only.
If in doubt, say "N".

View File

@@ -20,7 +20,6 @@ powervr-y := \
pvr_hwrt.o \
pvr_job.o \
pvr_mmu.o \
pvr_params.o \
pvr_power.o \
pvr_queue.o \
pvr_stream.o \
@@ -33,3 +32,5 @@ powervr-$(CONFIG_DEBUG_FS) += \
pvr_debugfs.o
obj-$(CONFIG_DRM_POWERVR) += powervr.o
obj-$(CONFIG_DRM_POWERVR_KUNIT_TEST) += pvr_test.o

View File

@@ -5,7 +5,6 @@
#include "pvr_device.h"
#include "pvr_fw_trace.h"
#include "pvr_params.h"
#include <linux/dcache.h>
#include <linux/debugfs.h>
@@ -18,7 +17,6 @@
#include <drm/drm_print.h>
static const struct pvr_debugfs_entry pvr_debugfs_entries[] = {
{"pvr_params", pvr_params_debugfs_init},
{"pvr_fw", pvr_fw_trace_debugfs_init},
};

View File

@@ -5,7 +5,6 @@
#include "pvr_device_info.h"
#include "pvr_fw.h"
#include "pvr_params.h"
#include "pvr_power.h"
#include "pvr_queue.h"
#include "pvr_rogue_cr_defs.h"
@@ -32,6 +31,8 @@
#include <linux/types.h>
#include <linux/workqueue.h>
#include <kunit/visibility.h>
/* Major number for the supported version of the firmware. */
#define PVR_FW_VERSION_MAJOR 1
@@ -422,23 +423,21 @@ err_free_filename:
}
/**
* pvr_load_gpu_id() - Load a PowerVR device's GPU ID (BVNC) from control registers.
* pvr_gpuid_decode_reg() - Decode the GPU ID from GPU register
*
* Sets struct pvr_dev.gpu_id.
* Sets the b, v, n, c fields of struct pvr_dev.gpu_id.
*
* @pvr_dev: Target PowerVR device.
* @gpu_id: Output to be updated with the GPU ID.
*/
static void
pvr_load_gpu_id(struct pvr_device *pvr_dev)
pvr_gpuid_decode_reg(const struct pvr_device *pvr_dev, struct pvr_gpu_id *gpu_id)
{
struct pvr_gpu_id *gpu_id = &pvr_dev->gpu_id;
u64 bvnc;
/*
* Try reading the BVNC using the newer (cleaner) method first. If the
* B value is zero, fall back to the older method.
*/
bvnc = pvr_cr_read64(pvr_dev, ROGUE_CR_CORE_ID__PBVNC);
u64 bvnc = pvr_cr_read64(pvr_dev, ROGUE_CR_CORE_ID__PBVNC);
gpu_id->b = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__BRANCH_ID);
if (gpu_id->b != 0) {
@@ -457,6 +456,179 @@ pvr_load_gpu_id(struct pvr_device *pvr_dev)
}
}
/**
* pvr_gpuid_decode_string() - Decode the GPU ID from a module input string
*
* Sets the b, v, n, c fields of struct pvr_dev.gpu_id.
*
* @pvr_dev: Target PowerVR device.
* @param_bvnc: GPU ID (BVNC) module parameter.
* @gpu_id: Output to be updated with the GPU ID.
*/
VISIBLE_IF_KUNIT int
pvr_gpuid_decode_string(const struct pvr_device *pvr_dev,
const char *param_bvnc, struct pvr_gpu_id *gpu_id)
{
const struct drm_device *drm_dev = &pvr_dev->base;
char str_cpy[PVR_GPUID_STRING_MAX_LENGTH];
char *pos, *tkn;
int ret, idx = 0;
u16 user_bvnc_u16[4];
u8 dot_cnt = 0;
ret = strscpy(str_cpy, param_bvnc);
/*
* strscpy() should return at least a size 7 for the input to be valid.
* Returns -E2BIG for the case when the string is empty or too long.
*/
if (ret < PVR_GPUID_STRING_MIN_LENGTH) {
drm_info(drm_dev,
"Invalid size of the input GPU ID (BVNC): %s",
str_cpy);
return -EINVAL;
}
while (*param_bvnc) {
if (*param_bvnc == '.')
dot_cnt++;
param_bvnc++;
}
if (dot_cnt != 3) {
drm_info(drm_dev,
"Invalid format of the input GPU ID (BVNC): %s",
str_cpy);
return -EINVAL;
}
pos = str_cpy;
while ((tkn = strsep(&pos, ".")) != NULL && idx < 4) {
/* kstrtou16() will also handle the case of consecutive dots */
ret = kstrtou16(tkn, 10, &user_bvnc_u16[idx]);
if (ret) {
drm_info(drm_dev,
"Invalid format of the input GPU ID (BVNC): %s",
str_cpy);
return -EINVAL;
}
idx++;
}
gpu_id->b = user_bvnc_u16[0];
gpu_id->v = user_bvnc_u16[1];
gpu_id->n = user_bvnc_u16[2];
gpu_id->c = user_bvnc_u16[3];
return 0;
}
EXPORT_SYMBOL_IF_KUNIT(pvr_gpuid_decode_string);
static bool pvr_exp_hw_support;
module_param_named(exp_hw_support, pvr_exp_hw_support, bool, 0600);
MODULE_PARM_DESC(exp_hw_support, "Bypass runtime checks for fully supported GPU cores. WARNING: enabling this option may result in a buggy, insecure, or otherwise unusable driver.");
/**
* enum pvr_gpu_support_level - The level of support for a gpu_id in the current
* version of the driver.
*
* @PVR_GPU_UNKNOWN: Cores that are unknown to the driver. These may not even exist.
* @PVR_GPU_EXPERIMENTAL: Cores that have experimental support.
* @PVR_GPU_SUPPORTED: Cores that are supported and maintained.
*/
enum pvr_gpu_support_level {
PVR_GPU_UNKNOWN,
PVR_GPU_EXPERIMENTAL,
PVR_GPU_SUPPORTED,
};
static enum pvr_gpu_support_level
pvr_gpu_support_level(const struct pvr_gpu_id *gpu_id)
{
switch (pvr_gpu_id_to_packed_bvnc(gpu_id)) {
case PVR_PACKED_BVNC(33, 15, 11, 3):
case PVR_PACKED_BVNC(36, 53, 104, 796):
return PVR_GPU_SUPPORTED;
case PVR_PACKED_BVNC(36, 52, 104, 182):
return PVR_GPU_EXPERIMENTAL;
default:
return PVR_GPU_UNKNOWN;
}
}
static int
pvr_check_gpu_supported(struct pvr_device *pvr_dev,
const struct pvr_gpu_id *gpu_id)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
switch (pvr_gpu_support_level(gpu_id)) {
case PVR_GPU_SUPPORTED:
if (pvr_exp_hw_support)
drm_info(drm_dev, "Module parameter 'exp_hw_support' was set, but this hardware is fully supported by the current driver.");
break;
case PVR_GPU_EXPERIMENTAL:
if (!pvr_exp_hw_support) {
drm_err(drm_dev, "Unsupported GPU! Set 'exp_hw_support' to bypass this check.");
return -ENODEV;
}
drm_warn(drm_dev, "Running on unsupported hardware; you may encounter bugs!");
break;
/* NOTE: This code path may indicate misbehaving hardware. */
case PVR_GPU_UNKNOWN:
default:
if (!pvr_exp_hw_support) {
drm_err(drm_dev, "Unknown GPU! Set 'exp_hw_support' to bypass this check.");
return -ENODEV;
}
drm_warn(drm_dev, "Running on unknown hardware; expect issues.");
break;
}
return 0;
}
static char *pvr_gpuid_override;
module_param_named(gpuid, pvr_gpuid_override, charp, 0400);
MODULE_PARM_DESC(gpuid, "GPU ID (BVNC) to be used instead of the value read from hardware.");
/**
* pvr_load_gpu_id() - Load a PowerVR device's GPU ID (BVNC) from control
* registers or input parameter. The input parameter is processed instead
* of the GPU register if provided.
*
* Sets the arch field of struct pvr_dev.gpu_id.
*
* @pvr_dev: Target PowerVR device.
*/
static int
pvr_load_gpu_id(struct pvr_device *pvr_dev)
{
struct pvr_gpu_id *gpu_id = &pvr_dev->gpu_id;
if (!pvr_gpuid_override || !pvr_gpuid_override[0]) {
pvr_gpuid_decode_reg(pvr_dev, gpu_id);
} else {
drm_warn(from_pvr_device(pvr_dev),
"Using custom GPU ID (BVNC) provided by the user!");
int err = pvr_gpuid_decode_string(pvr_dev, pvr_gpuid_override,
gpu_id);
if (err)
return err;
}
return pvr_check_gpu_supported(pvr_dev, gpu_id);
}
/**
* pvr_set_dma_info() - Set PowerVR device DMA information
* @pvr_dev: Target PowerVR device.
@@ -517,7 +689,9 @@ pvr_device_gpu_init(struct pvr_device *pvr_dev)
{
int err;
pvr_load_gpu_id(pvr_dev);
err = pvr_load_gpu_id(pvr_dev);
if (err)
return err;
err = pvr_request_firmware(pvr_dev);
if (err)
@@ -607,14 +781,6 @@ pvr_device_init(struct pvr_device *pvr_dev)
/* Get the platform-specific data based on the compatible string. */
pvr_dev->device_data = of_device_get_match_data(dev);
/*
* Setup device parameters. We do this first in case other steps
* depend on them.
*/
err = pvr_device_params_init(&pvr_dev->params);
if (err)
return err;
/* Enable and initialize clocks required for the device to operate. */
err = pvr_device_clk_init(pvr_dev);
if (err)

View File

@@ -7,7 +7,6 @@
#include "pvr_ccb.h"
#include "pvr_device_info.h"
#include "pvr_fw.h"
#include "pvr_params.h"
#include "pvr_rogue_fwif_stream.h"
#include "pvr_stream.h"
@@ -40,6 +39,9 @@ struct firmware;
/* Forward declaration from <linux/pwrseq/consumer.h> */
struct pwrseq_desc;
#define PVR_GPUID_STRING_MIN_LENGTH 7U
#define PVR_GPUID_STRING_MAX_LENGTH 32U
/**
* struct pvr_gpu_id - Hardware GPU ID information for a PowerVR device
* @b: Branch ID.
@@ -192,15 +194,6 @@ struct pvr_device {
/** @fw_dev: Firmware related data. */
struct pvr_fw_device fw_dev;
/**
* @params: Device-specific parameters.
*
* The values of these parameters are initialized from the
* defaults specified as module parameters. They may be
* modified at runtime via debugfs (if enabled).
*/
struct pvr_device_params params;
/** @stream_musthave_quirks: Bit array of "must-have" quirks for stream commands. */
u32 stream_musthave_quirks[PVR_STREAM_TYPE_MAX][PVR_STREAM_EXTHDR_TYPE_MAX];
@@ -526,7 +519,7 @@ struct pvr_file {
* Return: Packed BVNC.
*/
static __always_inline u64
pvr_gpu_id_to_packed_bvnc(struct pvr_gpu_id *gpu_id)
pvr_gpu_id_to_packed_bvnc(const struct pvr_gpu_id *gpu_id)
{
return PVR_PACKED_BVNC(gpu_id->b, gpu_id->v, gpu_id->n, gpu_id->c);
}
@@ -551,6 +544,11 @@ pvr_device_has_uapi_enhancement(struct pvr_device *pvr_dev, u32 enhancement);
bool
pvr_device_has_feature(struct pvr_device *pvr_dev, u32 feature);
#if IS_ENABLED(CONFIG_KUNIT)
int pvr_gpuid_decode_string(const struct pvr_device *pvr_dev,
const char *param_bvnc, struct pvr_gpu_id *gpu_id);
#endif
/**
* PVR_CR_FIELD_GET() - Extract a single field from a PowerVR control register
* @val: Value of the target register.
@@ -568,7 +566,7 @@ pvr_device_has_feature(struct pvr_device *pvr_dev, u32 feature);
* Return: The value of the requested register.
*/
static __always_inline u32
pvr_cr_read32(struct pvr_device *pvr_dev, u32 reg)
pvr_cr_read32(const struct pvr_device *pvr_dev, u32 reg)
{
return ioread32(pvr_dev->regs + reg);
}
@@ -581,7 +579,7 @@ pvr_cr_read32(struct pvr_device *pvr_dev, u32 reg)
* Return: The value of the requested register.
*/
static __always_inline u64
pvr_cr_read64(struct pvr_device *pvr_dev, u32 reg)
pvr_cr_read64(const struct pvr_device *pvr_dev, u32 reg)
{
return ioread64(pvr_dev->regs + reg);
}

View File

@@ -12,11 +12,77 @@
#include <drm/drm_print.h>
#include <linux/build_bug.h>
#include <linux/compiler_attributes.h>
#include <linux/dcache.h>
#include <linux/debugfs.h>
#include <linux/moduleparam.h>
#include <linux/sysfs.h>
#include <linux/types.h>
static int
validate_group_mask(struct pvr_device *pvr_dev, const u32 group_mask)
{
if (group_mask & ~ROGUE_FWIF_LOG_TYPE_GROUP_MASK) {
drm_warn(from_pvr_device(pvr_dev),
"Invalid fw_trace group mask 0x%08x (must be a subset of 0x%08x)",
group_mask, ROGUE_FWIF_LOG_TYPE_GROUP_MASK);
return -EINVAL;
}
return 0;
}
static inline u32
build_log_type(const u32 group_mask)
{
if (!group_mask)
return ROGUE_FWIF_LOG_TYPE_NONE;
return group_mask | ROGUE_FWIF_LOG_TYPE_TRACE;
}
/*
* Don't gate this behind CONFIG_DEBUG_FS so that it can be used as an initial
* value without further conditional code...
*/
static u32 pvr_fw_trace_init_mask;
/*
* ...but do only expose the module parameter if debugfs is enabled, since
* there's no reason to turn on fw_trace without it.
*/
#if IS_ENABLED(CONFIG_DEBUG_FS)
static int
pvr_fw_trace_init_mask_set(const char *val, const struct kernel_param *kp)
{
u32 mask = 0;
int err;
err = kstrtouint(val, 0, &mask);
if (err)
return err;
err = validate_group_mask(NULL, mask);
if (err)
return err;
*(unsigned int *)kp->arg = mask;
return 0;
}
const struct kernel_param_ops pvr_fw_trace_init_mask_ops = {
.set = pvr_fw_trace_init_mask_set,
.get = param_get_hexint,
};
param_check_hexint(init_fw_trace_mask, &pvr_fw_trace_init_mask);
module_param_cb(init_fw_trace_mask, &pvr_fw_trace_init_mask_ops, &pvr_fw_trace_init_mask, 0600);
__MODULE_PARM_TYPE(init_fw_trace_mask, "hexint");
MODULE_PARM_DESC(init_fw_trace_mask,
"Enable FW trace for the specified groups at device init time");
#endif
static void
tracebuf_ctrl_init(void *cpu_ptr, void *priv)
{
@@ -25,11 +91,7 @@ tracebuf_ctrl_init(void *cpu_ptr, void *priv)
tracebuf_ctrl->tracebuf_size_in_dwords = ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS;
tracebuf_ctrl->tracebuf_flags = 0;
if (fw_trace->group_mask)
tracebuf_ctrl->log_type = fw_trace->group_mask | ROGUE_FWIF_LOG_TYPE_TRACE;
else
tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE;
tracebuf_ctrl->log_type = build_log_type(fw_trace->group_mask);
for (u32 thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
struct rogue_fwif_tracebuf_space *tracebuf_space =
@@ -68,8 +130,13 @@ int pvr_fw_trace_init(struct pvr_device *pvr_dev)
}
}
/* TODO: Provide control of group mask. */
fw_trace->group_mask = 0;
/*
* Load the initial group_mask from the init_fw_trace_mask module
* parameter. This allows early tracing before the user can write to
* debugfs. Unlike update_logtype(), we don't set log_type here as that
* is initialised by tracebuf_ctrl_init().
*/
fw_trace->group_mask = pvr_fw_trace_init_mask;
fw_trace->tracebuf_ctrl =
pvr_fw_object_create_and_map(pvr_dev,
@@ -123,9 +190,11 @@ void pvr_fw_trace_fini(struct pvr_device *pvr_dev)
/**
* update_logtype() - Send KCCB command to trigger FW to update logtype
* @pvr_dev: Target PowerVR device
* @group_mask: New log group mask.
* @group_mask: New log group mask; must pass validate_group_mask().
*
* Returns:
* * 0 if the provided @group_mask is the same as the current value (this is a
* short-circuit evaluation),
* * 0 on success,
* * Any error returned by pvr_kccb_send_cmd(), or
* * -%EIO if the device is lost.
@@ -134,19 +203,20 @@ static int
update_logtype(struct pvr_device *pvr_dev, u32 group_mask)
{
struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
struct rogue_fwif_kccb_cmd cmd;
int idx;
int err;
if (group_mask)
fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_TRACE | group_mask;
else
fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE;
/* No change in group_mask => nothing to update. */
if (fw_trace->group_mask == group_mask)
return 0;
fw_trace->group_mask = group_mask;
fw_trace->tracebuf_ctrl->log_type = build_log_type(group_mask);
down_read(&pvr_dev->reset_sem);
if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx)) {
if (!drm_dev_enter(drm_dev, &idx)) {
err = -EIO;
goto err_up_read;
}
@@ -437,13 +507,31 @@ static const struct file_operations pvr_fw_trace_fops = {
.release = fw_trace_release,
};
void
pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask, u32 new_mask)
static int pvr_fw_trace_mask_get(void *data, u64 *value)
{
if (IS_ENABLED(CONFIG_DEBUG_FS) && old_mask != new_mask)
update_logtype(pvr_dev, new_mask);
struct pvr_device *pvr_dev = data;
*value = pvr_dev->fw_dev.fw_trace.group_mask;
return 0;
}
static int pvr_fw_trace_mask_set(void *data, u64 value)
{
struct pvr_device *pvr_dev = data;
const u32 group_mask = (u32)value;
int err;
err = validate_group_mask(pvr_dev, group_mask);
if (err)
return err;
return update_logtype(pvr_dev, group_mask);
}
DEFINE_DEBUGFS_ATTRIBUTE(pvr_fw_trace_mask_fops, pvr_fw_trace_mask_get,
pvr_fw_trace_mask_set, "0x%08llx\n");
void
pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir)
{
@@ -463,4 +551,7 @@ pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir)
&fw_trace->buffers[thread_nr],
&pvr_fw_trace_fops);
}
debugfs_create_file("trace_mask", 0600, dir, fw_trace,
&pvr_fw_trace_mask_fops);
}

View File

@@ -68,9 +68,6 @@ void pvr_fw_trace_fini(struct pvr_device *pvr_dev);
/* Forward declaration from <linux/dcache.h>. */
struct dentry;
void pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask,
u32 new_mask);
void pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir);
#endif /* PVR_FW_TRACE_H */

View File

@@ -1,147 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_params.h"
#include <linux/cache.h>
#include <linux/moduleparam.h>
static struct pvr_device_params pvr_device_param_defaults __read_mostly = {
#define X(type_, name_, value_, desc_, ...) .name_ = (value_),
PVR_DEVICE_PARAMS
#undef X
};
#define PVR_DEVICE_PARAM_NAMED(name_, type_, desc_) \
module_param_named(name_, pvr_device_param_defaults.name_, type_, \
0400); \
MODULE_PARM_DESC(name_, desc_);
/*
* This list of defines must contain every type specified in "pvr_params.h" as
* ``PVR_PARAM_TYPE_*_C``.
*/
#define PVR_PARAM_TYPE_X32_MODPARAM uint
#define X(type_, name_, value_, desc_, ...) \
PVR_DEVICE_PARAM_NAMED(name_, PVR_PARAM_TYPE_##type_##_MODPARAM, desc_);
PVR_DEVICE_PARAMS
#undef X
int
pvr_device_params_init(struct pvr_device_params *params)
{
/*
* If heap-allocated parameters are added in the future (e.g.
* modparam's charp type), they must be handled specially here (via
* kstrdup() in the case of charp). Since that's not necessary yet,
* a straight copy will do for now. This change will also require a
* pvr_device_params_fini() function to free any heap-allocated copies.
*/
*params = pvr_device_param_defaults;
return 0;
}
#if defined(CONFIG_DEBUG_FS)
#include "pvr_device.h"
#include <linux/dcache.h>
#include <linux/debugfs.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/stddef.h>
/*
* This list of defines must contain every type specified in "pvr_params.h" as
* ``PVR_PARAM_TYPE_*_C``.
*/
#define PVR_PARAM_TYPE_X32_FMT "0x%08llx"
#define X_SET(name_, mode_) X_SET_##mode_(name_)
#define X_SET_DEF(name_, update_, mode_) X_SET_DEF_##mode_(name_, update_)
#define X_SET_RO(name_) NULL
#define X_SET_RW(name_) __pvr_device_param_##name_##set
#define X_SET_DEF_RO(name_, update_)
#define X_SET_DEF_RW(name_, update_) \
static int \
X_SET_RW(name_)(void *data, u64 val) \
{ \
struct pvr_device *pvr_dev = data; \
/* This is not just (update_) to suppress -Waddress. */ \
if ((void *)(update_) != NULL) \
(update_)(pvr_dev, pvr_dev->params.name_, val); \
pvr_dev->params.name_ = val; \
return 0; \
}
#define X(type_, name_, value_, desc_, mode_, update_) \
static int \
__pvr_device_param_##name_##_get(void *data, u64 *val) \
{ \
struct pvr_device *pvr_dev = data; \
*val = pvr_dev->params.name_; \
return 0; \
} \
X_SET_DEF(name_, update_, mode_) \
static int \
__pvr_device_param_##name_##_open(struct inode *inode, \
struct file *file) \
{ \
__simple_attr_check_format(PVR_PARAM_TYPE_##type_##_FMT, \
0ull); \
return simple_attr_open(inode, file, \
__pvr_device_param_##name_##_get, \
X_SET(name_, mode_), \
PVR_PARAM_TYPE_##type_##_FMT); \
}
PVR_DEVICE_PARAMS
#undef X
#undef X_SET
#undef X_SET_RO
#undef X_SET_RW
#undef X_SET_DEF
#undef X_SET_DEF_RO
#undef X_SET_DEF_RW
static struct {
#define X(type_, name_, value_, desc_, mode_, update_) \
const struct file_operations name_;
PVR_DEVICE_PARAMS
#undef X
} pvr_device_param_debugfs_fops = {
#define X(type_, name_, value_, desc_, mode_, update_) \
.name_ = { \
.owner = THIS_MODULE, \
.open = __pvr_device_param_##name_##_open, \
.release = simple_attr_release, \
.read = simple_attr_read, \
.write = simple_attr_write, \
.llseek = generic_file_llseek, \
},
PVR_DEVICE_PARAMS
#undef X
};
void
pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir)
{
#define X_MODE(mode_) X_MODE_##mode_
#define X_MODE_RO 0400
#define X_MODE_RW 0600
#define X(type_, name_, value_, desc_, mode_, update_) \
debugfs_create_file(#name_, X_MODE(mode_), dir, pvr_dev, \
&pvr_device_param_debugfs_fops.name_);
PVR_DEVICE_PARAMS
#undef X
#undef X_MODE
#undef X_MODE_RO
#undef X_MODE_RW
}
#endif

View File

@@ -1,72 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_PARAMS_H
#define PVR_PARAMS_H
#include "pvr_rogue_fwif.h"
#include <linux/cache.h>
#include <linux/compiler_attributes.h>
/*
* This is the definitive list of types allowed in the definition of
* %PVR_DEVICE_PARAMS.
*/
#define PVR_PARAM_TYPE_X32_C u32
/*
* This macro defines all device-specific parameters; that is parameters which
* are set independently per device.
*
* The X-macro accepts the following arguments. Arguments marked with [debugfs]
* are ignored when debugfs is disabled; values used for these arguments may
* safely be gated behind CONFIG_DEBUG_FS.
*
* @type_: The definitive list of allowed values is PVR_PARAM_TYPE_*_C.
* @name_: Name of the parameter. This is used both as the field name in C and
* stringified as the parameter name.
* @value_: Initial/default value.
* @desc_: String literal used as help text to describe the usage of this
* parameter.
* @mode_: [debugfs] One of {RO,RW}. The access mode of the debugfs entry for
* this parameter.
* @update_: [debugfs] When debugfs support is enabled, parameters may be
* updated at runtime. When this happens, this function will be
* called to allow changes to propagate. The signature of this
* function is:
*
* void (*)(struct pvr_device *pvr_dev, T old_val, T new_val)
*
* Where T is the C type associated with @type_.
*
* If @mode_ does not allow write access, this function will never be
* called. In this case, or if no update callback is required, you
* should specify NULL for this argument.
*/
#define PVR_DEVICE_PARAMS \
X(X32, fw_trace_mask, ROGUE_FWIF_LOG_TYPE_NONE, \
"Enable FW trace for the specified groups. Specifying 0 disables " \
"all FW tracing.", \
RW, pvr_fw_trace_mask_update)
struct pvr_device_params {
#define X(type_, name_, value_, desc_, ...) \
PVR_PARAM_TYPE_##type_##_C name_;
PVR_DEVICE_PARAMS
#undef X
};
int pvr_device_params_init(struct pvr_device_params *params);
#if defined(CONFIG_DEBUG_FS)
/* Forward declaration from "pvr_device.h". */
struct pvr_device;
/* Forward declaration from <linux/dcache.h>. */
struct dentry;
void pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir);
#endif /* defined(CONFIG_DEBUG_FS) */
#endif /* PVR_PARAMS_H */

View File

@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2025 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include <linux/errno.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <kunit/test.h>
#include <kunit/visibility.h>
static void decode_gpuid_string(struct kunit *test)
{
const struct pvr_gpu_id bad_gpuid = { 0xdead, 0xbeef, 0xcafe, 0xface };
const u64 packed_bad_gpuid = pvr_gpu_id_to_packed_bvnc(&bad_gpuid);
#define GPUID_TEST_CASE(str_, err_, value_) \
do { \
struct pvr_gpu_id _gpuid_out = bad_gpuid; \
int _err; \
_err = pvr_gpuid_decode_string(NULL, str_, &_gpuid_out); \
KUNIT_EXPECT_EQ(test, _err, err_); \
KUNIT_EXPECT_EQ(test, \
pvr_gpu_id_to_packed_bvnc(&_gpuid_out), \
value_); \
} while (0)
#define GPUID_TEST_CASE_OK(str_, b_, v_, n_, c_) \
GPUID_TEST_CASE(str_, 0, PVR_PACKED_BVNC(b_, v_, n_, c_))
#define GPUID_TEST_CASE_INVAL(str_) \
GPUID_TEST_CASE(str_, -EINVAL, packed_bad_gpuid)
GPUID_TEST_CASE_OK("12.34.56.78", 12, 34, 56, 78);
GPUID_TEST_CASE_OK("0.0.0.0", 0, 0, 0, 0);
GPUID_TEST_CASE_INVAL("");
GPUID_TEST_CASE_INVAL("42.foobar-invalid.gpuid.bvnc");
/* String longer than PVR_GPUID_STRING_MAX_LENGTH. */
GPUID_TEST_CASE_INVAL("12.34.56.789012345678901234567890123456");
/* Single value overflowing u16. */
GPUID_TEST_CASE_INVAL("12.34.56.999999");
/* Wrong number of parts and/or dots. */
GPUID_TEST_CASE_INVAL("12.34.56.78.90");
GPUID_TEST_CASE_INVAL("12.34.56..78");
GPUID_TEST_CASE_INVAL("12.34..56");
GPUID_TEST_CASE_INVAL("12.34.56");
#undef GPUID_TEST_CASE_INVAL
#undef GPUID_TEST_CASE_OK
#undef GPUID_TEST_CASE
}
static struct kunit_case pvr_tests_cases[] = {
KUNIT_CASE(decode_gpuid_string),
{},
};
static struct kunit_suite pvr_tests_suite = {
.name = "pvr_tests",
.test_cases = pvr_tests_cases,
};
kunit_test_suite(pvr_tests_suite);
MODULE_AUTHOR("Imagination Technologies Ltd.");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("pvr kunit tests");
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");

View File

@@ -241,7 +241,7 @@ static int dw_hdmi_imx_probe(struct platform_device *pdev)
if (IS_ERR(hdmi->hdmi))
return PTR_ERR(hdmi->hdmi);
hdmi->bridge = of_drm_find_bridge(np);
hdmi->bridge = of_drm_find_and_get_bridge(np);
if (!hdmi->bridge) {
dev_err(hdmi->dev, "Unable to find bridge\n");
dw_hdmi_remove(hdmi->hdmi);
@@ -249,8 +249,10 @@ static int dw_hdmi_imx_probe(struct platform_device *pdev)
}
ret = component_add(&pdev->dev, &dw_hdmi_imx_ops);
if (ret)
if (ret) {
drm_bridge_put(hdmi->bridge);
dw_hdmi_remove(hdmi->hdmi);
}
return ret;
}
@@ -260,6 +262,7 @@ static void dw_hdmi_imx_remove(struct platform_device *pdev)
struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
component_del(&pdev->dev, &dw_hdmi_imx_ops);
drm_bridge_put(hdmi->bridge);
dw_hdmi_remove(hdmi->hdmi);
}

View File

@@ -986,8 +986,8 @@ static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge,
return -EINVAL;
}
if (hdmi->next_bridge) {
ret = drm_bridge_attach(encoder, hdmi->next_bridge,
if (hdmi->bridge.next_bridge) {
ret = drm_bridge_attach(encoder, hdmi->bridge.next_bridge,
bridge, flags);
if (ret)
return ret;

View File

@@ -315,8 +315,8 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device
return -EINVAL;
if (!of_device_is_compatible(remote, "hdmi-connector")) {
hdmi->next_bridge = of_drm_find_bridge(remote);
if (!hdmi->next_bridge) {
hdmi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
if (!hdmi->bridge.next_bridge) {
dev_err(dev, "Waiting for external bridge\n");
of_node_put(remote);
return -EPROBE_DEFER;
@@ -433,9 +433,11 @@ struct mtk_hdmi *mtk_hdmi_common_probe(struct platform_device *pdev)
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
| DRM_BRIDGE_OP_HPD;
if (ver_conf->bridge_funcs->hdmi_write_infoframe &&
ver_conf->bridge_funcs->hdmi_clear_infoframe)
hdmi->bridge.ops |= DRM_BRIDGE_OP_HDMI;
/* Only v2 support OP_HDMI now and it we know that it also support SPD */
if (ver_conf->bridge_funcs->hdmi_write_avi_infoframe &&
ver_conf->bridge_funcs->hdmi_clear_avi_infoframe)
hdmi->bridge.ops |= DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
hdmi->bridge.ddc = hdmi->ddc_adpt;

View File

@@ -150,7 +150,6 @@ struct mtk_hdmi_conf {
struct mtk_hdmi {
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
struct drm_connector *curr_conn;/* current connector (only valid when 'enabled') */
struct device *dev;
const struct mtk_hdmi_conf *conf;

View File

@@ -145,8 +145,11 @@ static inline u32 mtk_hdmi_v2_format_hw_packet(const u8 *buffer, u8 len)
return val;
}
static void mtk_hdmi_v2_hw_write_audio_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
static int mtk_hdmi_v2_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AUD_EN | AUD_EN_WR);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
@@ -158,10 +161,15 @@ static void mtk_hdmi_v2_hw_write_audio_infoframe(struct mtk_hdmi *hdmi, const u8
regmap_set_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
regmap_set_bits(hdmi->regs, TOP_INFO_EN, AUD_EN | AUD_EN_WR);
return 0;
}
static void mtk_hdmi_v2_hw_write_avi_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
static int mtk_hdmi_v2_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
@@ -175,10 +183,15 @@ static void mtk_hdmi_v2_hw_write_avi_infoframe(struct mtk_hdmi *hdmi, const u8 *
regmap_set_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
regmap_set_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
return 0;
}
static void mtk_hdmi_v2_hw_write_spd_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
static int mtk_hdmi_v2_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
@@ -194,10 +207,15 @@ static void mtk_hdmi_v2_hw_write_spd_infoframe(struct mtk_hdmi *hdmi, const u8 *
regmap_set_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
regmap_set_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
return 0;
}
static void mtk_hdmi_v2_hw_write_vendor_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
static int mtk_hdmi_v2_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
@@ -213,6 +231,8 @@ static void mtk_hdmi_v2_hw_write_vendor_infoframe(struct mtk_hdmi *hdmi, const u
regmap_set_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
regmap_set_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
return 0;
}
static void mtk_hdmi_yuv420_downsampling(struct mtk_hdmi *hdmi, bool enable)
@@ -255,7 +275,7 @@ static int mtk_hdmi_v2_setup_audio_infoframe(struct mtk_hdmi *hdmi)
if (ret < 0)
return ret;
mtk_hdmi_v2_hw_write_audio_infoframe(hdmi, buffer);
mtk_hdmi_v2_hdmi_write_audio_infoframe(&hdmi->bridge, buffer, sizeof(buffer));
return 0;
}
@@ -940,8 +960,8 @@ static int mtk_hdmi_v2_bridge_attach(struct drm_bridge *bridge,
DRM_ERROR("The flag DRM_BRIDGE_ATTACH_NO_CONNECTOR must be supplied\n");
return -EINVAL;
}
if (hdmi->next_bridge) {
ret = drm_bridge_attach(encoder, hdmi->next_bridge, bridge, flags);
if (hdmi->bridge.next_bridge) {
ret = drm_bridge_attach(encoder, hdmi->bridge.next_bridge, bridge, flags);
if (ret)
return ret;
}
@@ -1132,60 +1152,42 @@ static int mtk_hdmi_v2_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge
return MODE_OK;
}
static int mtk_hdmi_v2_hdmi_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
static int mtk_hdmi_v2_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
switch (type) {
case HDMI_INFOFRAME_TYPE_AUDIO:
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AUD_EN_WR | AUD_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
break;
case HDMI_INFOFRAME_TYPE_AVI:
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
break;
case HDMI_INFOFRAME_TYPE_SPD:
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
break;
case HDMI_INFOFRAME_TYPE_VENDOR:
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
break;
case HDMI_INFOFRAME_TYPE_DRM:
default:
break;
}
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AUD_EN_WR | AUD_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
return 0;
}
static int mtk_hdmi_v2_hdmi_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
static int mtk_hdmi_v2_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
switch (type) {
case HDMI_INFOFRAME_TYPE_AUDIO:
mtk_hdmi_v2_hw_write_audio_infoframe(hdmi, buffer);
break;
case HDMI_INFOFRAME_TYPE_AVI:
mtk_hdmi_v2_hw_write_avi_infoframe(hdmi, buffer);
break;
case HDMI_INFOFRAME_TYPE_SPD:
mtk_hdmi_v2_hw_write_spd_infoframe(hdmi, buffer);
break;
case HDMI_INFOFRAME_TYPE_VENDOR:
mtk_hdmi_v2_hw_write_vendor_infoframe(hdmi, buffer);
break;
case HDMI_INFOFRAME_TYPE_DRM:
default:
dev_err(hdmi->dev, "Unsupported HDMI infoframe type %u\n", type);
break;
}
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
return 0;
}
static int mtk_hdmi_v2_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
return 0;
}
static int mtk_hdmi_v2_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
regmap_clear_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
return 0;
}
@@ -1329,8 +1331,14 @@ static const struct drm_bridge_funcs mtk_v2_hdmi_bridge_funcs = {
.hpd_enable = mtk_hdmi_v2_hpd_enable,
.hpd_disable = mtk_hdmi_v2_hpd_disable,
.hdmi_tmds_char_rate_valid = mtk_hdmi_v2_hdmi_tmds_char_rate_valid,
.hdmi_clear_infoframe = mtk_hdmi_v2_hdmi_clear_infoframe,
.hdmi_write_infoframe = mtk_hdmi_v2_hdmi_write_infoframe,
.hdmi_clear_audio_infoframe = mtk_hdmi_v2_hdmi_clear_audio_infoframe,
.hdmi_write_audio_infoframe = mtk_hdmi_v2_hdmi_write_audio_infoframe,
.hdmi_clear_avi_infoframe = mtk_hdmi_v2_hdmi_clear_avi_infoframe,
.hdmi_write_avi_infoframe = mtk_hdmi_v2_hdmi_write_avi_infoframe,
.hdmi_clear_spd_infoframe = mtk_hdmi_v2_hdmi_clear_spd_infoframe,
.hdmi_write_spd_infoframe = mtk_hdmi_v2_hdmi_write_spd_infoframe,
.hdmi_clear_hdmi_infoframe = mtk_hdmi_v2_hdmi_clear_hdmi_infoframe,
.hdmi_write_hdmi_infoframe = mtk_hdmi_v2_hdmi_write_hdmi_infoframe,
.debugfs_init = mtk_hdmi_v2_debugfs_init,
};

View File

@@ -778,7 +778,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
if (IS_ERR(meson_dw_hdmi->hdmi))
return PTR_ERR(meson_dw_hdmi->hdmi);
meson_dw_hdmi->bridge = of_drm_find_bridge(pdev->dev.of_node);
meson_dw_hdmi->bridge = of_drm_find_and_get_bridge(pdev->dev.of_node);
DRM_DEBUG_DRIVER("HDMI controller initialized\n");
@@ -789,8 +789,12 @@ static void meson_dw_hdmi_unbind(struct device *dev, struct device *master,
void *data)
{
struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
struct platform_device *pdev = to_platform_device(dev);
int irq = platform_get_irq(pdev, 0);
devm_free_irq(dev, irq, meson_dw_hdmi);
dw_hdmi_unbind(meson_dw_hdmi->hdmi);
drm_bridge_put(meson_dw_hdmi->bridge);
}
static const struct component_ops meson_dw_hdmi_ops = {

View File

@@ -54,9 +54,80 @@ static void power_off(struct drm_bridge *bridge)
#define SPD_IFRAME_LINE_NUMBER 1
#define VENSPEC_IFRAME_LINE_NUMBER 3
static int msm_hdmi_config_avi_infoframe(struct hdmi *hdmi,
const u8 *buffer, size_t len)
static int msm_hdmi_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 val;
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0);
val &= ~(HDMI_INFOFRAME_CTRL0_AVI_SEND |
HDMI_INFOFRAME_CTRL0_AVI_CONT);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val);
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK;
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);
return 0;
}
static int msm_hdmi_bridge_clear_audio_infoframe(struct drm_bridge *bridge)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 val;
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0);
val &= ~(HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val);
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
val &= ~HDMI_INFOFRAME_CTRL1_AUDIO_INFO_LINE__MASK;
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);
return 0;
}
static int msm_hdmi_bridge_clear_spd_infoframe(struct drm_bridge *bridge)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 val;
val = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL);
val &= ~(HDMI_GEN_PKT_CTRL_GENERIC1_SEND |
HDMI_GEN_PKT_CTRL_GENERIC1_CONT |
HDMI_GEN_PKT_CTRL_GENERIC1_LINE__MASK);
hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, val);
return 0;
}
static int msm_hdmi_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 val;
val = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL);
val &= ~(HDMI_GEN_PKT_CTRL_GENERIC0_SEND |
HDMI_GEN_PKT_CTRL_GENERIC0_CONT |
HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE |
HDMI_GEN_PKT_CTRL_GENERIC0_LINE__MASK);
hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, val);
return 0;
}
static int msm_hdmi_bridge_write_avi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 buf[4] = {};
u32 val;
int i;
@@ -67,6 +138,8 @@ static int msm_hdmi_config_avi_infoframe(struct hdmi *hdmi,
return -EINVAL;
}
msm_hdmi_bridge_clear_avi_infoframe(bridge);
/*
* the AVI_INFOx registers don't map exactly to how the AVI infoframes
* are packed according to the spec. The checksum from the header is
@@ -93,9 +166,11 @@ static int msm_hdmi_config_avi_infoframe(struct hdmi *hdmi,
return 0;
}
static int msm_hdmi_config_audio_infoframe(struct hdmi *hdmi,
const u8 *buffer, size_t len)
static int msm_hdmi_bridge_write_audio_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 val;
if (len != HDMI_INFOFRAME_SIZE(AUDIO)) {
@@ -104,6 +179,8 @@ static int msm_hdmi_config_audio_infoframe(struct hdmi *hdmi,
return -EINVAL;
}
msm_hdmi_bridge_clear_audio_infoframe(bridge);
hdmi_write(hdmi, REG_HDMI_AUDIO_INFO0,
buffer[3] |
buffer[4] << 8 |
@@ -126,9 +203,11 @@ static int msm_hdmi_config_audio_infoframe(struct hdmi *hdmi,
return 0;
}
static int msm_hdmi_config_spd_infoframe(struct hdmi *hdmi,
const u8 *buffer, size_t len)
static int msm_hdmi_bridge_write_spd_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 buf[7] = {};
u32 val;
int i;
@@ -139,6 +218,8 @@ static int msm_hdmi_config_spd_infoframe(struct hdmi *hdmi,
return -EINVAL;
}
msm_hdmi_bridge_clear_spd_infoframe(bridge);
/* checksum gets written together with the body of the frame */
hdmi_write(hdmi, REG_HDMI_GENERIC1_HDR,
buffer[0] |
@@ -159,9 +240,11 @@ static int msm_hdmi_config_spd_infoframe(struct hdmi *hdmi,
return 0;
}
static int msm_hdmi_config_hdmi_infoframe(struct hdmi *hdmi,
const u8 *buffer, size_t len)
static int msm_hdmi_bridge_write_hdmi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 buf[7] = {};
u32 val;
int i;
@@ -173,6 +256,8 @@ static int msm_hdmi_config_hdmi_infoframe(struct hdmi *hdmi,
return -EINVAL;
}
msm_hdmi_bridge_clear_hdmi_infoframe(bridge);
/* checksum gets written together with the body of the frame */
hdmi_write(hdmi, REG_HDMI_GENERIC0_HDR,
buffer[0] |
@@ -194,90 +279,6 @@ static int msm_hdmi_config_hdmi_infoframe(struct hdmi *hdmi,
return 0;
}
static int msm_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 val;
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0);
val &= ~(HDMI_INFOFRAME_CTRL0_AVI_SEND |
HDMI_INFOFRAME_CTRL0_AVI_CONT);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val);
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK;
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);
break;
case HDMI_INFOFRAME_TYPE_AUDIO:
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0);
val &= ~(HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val);
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
val &= ~HDMI_INFOFRAME_CTRL1_AUDIO_INFO_LINE__MASK;
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);
break;
case HDMI_INFOFRAME_TYPE_SPD:
val = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL);
val &= ~(HDMI_GEN_PKT_CTRL_GENERIC1_SEND |
HDMI_GEN_PKT_CTRL_GENERIC1_CONT |
HDMI_GEN_PKT_CTRL_GENERIC1_LINE__MASK);
hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, val);
break;
case HDMI_INFOFRAME_TYPE_VENDOR:
val = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL);
val &= ~(HDMI_GEN_PKT_CTRL_GENERIC0_SEND |
HDMI_GEN_PKT_CTRL_GENERIC0_CONT |
HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE |
HDMI_GEN_PKT_CTRL_GENERIC0_LINE__MASK);
hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, val);
break;
default:
drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type);
}
return 0;
}
static int msm_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
msm_hdmi_bridge_clear_infoframe(bridge, type);
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
return msm_hdmi_config_avi_infoframe(hdmi, buffer, len);
case HDMI_INFOFRAME_TYPE_AUDIO:
return msm_hdmi_config_audio_infoframe(hdmi, buffer, len);
case HDMI_INFOFRAME_TYPE_SPD:
return msm_hdmi_config_spd_infoframe(hdmi, buffer, len);
case HDMI_INFOFRAME_TYPE_VENDOR:
return msm_hdmi_config_hdmi_infoframe(hdmi, buffer, len);
default:
drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type);
return 0;
}
}
static void msm_hdmi_set_timings(struct hdmi *hdmi,
const struct drm_display_mode *mode);
@@ -462,8 +463,14 @@ static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = {
.hpd_enable = msm_hdmi_hpd_enable,
.hpd_disable = msm_hdmi_hpd_disable,
.hdmi_tmds_char_rate_valid = msm_hdmi_bridge_tmds_char_rate_valid,
.hdmi_clear_infoframe = msm_hdmi_bridge_clear_infoframe,
.hdmi_write_infoframe = msm_hdmi_bridge_write_infoframe,
.hdmi_clear_audio_infoframe = msm_hdmi_bridge_clear_audio_infoframe,
.hdmi_write_audio_infoframe = msm_hdmi_bridge_write_audio_infoframe,
.hdmi_clear_avi_infoframe = msm_hdmi_bridge_clear_avi_infoframe,
.hdmi_write_avi_infoframe = msm_hdmi_bridge_write_avi_infoframe,
.hdmi_clear_spd_infoframe = msm_hdmi_bridge_clear_spd_infoframe,
.hdmi_write_spd_infoframe = msm_hdmi_bridge_write_spd_infoframe,
.hdmi_clear_hdmi_infoframe = msm_hdmi_bridge_clear_hdmi_infoframe,
.hdmi_write_hdmi_infoframe = msm_hdmi_bridge_write_hdmi_infoframe,
.hdmi_audio_prepare = msm_hdmi_bridge_audio_prepare,
.hdmi_audio_shutdown = msm_hdmi_bridge_audio_shutdown,
};

View File

@@ -33,6 +33,7 @@ struct jadard_panel_desc {
unsigned int backlight_off_to_display_off_delay_ms;
unsigned int display_off_to_enter_sleep_delay_ms;
unsigned int enter_sleep_to_reset_down_delay_ms;
unsigned long mode_flags;
};
struct jadard {
@@ -109,13 +110,13 @@ static int jadard_prepare(struct drm_panel *panel)
if (jadard->desc->lp11_to_reset_delay_ms)
msleep(jadard->desc->lp11_to_reset_delay_ms);
gpiod_set_value(jadard->reset, 0);
gpiod_set_value_cansleep(jadard->reset, 0);
msleep(5);
gpiod_set_value(jadard->reset, 1);
gpiod_set_value_cansleep(jadard->reset, 1);
msleep(10);
gpiod_set_value(jadard->reset, 0);
gpiod_set_value_cansleep(jadard->reset, 0);
msleep(130);
ret = jadard->desc->init(jadard);
@@ -129,11 +130,11 @@ static int jadard_unprepare(struct drm_panel *panel)
{
struct jadard *jadard = panel_to_jadard(panel);
gpiod_set_value(jadard->reset, 0);
gpiod_set_value_cansleep(jadard->reset, 0);
msleep(120);
if (jadard->desc->reset_before_power_off_vcioo) {
gpiod_set_value(jadard->reset, 1);
gpiod_set_value_cansleep(jadard->reset, 1);
usleep_range(1000, 2000);
}
@@ -1113,6 +1114,258 @@ static const struct jadard_panel_desc melfas_lmfbx101117480_desc = {
.enter_sleep_to_reset_down_delay_ms = 100,
};
static int anbernic_rgds_init_cmds(struct jadard *jadard)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
struct drm_panel *panel = &jadard->panel;
jd9365da_switch_page(&dsi_ctx, 0x0);
jadard_enable_standard_cmds(&dsi_ctx);
jd9365da_switch_page(&dsi_ctx, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x6a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x6a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x74);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xbf);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xbf);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xfe);
if (of_device_is_compatible(panel->dev->of_node,
"anbernic,rg-ds-display-top"))
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x05);
else
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0xf7);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0xff);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x3c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0xff);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x0a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x11);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x78);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x6d);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x0a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x7f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x56);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x34);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x2f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x20);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x22);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x0c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x24);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x24);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x25);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x33);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x3a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x2d);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x28);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x1b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x0b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x56);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x34);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x2f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x20);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x22);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x0c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x24);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x24);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x25);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x33);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x3a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x2d);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x28);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x1b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x0b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x00);
jd9365da_switch_page(&dsi_ctx, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x5e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x50);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x57);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x77);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x4a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x4a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x46);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x46);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x5e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x50);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x41);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x57);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x77);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x4b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x4b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x47);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x47);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2c, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2e, 0x1e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x07);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x07);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x0b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x0b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x0a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x0a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x07);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x30);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x30);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0xe9);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x0b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0xe9);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xda);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0xfc);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x83, 0xf4);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x10);
jd9365da_switch_page(&dsi_ctx, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xb3);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x60);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x58);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x0f);
jd9365da_switch_page(&dsi_ctx, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x1d);
jd9365da_switch_page(&dsi_ctx, 0x00);
mipi_dsi_msleep(&dsi_ctx, 120);
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 120);
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 10);
return dsi_ctx.accum_err;
};
static const struct jadard_panel_desc anbernic_rgds_display_desc = {
.mode = {
.clock = (640 + 260 + 220 + 260) * (480 + 10 + 2 + 16) * 60 / 1000,
.hdisplay = 640,
.hsync_start = 640 + 260,
.hsync_end = 640 + 260 + 220,
.htotal = 640 + 260 + 220 + 260,
.vdisplay = 480,
.vsync_start = 480 + 10,
.vsync_end = 480 + 10 + 2,
.vtotal = 480 + 10 + 2 + 16,
.width_mm = 68,
.height_mm = 87,
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
},
.lanes = 4,
.format = MIPI_DSI_FMT_RGB888,
.init = anbernic_rgds_init_cmds,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
};
static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
@@ -1126,8 +1379,14 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(jadard);
desc = of_device_get_match_data(dev);
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_NO_EOT_PACKET;
if (desc->mode_flags)
dsi->mode_flags = desc->mode_flags;
else
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_NO_EOT_PACKET;
dsi->format = desc->format;
dsi->lanes = desc->lanes;
@@ -1176,6 +1435,14 @@ static void jadard_dsi_remove(struct mipi_dsi_device *dsi)
}
static const struct of_device_id jadard_of_match[] = {
{
.compatible = "anbernic,rg-ds-display-bottom",
.data = &anbernic_rgds_display_desc
},
{
.compatible = "anbernic,rg-ds-display-top",
.data = &anbernic_rgds_display_desc
},
{
.compatible = "chongzhou,cz101b4001",
.data = &cz101b4001_desc

View File

@@ -633,6 +633,13 @@ static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge,
return true;
}
static void rcar_lvds_destroy(struct drm_bridge *bridge)
{
struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
drm_bridge_put(lvds->companion);
}
static int rcar_lvds_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -648,6 +655,7 @@ static int rcar_lvds_attach(struct drm_bridge *bridge,
static const struct drm_bridge_funcs rcar_lvds_bridge_ops = {
.attach = rcar_lvds_attach,
.destroy = rcar_lvds_destroy,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
@@ -740,7 +748,7 @@ static int rcar_lvds_parse_dt_companion(struct rcar_lvds *lvds)
goto done;
}
lvds->companion = of_drm_find_bridge(companion);
lvds->companion = of_drm_find_and_get_bridge(companion);
if (!lvds->companion) {
ret = -EPROBE_DEFER;
goto done;

View File

@@ -1068,6 +1068,13 @@ static void rzg2l_mipi_dsi_atomic_disable(struct drm_bridge *bridge,
rzg2l_mipi_dsi_stop_video(dsi);
rzg2l_mipi_dsi_stop_hs_clock(dsi);
}
static void rzg2l_mipi_dsi_atomic_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
rzg2l_mipi_dsi_stop(dsi);
}
@@ -1103,6 +1110,7 @@ static const struct drm_bridge_funcs rzg2l_mipi_dsi_bridge_ops = {
.atomic_pre_enable = rzg2l_mipi_dsi_atomic_pre_enable,
.atomic_enable = rzg2l_mipi_dsi_atomic_enable,
.atomic_disable = rzg2l_mipi_dsi_atomic_disable,
.atomic_post_disable = rzg2l_mipi_dsi_atomic_post_disable,
.mode_valid = rzg2l_mipi_dsi_bridge_mode_valid,
};

View File

@@ -280,12 +280,7 @@ static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id)
static irqreturn_t dw_hdmi_qp_rk3576_irq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
u32 intr_stat, val;
regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &intr_stat);
if (!intr_stat)
return IRQ_NONE;
u32 val;
val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1);
regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
@@ -320,11 +315,7 @@ static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
u32 intr_stat, val;
regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
if (!intr_stat)
return IRQ_NONE;
u32 val;
if (hdmi->port_id)
val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_CLR, 1);

View File

@@ -158,35 +158,33 @@ static void rk3066_hdmi_set_power_mode(struct rk3066_hdmi *hdmi, int mode)
hdmi->tmdsclk = DEFAULT_PLLA_RATE;
}
static int rk3066_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
static int rk3066_hdmi_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct rk3066_hdmi *hdmi = bridge_to_rk3066_hdmi(bridge);
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
return 0;
}
hdmi_writeb(hdmi, HDMI_CP_BUF_INDEX, HDMI_INFOFRAME_AVI);
return 0;
}
static int
rk3066_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
rk3066_hdmi_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
/* FIXME: add support for this InfoFrame */
drm_warn_once(bridge->encoder->dev, "HDMI VSI not supported\n");
return 0;
}
static int
rk3066_hdmi_bridge_write_avi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
struct rk3066_hdmi *hdmi = bridge_to_rk3066_hdmi(bridge);
ssize_t i;
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
return 0;
}
rk3066_hdmi_bridge_clear_infoframe(bridge, type);
rk3066_hdmi_bridge_clear_avi_infoframe(bridge);
for (i = 0; i < len; i++)
hdmi_writeb(hdmi, HDMI_CP_BUF_ACC_HB0 + i * 4, buffer[i]);
@@ -194,6 +192,17 @@ rk3066_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
return 0;
}
static int
rk3066_hdmi_bridge_write_hdmi_infoframe(struct drm_bridge *bridge,
const u8 *buffer, size_t len)
{
rk3066_hdmi_bridge_clear_hdmi_infoframe(bridge);
/* FIXME: add support for this InfoFrame */
return 0;
}
static int rk3066_hdmi_config_video_timing(struct rk3066_hdmi *hdmi,
struct drm_display_mode *mode)
{
@@ -493,8 +502,10 @@ static const struct drm_bridge_funcs rk3066_hdmi_bridge_funcs = {
.atomic_disable = rk3066_hdmi_bridge_atomic_disable,
.detect = rk3066_hdmi_bridge_detect,
.edid_read = rk3066_hdmi_bridge_edid_read,
.hdmi_clear_infoframe = rk3066_hdmi_bridge_clear_infoframe,
.hdmi_write_infoframe = rk3066_hdmi_bridge_write_infoframe,
.hdmi_clear_avi_infoframe = rk3066_hdmi_bridge_clear_avi_infoframe,
.hdmi_write_avi_infoframe = rk3066_hdmi_bridge_write_avi_infoframe,
.hdmi_clear_hdmi_infoframe = rk3066_hdmi_bridge_clear_hdmi_infoframe,
.hdmi_write_hdmi_infoframe = rk3066_hdmi_bridge_write_hdmi_infoframe,
.mode_valid = rk3066_hdmi_bridge_mode_valid,
};

View File

@@ -1426,6 +1426,17 @@ static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
}
static enum drm_mode_status vop2_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
struct vop2_video_port *vp = to_vop2_video_port(crtc);
if (mode->hdisplay > vp->data->max_output.width)
return MODE_BAD_HVALUE;
return MODE_OK;
}
static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
@@ -1871,6 +1882,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
.mode_fixup = vop2_crtc_mode_fixup,
.mode_valid = vop2_crtc_mode_valid,
.atomic_check = vop2_crtc_atomic_check,
.atomic_begin = vop2_crtc_atomic_begin,
.atomic_flush = vop2_crtc_atomic_flush,

View File

@@ -40,19 +40,19 @@
#define drm_connector_to_sun4i_hdmi(c) \
container_of_const(c, struct sun4i_hdmi, connector)
static int sun4i_hdmi_write_infoframe(struct drm_connector *connector,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
static int sun4i_hdmi_clear_avi_infoframe(struct drm_connector *connector)
{
drm_warn_once(connector->dev, "clearing of AVI infoframe is not implemented\n");
return 0;
}
static int sun4i_hdmi_write_avi_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
int i;
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
"Unsupported infoframe type: %u\n", type);
return 0;
}
for (i = 0; i < len; i++)
writeb(buffer[i], hdmi->base + SUN4I_HDMI_AVI_INFOFRAME_REG(i));
@@ -60,6 +60,21 @@ static int sun4i_hdmi_write_infoframe(struct drm_connector *connector,
}
static int sun4i_hdmi_clear_hdmi_infoframe(struct drm_connector *connector)
{
drm_warn_once(connector->dev, "HDMI VSI not implemented\n");
return 0;
}
static int sun4i_hdmi_write_hdmi_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
drm_warn_once(connector->dev, "HDMI VSI not implemented\n");
return 0;
}
static void sun4i_hdmi_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
@@ -236,7 +251,14 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev)
static const struct drm_connector_hdmi_funcs sun4i_hdmi_hdmi_connector_funcs = {
.tmds_char_rate_valid = sun4i_hdmi_connector_clock_valid,
.write_infoframe = sun4i_hdmi_write_infoframe,
.avi = {
.clear_infoframe = sun4i_hdmi_clear_avi_infoframe,
.write_infoframe = sun4i_hdmi_write_avi_infoframe,
},
.hdmi = {
.clear_infoframe = sun4i_hdmi_clear_hdmi_infoframe,
.write_infoframe = sun4i_hdmi_write_hdmi_infoframe,
},
};
static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = {

View File

@@ -857,6 +857,40 @@ static void drm_test_buddy_alloc_limit(struct kunit *test)
drm_buddy_fini(&mm);
}
static void drm_test_buddy_alloc_exceeds_max_order(struct kunit *test)
{
u64 mm_size = SZ_8G + SZ_2G, size = SZ_8G + SZ_1G, min_block_size = SZ_8G;
struct drm_buddy mm;
LIST_HEAD(blocks);
int err;
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, SZ_4K),
"buddy_init failed\n");
/* CONTIGUOUS allocation should succeed via try_harder fallback */
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, size,
SZ_4K, &blocks,
DRM_BUDDY_CONTIGUOUS_ALLOCATION),
"buddy_alloc hit an error size=%llu\n", size);
drm_buddy_free_list(&mm, &blocks, 0);
/* Non-CONTIGUOUS with large min_block_size should return -EINVAL */
err = drm_buddy_alloc_blocks(&mm, 0, mm_size, size, min_block_size, &blocks, 0);
KUNIT_EXPECT_EQ(test, err, -EINVAL);
/* Non-CONTIGUOUS + RANGE with large min_block_size should return -EINVAL */
err = drm_buddy_alloc_blocks(&mm, 0, mm_size, size, min_block_size, &blocks,
DRM_BUDDY_RANGE_ALLOCATION);
KUNIT_EXPECT_EQ(test, err, -EINVAL);
/* CONTIGUOUS + RANGE should return -EINVAL (no try_harder for RANGE) */
err = drm_buddy_alloc_blocks(&mm, 0, mm_size, size, SZ_4K, &blocks,
DRM_BUDDY_CONTIGUOUS_ALLOCATION | DRM_BUDDY_RANGE_ALLOCATION);
KUNIT_EXPECT_EQ(test, err, -EINVAL);
drm_buddy_fini(&mm);
}
static int drm_buddy_suite_init(struct kunit_suite *suite)
{
while (!random_seed)
@@ -877,6 +911,7 @@ static struct kunit_case drm_buddy_tests[] = {
KUNIT_CASE(drm_test_buddy_alloc_clear),
KUNIT_CASE(drm_test_buddy_alloc_range_bias),
KUNIT_CASE(drm_test_buddy_fragmentation_performance),
KUNIT_CASE(drm_test_buddy_alloc_exceeds_max_order),
{}
};

View File

@@ -5,6 +5,7 @@
#include <kunit/test.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_drv.h>
@@ -48,6 +49,8 @@ static const struct drm_connector_helper_funcs drm_client_modeset_connector_help
};
static const struct drm_connector_funcs drm_client_modeset_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state
};
static int drm_client_modeset_test_init(struct kunit *test)

View File

@@ -25,7 +25,26 @@ struct drm_connector_init_priv {
struct i2c_adapter ddc;
};
static int accept_infoframe_clear_infoframe(struct drm_connector *connector)
{
return 0;
}
static int accept_infoframe_write_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return 0;
}
static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
static const struct drm_connector_funcs dummy_funcs = {

View File

@@ -35,11 +35,16 @@ struct drm_atomic_helper_connector_hdmi_priv {
const void *current_edid;
size_t current_edid_len;
int hdmi_update_failures;
};
#define connector_to_priv(c) \
container_of_const(c, struct drm_atomic_helper_connector_hdmi_priv, connector)
#define encoder_to_priv(e) \
container_of_const(e, struct drm_atomic_helper_connector_hdmi_priv, encoder)
static struct drm_display_mode *find_preferred_mode(struct drm_connector *connector)
{
struct drm_device *drm = connector->dev;
@@ -73,7 +78,26 @@ static int set_connector_edid(struct kunit *test, struct drm_connector *connecto
return ret;
}
static int accept_infoframe_clear_infoframe(struct drm_connector *connector)
{
return 0;
}
static int accept_infoframe_write_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return 0;
}
static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
static enum drm_mode_status
@@ -86,6 +110,14 @@ reject_connector_tmds_char_rate_valid(const struct drm_connector *connector,
static const struct drm_connector_hdmi_funcs reject_connector_hdmi_funcs = {
.tmds_char_rate_valid = reject_connector_tmds_char_rate_valid,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
static enum drm_mode_status
@@ -98,6 +130,14 @@ reject_100mhz_connector_tmds_char_rate_valid(const struct drm_connector *connect
static const struct drm_connector_hdmi_funcs reject_100mhz_connector_hdmi_funcs = {
.tmds_char_rate_valid = reject_100mhz_connector_tmds_char_rate_valid,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
static int dummy_connector_get_modes(struct drm_connector *connector)
@@ -138,6 +178,22 @@ static const struct drm_connector_funcs dummy_connector_funcs = {
.reset = dummy_hdmi_connector_reset,
};
static void test_encoder_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_atomic_helper_connector_hdmi_priv *priv =
encoder_to_priv(encoder);
int ret;
ret = drm_atomic_helper_connector_hdmi_update_infoframes(&priv->connector, state);
if (ret)
priv->hdmi_update_failures++;
}
static const struct drm_encoder_helper_funcs test_encoder_helper_funcs = {
.atomic_enable = test_encoder_atomic_enable,
};
static
struct drm_atomic_helper_connector_hdmi_priv *
__connector_hdmi_init(struct kunit *test,
@@ -2466,10 +2522,621 @@ static struct kunit_suite drm_atomic_helper_connector_hdmi_mode_valid_test_suite
.test_cases = drm_atomic_helper_connector_hdmi_mode_valid_tests,
};
/*
* Test that the default behaviour works without errors. We expect that
* infoframe-related hooks are called and there are no errors raised.
*/
static void drm_test_check_infoframes(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_GE(test, old_hdmi_update_failures, priv->hdmi_update_failures);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static int reject_infoframe_write_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return -EOPNOTSUPP;
}
static const struct drm_connector_hdmi_funcs reject_avi_infoframe_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = reject_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
/*
* Test that the rejection of AVI InfoFrame results in the failure of
* drm_atomic_helper_connector_hdmi_update_infoframes().
*/
static void drm_test_check_reject_avi_infoframe(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&reject_avi_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_NE(test, old_hdmi_update_failures, priv->hdmi_update_failures);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static const struct drm_connector_hdmi_funcs reject_hdr_infoframe_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdr_drm = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = reject_infoframe_write_infoframe,
},
};
/*
* Test that the HDR InfoFrame isn't programmed in
* drm_atomic_helper_connector_hdmi_update_infoframes() if the max_bpc is 8.
*/
static void drm_test_check_reject_hdr_infoframe_bpc_8(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&reject_hdr_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz_hdr);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_conn_state:
new_conn_state = drm_atomic_get_connector_state(state, conn);
if (PTR_ERR(new_conn_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
/* Verify that there is no HDR property, so "userspace" can't set it. */
for (int i = 0; i < conn->base.properties->count; i++)
KUNIT_ASSERT_PTR_NE(test,
drm->mode_config.hdr_output_metadata_property,
conn->base.properties->properties[i]);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, old_hdmi_update_failures, priv->hdmi_update_failures);
new_conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.output_bpc, 8);
KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.infoframes.hdr_drm.set, false);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
/*
* Test that the rejection of HDR InfoFrame results in the failure of
* drm_atomic_helper_connector_hdmi_update_infoframes() in the high bpc is
* supported.
*/
static void drm_test_check_reject_hdr_infoframe_bpc_10(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
struct hdr_output_metadata hdr_data;
struct drm_property_blob *hdr_blob;
bool replaced;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
10,
&reject_hdr_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz_hdr);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_conn_state:
new_conn_state = drm_atomic_get_connector_state(state, conn);
if (PTR_ERR(new_conn_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
hdr_data.metadata_type = HDMI_STATIC_METADATA_TYPE1;
hdr_data.hdmi_metadata_type1.eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
hdr_data.hdmi_metadata_type1.metadata_type = HDMI_STATIC_METADATA_TYPE1;
hdr_blob = drm_property_create_blob(drm, sizeof(hdr_data), &hdr_data);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hdr_blob);
ret = drm_property_replace_blob_from_id(drm,
&new_conn_state->hdr_output_metadata,
hdr_blob->base.id,
-1, sizeof(struct hdr_output_metadata), -1,
&replaced);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_ASSERT_EQ(test, replaced, true);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_LE(test, old_hdmi_update_failures, priv->hdmi_update_failures);
new_conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.output_bpc, 10);
KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.infoframes.hdr_drm.set, true);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static const struct drm_connector_hdmi_funcs reject_audio_infoframe_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.audio = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = reject_infoframe_write_infoframe,
},
};
/*
* Test that Audio InfoFrame is only programmed if we call a corresponding API,
* thus the drivers can safely assume that they won't get Audio InfoFrames if
* they don't call it.
*/
static void drm_test_check_reject_audio_infoframe(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
struct hdmi_audio_infoframe cea;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&reject_audio_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, old_hdmi_update_failures, priv->hdmi_update_failures);
/*
* So, it works without Audio InfoFrame, let's fail with it in place,
* checking that writing the infofraem actually gets triggered.
*/
hdmi_audio_infoframe_init(&cea);
cea.channels = 2;
cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(conn, &cea);
KUNIT_ASSERT_EQ(test, ret, -EOPNOTSUPP);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state_2:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state_2;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state_2;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state_2;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_LE(test, old_hdmi_update_failures, priv->hdmi_update_failures);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static struct kunit_case drm_atomic_helper_connector_hdmi_infoframes_tests[] = {
KUNIT_CASE(drm_test_check_infoframes),
KUNIT_CASE(drm_test_check_reject_avi_infoframe),
KUNIT_CASE(drm_test_check_reject_hdr_infoframe_bpc_8),
KUNIT_CASE(drm_test_check_reject_hdr_infoframe_bpc_10),
KUNIT_CASE(drm_test_check_reject_audio_infoframe),
{ }
};
static struct kunit_suite drm_atomic_helper_connector_hdmi_infoframes_test_suite = {
.name = "drm_atomic_helper_connector_hdmi_infoframes",
.test_cases = drm_atomic_helper_connector_hdmi_infoframes_tests,
};
kunit_test_suites(
&drm_atomic_helper_connector_hdmi_check_test_suite,
&drm_atomic_helper_connector_hdmi_reset_test_suite,
&drm_atomic_helper_connector_hdmi_mode_valid_test_suite,
&drm_atomic_helper_connector_hdmi_infoframes_test_suite,
);
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");

View File

@@ -293,6 +293,125 @@ static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = {
0x00, 0x00, 0x00, 0xfc
};
/*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
* 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
* 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
* 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
*
* 02 03 1c 81 e3 05 c0 20 41 10 e2 00 4a 67 03 0c
* 00 12 34 00 28 e6 06 05 01 52 52 51 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4e
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 42
* Made in: 2023
* Basic Display Parameters & Features:
* Digital display
* DFP 1.x compatible TMDS
* Maximum image size: 160 cm x 90 cm
* Gamma: 2.20
* Monochrome or grayscale display
* First detailed timing is the preferred timing
* Color Characteristics:
* Red : 0.0000, 0.0000
* Green: 0.0000, 0.0000
* Blue : 0.0000, 0.0000
* White: 0.0000, 0.0000
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0x92
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Underscans IT Video Formats by default
* Native detailed modes: 1
* Colorimetry Data Block:
* BT2020YCC
* BT2020RGB
* sRGB
* Video Data Block:
* VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
* Video Capability Data Block:
* YCbCr quantization: No Data
* RGB quantization: Selectable (via AVI Q)
* PT scan behavior: No Data
* IT scan behavior: Always Underscanned
* CE scan behavior: Always Underscanned
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.2.3.4
* Maximum TMDS clock: 200 MHz
* HDR Static Metadata Data Block:
* Electro optical transfer functions:
* Traditional gamma - SDR luminance range
* SMPTE ST2084
* Supported static metadata descriptors:
* Static metadata type 1
* Desired content max luminance: 82 (295.365 cd/m^2)
* Desired content max frame-average luminance: 82 (295.365 cd/m^2)
* Desired content min luminance: 81 (0.298 cd/m^2)
* Checksum: 0x4e Unused space in Extension Block: 99 bytes
*
* ----------------
*
* edid-decode 1.31.0-5387
* edid-decode SHA: 5508bc4301ac 2025-08-25 08:14:22
*
* EDID conformity: PASS
*/
static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz_hdr[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x1c, 0x81,
0xe3, 0x05, 0xc0, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
0x00, 0x12, 0x34, 0x78, 0x28, 0xe6, 0x06, 0x05, 0x01, 0x52, 0x52, 0x51,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xd6,
};
/*
* edid-decode (hex):
*

View File

@@ -8,6 +8,7 @@ use kernel::platform;
use kernel::prelude::*;
use kernel::time;
use kernel::transmute::AsBytes;
use kernel::uapi;
use crate::driver::IoMem;
use crate::regs;
@@ -34,7 +35,7 @@ pub(crate) struct GpuInfo {
pub(crate) coherency_features: u32,
pub(crate) texture_features: [u32; 4],
pub(crate) as_present: u32,
pub(crate) pad0: u32,
pub(crate) selected_coherency: u32,
pub(crate) shader_present: u64,
pub(crate) l2_present: u64,
pub(crate) tiler_present: u64,
@@ -89,7 +90,7 @@ impl GpuInfo {
// TODO: Add texture_features_{1,2,3}.
texture_features: [texture_features, 0, 0, 0],
as_present,
pad0: 0,
selected_coherency: uapi::drm_panthor_gpu_coherency_DRM_PANTHOR_GPU_COHERENCY_NONE,
shader_present,
l2_present,
tiler_present,

View File

@@ -213,7 +213,7 @@ int v3d_create_bo_ioctl(struct drm_device *dev, void *data,
int ret;
if (args->flags != 0) {
DRM_INFO("unknown create_bo flags: %d\n", args->flags);
drm_dbg(dev, "unknown create_bo flags: %d\n", args->flags);
return -EINVAL;
}
@@ -236,13 +236,13 @@ int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
struct drm_gem_object *gem_obj;
if (args->flags != 0) {
DRM_INFO("unknown mmap_bo flags: %d\n", args->flags);
drm_dbg(dev, "unknown mmap_bo flags: %d\n", args->flags);
return -EINVAL;
}
gem_obj = drm_gem_object_lookup(file_priv, args->handle);
if (!gem_obj) {
DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
drm_dbg(dev, "Failed to look up GEM BO %d\n", args->handle);
return -ENOENT;
}
@@ -261,7 +261,7 @@ int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
gem_obj = drm_gem_object_lookup(file_priv, args->handle);
if (!gem_obj) {
DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
drm_dbg(dev, "Failed to look up GEM BO %d\n", args->handle);
return -ENOENT;
}
bo = to_v3d_bo(gem_obj);

View File

@@ -120,7 +120,7 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
mutex_unlock(&v3d->reset_lock);
return 0;
default:
DRM_DEBUG("Unknown parameter %d\n", args->param);
drm_dbg(dev, "Unknown parameter %d\n", args->param);
return -EINVAL;
}
}
@@ -297,7 +297,7 @@ v3d_idle_sms(struct v3d_dev *v3d)
if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) {
DRM_ERROR("Failed to power up SMS\n");
drm_err(&v3d->drm, "Failed to power up SMS\n");
}
v3d_reset_sms(v3d);
@@ -313,7 +313,7 @@ v3d_power_off_sms(struct v3d_dev *v3d)
if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) {
DRM_ERROR("Failed to power off SMS\n");
drm_err(&v3d->drm, "Failed to power off SMS\n");
}
}

View File

@@ -52,7 +52,7 @@ v3d_idle_axi(struct v3d_dev *v3d, int core)
(V3D_GMP_STATUS_RD_COUNT_MASK |
V3D_GMP_STATUS_WR_COUNT_MASK |
V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) {
DRM_ERROR("Failed to wait for safe GMP shutdown\n");
drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n");
}
}
@@ -67,7 +67,7 @@ v3d_idle_gca(struct v3d_dev *v3d)
if (wait_for((V3D_GCA_READ(V3D_GCA_SAFE_SHUTDOWN_ACK) &
V3D_GCA_SAFE_SHUTDOWN_ACK_ACKED) ==
V3D_GCA_SAFE_SHUTDOWN_ACK_ACKED, 100)) {
DRM_ERROR("Failed to wait for safe GCA shutdown\n");
drm_err(&v3d->drm, "Failed to wait for safe GCA shutdown\n");
}
}
@@ -117,7 +117,7 @@ v3d_reset_sms(struct v3d_dev *v3d)
V3D_SMS_STATE) == V3D_SMS_ISOLATING_FOR_RESET) &&
!(V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_REE_CS),
V3D_SMS_STATE) == V3D_SMS_RESETTING), 100)) {
DRM_ERROR("Failed to wait for SMS reset\n");
drm_err(&v3d->drm, "Failed to wait for SMS reset\n");
}
}
@@ -126,9 +126,9 @@ v3d_reset(struct v3d_dev *v3d)
{
struct drm_device *dev = &v3d->drm;
DRM_DEV_ERROR(dev->dev, "Resetting GPU for hang.\n");
DRM_DEV_ERROR(dev->dev, "V3D_ERR_STAT: 0x%08x\n",
V3D_CORE_READ(0, V3D_ERR_STAT));
drm_err(dev, "Resetting GPU for hang.\n");
drm_err(dev, "V3D_ERR_STAT: 0x%08x\n", V3D_CORE_READ(0, V3D_ERR_STAT));
trace_v3d_reset_begin(dev);
/* XXX: only needed for safe powerdown, not reset. */
@@ -216,7 +216,7 @@ v3d_clean_caches(struct v3d_dev *v3d)
V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF);
if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) &
V3D_L2TCACTL_TMUWCF), 100)) {
DRM_ERROR("Timeout waiting for TMU write combiner flush\n");
drm_err(dev, "Timeout waiting for TMU write combiner flush\n");
}
mutex_lock(&v3d->cache_clean_lock);
@@ -226,7 +226,7 @@ v3d_clean_caches(struct v3d_dev *v3d)
if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) &
V3D_L2TCACTL_L2TFLS), 100)) {
DRM_ERROR("Timeout waiting for L2T clean\n");
drm_err(dev, "Timeout waiting for L2T clean\n");
}
mutex_unlock(&v3d->cache_clean_lock);

View File

@@ -50,7 +50,7 @@ v3d_overflow_mem_work(struct work_struct *work)
unsigned long irqflags;
if (IS_ERR(bo)) {
DRM_ERROR("Couldn't allocate binner overflow mem\n");
drm_err(dev, "Couldn't allocate binner overflow mem\n");
return;
}
obj = &bo->base.base;
@@ -140,7 +140,7 @@ v3d_irq(int irq, void *arg)
* always-allowed mode.
*/
if (v3d->ver < V3D_GEN_71 && (intsts & V3D_INT_GMPV))
dev_err(v3d->drm.dev, "GMP violation\n");
drm_err(&v3d->drm, "GMP violation\n");
/* V3D 4.2 wires the hub and core IRQs together, so if we &
* didn't see the common one then check hub for MMU IRQs.
@@ -226,7 +226,7 @@ v3d_hub_irq(int irq, void *arg)
}
}
dev_err(v3d->drm.dev, "MMU error from client %s (0x%x) at 0x%llx%s%s%s\n",
drm_dbg(&v3d->drm, "MMU error from client %s (0x%x) at 0x%llx%s%s%s\n",
client, axi_id, (long long)vio_addr,
((intsts & V3D_HUB_INT_MMU_WRV) ?
", write violation" : ""),
@@ -238,7 +238,7 @@ v3d_hub_irq(int irq, void *arg)
}
if (v3d->ver >= V3D_GEN_71 && (intsts & V3D_V7_HUB_INT_GMPV)) {
dev_err(v3d->drm.dev, "GMP Violation\n");
drm_err(&v3d->drm, "GMP Violation\n");
status = IRQ_HANDLED;
}

View File

@@ -18,6 +18,8 @@
* each client. This is not yet implemented.
*/
#include <drm/drm_print.h>
#include "v3d_drv.h"
#include "v3d_regs.h"
@@ -125,7 +127,7 @@ void v3d_mmu_insert_ptes(struct v3d_bo *bo)
shmem_obj->base.size >> V3D_MMU_PAGE_SHIFT);
if (v3d_mmu_flush_all(v3d))
dev_err(v3d->drm.dev, "MMU flush timeout\n");
drm_err(&v3d->drm, "MMU flush timeout\n");
}
void v3d_mmu_remove_ptes(struct v3d_bo *bo)
@@ -138,5 +140,5 @@ void v3d_mmu_remove_ptes(struct v3d_bo *bo)
v3d->pt[page] = 0;
if (v3d_mmu_flush_all(v3d))
dev_err(v3d->drm.dev, "MMU flush timeout\n");
drm_err(&v3d->drm, "MMU flush timeout\n");
}

View File

@@ -585,7 +585,7 @@ v3d_reset_performance_queries(struct v3d_cpu_job *job)
perfmon = v3d_perfmon_find(v3d_priv,
performance_query->queries[i].kperfmon_ids[j]);
if (!perfmon) {
DRM_DEBUG("Failed to find perfmon.");
drm_dbg(&v3d->drm, "Failed to find perfmon.");
continue;
}
@@ -620,7 +620,7 @@ v3d_write_performance_query_result(struct v3d_cpu_job *job, void *data,
perfmon = v3d_perfmon_find(v3d_priv,
perf_query->kperfmon_ids[i]);
if (!perfmon) {
DRM_DEBUG("Failed to find perfmon.");
drm_dbg(&v3d->drm, "Failed to find perfmon.");
continue;
}
@@ -690,7 +690,7 @@ v3d_cpu_job_run(struct drm_sched_job *sched_job)
struct v3d_dev *v3d = job->base.v3d;
if (job->job_type >= ARRAY_SIZE(cpu_job_function)) {
DRM_DEBUG_DRIVER("Unknown CPU job: %d\n", job->job_type);
drm_dbg(&v3d->drm, "Unknown CPU job: %d\n", job->job_type);
return NULL;
}

View File

@@ -76,7 +76,7 @@ v3d_lookup_bos(struct drm_device *dev,
/* See comment on bo_index for why we have to check
* this.
*/
DRM_DEBUG("Rendering requires BOs\n");
drm_warn(dev, "Rendering requires BOs\n");
return -EINVAL;
}
@@ -138,11 +138,11 @@ void v3d_job_put(struct v3d_job *job)
}
static int
v3d_job_allocate(void **container, size_t size)
v3d_job_allocate(struct v3d_dev *v3d, void **container, size_t size)
{
*container = kcalloc(1, size, GFP_KERNEL);
if (!*container) {
DRM_ERROR("Cannot allocate memory for V3D job.\n");
drm_err(&v3d->drm, "Cannot allocate memory for V3D job.\n");
return -ENOMEM;
}
@@ -183,7 +183,7 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
if (copy_from_user(&in, handle++, sizeof(in))) {
ret = -EFAULT;
DRM_DEBUG("Failed to copy wait dep handle.\n");
drm_dbg(&v3d->drm, "Failed to copy wait dep handle.\n");
goto fail_deps;
}
ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in.handle, 0);
@@ -276,7 +276,7 @@ v3d_setup_csd_jobs_and_bos(struct drm_file *file_priv,
{
int ret;
ret = v3d_job_allocate((void *)job, sizeof(**job));
ret = v3d_job_allocate(v3d, (void *)job, sizeof(**job));
if (ret)
return ret;
@@ -287,7 +287,7 @@ v3d_setup_csd_jobs_and_bos(struct drm_file *file_priv,
return ret;
}
ret = v3d_job_allocate((void *)clean_job, sizeof(**clean_job));
ret = v3d_job_allocate(v3d, (void *)clean_job, sizeof(**clean_job));
if (ret)
return ret;
@@ -326,6 +326,8 @@ v3d_get_multisync_post_deps(struct drm_file *file_priv,
struct v3d_submit_ext *se,
u32 count, u64 handles)
{
struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
struct v3d_dev *v3d = v3d_priv->v3d;
struct drm_v3d_sem __user *post_deps;
int i, ret;
@@ -346,7 +348,7 @@ v3d_get_multisync_post_deps(struct drm_file *file_priv,
if (copy_from_user(&out, post_deps++, sizeof(out))) {
ret = -EFAULT;
DRM_DEBUG("Failed to copy post dep handles\n");
drm_dbg(&v3d->drm, "Failed to copy post dep handles\n");
goto fail;
}
@@ -377,11 +379,13 @@ v3d_get_multisync_submit_deps(struct drm_file *file_priv,
struct drm_v3d_extension __user *ext,
struct v3d_submit_ext *se)
{
struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
struct v3d_dev *v3d = v3d_priv->v3d;
struct drm_v3d_multi_sync multisync;
int ret;
if (se->in_sync_count || se->out_sync_count) {
DRM_DEBUG("Two multisync extensions were added to the same job.");
drm_dbg(&v3d->drm, "Two multisync extensions were added to the same job.");
return -EINVAL;
}
@@ -404,6 +408,26 @@ v3d_get_multisync_submit_deps(struct drm_file *file_priv,
return 0;
}
/* Returns false if the CPU job has an invalid configuration. */
static bool
v3d_validate_cpu_job(struct drm_file *file_priv, struct v3d_cpu_job *job)
{
struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
struct v3d_dev *v3d = v3d_priv->v3d;
if (!job) {
drm_dbg(&v3d->drm, "CPU job extension was attached to a GPU job.\n");
return false;
}
if (job->job_type) {
drm_dbg(&v3d->drm, "Two CPU job extensions were added to the same CPU job.\n");
return false;
}
return true;
}
/* Get data for the indirect CSD job submission. */
static int
v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv,
@@ -415,21 +439,14 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv,
struct drm_v3d_indirect_csd indirect_csd;
struct v3d_indirect_csd_info *info = &job->indirect_csd;
if (!job) {
DRM_DEBUG("CPU job extension was attached to a GPU job.\n");
if (!v3d_validate_cpu_job(file_priv, job))
return -EINVAL;
}
if (job->job_type) {
DRM_DEBUG("Two CPU job extensions were added to the same CPU job.\n");
return -EINVAL;
}
if (copy_from_user(&indirect_csd, ext, sizeof(indirect_csd)))
return -EFAULT;
if (!v3d_has_csd(v3d)) {
DRM_DEBUG("Attempting CSD submit on non-CSD hardware.\n");
drm_warn(&v3d->drm, "Attempting CSD submit on non-CSD hardware.\n");
return -EINVAL;
}
@@ -458,15 +475,8 @@ v3d_get_cpu_timestamp_query_params(struct drm_file *file_priv,
unsigned int i;
int err;
if (!job) {
DRM_DEBUG("CPU job extension was attached to a GPU job.\n");
if (!v3d_validate_cpu_job(file_priv, job))
return -EINVAL;
}
if (job->job_type) {
DRM_DEBUG("Two CPU job extensions were added to the same CPU job.\n");
return -EINVAL;
}
if (copy_from_user(&timestamp, ext, sizeof(timestamp)))
return -EFAULT;
@@ -527,15 +537,8 @@ v3d_get_cpu_reset_timestamp_params(struct drm_file *file_priv,
unsigned int i;
int err;
if (!job) {
DRM_DEBUG("CPU job extension was attached to a GPU job.\n");
if (!v3d_validate_cpu_job(file_priv, job))
return -EINVAL;
}
if (job->job_type) {
DRM_DEBUG("Two CPU job extensions were added to the same CPU job.\n");
return -EINVAL;
}
if (copy_from_user(&reset, ext, sizeof(reset)))
return -EFAULT;
@@ -588,15 +591,8 @@ v3d_get_cpu_copy_query_results_params(struct drm_file *file_priv,
unsigned int i;
int err;
if (!job) {
DRM_DEBUG("CPU job extension was attached to a GPU job.\n");
if (!v3d_validate_cpu_job(file_priv, job))
return -EINVAL;
}
if (job->job_type) {
DRM_DEBUG("Two CPU job extensions were added to the same CPU job.\n");
return -EINVAL;
}
if (copy_from_user(&copy, ext, sizeof(copy)))
return -EFAULT;
@@ -724,15 +720,8 @@ v3d_get_cpu_reset_performance_params(struct drm_file *file_priv,
struct drm_v3d_reset_performance_query reset;
int err;
if (!job) {
DRM_DEBUG("CPU job extension was attached to a GPU job.\n");
if (!v3d_validate_cpu_job(file_priv, job))
return -EINVAL;
}
if (job->job_type) {
DRM_DEBUG("Two CPU job extensions were added to the same CPU job.\n");
return -EINVAL;
}
if (copy_from_user(&reset, ext, sizeof(reset)))
return -EFAULT;
@@ -770,15 +759,8 @@ v3d_get_cpu_copy_performance_query_params(struct drm_file *file_priv,
struct drm_v3d_copy_performance_query copy;
int err;
if (!job) {
DRM_DEBUG("CPU job extension was attached to a GPU job.\n");
if (!v3d_validate_cpu_job(file_priv, job))
return -EINVAL;
}
if (job->job_type) {
DRM_DEBUG("Two CPU job extensions were added to the same CPU job.\n");
return -EINVAL;
}
if (copy_from_user(&copy, ext, sizeof(copy)))
return -EFAULT;
@@ -826,6 +808,8 @@ v3d_get_extensions(struct drm_file *file_priv,
struct v3d_submit_ext *se,
struct v3d_cpu_job *job)
{
struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
struct v3d_dev *v3d = v3d_priv->v3d;
struct drm_v3d_extension __user *user_ext;
int ret;
@@ -834,7 +818,7 @@ v3d_get_extensions(struct drm_file *file_priv,
struct drm_v3d_extension ext;
if (copy_from_user(&ext, user_ext, sizeof(ext))) {
DRM_DEBUG("Failed to copy submit extension\n");
drm_dbg(&v3d->drm, "Failed to copy submit extension\n");
return -EFAULT;
}
@@ -861,7 +845,7 @@ v3d_get_extensions(struct drm_file *file_priv,
ret = v3d_get_cpu_copy_performance_query_params(file_priv, user_ext, job);
break;
default:
DRM_DEBUG_DRIVER("Unknown extension id: %d\n", ext.id);
drm_dbg(&v3d->drm, "Unknown V3D extension ID: %d\n", ext.id);
return -EINVAL;
}
@@ -909,19 +893,19 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
if (args->flags &&
args->flags & ~(DRM_V3D_SUBMIT_CL_FLUSH_CACHE |
DRM_V3D_SUBMIT_EXTENSION)) {
DRM_INFO("invalid flags: %d\n", args->flags);
drm_dbg(dev, "invalid flags: %d\n", args->flags);
return -EINVAL;
}
if (args->flags & DRM_V3D_SUBMIT_EXTENSION) {
ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL);
if (ret) {
DRM_DEBUG("Failed to get extensions.\n");
drm_dbg(dev, "Failed to get extensions.\n");
return ret;
}
}
ret = v3d_job_allocate((void *)&render, sizeof(*render));
ret = v3d_job_allocate(v3d, (void *)&render, sizeof(*render));
if (ret)
return ret;
@@ -937,7 +921,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
INIT_LIST_HEAD(&render->unref_list);
if (args->bcl_start != args->bcl_end) {
ret = v3d_job_allocate((void *)&bin, sizeof(*bin));
ret = v3d_job_allocate(v3d, (void *)&bin, sizeof(*bin));
if (ret)
goto fail;
@@ -957,7 +941,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
}
if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) {
ret = v3d_job_allocate((void *)&clean_job, sizeof(*clean_job));
ret = v3d_job_allocate(v3d, (void *)&clean_job, sizeof(*clean_job));
if (ret)
goto fail;
@@ -1075,19 +1059,19 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data,
trace_v3d_submit_tfu_ioctl(&v3d->drm, args->iia);
if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) {
DRM_DEBUG("invalid flags: %d\n", args->flags);
drm_dbg(dev, "invalid flags: %d\n", args->flags);
return -EINVAL;
}
if (args->flags & DRM_V3D_SUBMIT_EXTENSION) {
ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL);
if (ret) {
DRM_DEBUG("Failed to get extensions.\n");
drm_dbg(dev, "Failed to get extensions.\n");
return ret;
}
}
ret = v3d_job_allocate((void *)&job, sizeof(*job));
ret = v3d_job_allocate(v3d, (void *)&job, sizeof(*job));
if (ret)
return ret;
@@ -1117,9 +1101,9 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data,
bo = drm_gem_object_lookup(file_priv, args->bo_handles[job->base.bo_count]);
if (!bo) {
DRM_DEBUG("Failed to look up GEM BO %d: %d\n",
job->base.bo_count,
args->bo_handles[job->base.bo_count]);
drm_dbg(dev, "Failed to look up GEM BO %d: %d\n",
job->base.bo_count,
args->bo_handles[job->base.bo_count]);
ret = -ENOENT;
goto fail;
}
@@ -1179,19 +1163,19 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
if (!v3d_has_csd(v3d)) {
DRM_DEBUG("Attempting CSD submit on non-CSD hardware\n");
drm_warn(dev, "Attempting CSD submit on non-CSD hardware\n");
return -EINVAL;
}
if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) {
DRM_INFO("invalid flags: %d\n", args->flags);
drm_dbg(dev, "invalid flags: %d\n", args->flags);
return -EINVAL;
}
if (args->flags & DRM_V3D_SUBMIT_EXTENSION) {
ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL);
if (ret) {
DRM_DEBUG("Failed to get extensions.\n");
drm_dbg(dev, "Failed to get extensions.\n");
return ret;
}
}
@@ -1285,31 +1269,31 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
int ret;
if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) {
DRM_INFO("Invalid flags: %d\n", args->flags);
drm_dbg(dev, "Invalid flags: %d\n", args->flags);
return -EINVAL;
}
ret = v3d_job_allocate((void *)&cpu_job, sizeof(*cpu_job));
ret = v3d_job_allocate(v3d, (void *)&cpu_job, sizeof(*cpu_job));
if (ret)
return ret;
if (args->flags & DRM_V3D_SUBMIT_EXTENSION) {
ret = v3d_get_extensions(file_priv, args->extensions, &se, cpu_job);
if (ret) {
DRM_DEBUG("Failed to get extensions.\n");
drm_dbg(dev, "Failed to get extensions.\n");
goto fail;
}
}
/* Every CPU job must have a CPU job user extension */
if (!cpu_job->job_type) {
DRM_DEBUG("CPU job must have a CPU job user extension.\n");
drm_dbg(dev, "CPU job must have a CPU job user extension.\n");
ret = -EINVAL;
goto fail;
}
if (args->bo_handle_count != cpu_job_bo_handle_count[cpu_job->job_type]) {
DRM_DEBUG("This CPU job was not submitted with the proper number of BOs.\n");
drm_dbg(dev, "This CPU job was not submitted with the proper number of BOs.\n");
ret = -EINVAL;
goto fail;
}

View File

@@ -624,6 +624,30 @@ static int vc4_hdmi_stop_packet(struct vc4_hdmi *vc4_hdmi,
return ret;
}
static int vc4_hdmi_clear_infoframe(struct drm_connector *connector,
enum hdmi_infoframe_type type)
{
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct drm_device *drm = connector->dev;
int ret;
int idx;
if (!drm_dev_enter(drm, &idx))
return 0;
WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
VC4_HDMI_RAM_PACKET_ENABLE),
"Packet RAM has to be on to store the packet.");
ret = vc4_hdmi_stop_packet(vc4_hdmi, type, true);
if (ret)
drm_err(drm, "Failed to wait for infoframe to go idle: %d\n", ret);
drm_dev_exit(idx);
return ret;
}
static int vc4_hdmi_write_infoframe(struct drm_connector *connector,
enum hdmi_infoframe_type type,
const u8 *infoframe, size_t len)
@@ -703,6 +727,66 @@ out:
return ret;
}
static int vc4_hdmi_clear_avi_infoframe(struct drm_connector *connector)
{
return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_AVI);
}
static int vc4_hdmi_clear_hdmi_infoframe(struct drm_connector *connector)
{
return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_VENDOR);
}
static int vc4_hdmi_clear_audio_infoframe(struct drm_connector *connector)
{
return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_AUDIO);
}
static int vc4_hdmi_clear_hdr_drm_infoframe(struct drm_connector *connector)
{
return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_DRM);
}
static int vc4_hdmi_clear_spd_infoframe(struct drm_connector *connector)
{
return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_SPD);
}
static int vc4_hdmi_write_avi_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_AVI,
buffer, len);
}
static int vc4_hdmi_write_hdmi_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_VENDOR,
buffer, len);
}
static int vc4_hdmi_write_audio_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_AUDIO,
buffer, len);
}
static int vc4_hdmi_write_hdr_drm_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_DRM,
buffer, len);
}
static int vc4_hdmi_write_spd_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_SPD,
buffer, len);
}
#define SCRAMBLING_POLLING_DELAY_MS 1000
static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
@@ -1660,7 +1744,26 @@ vc4_hdmi_connector_clock_valid(const struct drm_connector *connector,
static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs = {
.tmds_char_rate_valid = vc4_hdmi_connector_clock_valid,
.write_infoframe = vc4_hdmi_write_infoframe,
.avi = {
.clear_infoframe = vc4_hdmi_clear_avi_infoframe,
.write_infoframe = vc4_hdmi_write_avi_infoframe,
},
.hdmi = {
.clear_infoframe = vc4_hdmi_clear_hdmi_infoframe,
.write_infoframe = vc4_hdmi_write_hdmi_infoframe,
},
.audio = {
.clear_infoframe = vc4_hdmi_clear_audio_infoframe,
.write_infoframe = vc4_hdmi_write_audio_infoframe,
},
.hdr_drm = {
.clear_infoframe = vc4_hdmi_clear_hdr_drm_infoframe,
.write_infoframe = vc4_hdmi_write_hdr_drm_infoframe,
},
.spd = {
.clear_infoframe = vc4_hdmi_clear_spd_infoframe,
.write_infoframe = vc4_hdmi_write_spd_infoframe,
},
};
#define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL

View File

@@ -6,6 +6,8 @@
#ifndef __INNO_HDMI__
#define __INNO_HDMI__
#include <linux/types.h>
struct device;
struct drm_encoder;
struct drm_display_mode;

View File

@@ -100,7 +100,6 @@ struct samsung_dsim_plat_data {
struct samsung_dsim {
struct mipi_dsi_host dsi_host;
struct drm_bridge bridge;
struct drm_bridge *out_bridge;
struct device *dev;
struct drm_display_mode mode;

View File

@@ -785,29 +785,113 @@ struct drm_bridge_funcs {
unsigned long long tmds_rate);
/**
* @hdmi_clear_infoframe:
* @hdmi_clear_avi_infoframe:
*
* This callback clears the infoframes in the hardware during commit.
* It will be called multiple times, once for every disabled infoframe
* type.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops.
*/
int (*hdmi_clear_infoframe)(struct drm_bridge *bridge,
enum hdmi_infoframe_type type);
int (*hdmi_clear_avi_infoframe)(struct drm_bridge *bridge);
/**
* @hdmi_write_infoframe:
* @hdmi_write_avi_infoframe:
*
* Program the infoframe into the hardware. It will be called multiple
* times, once for every updated infoframe type.
* Program the infoframe into the hardware.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops.
*/
int (*hdmi_write_infoframe)(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len);
int (*hdmi_write_avi_infoframe)(struct drm_bridge *bridge,
const u8 *buffer, size_t len);
/**
* @hdmi_clear_hdmi_infoframe:
*
* This callback clears the infoframes in the hardware during commit.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops.
*/
int (*hdmi_clear_hdmi_infoframe)(struct drm_bridge *bridge);
/**
* @hdmi_write_hdmi_infoframe:
*
* Program the infoframe into the hardware.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops.
*/
int (*hdmi_write_hdmi_infoframe)(struct drm_bridge *bridge,
const u8 *buffer, size_t len);
/**
* @hdmi_clear_hdr_drm_infoframe:
*
* This callback clears the infoframes in the hardware during commit.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME flag in their
* &drm_bridge->ops.
*/
int (*hdmi_clear_hdr_drm_infoframe)(struct drm_bridge *bridge);
/**
* @hdmi_write_hdr_drm_infoframe:
*
* Program the infoframe into the hardware.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME flag in their
* &drm_bridge->ops.
*/
int (*hdmi_write_hdr_drm_infoframe)(struct drm_bridge *bridge,
const u8 *buffer, size_t len);
/**
* @hdmi_clear_spd_infoframe:
*
* This callback clears the infoframes in the hardware during commit.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME flag in their
* &drm_bridge->ops.
*/
int (*hdmi_clear_spd_infoframe)(struct drm_bridge *bridge);
/**
* @hdmi_write_spd_infoframe:
*
* Program the infoframe into the hardware.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME flag in their
* &drm_bridge->ops.
*/
int (*hdmi_write_spd_infoframe)(struct drm_bridge *bridge,
const u8 *buffer, size_t len);
/**
* @hdmi_clear_audio_infoframe:
*
* This callback clears the infoframes in the hardware during commit.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI_AUDIO flag in their &drm_bridge->ops.
*/
int (*hdmi_clear_audio_infoframe)(struct drm_bridge *bridge);
/**
* @hdmi_write_audio_infoframe:
*
* Program the infoframe into the hardware.
*
* This callback is optional but it must be implemented by bridges that
* set the DRM_BRIDGE_OP_HDMI_AUDIO flag in their &drm_bridge->ops.
*/
int (*hdmi_write_audio_infoframe)(struct drm_bridge *bridge,
const u8 *buffer, size_t len);
/**
* @hdmi_audio_startup:
@@ -1063,7 +1147,11 @@ enum drm_bridge_ops {
/**
* @DRM_BRIDGE_OP_HDMI: The bridge provides HDMI connector operations,
* including infoframes support. Bridges that set this flag must
* implement the &drm_bridge_funcs->write_infoframe callback.
* provide HDMI-related information and implement the
* &drm_bridge_funcs->clear_avi_infoframe,
* &drm_bridge_funcs->write_avi_infoframe,
* &drm_bridge_funcs->clear_hdmi_infoframe and
* &drm_bridge_funcs->write_hdmi_infoframe callbacks.
*
* Note: currently there can be at most one bridge in a chain that sets
* this bit. This is to simplify corresponding glue code in connector
@@ -1075,6 +1163,9 @@ enum drm_bridge_ops {
* Bridges that set this flag must implement the
* &drm_bridge_funcs->hdmi_audio_prepare and
* &drm_bridge_funcs->hdmi_audio_shutdown callbacks.
* If the bridge implements @DRM_BRIDGE_OP_HDMI, it also must implement
* &drm_bridge_funcs->hdmi_write_audio_infoframe and
* &drm_bridge_funcs->hdmi_cleaer_audio_infoframe callbacks.
*
* Note: currently there can be at most one bridge in a chain that sets
* this bit. This is to simplify corresponding glue code in connector
@@ -1106,6 +1197,18 @@ enum drm_bridge_ops {
* to be present.
*/
DRM_BRIDGE_OP_HDMI_CEC_ADAPTER = BIT(8),
/**
* @DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME: The bridge supports
* &drm_bridge_funcs->hdmi_write_hdr_drm_infoframe and
* &drm_bridge_funcs->hdmi_clear_hdr_drm_infoframe callbacks.
*/
DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME = BIT(9),
/**
* @DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME: The bridge supports
* &drm_bridge_funcs->hdmi_write_spd_infoframe and
* &drm_bridge_funcs->hdmi_clear_spd_infoframe callbacks.
*/
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME = BIT(10),
};
/**

View File

@@ -1221,6 +1221,45 @@ struct drm_connector_cec_funcs {
void (*phys_addr_set)(struct drm_connector *connector, u16 addr);
};
/**
* struct drm_connector_infoframe_funcs - InfoFrame-related functions
*/
struct drm_connector_infoframe_funcs {
/**
* @clear_infoframe:
*
* This callback is invoked through
* @drm_atomic_helper_connector_hdmi_update_infoframes during a
* commit to clear the infoframes into the hardware. It will be
* called once for each frame type to be disabled.
*
* The @clear_infoframe callback is mandatory for AVI and HDMI-VS
* InfoFrame types.
*
* Returns:
* 0 on success, a negative error code otherwise
*/
int (*clear_infoframe)(struct drm_connector *connector);
/**
* @write_infoframe:
*
* This callback is invoked through
* @drm_atomic_helper_connector_hdmi_update_infoframes during a
* commit to program the infoframes into the hardware. It will
* be called for every updated infoframe type.
*
* The @write_infoframe callback is mandatory for AVI and HDMI-VS
* InfoFrame types.
*
* Returns:
* 0 on success, a negative error code otherwise
*/
int (*write_infoframe)(struct drm_connector *connector,
const u8 *buffer, size_t len);
};
/**
* struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions
*/
@@ -1244,41 +1283,6 @@ struct drm_connector_hdmi_funcs {
const struct drm_display_mode *mode,
unsigned long long tmds_rate);
/**
* @clear_infoframe:
*
* This callback is invoked through
* @drm_atomic_helper_connector_hdmi_update_infoframes during a
* commit to clear the infoframes into the hardware. It will be
* called multiple times, once for every disabled infoframe
* type.
*
* The @clear_infoframe callback is optional.
*
* Returns:
* 0 on success, a negative error code otherwise
*/
int (*clear_infoframe)(struct drm_connector *connector,
enum hdmi_infoframe_type type);
/**
* @write_infoframe:
*
* This callback is invoked through
* @drm_atomic_helper_connector_hdmi_update_infoframes during a
* commit to program the infoframes into the hardware. It will
* be called multiple times, once for every updated infoframe
* type.
*
* The @write_infoframe callback is mandatory.
*
* Returns:
* 0 on success, a negative error code otherwise
*/
int (*write_infoframe)(struct drm_connector *connector,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len);
/**
* @read_edid:
*
@@ -1293,6 +1297,47 @@ struct drm_connector_hdmi_funcs {
* Valid EDID on success, NULL in case of failure.
*/
const struct drm_edid *(*read_edid)(struct drm_connector *connector);
/**
* @avi:
*
* Set of callbacks for handling the AVI InfoFrame. These callbacks are
* mandatory.
*/
struct drm_connector_infoframe_funcs avi;
/**
* @hdmi:
*
* Set of callbacks for handling the HDMI Vendor-Specific InfoFrame.
* These callbacks are mandatory.
*/
struct drm_connector_infoframe_funcs hdmi;
/**
* @audio:
*
* Set of callbacks for handling the Audio InfoFrame. These callbacks
* are optional, but they are required for drivers which use
* drm_atomic_helper_connector_hdmi_update_audio_infoframe().
*/
struct drm_connector_infoframe_funcs audio;
/**
* @hdr_drm:
*
* Set of callbacks for handling the HDR DRM InfoFrame. These callbacks
* are mandatory if HDR output is to be supported.
*/
struct drm_connector_infoframe_funcs hdr_drm;
/**
* @spd:
*
* Set of callbacks for handling the SPD InfoFrame. These callbacks are
* optional.
*/
struct drm_connector_infoframe_funcs spd;
};
/**

View File

@@ -5,6 +5,7 @@
#include <linux/err.h>
#include <linux/of_graph.h>
#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE)
#include <linux/of.h>
#include <drm/drm_bridge.h>
#endif
@@ -170,9 +171,12 @@ static inline int drm_of_panel_bridge_remove(const struct device_node *np,
if (!remote)
return -ENODEV;
bridge = of_drm_find_bridge(remote);
bridge = of_drm_find_and_get_bridge(remote);
drm_panel_bridge_remove(bridge);
drm_bridge_put(bridge);
of_node_put(remote);
return 0;
#else
return -EINVAL;

View File

@@ -429,18 +429,6 @@ struct dma_buf {
__poll_t active;
} cb_in, cb_out;
#ifdef CONFIG_DMABUF_SYSFS_STATS
/**
* @sysfs_entry:
*
* For exposing information about this buffer in sysfs. See also
* `DMA-BUF statistics`_ for the uapi this enables.
*/
struct dma_buf_sysfs_entry {
struct kobject kobj;
struct dma_buf *dmabuf;
} *sysfs_entry;
#endif
};
/**

View File

@@ -46,4 +46,6 @@ const char *dma_heap_get_name(struct dma_heap *heap);
struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info);
extern bool mem_accounting;
#endif /* _DMA_HEAPS_H */

View File

@@ -15,15 +15,15 @@ DECLARE_EVENT_CLASS(dma_buf,
TP_ARGS(dmabuf),
TP_STRUCT__entry(
__string(exp_name, dmabuf->exp_name)
__field(size_t, size)
__field(ino_t, ino)
__string( exp_name, dmabuf->exp_name)
__field( size_t, size)
__field( ino_t, ino)
),
TP_fast_assign(
__assign_str(exp_name);
__entry->size = dmabuf->size;
__entry->ino = dmabuf->file->f_inode->i_ino;
__entry->size = dmabuf->size;
__entry->ino = dmabuf->file->f_inode->i_ino;
),
TP_printk("exp_name=%s size=%zu ino=%lu",
@@ -40,21 +40,21 @@ DECLARE_EVENT_CLASS(dma_buf_attach_dev,
TP_ARGS(dmabuf, attach, is_dynamic, dev),
TP_STRUCT__entry(
__string(dev_name, dev_name(dev))
__string(exp_name, dmabuf->exp_name)
__field(size_t, size)
__field(ino_t, ino)
__field(struct dma_buf_attachment *, attach)
__field(bool, is_dynamic)
__string( dev_name, dev_name(dev))
__string( exp_name, dmabuf->exp_name)
__field( size_t, size)
__field( ino_t, ino)
__field( struct dma_buf_attachment *, attach)
__field( bool, is_dynamic)
),
TP_fast_assign(
__assign_str(dev_name);
__assign_str(exp_name);
__entry->size = dmabuf->size;
__entry->ino = dmabuf->file->f_inode->i_ino;
__entry->is_dynamic = is_dynamic;
__entry->attach = attach;
__entry->size = dmabuf->size;
__entry->ino = dmabuf->file->f_inode->i_ino;
__entry->is_dynamic = is_dynamic;
__entry->attach = attach;
),
TP_printk("exp_name=%s size=%zu ino=%lu attachment:%p is_dynamic=%d dev_name=%s",
@@ -73,17 +73,17 @@ DECLARE_EVENT_CLASS(dma_buf_fd,
TP_ARGS(dmabuf, fd),
TP_STRUCT__entry(
__string(exp_name, dmabuf->exp_name)
__field(size_t, size)
__field(ino_t, ino)
__field(int, fd)
__string( exp_name, dmabuf->exp_name)
__field( size_t, size)
__field( ino_t, ino)
__field( int, fd)
),
TP_fast_assign(
__assign_str(exp_name);
__entry->size = dmabuf->size;
__entry->ino = dmabuf->file->f_inode->i_ino;
__entry->fd = fd;
__entry->size = dmabuf->size;
__entry->ino = dmabuf->file->f_inode->i_ino;
__entry->fd = fd;
),
TP_printk("exp_name=%s size=%zu ino=%lu fd=%d",
@@ -137,11 +137,13 @@ DEFINE_EVENT(dma_buf_attach_dev, dma_buf_detach,
TP_ARGS(dmabuf, attach, is_dynamic, dev)
);
DEFINE_EVENT(dma_buf_fd, dma_buf_fd,
DEFINE_EVENT_CONDITION(dma_buf_fd, dma_buf_fd,
TP_PROTO(struct dma_buf *dmabuf, int fd),
TP_ARGS(dmabuf, fd)
TP_ARGS(dmabuf, fd),
TP_CONDITION(fd >= 0)
);
DEFINE_EVENT(dma_buf_fd, dma_buf_get,