mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
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:
4
.mailmap
4
.mailmap
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
@@ -27,6 +27,7 @@ properties:
|
||||
- const: adi,adv7123
|
||||
- enum:
|
||||
- adi,adv7123
|
||||
- algoltek,ag6311
|
||||
- asl-tek,cs5263
|
||||
- dumb-vga-dac
|
||||
- parade,ps185hdm
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -86,6 +86,8 @@ patternProperties:
|
||||
description: Aldec, Inc.
|
||||
"^alfa-network,.*":
|
||||
description: ALFA Network Inc.
|
||||
"^algoltek,.*":
|
||||
description: AlgolTek, Inc.
|
||||
"^allegro,.*":
|
||||
description: Allegro DVT
|
||||
"^allegromicro,.*":
|
||||
|
||||
@@ -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
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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, ©->base);
|
||||
|
||||
return ©->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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = <->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,
|
||||
<8912_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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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".
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
73
drivers/gpu/drm/imagination/pvr_test.c
Normal file
73
drivers/gpu/drm/imagination/pvr_test.c
Normal 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");
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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>");
|
||||
|
||||
@@ -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):
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(×tamp, 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(©, 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(©, 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef __INNO_HDMI__
|
||||
#define __INNO_HDMI__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device;
|
||||
struct drm_encoder;
|
||||
struct drm_display_mode;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user