diff --git a/.mailmap b/.mailmap index 44cea28596e7..c7bea2dea258 100644 --- a/.mailmap +++ b/.mailmap @@ -373,7 +373,9 @@ Jesper Dangaard Brouer Jesper Dangaard Brouer Jesper Dangaard Brouer Jesper Dangaard Brouer -Jessica Zhang +Jessica Zhang +Jessica Zhang +Jessica Zhang Jilai Wang Jiri Kosina Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers deleted file mode 100644 index 5d3bc997dc64..000000000000 --- a/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers +++ /dev/null @@ -1,24 +0,0 @@ -What: /sys/kernel/dmabuf/buffers -Date: May 2021 -KernelVersion: v5.13 -Contact: Hridya Valsaraju -Description: The /sys/kernel/dmabuf/buffers directory contains a - snapshot of the internal state of every DMA-BUF. - /sys/kernel/dmabuf/buffers/ will contain the - statistics for the DMA-BUF with the unique inode number - -Users: kernel memory tuning/debugging tools - -What: /sys/kernel/dmabuf/buffers//exporter_name -Date: May 2021 -KernelVersion: v5.13 -Contact: Hridya Valsaraju -Description: This file is read-only and contains the name of the exporter of - the DMA-BUF. - -What: /sys/kernel/dmabuf/buffers//size -Date: May 2021 -KernelVersion: v5.13 -Contact: Hridya Valsaraju -Description: This file is read-only and specifies the size of the DMA-BUF in - bytes. diff --git a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml index 20c7e0a77802..e6808419f625 100644 --- a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml +++ b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml @@ -27,6 +27,7 @@ properties: - const: adi,adv7123 - enum: - adi,adv7123 + - algoltek,ag6311 - asl-tek,cs5263 - dumb-vga-dac - parade,ps185hdm diff --git a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml index b8783eba3ddc..5802fb3c9ffe 100644 --- a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml +++ b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml @@ -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 diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index c7591b2aec2a..d459886e515a 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -86,6 +86,8 @@ patternProperties: description: Aldec, Inc. "^alfa-network,.*": description: ALFA Network Inc. + "^algoltek,.*": + description: AlgolTek, Inc. "^allegro,.*": description: Allegro DVT "^allegromicro,.*": diff --git a/Documentation/driver-api/dma-buf.rst b/Documentation/driver-api/dma-buf.rst index 29abf1eebf9f..2f36c21d9948 100644 --- a/Documentation/driver-api/dma-buf.rst +++ b/Documentation/driver-api/dma-buf.rst @@ -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 ~~~~~~~~~~~~~~~~~ diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index fdd823e446cc..012d22e941d6 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -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/ will contain - statistics for the DMA-BUF with the unique 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 diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index 2008fb7481b3..7a85565d906b 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -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 \ diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c deleted file mode 100644 index b5b62e40ccc1..000000000000 --- a/drivers/dma-buf/dma-buf-sysfs-stats.c +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * DMA-BUF sysfs statistics. - * - * Copyright (C) 2021 Google LLC. - */ - -#include -#include -#include -#include -#include -#include - -#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//fdinfo/`` 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//exporter_name`` - * * ``/sys/kernel/dmabuf/buffers//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; -} diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.h b/drivers/dma-buf/dma-buf-sysfs-stats.h deleted file mode 100644 index 7a8a995b75ba..000000000000 --- a/drivers/dma-buf/dma-buf-sysfs-stats.h +++ /dev/null @@ -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 diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index a4d8f2ff94e4..77555096e4c7 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -33,8 +33,6 @@ #include #include -#include "dma-buf-sysfs-stats.h" - #define CREATE_TRACE_POINTS #include @@ -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); diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c index 8ab49924f8b7..d230ddeb24e0 100644 --- a/drivers/dma-buf/dma-heap.c +++ b/drivers/dma-buf/dma-heap.c @@ -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) diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c index 4c782fe33fd4..4049d042afa1 100644 --- a/drivers/dma-buf/heaps/system_heap.c +++ b/drivers/dma-buf/heaps/system_heap.c @@ -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; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index e0efc7309b1b..b075f291847f 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -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; } diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index dd70894c8f38..d1f5451ebfea 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -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) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 53d47f01db0b..26b26185cf34 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -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; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 0b8a86afb096..e8aea905fb10 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -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; } diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 92132be9823f..c52da47982ee 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -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); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index b9be86541307..1050bb62280b 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -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; diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 6f3fdcb6afdb..4e49e4f28d55 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -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: diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c index 3a6f8587a257..15fbb1be07cd 100644 --- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c @@ -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); diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c index 122502968927..675995cbeb6b 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c @@ -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, diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c index ab4572eb8395..a26b99b101c4 100644 --- a/drivers/gpu/drm/bridge/inno-hdmi.c +++ b/drivers/gpu/drm/bridge/inno-hdmi.c @@ -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, }; diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c index 2eb8fba7016c..3991fb76143c 100644 --- a/drivers/gpu/drm/bridge/ite-it6263.c +++ b/drivers/gpu/drm/bridge/ite-it6263.c @@ -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) diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index 342374cb8fc6..8a0b48efca58 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -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; diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index a2d032ee4744..0628d8e737ab 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -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"; diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index eabc4c32f6ab..1d85e706c74b 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -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); diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 1f0aba28ad1e..12497f5ce4ff 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -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"); } diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 873b32cfb508..8aa31ca3c72d 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -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) { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 0c7ad06aaca4..036316e2b60d 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -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; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 3b77e73ac0ea..ee88c0e793b0 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -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; diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c index 2cb7cd0c0608..c804222846c3 100644 --- a/drivers/gpu/drm/bridge/thc63lvd1024.c +++ b/drivers/gpu/drm/bridge/thc63lvd1024.c @@ -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, diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index b80ee089f880..11b5bb50e9f4 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -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. */ diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c b/drivers/gpu/drm/bridge/ti-tpd12s015.c index dcf686c4e73d..136e47ad1a10 100644 --- a/drivers/gpu/drm/bridge/ti-tpd12s015.c +++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c @@ -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. */ diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 57a0cceabd34..ba8ff113cff1 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -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); diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index a561f124be99..a1d16762ac7a 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -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)); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 6dcf8f6d3ecf..3b165a0d1e77 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -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 * diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index 2f279b46bd2c..fd34d3755f7c 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -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); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 4d6dc9ebfdb5..4f5b27fab475 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -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; diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 365cf337529f..ae1c6126c2c5 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -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); \ diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c index 955fa960843b..540cd41d8368 100644 --- a/drivers/gpu/drm/drm_property.c +++ b/drivers/gpu/drm/drm_property.c @@ -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); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 01813e11e6c6..bfcf2fa62fe1 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -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); } diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h index 4add05c7f161..f9ee7ebfec55 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h @@ -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; }; diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h index 08f9e1caf7fc..efb30a758475 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h @@ -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 diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c index 8f0daec7d174..d5bd3c45649b 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c @@ -2,6 +2,7 @@ // Copyright (c) 2024 Hisilicon Limited. #include +#include #include #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; +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h index 665f5b166dfb..31316fe1ea8d 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -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 diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index 394b1e933c3a..64306abcd986 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -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) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index d06832e62e96..616821e3c933 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -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); diff --git a/drivers/gpu/drm/imagination/Kconfig b/drivers/gpu/drm/imagination/Kconfig index 0482bfcefdde..1fd4c635c2c9 100644 --- a/drivers/gpu/drm/imagination/Kconfig +++ b/drivers/gpu/drm/imagination/Kconfig @@ -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". diff --git a/drivers/gpu/drm/imagination/Makefile b/drivers/gpu/drm/imagination/Makefile index 7cca66f00a38..f5072f06b4c4 100644 --- a/drivers/gpu/drm/imagination/Makefile +++ b/drivers/gpu/drm/imagination/Makefile @@ -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 diff --git a/drivers/gpu/drm/imagination/pvr_debugfs.c b/drivers/gpu/drm/imagination/pvr_debugfs.c index c7ce7daaa87a..ebdb05de4072 100644 --- a/drivers/gpu/drm/imagination/pvr_debugfs.c +++ b/drivers/gpu/drm/imagination/pvr_debugfs.c @@ -5,7 +5,6 @@ #include "pvr_device.h" #include "pvr_fw_trace.h" -#include "pvr_params.h" #include #include @@ -18,7 +17,6 @@ #include static const struct pvr_debugfs_entry pvr_debugfs_entries[] = { - {"pvr_params", pvr_params_debugfs_init}, {"pvr_fw", pvr_fw_trace_debugfs_init}, }; diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c index 78d6b8a0a450..f58bb66a6327 100644 --- a/drivers/gpu/drm/imagination/pvr_device.c +++ b/drivers/gpu/drm/imagination/pvr_device.c @@ -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 #include +#include + /* 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) diff --git a/drivers/gpu/drm/imagination/pvr_device.h b/drivers/gpu/drm/imagination/pvr_device.h index ec53ff275541..cfda215e7428 100644 --- a/drivers/gpu/drm/imagination/pvr_device.h +++ b/drivers/gpu/drm/imagination/pvr_device.h @@ -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 */ 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); } diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c index 8a56952f6730..93119f0f23a9 100644 --- a/drivers/gpu/drm/imagination/pvr_fw_trace.c +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c @@ -12,11 +12,77 @@ #include #include +#include #include #include +#include #include #include +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); } diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.h b/drivers/gpu/drm/imagination/pvr_fw_trace.h index 1d0ef937427a..0cc57f66675d 100644 --- a/drivers/gpu/drm/imagination/pvr_fw_trace.h +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.h @@ -68,9 +68,6 @@ void pvr_fw_trace_fini(struct pvr_device *pvr_dev); /* Forward declaration from . */ 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 */ diff --git a/drivers/gpu/drm/imagination/pvr_params.c b/drivers/gpu/drm/imagination/pvr_params.c deleted file mode 100644 index b91759f362c5..000000000000 --- a/drivers/gpu/drm/imagination/pvr_params.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT -/* Copyright (c) 2023 Imagination Technologies Ltd. */ - -#include "pvr_params.h" - -#include -#include - -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 -#include -#include -#include -#include - -/* - * 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 diff --git a/drivers/gpu/drm/imagination/pvr_params.h b/drivers/gpu/drm/imagination/pvr_params.h deleted file mode 100644 index 5807915b456b..000000000000 --- a/drivers/gpu/drm/imagination/pvr_params.h +++ /dev/null @@ -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 -#include - -/* - * 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 . */ -struct dentry; - -void pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir); -#endif /* defined(CONFIG_DEBUG_FS) */ - -#endif /* PVR_PARAMS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_test.c b/drivers/gpu/drm/imagination/pvr_test.c new file mode 100644 index 000000000000..506cfa5a02f1 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_test.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2025 Imagination Technologies Ltd. */ + +#include "pvr_device.h" + +#include +#include +#include +#include + +#include +#include + +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"); diff --git a/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c b/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c index 07e5f96202d4..398f3cce5532 100644 --- a/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c @@ -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); } diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 0face4dcaa36..1ea259854780 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -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; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c index e78eb0876f16..f32862bbe220 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c @@ -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; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h index de5e064585f8..cace3c5dc067 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h @@ -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; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c index d510ca8cfc4a..e17e2c2dce32 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c @@ -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, }; diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c index 0d7c68b29dff..fef1702acb14 100644 --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -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 = { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 46fd58646d32..98cd490e7ab0 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -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, }; diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c index aa05316dc57b..5386a06fcd08 100644 --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -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 diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c index 001b3543924a..227818e37390 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c @@ -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; diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index 5edd45424562..f74a0aa85ba8 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -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, }; diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c index e91caae7e353..a8190a6e8978 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c @@ -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); diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index 997429115068..9066ee2d1dff 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -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, }; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index a0099e4dd4ea..ec3b4fde10db 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -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, diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index ab0938ba61f7..a50f260c73e4 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -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 = { diff --git a/drivers/gpu/drm/tests/drm_buddy_test.c b/drivers/gpu/drm/tests/drm_buddy_test.c index 5f40b5343bd8..e6f8459c6c54 100644 --- a/drivers/gpu/drm/tests/drm_buddy_test.c +++ b/drivers/gpu/drm/tests/drm_buddy_test.c @@ -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), {} }; diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 3f44fe5e92e4..ec58fe064d86 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -5,6 +5,7 @@ #include +#include #include #include #include @@ -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) diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 22e2d959eb31..86860ad0861c 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -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 = { diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index 70f9aa702143..4bdcea3c7435 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -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 "); diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h index c59c8528a3f7..f4923157f5bf 100644 --- a/drivers/gpu/drm/tests/drm_kunit_edid.h +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h @@ -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): * diff --git a/drivers/gpu/drm/tyr/gpu.rs b/drivers/gpu/drm/tyr/gpu.rs index 6c582910dd5d..fb7ef7145402 100644 --- a/drivers/gpu/drm/tyr/gpu.rs +++ b/drivers/gpu/drm/tyr/gpu.rs @@ -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, diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c index c4316b768b3d..36aae97cf6da 100644 --- a/drivers/gpu/drm/v3d/v3d_bo.c +++ b/drivers/gpu/drm/v3d/v3d_bo.c @@ -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); diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index a11ca276061a..8de4f151a5c0 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -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"); } } diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 697b0b3ca92c..57965c0d6f6e 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -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); diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c index b55880fd6c50..1249f6e64b97 100644 --- a/drivers/gpu/drm/v3d/v3d_irq.c +++ b/drivers/gpu/drm/v3d/v3d_irq.c @@ -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; } diff --git a/drivers/gpu/drm/v3d/v3d_mmu.c b/drivers/gpu/drm/v3d/v3d_mmu.c index a25d25a8ae61..c513a393c031 100644 --- a/drivers/gpu/drm/v3d/v3d_mmu.c +++ b/drivers/gpu/drm/v3d/v3d_mmu.c @@ -18,6 +18,8 @@ * each client. This is not yet implemented. */ +#include + #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"); } diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 0867250db7a6..6dc871fc9a62 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -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; } diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 7de5a95ee7ca..794c3571662d 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -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; } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 1798d1156d10..9fe605a42df7 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -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 diff --git a/include/drm/bridge/inno_hdmi.h b/include/drm/bridge/inno_hdmi.h index 8b39655212e2..5bbcaeea94e2 100644 --- a/include/drm/bridge/inno_hdmi.h +++ b/include/drm/bridge/inno_hdmi.h @@ -6,6 +6,8 @@ #ifndef __INNO_HDMI__ #define __INNO_HDMI__ +#include + struct device; struct drm_encoder; struct drm_display_mode; diff --git a/include/drm/bridge/samsung-dsim.h b/include/drm/bridge/samsung-dsim.h index 31d7ed589233..03005e474704 100644 --- a/include/drm/bridge/samsung-dsim.h +++ b/include/drm/bridge/samsung-dsim.h @@ -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; diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3e6cbfa9dc44..73c23fece792 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -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), }; /** diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8f34f4b8183d..7eaec37ae1c7 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -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; }; /** diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 7f0256dae3f1..f2f2bf82eff9 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -5,6 +5,7 @@ #include #include #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE) +#include #include #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; diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 0bc492090237..91f4939db89b 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -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 }; /** diff --git a/include/linux/dma-heap.h b/include/linux/dma-heap.h index 27d15f60950a..648328a64b27 100644 --- a/include/linux/dma-heap.h +++ b/include/linux/dma-heap.h @@ -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 */ diff --git a/include/trace/events/dma_buf.h b/include/trace/events/dma_buf.h index 35f8140095f4..3bb88d05bcc8 100644 --- a/include/trace/events/dma_buf.h +++ b/include/trace/events/dma_buf.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,