mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 20:14:06 +02:00
drm-misc-next for 6.14:
UAPI Changes:
- Clarify drm memory stats documentation
Cross-subsystem Changes:
Core Changes:
- sched: Documentation fixes,
Driver Changes:
- amdgpu: Track BO memory stats at runtime
- amdxdna: Various fixes
- hisilicon: New HIBMC driver
- bridges:
- Provide default implementation of atomic_check for HDMI bridges
- it605: HDCP improvements, MCCS Support
-----BEGIN PGP SIGNATURE-----
iJUEABMJAB0WIQTkHFbLp4ejekA/qfgnX84Zoj2+dgUCZ3uZUQAKCRAnX84Zoj2+
dgM8AX4ur9y2eXLVQPS2IhCouWpFsYgSRnCysdVG43vszZ2kcObvlj4UV8nrrv7j
W6x9FZYBfRLm6ctAUnu05ppm3zSbSdmsocadu1mfoDbShy31Pc5xklnB1u6M3Asw
3mOtO5dxkA==
=QZ6P
-----END PGP SIGNATURE-----
Merge tag 'drm-misc-next-2025-01-06' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for 6.14:
UAPI Changes:
- Clarify drm memory stats documentation
Cross-subsystem Changes:
Core Changes:
- sched: Documentation fixes,
Driver Changes:
- amdgpu: Track BO memory stats at runtime
- amdxdna: Various fixes
- hisilicon: New HIBMC driver
- bridges:
- Provide default implementation of atomic_check for HDMI bridges
- it605: HDCP improvements, MCCS Support
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <mripard@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250106-augmented-kakapo-of-action-0cf000@houat
This commit is contained in:
commit
0739b8ba82
|
|
@ -290,6 +290,8 @@ properties:
|
|||
- tianma,tm070jvhg33
|
||||
# Tianma Micro-electronics TM070RVHG71 7.0" WXGA TFT LCD panel
|
||||
- tianma,tm070rvhg71
|
||||
# Topland TIAN-G07017-01 7.0" WSVGA TFT-LCD panel with capacitive touch
|
||||
- topland,tian-g07017-01
|
||||
# Toshiba 8.9" WXGA (1280x768) TFT LCD panel
|
||||
- toshiba,lt089ac29000
|
||||
# TPK U.S.A. LLC Fusion 7" 800 x 480 (WVGA) LCD panel with capacitive touch
|
||||
|
|
|
|||
|
|
@ -1524,6 +1524,8 @@ patternProperties:
|
|||
description: Topeet
|
||||
"^topic,.*":
|
||||
description: Topic Embedded Systems
|
||||
"^topland,.*":
|
||||
description: Topland Electronics (H.K) Co., Ltd.
|
||||
"^toppoly,.*":
|
||||
description: TPO (deprecated, use tpo)
|
||||
deprecated: true
|
||||
|
|
|
|||
|
|
@ -145,57 +145,57 @@ both.
|
|||
Memory
|
||||
^^^^^^
|
||||
|
||||
- drm-memory-<region>: <uint> [KiB|MiB]
|
||||
|
||||
Each possible memory type which can be used to store buffer objects by the
|
||||
GPU in question shall be given a stable and unique name to be returned as the
|
||||
string here.
|
||||
Each possible memory type which can be used to store buffer objects by the GPU
|
||||
in question shall be given a stable and unique name to be used as the "<region>"
|
||||
string.
|
||||
|
||||
The region name "memory" is reserved to refer to normal system memory.
|
||||
|
||||
Value shall reflect the amount of storage currently consumed by the buffer
|
||||
The value shall reflect the amount of storage currently consumed by the buffer
|
||||
objects belong to this client, in the respective memory region.
|
||||
|
||||
Default unit shall be bytes with optional unit specifiers of 'KiB' or 'MiB'
|
||||
indicating kibi- or mebi-bytes.
|
||||
|
||||
This key is deprecated and is an alias for drm-resident-<region>. Only one of
|
||||
the two should be present in the output.
|
||||
- drm-total-<region>: <uint> [KiB|MiB]
|
||||
|
||||
The total size of all requested buffers, including both shared and private
|
||||
memory. The backing store for the buffers does not need to be currently
|
||||
instantiated to count under this category. To avoid double-counting, if a buffer
|
||||
has multiple regions where it can be allocated to, the implementation should
|
||||
consistently select a single region for accounting purposes.
|
||||
|
||||
- drm-shared-<region>: <uint> [KiB|MiB]
|
||||
|
||||
The total size of buffers that are shared with another file (e.g., have more
|
||||
than a single handle).
|
||||
|
||||
- drm-total-<region>: <uint> [KiB|MiB]
|
||||
|
||||
The total size of all created buffers including shared and private memory. The
|
||||
backing store for the buffers does not have to be currently instantiated to be
|
||||
counted under this category.
|
||||
The total size of buffers that are shared with another file (i.e., have more
|
||||
than one handle). The same requirement to avoid double-counting that applies to
|
||||
drm-total-<region> also applies here.
|
||||
|
||||
- drm-resident-<region>: <uint> [KiB|MiB]
|
||||
|
||||
The total size of buffers that are resident (have their backing store present or
|
||||
instantiated) in the specified region.
|
||||
The total size of buffers that are resident (i.e., have their backing store
|
||||
present or instantiated) in the specified region.
|
||||
|
||||
This is an alias for drm-memory-<region> and only one of the two should be
|
||||
present in the output.
|
||||
- drm-memory-<region>: <uint> [KiB|MiB]
|
||||
|
||||
This key is deprecated and is only printed by amdgpu; it is an alias for
|
||||
drm-resident-<region>.
|
||||
|
||||
- drm-purgeable-<region>: <uint> [KiB|MiB]
|
||||
|
||||
The total size of buffers that are purgeable.
|
||||
The total size of buffers that are resident and purgeable.
|
||||
|
||||
For example drivers which implement a form of 'madvise' like functionality can
|
||||
here count buffers which have instantiated backing store, but have been marked
|
||||
with an equivalent of MADV_DONTNEED.
|
||||
For example, drivers that implement functionality similar to 'madvise' can count
|
||||
buffers that have instantiated backing stores but have been marked with an
|
||||
equivalent of MADV_DONTNEED.
|
||||
|
||||
- drm-active-<region>: <uint> [KiB|MiB]
|
||||
|
||||
The total size of buffers that are active on one or more engines.
|
||||
|
||||
One practical example of this can be presence of unsignaled fences in an GEM
|
||||
buffer reservation object. Therefore the active category is a subset of
|
||||
resident.
|
||||
One practical example of this could be the presence of unsignaled fences in a
|
||||
GEM buffer reservation object. Therefore, the active category is a subset of the
|
||||
resident category.
|
||||
|
||||
Implementation Details
|
||||
======================
|
||||
|
|
|
|||
|
|
@ -7081,7 +7081,8 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
|||
F: drivers/gpu/drm/sun4i/sun8i*
|
||||
|
||||
DRM DRIVER FOR ARM PL111 CLCD
|
||||
S: Orphan
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
S: Maintained
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/gpu/drm/pl111/
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include "amdxdna_mailbox.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
bool force_cmdlist;
|
||||
static bool force_cmdlist;
|
||||
module_param(force_cmdlist, bool, 0600);
|
||||
MODULE_PARM_DESC(force_cmdlist, "Force use command list (Default false)");
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include "amdxdna_mailbox.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
int aie2_max_col = XRS_MAX_COL;
|
||||
static int aie2_max_col = XRS_MAX_COL;
|
||||
module_param(aie2_max_col, uint, 0600);
|
||||
MODULE_PARM_DESC(aie2_max_col, "Maximum column could be used");
|
||||
|
||||
|
|
@ -380,9 +380,9 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
|
|||
goto stop_psp;
|
||||
}
|
||||
|
||||
mbox_res.ringbuf_base = (u64)ndev->sram_base;
|
||||
mbox_res.ringbuf_base = ndev->sram_base;
|
||||
mbox_res.ringbuf_size = pci_resource_len(pdev, xdna->dev_info->sram_bar);
|
||||
mbox_res.mbox_base = (u64)ndev->mbox_base;
|
||||
mbox_res.mbox_base = ndev->mbox_base;
|
||||
mbox_res.mbox_size = MBOX_SIZE(ndev);
|
||||
mbox_res.name = "xdna_mailbox";
|
||||
ndev->mbox = xdnam_mailbox_create(&xdna->ddev, &mbox_res);
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
|
|||
int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
|
||||
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
|
||||
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
|
||||
int aie2_query_status(struct amdxdna_dev_hdl *ndev, char *buf, u32 size, u32 *cols_filled);
|
||||
int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, u32 *cols_filled);
|
||||
int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
|
||||
void *handle, int (*cb)(void*, const u32 *, size_t));
|
||||
int aie2_config_cu(struct amdxdna_hwctx *hwctx);
|
||||
|
|
|
|||
|
|
@ -98,27 +98,27 @@ struct mailbox_msg {
|
|||
static void mailbox_reg_write(struct mailbox_channel *mb_chann, u32 mbox_reg, u32 data)
|
||||
{
|
||||
struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
|
||||
u64 ringbuf_addr = mb_res->mbox_base + mbox_reg;
|
||||
void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
|
||||
|
||||
writel(data, (void *)ringbuf_addr);
|
||||
writel(data, ringbuf_addr);
|
||||
}
|
||||
|
||||
static u32 mailbox_reg_read(struct mailbox_channel *mb_chann, u32 mbox_reg)
|
||||
{
|
||||
struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
|
||||
u64 ringbuf_addr = mb_res->mbox_base + mbox_reg;
|
||||
void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
|
||||
|
||||
return readl((void *)ringbuf_addr);
|
||||
return readl(ringbuf_addr);
|
||||
}
|
||||
|
||||
static int mailbox_reg_read_non_zero(struct mailbox_channel *mb_chann, u32 mbox_reg, u32 *val)
|
||||
{
|
||||
struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
|
||||
u64 ringbuf_addr = mb_res->mbox_base + mbox_reg;
|
||||
void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
|
||||
int ret, value;
|
||||
|
||||
/* Poll till value is not zero */
|
||||
ret = readx_poll_timeout(readl, (void *)ringbuf_addr, value,
|
||||
ret = readx_poll_timeout(readl, ringbuf_addr, value,
|
||||
value, 1 /* us */, 100);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -200,10 +200,10 @@ static void mailbox_release_msg(struct mailbox_channel *mb_chann,
|
|||
static int
|
||||
mailbox_send_msg(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
|
||||
{
|
||||
void __iomem *write_addr;
|
||||
u32 ringbuf_size;
|
||||
u32 head, tail;
|
||||
u32 start_addr;
|
||||
u64 write_addr;
|
||||
u32 tmp_tail;
|
||||
|
||||
head = mailbox_get_headptr(mb_chann, CHAN_RES_X2I);
|
||||
|
|
@ -221,14 +221,14 @@ mailbox_send_msg(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
|
|||
|
||||
if (tail >= head && tmp_tail > ringbuf_size - sizeof(u32)) {
|
||||
write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
|
||||
writel(TOMBSTONE, (void *)write_addr);
|
||||
writel(TOMBSTONE, write_addr);
|
||||
|
||||
/* tombstone is set. Write from the start of the ringbuf */
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
|
||||
memcpy_toio((void *)write_addr, &mb_msg->pkg, mb_msg->pkg_size);
|
||||
memcpy_toio(write_addr, &mb_msg->pkg, mb_msg->pkg_size);
|
||||
mailbox_set_tailptr(mb_chann, tail + mb_msg->pkg_size);
|
||||
|
||||
trace_mbox_set_tail(MAILBOX_NAME, mb_chann->msix_irq,
|
||||
|
|
@ -275,11 +275,11 @@ mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *heade
|
|||
static int mailbox_get_msg(struct mailbox_channel *mb_chann)
|
||||
{
|
||||
struct xdna_msg_header header;
|
||||
void __iomem *read_addr;
|
||||
u32 msg_size, rest;
|
||||
u32 ringbuf_size;
|
||||
u32 head, tail;
|
||||
u32 start_addr;
|
||||
u64 read_addr;
|
||||
int ret;
|
||||
|
||||
if (mailbox_reg_read_non_zero(mb_chann, mb_chann->res[CHAN_RES_I2X].mb_tail_ptr_reg, &tail))
|
||||
|
|
@ -302,7 +302,7 @@ static int mailbox_get_msg(struct mailbox_channel *mb_chann)
|
|||
|
||||
/* Peek size of the message or TOMBSTONE */
|
||||
read_addr = mb_chann->mb->res.ringbuf_base + start_addr + head;
|
||||
header.total_size = readl((void *)read_addr);
|
||||
header.total_size = readl(read_addr);
|
||||
/* size is TOMBSTONE, set next read from 0 */
|
||||
if (header.total_size == TOMBSTONE) {
|
||||
if (head < tail) {
|
||||
|
|
@ -328,7 +328,7 @@ static int mailbox_get_msg(struct mailbox_channel *mb_chann)
|
|||
|
||||
rest = sizeof(header) - sizeof(u32);
|
||||
read_addr += sizeof(u32);
|
||||
memcpy_fromio((u32 *)&header + 1, (void *)read_addr, rest);
|
||||
memcpy_fromio((u32 *)&header + 1, read_addr, rest);
|
||||
read_addr += rest;
|
||||
|
||||
ret = mailbox_get_resp(mb_chann, &header, (u32 *)read_addr);
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ struct xdna_mailbox_msg {
|
|||
* @mbox_size: mailbox size
|
||||
*/
|
||||
struct xdna_mailbox_res {
|
||||
u64 ringbuf_base;
|
||||
void __iomem *ringbuf_base;
|
||||
size_t ringbuf_size;
|
||||
u64 mbox_base;
|
||||
void __iomem *mbox_base;
|
||||
size_t mbox_size;
|
||||
const char *name;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
|
|||
goto put_rpm;
|
||||
}
|
||||
|
||||
client->pid = pid_nr(filp->pid);
|
||||
client->pid = pid_nr(rcu_access_pointer(filp->pid));
|
||||
client->xdna = xdna;
|
||||
|
||||
client->sva = iommu_sva_bind_device(xdna->ddev.dev, current->mm);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ const struct dpm_clk_freq npu1_dpm_clk_table[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
const struct amdxdna_dev_priv npu1_dev_priv = {
|
||||
static const struct amdxdna_dev_priv npu1_dev_priv = {
|
||||
.fw_path = "amdnpu/1502_00/npu.sbin",
|
||||
.protocol_major = 0x5,
|
||||
.protocol_minor = 0x7,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
#define NPU2_SMU_BAR_BASE MMNPU_APERTURE4_BASE
|
||||
#define NPU2_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
|
||||
|
||||
const struct amdxdna_dev_priv npu2_dev_priv = {
|
||||
static const struct amdxdna_dev_priv npu2_dev_priv = {
|
||||
.fw_path = "amdnpu/17f0_00/npu.sbin",
|
||||
.protocol_major = 0x6,
|
||||
.protocol_minor = 0x6,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const struct dpm_clk_freq npu4_dpm_clk_table[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
const struct amdxdna_dev_priv npu4_dev_priv = {
|
||||
static const struct amdxdna_dev_priv npu4_dev_priv = {
|
||||
.fw_path = "amdnpu/17f0_10/npu.sbin",
|
||||
.protocol_major = 0x6,
|
||||
.protocol_minor = 12,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
#define NPU5_SMU_BAR_BASE MMNPU_APERTURE4_BASE
|
||||
#define NPU5_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
|
||||
|
||||
const struct amdxdna_dev_priv npu5_dev_priv = {
|
||||
static const struct amdxdna_dev_priv npu5_dev_priv = {
|
||||
.fw_path = "amdnpu/17f0_11/npu.sbin",
|
||||
.protocol_major = 0x6,
|
||||
.protocol_minor = 12,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
#define NPU6_SMU_BAR_BASE MMNPU_APERTURE4_BASE
|
||||
#define NPU6_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
|
||||
|
||||
const struct amdxdna_dev_priv npu6_dev_priv = {
|
||||
static const struct amdxdna_dev_priv npu6_dev_priv = {
|
||||
.fw_path = "amdnpu/17f0_10/npu.sbin",
|
||||
.protocol_major = 0x6,
|
||||
.protocol_minor = 12,
|
||||
|
|
|
|||
|
|
@ -1105,7 +1105,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
|
|||
* We can't use gang submit on with reserved VMIDs when the VM changes
|
||||
* can't be invalidated by more than one engine at the same time.
|
||||
*/
|
||||
if (p->gang_size > 1 && !p->adev->vm_manager.concurrent_flush) {
|
||||
if (p->gang_size > 1 && !adev->vm_manager.concurrent_flush) {
|
||||
for (i = 0; i < p->gang_size; ++i) {
|
||||
struct drm_sched_entity *entity = p->entities[i];
|
||||
struct drm_gpu_scheduler *sched = entity->rq->sched;
|
||||
|
|
@ -1189,7 +1189,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
|
|||
if (!bo)
|
||||
continue;
|
||||
|
||||
amdgpu_vm_bo_invalidate(adev, bo, false);
|
||||
amdgpu_vm_bo_invalidate(bo, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include "amdgpu_gem.h"
|
||||
#include "amdgpu_dma_buf.h"
|
||||
#include "amdgpu_xgmi.h"
|
||||
#include "amdgpu_vm.h"
|
||||
#include <drm/amdgpu_drm.h>
|
||||
#include <drm/ttm/ttm_tt.h>
|
||||
#include <linux/dma-buf.h>
|
||||
|
|
@ -60,6 +61,8 @@ static int amdgpu_dma_buf_attach(struct dma_buf *dmabuf,
|
|||
if (pci_p2pdma_distance(adev->pdev, attach->dev, false) < 0)
|
||||
attach->peer2peer = false;
|
||||
|
||||
amdgpu_vm_bo_update_shared(bo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -345,7 +348,7 @@ amdgpu_dma_buf_move_notify(struct dma_buf_attachment *attach)
|
|||
/* FIXME: This should be after the "if", but needs a fix to make sure
|
||||
* DMABuf imports are initialized in the right VM list.
|
||||
*/
|
||||
amdgpu_vm_bo_invalidate(adev, bo, false);
|
||||
amdgpu_vm_bo_invalidate(bo, false);
|
||||
if (!bo->tbo.resource || bo->tbo.resource->mem_type == TTM_PL_SYSTEM)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ void amdgpu_show_fdinfo(struct drm_printer *p, struct drm_file *file)
|
|||
struct amdgpu_fpriv *fpriv = file->driver_priv;
|
||||
struct amdgpu_vm *vm = &fpriv->vm;
|
||||
|
||||
struct amdgpu_mem_stats stats[__AMDGPU_PL_LAST + 1] = { };
|
||||
struct amdgpu_mem_stats stats[__AMDGPU_PL_NUM];
|
||||
ktime_t usage[AMDGPU_HW_IP_NUM];
|
||||
const char *pl_name[] = {
|
||||
[TTM_PL_VRAM] = "vram",
|
||||
|
|
@ -72,15 +72,8 @@ void amdgpu_show_fdinfo(struct drm_printer *p, struct drm_file *file)
|
|||
[AMDGPU_PL_DOORBELL] = "doorbell",
|
||||
};
|
||||
unsigned int hw_ip, i;
|
||||
int ret;
|
||||
|
||||
ret = amdgpu_bo_reserve(vm->root.bo, false);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
amdgpu_vm_get_memory(vm, stats, ARRAY_SIZE(stats));
|
||||
amdgpu_bo_unreserve(vm->root.bo);
|
||||
|
||||
amdgpu_vm_get_memory(vm, stats);
|
||||
amdgpu_ctx_mgr_usage(&fpriv->ctx_mgr, usage);
|
||||
|
||||
/*
|
||||
|
|
@ -114,9 +107,11 @@ void amdgpu_show_fdinfo(struct drm_printer *p, struct drm_file *file)
|
|||
drm_printf(p, "amd-evicted-vram:\t%llu KiB\n",
|
||||
stats[TTM_PL_VRAM].evicted/1024UL);
|
||||
drm_printf(p, "amd-requested-vram:\t%llu KiB\n",
|
||||
stats[TTM_PL_VRAM].requested/1024UL);
|
||||
(stats[TTM_PL_VRAM].drm.shared +
|
||||
stats[TTM_PL_VRAM].drm.private) / 1024UL);
|
||||
drm_printf(p, "amd-requested-gtt:\t%llu KiB\n",
|
||||
stats[TTM_PL_TT].requested/1024UL);
|
||||
(stats[TTM_PL_TT].drm.shared +
|
||||
stats[TTM_PL_TT].drm.private) / 1024UL);
|
||||
|
||||
for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) {
|
||||
if (!usage[hw_ip])
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include "amdgpu_dma_buf.h"
|
||||
#include "amdgpu_hmm.h"
|
||||
#include "amdgpu_xgmi.h"
|
||||
#include "amdgpu_vm.h"
|
||||
|
||||
static vm_fault_t amdgpu_gem_fault(struct vm_fault *vmf)
|
||||
{
|
||||
|
|
@ -179,6 +180,7 @@ static int amdgpu_gem_object_open(struct drm_gem_object *obj,
|
|||
if (r)
|
||||
return r;
|
||||
|
||||
amdgpu_vm_bo_update_shared(abo);
|
||||
bo_va = amdgpu_vm_bo_find(vm, abo);
|
||||
if (!bo_va)
|
||||
bo_va = amdgpu_vm_bo_add(adev, vm, abo);
|
||||
|
|
@ -252,6 +254,7 @@ static void amdgpu_gem_object_close(struct drm_gem_object *obj,
|
|||
goto out_unlock;
|
||||
|
||||
amdgpu_vm_bo_del(adev, bo_va);
|
||||
amdgpu_vm_bo_update_shared(bo);
|
||||
if (!amdgpu_vm_ready(vm))
|
||||
goto out_unlock;
|
||||
|
||||
|
|
@ -839,7 +842,6 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
|
|||
int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *filp)
|
||||
{
|
||||
struct amdgpu_device *adev = drm_to_adev(dev);
|
||||
struct drm_amdgpu_gem_op *args = data;
|
||||
struct drm_gem_object *gobj;
|
||||
struct amdgpu_vm_bo_base *base;
|
||||
|
|
@ -899,7 +901,7 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data,
|
|||
robj->allowed_domains |= AMDGPU_GEM_DOMAIN_GTT;
|
||||
|
||||
if (robj->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID)
|
||||
amdgpu_vm_bo_invalidate(adev, robj, true);
|
||||
amdgpu_vm_bo_invalidate(robj, true);
|
||||
|
||||
amdgpu_bo_unreserve(robj);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1251,7 +1251,6 @@ void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
|
|||
bool evict,
|
||||
struct ttm_resource *new_mem)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
|
||||
struct ttm_resource *old_mem = bo->resource;
|
||||
struct amdgpu_bo *abo;
|
||||
|
||||
|
|
@ -1259,7 +1258,7 @@ void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
|
|||
return;
|
||||
|
||||
abo = ttm_to_amdgpu_bo(bo);
|
||||
amdgpu_vm_bo_invalidate(adev, abo, evict);
|
||||
amdgpu_vm_bo_move(abo, new_mem, evict);
|
||||
|
||||
amdgpu_bo_kunmap(abo);
|
||||
|
||||
|
|
@ -1272,75 +1271,6 @@ void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
|
|||
old_mem ? old_mem->mem_type : -1);
|
||||
}
|
||||
|
||||
void amdgpu_bo_get_memory(struct amdgpu_bo *bo,
|
||||
struct amdgpu_mem_stats *stats,
|
||||
unsigned int sz)
|
||||
{
|
||||
const unsigned int domain_to_pl[] = {
|
||||
[ilog2(AMDGPU_GEM_DOMAIN_CPU)] = TTM_PL_SYSTEM,
|
||||
[ilog2(AMDGPU_GEM_DOMAIN_GTT)] = TTM_PL_TT,
|
||||
[ilog2(AMDGPU_GEM_DOMAIN_VRAM)] = TTM_PL_VRAM,
|
||||
[ilog2(AMDGPU_GEM_DOMAIN_GDS)] = AMDGPU_PL_GDS,
|
||||
[ilog2(AMDGPU_GEM_DOMAIN_GWS)] = AMDGPU_PL_GWS,
|
||||
[ilog2(AMDGPU_GEM_DOMAIN_OA)] = AMDGPU_PL_OA,
|
||||
[ilog2(AMDGPU_GEM_DOMAIN_DOORBELL)] = AMDGPU_PL_DOORBELL,
|
||||
};
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
|
||||
struct ttm_resource *res = bo->tbo.resource;
|
||||
struct drm_gem_object *obj = &bo->tbo.base;
|
||||
uint64_t size = amdgpu_bo_size(bo);
|
||||
unsigned int type;
|
||||
|
||||
if (!res) {
|
||||
/*
|
||||
* If no backing store use one of the preferred domain for basic
|
||||
* stats. We take the MSB since that should give a reasonable
|
||||
* view.
|
||||
*/
|
||||
BUILD_BUG_ON(TTM_PL_VRAM < TTM_PL_TT ||
|
||||
TTM_PL_VRAM < TTM_PL_SYSTEM);
|
||||
type = fls(bo->preferred_domains & AMDGPU_GEM_DOMAIN_MASK);
|
||||
if (!type)
|
||||
return;
|
||||
type--;
|
||||
if (drm_WARN_ON_ONCE(&adev->ddev,
|
||||
type >= ARRAY_SIZE(domain_to_pl)))
|
||||
return;
|
||||
type = domain_to_pl[type];
|
||||
} else {
|
||||
type = res->mem_type;
|
||||
}
|
||||
|
||||
if (drm_WARN_ON_ONCE(&adev->ddev, type >= sz))
|
||||
return;
|
||||
|
||||
/* DRM stats common fields: */
|
||||
|
||||
if (drm_gem_object_is_shared_for_memory_stats(obj))
|
||||
stats[type].drm.shared += size;
|
||||
else
|
||||
stats[type].drm.private += size;
|
||||
|
||||
if (res) {
|
||||
stats[type].drm.resident += size;
|
||||
|
||||
if (!dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_BOOKKEEP))
|
||||
stats[type].drm.active += size;
|
||||
else if (bo->flags & AMDGPU_GEM_CREATE_DISCARDABLE)
|
||||
stats[type].drm.purgeable += size;
|
||||
}
|
||||
|
||||
/* amdgpu specific stats: */
|
||||
|
||||
if (bo->preferred_domains & AMDGPU_GEM_DOMAIN_VRAM) {
|
||||
stats[TTM_PL_VRAM].requested += size;
|
||||
if (type != TTM_PL_VRAM)
|
||||
stats[TTM_PL_VRAM].evicted += size;
|
||||
} else if (bo->preferred_domains & AMDGPU_GEM_DOMAIN_GTT) {
|
||||
stats[TTM_PL_TT].requested += size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_release_notify - notification about a BO being released
|
||||
* @bo: pointer to a buffer object
|
||||
|
|
@ -1555,6 +1485,45 @@ u64 amdgpu_bo_gpu_offset_no_check(struct amdgpu_bo *bo)
|
|||
return amdgpu_gmc_sign_extend(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_mem_stats_placement - bo placement for memory accounting
|
||||
* @bo: the buffer object we should look at
|
||||
*
|
||||
* BO can have multiple preferred placements, to avoid double counting we want
|
||||
* to file it under a single placement for memory stats.
|
||||
* Luckily, if we take the highest set bit in preferred_domains the result is
|
||||
* quite sensible.
|
||||
*
|
||||
* Returns:
|
||||
* Which of the placements should the BO be accounted under.
|
||||
*/
|
||||
uint32_t amdgpu_bo_mem_stats_placement(struct amdgpu_bo *bo)
|
||||
{
|
||||
uint32_t domain = bo->preferred_domains & AMDGPU_GEM_DOMAIN_MASK;
|
||||
|
||||
if (!domain)
|
||||
return TTM_PL_SYSTEM;
|
||||
|
||||
switch (rounddown_pow_of_two(domain)) {
|
||||
case AMDGPU_GEM_DOMAIN_CPU:
|
||||
return TTM_PL_SYSTEM;
|
||||
case AMDGPU_GEM_DOMAIN_GTT:
|
||||
return TTM_PL_TT;
|
||||
case AMDGPU_GEM_DOMAIN_VRAM:
|
||||
return TTM_PL_VRAM;
|
||||
case AMDGPU_GEM_DOMAIN_GDS:
|
||||
return AMDGPU_PL_GDS;
|
||||
case AMDGPU_GEM_DOMAIN_GWS:
|
||||
return AMDGPU_PL_GWS;
|
||||
case AMDGPU_GEM_DOMAIN_OA:
|
||||
return AMDGPU_PL_OA;
|
||||
case AMDGPU_GEM_DOMAIN_DOORBELL:
|
||||
return AMDGPU_PL_DOORBELL;
|
||||
default:
|
||||
return TTM_PL_SYSTEM;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_bo_get_preferred_domain - get preferred domain
|
||||
* @adev: amdgpu device object
|
||||
|
|
|
|||
|
|
@ -305,9 +305,7 @@ int amdgpu_bo_sync_wait_resv(struct amdgpu_device *adev, struct dma_resv *resv,
|
|||
int amdgpu_bo_sync_wait(struct amdgpu_bo *bo, void *owner, bool intr);
|
||||
u64 amdgpu_bo_gpu_offset(struct amdgpu_bo *bo);
|
||||
u64 amdgpu_bo_gpu_offset_no_check(struct amdgpu_bo *bo);
|
||||
void amdgpu_bo_get_memory(struct amdgpu_bo *bo,
|
||||
struct amdgpu_mem_stats *stats,
|
||||
unsigned int size);
|
||||
uint32_t amdgpu_bo_mem_stats_placement(struct amdgpu_bo *bo);
|
||||
uint32_t amdgpu_bo_get_preferred_domain(struct amdgpu_device *adev,
|
||||
uint32_t domain);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,15 +26,15 @@
|
|||
|
||||
#include <linux/dma-direction.h>
|
||||
#include <drm/gpu_scheduler.h>
|
||||
#include <drm/ttm/ttm_placement.h>
|
||||
#include "amdgpu_vram_mgr.h"
|
||||
#include "amdgpu.h"
|
||||
|
||||
#define AMDGPU_PL_GDS (TTM_PL_PRIV + 0)
|
||||
#define AMDGPU_PL_GWS (TTM_PL_PRIV + 1)
|
||||
#define AMDGPU_PL_OA (TTM_PL_PRIV + 2)
|
||||
#define AMDGPU_PL_PREEMPT (TTM_PL_PRIV + 3)
|
||||
#define AMDGPU_PL_DOORBELL (TTM_PL_PRIV + 4)
|
||||
#define __AMDGPU_PL_LAST (TTM_PL_PRIV + 4)
|
||||
#define __AMDGPU_PL_NUM (TTM_PL_PRIV + 5)
|
||||
|
||||
#define AMDGPU_GTT_MAX_TRANSFER_SIZE 512
|
||||
#define AMDGPU_GTT_NUM_TRANSFER_WINDOWS 2
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <drm/ttm/ttm_tt.h>
|
||||
#include <drm/drm_exec.h>
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_vm.h"
|
||||
#include "amdgpu_trace.h"
|
||||
#include "amdgpu_amdkfd.h"
|
||||
#include "amdgpu_gmc.h"
|
||||
|
|
@ -310,6 +311,111 @@ static void amdgpu_vm_bo_reset_state_machine(struct amdgpu_vm *vm)
|
|||
spin_unlock(&vm->status_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_update_shared - helper to update shared memory stat
|
||||
* @base: base structure for tracking BO usage in a VM
|
||||
*
|
||||
* Takes the vm status_lock and updates the shared memory stat. If the basic
|
||||
* stat changed (e.g. buffer was moved) amdgpu_vm_update_stats need to be called
|
||||
* as well.
|
||||
*/
|
||||
static void amdgpu_vm_update_shared(struct amdgpu_vm_bo_base *base)
|
||||
{
|
||||
struct amdgpu_vm *vm = base->vm;
|
||||
struct amdgpu_bo *bo = base->bo;
|
||||
uint64_t size = amdgpu_bo_size(bo);
|
||||
uint32_t bo_memtype = amdgpu_bo_mem_stats_placement(bo);
|
||||
bool shared;
|
||||
|
||||
spin_lock(&vm->status_lock);
|
||||
shared = drm_gem_object_is_shared_for_memory_stats(&bo->tbo.base);
|
||||
if (base->shared != shared) {
|
||||
base->shared = shared;
|
||||
if (shared) {
|
||||
vm->stats[bo_memtype].drm.shared += size;
|
||||
vm->stats[bo_memtype].drm.private -= size;
|
||||
} else {
|
||||
vm->stats[bo_memtype].drm.shared -= size;
|
||||
vm->stats[bo_memtype].drm.private += size;
|
||||
}
|
||||
}
|
||||
spin_unlock(&vm->status_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_bo_update_shared - callback when bo gets shared/unshared
|
||||
* @bo: amdgpu buffer object
|
||||
*
|
||||
* Update the per VM stats for all the vm if needed from private to shared or
|
||||
* vice versa.
|
||||
*/
|
||||
void amdgpu_vm_bo_update_shared(struct amdgpu_bo *bo)
|
||||
{
|
||||
struct amdgpu_vm_bo_base *base;
|
||||
|
||||
for (base = bo->vm_bo; base; base = base->next)
|
||||
amdgpu_vm_update_shared(base);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_update_stats_locked - helper to update normal memory stat
|
||||
* @base: base structure for tracking BO usage in a VM
|
||||
* @res: the ttm_resource to use for the purpose of accounting, may or may not
|
||||
* be bo->tbo.resource
|
||||
* @sign: if we should add (+1) or subtract (-1) from the stat
|
||||
*
|
||||
* Caller need to have the vm status_lock held. Useful for when multiple update
|
||||
* need to happen at the same time.
|
||||
*/
|
||||
static void amdgpu_vm_update_stats_locked(struct amdgpu_vm_bo_base *base,
|
||||
struct ttm_resource *res, int sign)
|
||||
{
|
||||
struct amdgpu_vm *vm = base->vm;
|
||||
struct amdgpu_bo *bo = base->bo;
|
||||
int64_t size = sign * amdgpu_bo_size(bo);
|
||||
uint32_t bo_memtype = amdgpu_bo_mem_stats_placement(bo);
|
||||
|
||||
/* For drm-total- and drm-shared-, BO are accounted by their preferred
|
||||
* placement, see also amdgpu_bo_mem_stats_placement.
|
||||
*/
|
||||
if (base->shared)
|
||||
vm->stats[bo_memtype].drm.shared += size;
|
||||
else
|
||||
vm->stats[bo_memtype].drm.private += size;
|
||||
|
||||
if (res && res->mem_type < __AMDGPU_PL_NUM) {
|
||||
uint32_t res_memtype = res->mem_type;
|
||||
|
||||
vm->stats[res_memtype].drm.resident += size;
|
||||
/* BO only count as purgeable if it is resident,
|
||||
* since otherwise there's nothing to purge.
|
||||
*/
|
||||
if (bo->flags & AMDGPU_GEM_CREATE_DISCARDABLE)
|
||||
vm->stats[res_memtype].drm.purgeable += size;
|
||||
if (!(bo->preferred_domains & amdgpu_mem_type_to_domain(res_memtype)))
|
||||
vm->stats[bo_memtype].evicted += size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_update_stats - helper to update normal memory stat
|
||||
* @base: base structure for tracking BO usage in a VM
|
||||
* @res: the ttm_resource to use for the purpose of accounting, may or may not
|
||||
* be bo->tbo.resource
|
||||
* @sign: if we should add (+1) or subtract (-1) from the stat
|
||||
*
|
||||
* Updates the basic memory stat when bo is added/deleted/moved.
|
||||
*/
|
||||
void amdgpu_vm_update_stats(struct amdgpu_vm_bo_base *base,
|
||||
struct ttm_resource *res, int sign)
|
||||
{
|
||||
struct amdgpu_vm *vm = base->vm;
|
||||
|
||||
spin_lock(&vm->status_lock);
|
||||
amdgpu_vm_update_stats_locked(base, res, sign);
|
||||
spin_unlock(&vm->status_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_bo_base_init - Adds bo to the list of bos associated with the vm
|
||||
*
|
||||
|
|
@ -333,6 +439,11 @@ void amdgpu_vm_bo_base_init(struct amdgpu_vm_bo_base *base,
|
|||
base->next = bo->vm_bo;
|
||||
bo->vm_bo = base;
|
||||
|
||||
spin_lock(&vm->status_lock);
|
||||
base->shared = drm_gem_object_is_shared_for_memory_stats(&bo->tbo.base);
|
||||
amdgpu_vm_update_stats_locked(base, bo->tbo.resource, +1);
|
||||
spin_unlock(&vm->status_lock);
|
||||
|
||||
if (!amdgpu_vm_is_bo_always_valid(vm, bo))
|
||||
return;
|
||||
|
||||
|
|
@ -1083,53 +1194,11 @@ int amdgpu_vm_update_range(struct amdgpu_device *adev, struct amdgpu_vm *vm,
|
|||
return r;
|
||||
}
|
||||
|
||||
static void amdgpu_vm_bo_get_memory(struct amdgpu_bo_va *bo_va,
|
||||
struct amdgpu_mem_stats *stats,
|
||||
unsigned int size)
|
||||
{
|
||||
struct amdgpu_vm *vm = bo_va->base.vm;
|
||||
struct amdgpu_bo *bo = bo_va->base.bo;
|
||||
|
||||
if (!bo)
|
||||
return;
|
||||
|
||||
/*
|
||||
* For now ignore BOs which are currently locked and potentially
|
||||
* changing their location.
|
||||
*/
|
||||
if (!amdgpu_vm_is_bo_always_valid(vm, bo) &&
|
||||
!dma_resv_trylock(bo->tbo.base.resv))
|
||||
return;
|
||||
|
||||
amdgpu_bo_get_memory(bo, stats, size);
|
||||
if (!amdgpu_vm_is_bo_always_valid(vm, bo))
|
||||
dma_resv_unlock(bo->tbo.base.resv);
|
||||
}
|
||||
|
||||
void amdgpu_vm_get_memory(struct amdgpu_vm *vm,
|
||||
struct amdgpu_mem_stats *stats,
|
||||
unsigned int size)
|
||||
struct amdgpu_mem_stats stats[__AMDGPU_PL_NUM])
|
||||
{
|
||||
struct amdgpu_bo_va *bo_va, *tmp;
|
||||
|
||||
spin_lock(&vm->status_lock);
|
||||
list_for_each_entry_safe(bo_va, tmp, &vm->idle, base.vm_status)
|
||||
amdgpu_vm_bo_get_memory(bo_va, stats, size);
|
||||
|
||||
list_for_each_entry_safe(bo_va, tmp, &vm->evicted, base.vm_status)
|
||||
amdgpu_vm_bo_get_memory(bo_va, stats, size);
|
||||
|
||||
list_for_each_entry_safe(bo_va, tmp, &vm->relocated, base.vm_status)
|
||||
amdgpu_vm_bo_get_memory(bo_va, stats, size);
|
||||
|
||||
list_for_each_entry_safe(bo_va, tmp, &vm->moved, base.vm_status)
|
||||
amdgpu_vm_bo_get_memory(bo_va, stats, size);
|
||||
|
||||
list_for_each_entry_safe(bo_va, tmp, &vm->invalidated, base.vm_status)
|
||||
amdgpu_vm_bo_get_memory(bo_va, stats, size);
|
||||
|
||||
list_for_each_entry_safe(bo_va, tmp, &vm->done, base.vm_status)
|
||||
amdgpu_vm_bo_get_memory(bo_va, stats, size);
|
||||
memcpy(stats, vm->stats, sizeof(*stats) * __AMDGPU_PL_NUM);
|
||||
spin_unlock(&vm->status_lock);
|
||||
}
|
||||
|
||||
|
|
@ -2075,6 +2144,7 @@ void amdgpu_vm_bo_del(struct amdgpu_device *adev,
|
|||
if (*base != &bo_va->base)
|
||||
continue;
|
||||
|
||||
amdgpu_vm_update_stats(*base, bo->tbo.resource, -1);
|
||||
*base = bo_va->base.next;
|
||||
break;
|
||||
}
|
||||
|
|
@ -2143,14 +2213,12 @@ bool amdgpu_vm_evictable(struct amdgpu_bo *bo)
|
|||
/**
|
||||
* amdgpu_vm_bo_invalidate - mark the bo as invalid
|
||||
*
|
||||
* @adev: amdgpu_device pointer
|
||||
* @bo: amdgpu buffer object
|
||||
* @evicted: is the BO evicted
|
||||
*
|
||||
* Mark @bo as invalid.
|
||||
*/
|
||||
void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev,
|
||||
struct amdgpu_bo *bo, bool evicted)
|
||||
void amdgpu_vm_bo_invalidate(struct amdgpu_bo *bo, bool evicted)
|
||||
{
|
||||
struct amdgpu_vm_bo_base *bo_base;
|
||||
|
||||
|
|
@ -2175,6 +2243,32 @@ void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_bo_move - handle BO move
|
||||
*
|
||||
* @bo: amdgpu buffer object
|
||||
* @new_mem: the new placement of the BO move
|
||||
* @evicted: is the BO evicted
|
||||
*
|
||||
* Update the memory stats for the new placement and mark @bo as invalid.
|
||||
*/
|
||||
void amdgpu_vm_bo_move(struct amdgpu_bo *bo, struct ttm_resource *new_mem,
|
||||
bool evicted)
|
||||
{
|
||||
struct amdgpu_vm_bo_base *bo_base;
|
||||
|
||||
for (bo_base = bo->vm_bo; bo_base; bo_base = bo_base->next) {
|
||||
struct amdgpu_vm *vm = bo_base->vm;
|
||||
|
||||
spin_lock(&vm->status_lock);
|
||||
amdgpu_vm_update_stats_locked(bo_base, bo->tbo.resource, -1);
|
||||
amdgpu_vm_update_stats_locked(bo_base, new_mem, +1);
|
||||
spin_unlock(&vm->status_lock);
|
||||
}
|
||||
|
||||
amdgpu_vm_bo_invalidate(bo, evicted);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_get_block_size - calculate VM page table size as power of two
|
||||
*
|
||||
|
|
@ -2594,6 +2688,16 @@ void amdgpu_vm_release_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm)
|
|||
vm->is_compute_context = false;
|
||||
}
|
||||
|
||||
static int amdgpu_vm_stats_is_zero(struct amdgpu_vm *vm)
|
||||
{
|
||||
for (int i = 0; i < __AMDGPU_PL_NUM; ++i) {
|
||||
if (!(drm_memory_stats_is_zero(&vm->stats[i].drm) &&
|
||||
vm->stats[i].evicted == 0))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_fini - tear down a vm instance
|
||||
*
|
||||
|
|
@ -2617,7 +2721,6 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
|
|||
|
||||
root = amdgpu_bo_ref(vm->root.bo);
|
||||
amdgpu_bo_reserve(root, true);
|
||||
amdgpu_vm_put_task_info(vm->task_info);
|
||||
amdgpu_vm_set_pasid(adev, vm, 0);
|
||||
dma_fence_wait(vm->last_unlocked, false);
|
||||
dma_fence_put(vm->last_unlocked);
|
||||
|
|
@ -2666,6 +2769,16 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
|
|||
}
|
||||
|
||||
ttm_lru_bulk_move_fini(&adev->mman.bdev, &vm->lru_bulk_move);
|
||||
|
||||
if (!amdgpu_vm_stats_is_zero(vm)) {
|
||||
struct amdgpu_task_info *ti = vm->task_info;
|
||||
|
||||
dev_warn(adev->dev,
|
||||
"VM memory stats for proc %s(%d) task %s(%d) is non-zero when fini\n",
|
||||
ti->process_name, ti->pid, ti->task_name, ti->tgid);
|
||||
}
|
||||
|
||||
amdgpu_vm_put_task_info(vm->task_info);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "amdgpu_sync.h"
|
||||
#include "amdgpu_ring.h"
|
||||
#include "amdgpu_ids.h"
|
||||
#include "amdgpu_ttm.h"
|
||||
|
||||
struct drm_exec;
|
||||
|
||||
|
|
@ -202,9 +203,13 @@ struct amdgpu_vm_bo_base {
|
|||
/* protected by bo being reserved */
|
||||
struct amdgpu_vm_bo_base *next;
|
||||
|
||||
/* protected by spinlock */
|
||||
/* protected by vm status_lock */
|
||||
struct list_head vm_status;
|
||||
|
||||
/* if the bo is counted as shared in mem stats
|
||||
* protected by vm status_lock */
|
||||
bool shared;
|
||||
|
||||
/* protected by the BO being reserved */
|
||||
bool moved;
|
||||
};
|
||||
|
|
@ -324,10 +329,7 @@ struct amdgpu_vm_fault_info {
|
|||
struct amdgpu_mem_stats {
|
||||
struct drm_memory_stats drm;
|
||||
|
||||
/* buffers that requested this placement */
|
||||
uint64_t requested;
|
||||
/* buffers that requested this placement
|
||||
* but are currently evicted */
|
||||
/* buffers that requested this placement but are currently evicted */
|
||||
uint64_t evicted;
|
||||
};
|
||||
|
||||
|
|
@ -345,6 +347,9 @@ struct amdgpu_vm {
|
|||
/* Lock to protect vm_bo add/del/move on all lists of vm */
|
||||
spinlock_t status_lock;
|
||||
|
||||
/* Memory statistics for this vm, protected by status_lock */
|
||||
struct amdgpu_mem_stats stats[__AMDGPU_PL_NUM];
|
||||
|
||||
/* Per-VM and PT BOs who needs a validation */
|
||||
struct list_head evicted;
|
||||
|
||||
|
|
@ -524,8 +529,12 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev,
|
|||
struct amdgpu_bo_va *bo_va,
|
||||
bool clear);
|
||||
bool amdgpu_vm_evictable(struct amdgpu_bo *bo);
|
||||
void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev,
|
||||
struct amdgpu_bo *bo, bool evicted);
|
||||
void amdgpu_vm_bo_invalidate(struct amdgpu_bo *bo, bool evicted);
|
||||
void amdgpu_vm_update_stats(struct amdgpu_vm_bo_base *base,
|
||||
struct ttm_resource *new_res, int sign);
|
||||
void amdgpu_vm_bo_update_shared(struct amdgpu_bo *bo);
|
||||
void amdgpu_vm_bo_move(struct amdgpu_bo *bo, struct ttm_resource *new_mem,
|
||||
bool evicted);
|
||||
uint64_t amdgpu_vm_map_gart(const dma_addr_t *pages_addr, uint64_t addr);
|
||||
struct amdgpu_bo_va *amdgpu_vm_bo_find(struct amdgpu_vm *vm,
|
||||
struct amdgpu_bo *bo);
|
||||
|
|
@ -576,8 +585,7 @@ void amdgpu_vm_set_task_info(struct amdgpu_vm *vm);
|
|||
void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev,
|
||||
struct amdgpu_vm *vm);
|
||||
void amdgpu_vm_get_memory(struct amdgpu_vm *vm,
|
||||
struct amdgpu_mem_stats *stats,
|
||||
unsigned int size);
|
||||
struct amdgpu_mem_stats stats[__AMDGPU_PL_NUM]);
|
||||
|
||||
int amdgpu_vm_pt_clear(struct amdgpu_device *adev, struct amdgpu_vm *vm,
|
||||
struct amdgpu_bo_vm *vmbo, bool immediate);
|
||||
|
|
|
|||
|
|
@ -537,6 +537,7 @@ static void amdgpu_vm_pt_free(struct amdgpu_vm_bo_base *entry)
|
|||
if (!entry->bo)
|
||||
return;
|
||||
|
||||
amdgpu_vm_update_stats(entry, entry->bo->tbo.resource, -1);
|
||||
entry->bo->vm_bo = NULL;
|
||||
ttm_bo_set_bulk_move(&entry->bo->tbo, NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -204,7 +204,8 @@ static void audio_shutdown(struct device *dev, void *data)
|
|||
}
|
||||
|
||||
static int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
struct device_node *endpoint,
|
||||
void *data)
|
||||
{
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
|
|
|||
|
|
@ -1952,7 +1952,8 @@ static void anx7625_audio_shutdown(struct device *dev, void *data)
|
|||
}
|
||||
|
||||
static int anx7625_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
struct device_node *endpoint,
|
||||
void *data)
|
||||
{
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
|
@ -2139,49 +2140,6 @@ static void hdcp_check_work_func(struct work_struct *work)
|
|||
drm_modeset_unlock(&drm_dev->mode_config.connection_mutex);
|
||||
}
|
||||
|
||||
static int anx7625_connector_atomic_check(struct anx7625_data *ctx,
|
||||
struct drm_connector_state *state)
|
||||
{
|
||||
struct device *dev = ctx->dev;
|
||||
int cp;
|
||||
|
||||
dev_dbg(dev, "hdcp state check\n");
|
||||
cp = state->content_protection;
|
||||
|
||||
if (cp == ctx->hdcp_cp)
|
||||
return 0;
|
||||
|
||||
if (cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) {
|
||||
if (ctx->dp_en) {
|
||||
dev_dbg(dev, "enable HDCP\n");
|
||||
anx7625_hdcp_enable(ctx);
|
||||
|
||||
queue_delayed_work(ctx->hdcp_workqueue,
|
||||
&ctx->hdcp_work,
|
||||
msecs_to_jiffies(2000));
|
||||
}
|
||||
}
|
||||
|
||||
if (cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
|
||||
if (ctx->hdcp_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED) {
|
||||
dev_err(dev, "current CP is not ENABLED\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
anx7625_hdcp_disable(ctx);
|
||||
ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
|
||||
drm_hdcp_update_content_protection(ctx->connector,
|
||||
ctx->hdcp_cp);
|
||||
dev_dbg(dev, "update CP to UNDESIRE\n");
|
||||
}
|
||||
|
||||
if (cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
|
||||
dev_err(dev, "Userspace illegal set to PROTECTION ENABLE\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int anx7625_bridge_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
|
|
@ -2418,7 +2376,7 @@ static int anx7625_bridge_atomic_check(struct drm_bridge *bridge,
|
|||
anx7625_bridge_mode_fixup(bridge, &crtc_state->mode,
|
||||
&crtc_state->adjusted_mode);
|
||||
|
||||
return anx7625_connector_atomic_check(ctx, conn_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void anx7625_bridge_atomic_enable(struct drm_bridge *bridge,
|
||||
|
|
@ -2427,6 +2385,7 @@ static void anx7625_bridge_atomic_enable(struct drm_bridge *bridge,
|
|||
struct anx7625_data *ctx = bridge_to_anx7625(bridge);
|
||||
struct device *dev = ctx->dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_state *conn_state;
|
||||
|
||||
dev_dbg(dev, "drm atomic enable\n");
|
||||
|
||||
|
|
@ -2441,6 +2400,22 @@ static void anx7625_bridge_atomic_enable(struct drm_bridge *bridge,
|
|||
_anx7625_hpd_polling(ctx, 5000 * 100);
|
||||
|
||||
anx7625_dp_start(ctx);
|
||||
|
||||
conn_state = drm_atomic_get_new_connector_state(state->base.state, connector);
|
||||
|
||||
if (WARN_ON(!conn_state))
|
||||
return;
|
||||
|
||||
if (conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) {
|
||||
if (ctx->dp_en) {
|
||||
dev_dbg(dev, "enable HDCP\n");
|
||||
anx7625_hdcp_enable(ctx);
|
||||
|
||||
queue_delayed_work(ctx->hdcp_workqueue,
|
||||
&ctx->hdcp_work,
|
||||
msecs_to_jiffies(2000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void anx7625_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
|
|
@ -2451,6 +2426,17 @@ static void anx7625_bridge_atomic_disable(struct drm_bridge *bridge,
|
|||
|
||||
dev_dbg(dev, "drm atomic disable\n");
|
||||
|
||||
flush_workqueue(ctx->hdcp_workqueue);
|
||||
|
||||
if (ctx->connector &&
|
||||
ctx->hdcp_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
|
||||
anx7625_hdcp_disable(ctx);
|
||||
ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_DESIRED;
|
||||
drm_hdcp_update_content_protection(ctx->connector,
|
||||
ctx->hdcp_cp);
|
||||
dev_dbg(dev, "update CP to DESIRE\n");
|
||||
}
|
||||
|
||||
ctx->connector = NULL;
|
||||
anx7625_dp_stop(ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -569,15 +569,6 @@ static int it6263_read_edid(void *data, u8 *buf, unsigned int block, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int it6263_bridge_atomic_check(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
return drm_atomic_helper_connector_hdmi_check(conn_state->connector,
|
||||
conn_state->state);
|
||||
}
|
||||
|
||||
static void
|
||||
it6263_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *old_bridge_state)
|
||||
|
|
@ -812,7 +803,6 @@ static const struct drm_bridge_funcs it6263_bridge_funcs = {
|
|||
.mode_valid = it6263_bridge_mode_valid,
|
||||
.atomic_disable = it6263_bridge_atomic_disable,
|
||||
.atomic_enable = it6263_bridge_atomic_enable,
|
||||
.atomic_check = it6263_bridge_atomic_check,
|
||||
.detect = it6263_bridge_detect,
|
||||
.edid_read = it6263_bridge_edid_read,
|
||||
.atomic_get_input_bus_fmts = it6263_bridge_atomic_get_input_bus_fmts,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include <crypto/hash.h>
|
||||
|
||||
|
|
@ -126,6 +127,7 @@
|
|||
#define REG_AUX_OUT_DATA0 0x27
|
||||
|
||||
#define REG_AUX_CMD_REQ 0x2B
|
||||
#define M_AUX_REQ_CMD 0x0F
|
||||
#define AUX_BUSY BIT(5)
|
||||
|
||||
#define REG_AUX_DATA_0_7 0x2C
|
||||
|
|
@ -266,6 +268,18 @@
|
|||
#define REG_SSC_CTRL1 0x189
|
||||
#define REG_SSC_CTRL2 0x18A
|
||||
|
||||
#define REG_AUX_USER_CTRL 0x190
|
||||
#define EN_USER_AUX BIT(0)
|
||||
#define USER_AUX_DONE BIT(1)
|
||||
#define AUX_EVENT BIT(4)
|
||||
|
||||
#define REG_AUX_USER_DATA_REC 0x191
|
||||
#define M_AUX_IN_REC 0xF0
|
||||
#define M_AUX_OUT_REC 0x0F
|
||||
|
||||
#define REG_AUX_USER_REPLY 0x19A
|
||||
#define REG_AUX_USER_RXB(n) (n + 0x19B)
|
||||
|
||||
#define RBR DP_LINK_BW_1_62
|
||||
#define HBR DP_LINK_BW_2_7
|
||||
#define HBR2 DP_LINK_BW_5_4
|
||||
|
|
@ -296,11 +310,13 @@
|
|||
#define MAX_LANE_COUNT 4
|
||||
#define MAX_LINK_RATE HBR
|
||||
#define AUTO_TRAIN_RETRY 3
|
||||
#define MAX_HDCP_DOWN_STREAM_COUNT 10
|
||||
#define MAX_HDCP_DOWN_STREAM_COUNT 127
|
||||
#define MAX_CR_LEVEL 0x03
|
||||
#define MAX_EQ_LEVEL 0x03
|
||||
#define AUX_WAIT_TIMEOUT_MS 15
|
||||
#define AUX_FIFO_MAX_SIZE 32
|
||||
#define AUX_FIFO_MAX_SIZE 16
|
||||
#define AUX_I2C_MAX_SIZE 4
|
||||
#define AUX_I2C_DEFER_RETRY 4
|
||||
#define PIXEL_CLK_DELAY 1
|
||||
#define PIXEL_CLK_INVERSE 0
|
||||
#define ADJUST_PHASE_THRESHOLD 80000
|
||||
|
|
@ -323,7 +339,15 @@
|
|||
enum aux_cmd_type {
|
||||
CMD_AUX_NATIVE_READ = 0x0,
|
||||
CMD_AUX_NATIVE_WRITE = 0x5,
|
||||
CMD_AUX_GI2C_ADR = 0x08,
|
||||
CMD_AUX_GI2C_READ = 0x09,
|
||||
CMD_AUX_GI2C_WRITE = 0x0A,
|
||||
CMD_AUX_I2C_EDID_READ = 0xB,
|
||||
CMD_AUX_I2C_READ = 0x0D,
|
||||
CMD_AUX_I2C_WRITE = 0x0C,
|
||||
|
||||
/* KSV read with AUX FIFO extend from CMD_AUX_NATIVE_READ*/
|
||||
CMD_AUX_GET_KSV_LIST = 0x10,
|
||||
};
|
||||
|
||||
enum aux_cmd_reply {
|
||||
|
|
@ -965,7 +989,8 @@ static ssize_t it6505_aux_operation(struct it6505 *it6505,
|
|||
it6505_set_bits(it6505, REG_AUX_CTRL, AUX_USER_MODE, AUX_USER_MODE);
|
||||
|
||||
aux_op_start:
|
||||
if (cmd == CMD_AUX_I2C_EDID_READ) {
|
||||
/* HW AUX FIFO supports only EDID and DCPD KSV FIFO area */
|
||||
if (cmd == CMD_AUX_I2C_EDID_READ || cmd == CMD_AUX_GET_KSV_LIST) {
|
||||
/* AUX EDID FIFO has max length of AUX_FIFO_MAX_SIZE bytes. */
|
||||
size = min_t(size_t, size, AUX_FIFO_MAX_SIZE);
|
||||
/* Enable AUX FIFO read back and clear FIFO */
|
||||
|
|
@ -996,7 +1021,7 @@ static ssize_t it6505_aux_operation(struct it6505 *it6505,
|
|||
size);
|
||||
|
||||
/* Aux Fire */
|
||||
it6505_write(it6505, REG_AUX_CMD_REQ, cmd);
|
||||
it6505_write(it6505, REG_AUX_CMD_REQ, FIELD_GET(M_AUX_REQ_CMD, cmd));
|
||||
|
||||
ret = it6505_aux_wait(it6505);
|
||||
if (ret < 0)
|
||||
|
|
@ -1030,7 +1055,7 @@ static ssize_t it6505_aux_operation(struct it6505 *it6505,
|
|||
goto aux_op_start;
|
||||
}
|
||||
|
||||
if (cmd == CMD_AUX_I2C_EDID_READ) {
|
||||
if (cmd == CMD_AUX_I2C_EDID_READ || cmd == CMD_AUX_GET_KSV_LIST) {
|
||||
for (i = 0; i < size; i++) {
|
||||
ret = it6505_read(it6505, REG_AUX_DATA_FIFO);
|
||||
if (ret < 0)
|
||||
|
|
@ -1055,7 +1080,7 @@ static ssize_t it6505_aux_operation(struct it6505 *it6505,
|
|||
ret = i;
|
||||
|
||||
aux_op_err:
|
||||
if (cmd == CMD_AUX_I2C_EDID_READ) {
|
||||
if (cmd == CMD_AUX_I2C_EDID_READ || cmd == CMD_AUX_GET_KSV_LIST) {
|
||||
/* clear AUX FIFO */
|
||||
it6505_set_bits(it6505, REG_AUX_CTRL,
|
||||
AUX_EN_FIFO_READ | CLR_EDID_FIFO,
|
||||
|
|
@ -1076,10 +1101,14 @@ static ssize_t it6505_aux_do_transfer(struct it6505 *it6505,
|
|||
size_t size, enum aux_cmd_reply *reply)
|
||||
{
|
||||
int i, ret_size, ret = 0, request_size;
|
||||
int fifo_max_size = (cmd == CMD_AUX_I2C_EDID_READ || cmd == CMD_AUX_GET_KSV_LIST) ?
|
||||
AUX_FIFO_MAX_SIZE : 4;
|
||||
|
||||
mutex_lock(&it6505->aux_lock);
|
||||
for (i = 0; i < size; i += 4) {
|
||||
request_size = min((int)size - i, 4);
|
||||
i = 0;
|
||||
do {
|
||||
request_size = min_t(int, (int)size - i, fifo_max_size);
|
||||
|
||||
ret_size = it6505_aux_operation(it6505, cmd, address + i,
|
||||
buffer + i, request_size,
|
||||
reply);
|
||||
|
|
@ -1088,14 +1117,170 @@ static ssize_t it6505_aux_do_transfer(struct it6505 *it6505,
|
|||
goto aux_op_err;
|
||||
}
|
||||
|
||||
i += request_size;
|
||||
ret += ret_size;
|
||||
}
|
||||
} while (i < size);
|
||||
|
||||
aux_op_err:
|
||||
mutex_unlock(&it6505->aux_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool it6505_aux_i2c_reply_defer(u8 reply)
|
||||
{
|
||||
if (reply == DP_AUX_NATIVE_REPLY_DEFER || reply == DP_AUX_I2C_REPLY_DEFER)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool it6505_aux_i2c_reply_nack(u8 reply)
|
||||
{
|
||||
if (reply == DP_AUX_NATIVE_REPLY_NACK || reply == DP_AUX_I2C_REPLY_NACK)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int it6505_aux_i2c_wait(struct it6505 *it6505, u8 *reply)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned long timeout;
|
||||
struct device *dev = it6505->dev;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1;
|
||||
|
||||
do {
|
||||
if (it6505_read(it6505, REG_AUX_USER_CTRL) & AUX_EVENT)
|
||||
break;
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(dev, "Timed out waiting AUX I2C, BUSY = %X\n",
|
||||
it6505_aux_op_finished(it6505));
|
||||
err = -ETIMEDOUT;
|
||||
goto end_aux_i2c_wait;
|
||||
}
|
||||
usleep_range(300, 800);
|
||||
} while (!it6505_aux_op_finished(it6505));
|
||||
|
||||
*reply = it6505_read(it6505, REG_AUX_USER_REPLY) >> 4;
|
||||
|
||||
if (*reply == 0)
|
||||
goto end_aux_i2c_wait;
|
||||
|
||||
if (it6505_aux_i2c_reply_defer(*reply))
|
||||
err = -EBUSY;
|
||||
else if (it6505_aux_i2c_reply_nack(*reply))
|
||||
err = -ENXIO;
|
||||
|
||||
end_aux_i2c_wait:
|
||||
it6505_set_bits(it6505, REG_AUX_USER_CTRL, USER_AUX_DONE, USER_AUX_DONE);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int it6505_aux_i2c_readb(struct it6505 *it6505, u8 *buf, size_t size, u8 *reply)
|
||||
{
|
||||
int ret, i;
|
||||
int retry;
|
||||
|
||||
for (retry = 0; retry < AUX_I2C_DEFER_RETRY; retry++) {
|
||||
it6505_write(it6505, REG_AUX_CMD_REQ, CMD_AUX_GI2C_READ);
|
||||
|
||||
ret = it6505_aux_i2c_wait(it6505, reply);
|
||||
if (it6505_aux_i2c_reply_defer(*reply))
|
||||
continue;
|
||||
if (ret >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
buf[i] = it6505_read(it6505, REG_AUX_USER_RXB(0 + i));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int it6505_aux_i2c_writeb(struct it6505 *it6505, u8 *buf, size_t size, u8 *reply)
|
||||
{
|
||||
int i, ret;
|
||||
int retry;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
it6505_write(it6505, REG_AUX_OUT_DATA0 + i, buf[i]);
|
||||
|
||||
for (retry = 0; retry < AUX_I2C_DEFER_RETRY; retry++) {
|
||||
it6505_write(it6505, REG_AUX_CMD_REQ, CMD_AUX_GI2C_WRITE);
|
||||
|
||||
ret = it6505_aux_i2c_wait(it6505, reply);
|
||||
if (it6505_aux_i2c_reply_defer(*reply))
|
||||
continue;
|
||||
if (ret >= 0)
|
||||
break;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t it6505_aux_i2c_operation(struct it6505 *it6505,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
int ret;
|
||||
ssize_t request_size, data_cnt = 0;
|
||||
u8 *buffer = msg->buffer;
|
||||
|
||||
/* set AUX user mode */
|
||||
it6505_set_bits(it6505, REG_AUX_CTRL,
|
||||
AUX_USER_MODE | AUX_NO_SEGMENT_WR, AUX_USER_MODE);
|
||||
it6505_set_bits(it6505, REG_AUX_USER_CTRL, EN_USER_AUX, EN_USER_AUX);
|
||||
/* clear AUX FIFO */
|
||||
it6505_set_bits(it6505, REG_AUX_CTRL,
|
||||
AUX_EN_FIFO_READ | CLR_EDID_FIFO,
|
||||
AUX_EN_FIFO_READ | CLR_EDID_FIFO);
|
||||
|
||||
it6505_set_bits(it6505, REG_AUX_CTRL,
|
||||
AUX_EN_FIFO_READ | CLR_EDID_FIFO, 0x00);
|
||||
|
||||
it6505_write(it6505, REG_AUX_ADR_0_7, 0x00);
|
||||
it6505_write(it6505, REG_AUX_ADR_8_15, msg->address << 1);
|
||||
|
||||
if (msg->size == 0) {
|
||||
/* IIC Start/STOP dummy write */
|
||||
it6505_write(it6505, REG_AUX_ADR_16_19, msg->request);
|
||||
it6505_write(it6505, REG_AUX_CMD_REQ, CMD_AUX_GI2C_ADR);
|
||||
ret = it6505_aux_i2c_wait(it6505, &msg->reply);
|
||||
goto end_aux_i2c_transfer;
|
||||
}
|
||||
|
||||
/* IIC data transfer */
|
||||
data_cnt = 0;
|
||||
do {
|
||||
request_size = min_t(ssize_t, msg->size - data_cnt, AUX_I2C_MAX_SIZE);
|
||||
it6505_write(it6505, REG_AUX_ADR_16_19,
|
||||
msg->request | ((request_size - 1) << 4));
|
||||
if ((msg->request & DP_AUX_I2C_READ) == DP_AUX_I2C_READ)
|
||||
ret = it6505_aux_i2c_readb(it6505, &buffer[data_cnt],
|
||||
request_size, &msg->reply);
|
||||
else
|
||||
ret = it6505_aux_i2c_writeb(it6505, &buffer[data_cnt],
|
||||
request_size, &msg->reply);
|
||||
|
||||
if (ret < 0)
|
||||
goto end_aux_i2c_transfer;
|
||||
|
||||
data_cnt += request_size;
|
||||
} while (data_cnt < msg->size);
|
||||
ret = data_cnt;
|
||||
end_aux_i2c_transfer:
|
||||
|
||||
it6505_set_bits(it6505, REG_AUX_USER_CTRL, EN_USER_AUX, 0);
|
||||
it6505_set_bits(it6505, REG_AUX_CTRL, AUX_USER_MODE, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t it6505_aux_i2c_transfer(struct drm_dp_aux *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
struct it6505 *it6505 = container_of(aux, struct it6505, aux);
|
||||
|
||||
guard(mutex)(&it6505->aux_lock);
|
||||
return it6505_aux_i2c_operation(it6505, msg);
|
||||
}
|
||||
|
||||
static ssize_t it6505_aux_transfer(struct drm_dp_aux *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
|
|
@ -1105,9 +1290,8 @@ static ssize_t it6505_aux_transfer(struct drm_dp_aux *aux,
|
|||
int ret;
|
||||
enum aux_cmd_reply reply;
|
||||
|
||||
/* IT6505 doesn't support arbitrary I2C read / write. */
|
||||
if (is_i2c)
|
||||
return -EINVAL;
|
||||
return it6505_aux_i2c_transfer(aux, msg);
|
||||
|
||||
switch (msg->request) {
|
||||
case DP_AUX_NATIVE_READ:
|
||||
|
|
@ -1178,6 +1362,37 @@ static int it6505_get_edid_block(void *data, u8 *buf, unsigned int block,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int it6505_get_ksvlist(struct it6505 *it6505, u8 *buf, size_t len)
|
||||
{
|
||||
struct device *dev = it6505->dev;
|
||||
enum aux_cmd_reply reply;
|
||||
int request_size, ret;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
request_size = min_t(int, (int)len - i, 15);
|
||||
|
||||
ret = it6505_aux_do_transfer(it6505, CMD_AUX_GET_KSV_LIST,
|
||||
DP_AUX_HDCP_KSV_FIFO,
|
||||
buf + i, request_size, &reply);
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "request_size = %d, ret =%d", request_size, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i += request_size;
|
||||
} while (i < len);
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "ksv read cnt = %d down_stream_cnt=%d ", i, i / 5);
|
||||
|
||||
for (i = 0 ; i < len; i += 5) {
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "ksv[%d] = %02X%02X%02X%02X%02X",
|
||||
i / 5, buf[i], buf[i + 1], buf[i + 2], buf[i + 3], buf[i + 4]);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void it6505_variable_config(struct it6505 *it6505)
|
||||
{
|
||||
it6505->link_rate_bw_code = HBR;
|
||||
|
|
@ -1959,7 +2174,7 @@ static int it6505_setup_sha1_input(struct it6505 *it6505, u8 *sha1_input)
|
|||
{
|
||||
struct device *dev = it6505->dev;
|
||||
u8 binfo[2];
|
||||
int down_stream_count, i, err, msg_count = 0;
|
||||
int down_stream_count, err, msg_count = 0;
|
||||
|
||||
err = it6505_get_dpcd(it6505, DP_AUX_HDCP_BINFO, binfo,
|
||||
ARRAY_SIZE(binfo));
|
||||
|
|
@ -1984,18 +2199,11 @@ static int it6505_setup_sha1_input(struct it6505 *it6505, u8 *sha1_input)
|
|||
down_stream_count);
|
||||
return 0;
|
||||
}
|
||||
err = it6505_get_ksvlist(it6505, sha1_input, down_stream_count * 5);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < down_stream_count; i++) {
|
||||
err = it6505_get_dpcd(it6505, DP_AUX_HDCP_KSV_FIFO +
|
||||
(i % 3) * DRM_HDCP_KSV_LEN,
|
||||
sha1_input + msg_count,
|
||||
DRM_HDCP_KSV_LEN);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msg_count += 5;
|
||||
}
|
||||
msg_count += down_stream_count * 5;
|
||||
|
||||
it6505->hdcp_down_stream_count = down_stream_count;
|
||||
sha1_input[msg_count++] = binfo[0];
|
||||
|
|
@ -2023,7 +2231,7 @@ static bool it6505_hdcp_part2_ksvlist_check(struct it6505 *it6505)
|
|||
{
|
||||
struct device *dev = it6505->dev;
|
||||
u8 av[5][4], bv[5][4];
|
||||
int i, err;
|
||||
int i, err, retry;
|
||||
|
||||
i = it6505_setup_sha1_input(it6505, it6505->sha1_input);
|
||||
if (i <= 0) {
|
||||
|
|
@ -2032,22 +2240,28 @@ static bool it6505_hdcp_part2_ksvlist_check(struct it6505 *it6505)
|
|||
}
|
||||
|
||||
it6505_sha1_digest(it6505, it6505->sha1_input, i, (u8 *)av);
|
||||
/*1B-05 V' must retry 3 times */
|
||||
for (retry = 0; retry < 3; retry++) {
|
||||
err = it6505_get_dpcd(it6505, DP_AUX_HDCP_V_PRIME(0), (u8 *)bv,
|
||||
sizeof(bv));
|
||||
|
||||
err = it6505_get_dpcd(it6505, DP_AUX_HDCP_V_PRIME(0), (u8 *)bv,
|
||||
sizeof(bv));
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Read V' value Fail %d", retry);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Read V' value Fail");
|
||||
return false;
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (bv[i][3] != av[i][0] || bv[i][2] != av[i][1] ||
|
||||
av[i][1] != av[i][2] || bv[i][0] != av[i][3])
|
||||
break;
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "V' all match!! %d, %d", retry, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
if (bv[i][3] != av[i][0] || bv[i][2] != av[i][1] ||
|
||||
bv[i][1] != av[i][2] || bv[i][0] != av[i][3])
|
||||
return false;
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "V' all match!!");
|
||||
return true;
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "V' NOT match!! %d", retry);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void it6505_hdcp_wait_ksv_list(struct work_struct *work)
|
||||
|
|
@ -2055,12 +2269,13 @@ static void it6505_hdcp_wait_ksv_list(struct work_struct *work)
|
|||
struct it6505 *it6505 = container_of(work, struct it6505,
|
||||
hdcp_wait_ksv_list);
|
||||
struct device *dev = it6505->dev;
|
||||
unsigned int timeout = 5000;
|
||||
u8 bstatus = 0;
|
||||
u8 bstatus;
|
||||
bool ksv_list_check;
|
||||
/* 1B-04 wait ksv list for 5s */
|
||||
unsigned long timeout = jiffies +
|
||||
msecs_to_jiffies(5000) + 1;
|
||||
|
||||
timeout /= 20;
|
||||
while (timeout > 0) {
|
||||
for (;;) {
|
||||
if (!it6505_get_sink_hpd_status(it6505))
|
||||
return;
|
||||
|
||||
|
|
@ -2069,27 +2284,23 @@ static void it6505_hdcp_wait_ksv_list(struct work_struct *work)
|
|||
if (bstatus & DP_BSTATUS_READY)
|
||||
break;
|
||||
|
||||
msleep(20);
|
||||
timeout--;
|
||||
}
|
||||
if (time_after(jiffies, timeout)) {
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "KSV list wait timeout");
|
||||
goto timeout;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "timeout and ksv list wait failed");
|
||||
goto timeout;
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
ksv_list_check = it6505_hdcp_part2_ksvlist_check(it6505);
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "ksv list ready, ksv list check %s",
|
||||
ksv_list_check ? "pass" : "fail");
|
||||
if (ksv_list_check) {
|
||||
it6505_set_bits(it6505, REG_HDCP_TRIGGER,
|
||||
HDCP_TRIGGER_KSV_DONE, HDCP_TRIGGER_KSV_DONE);
|
||||
|
||||
if (ksv_list_check)
|
||||
return;
|
||||
}
|
||||
|
||||
timeout:
|
||||
it6505_set_bits(it6505, REG_HDCP_TRIGGER,
|
||||
HDCP_TRIGGER_KSV_DONE | HDCP_TRIGGER_KSV_FAIL,
|
||||
HDCP_TRIGGER_KSV_DONE | HDCP_TRIGGER_KSV_FAIL);
|
||||
it6505_start_hdcp(it6505);
|
||||
}
|
||||
|
||||
static void it6505_hdcp_work(struct work_struct *work)
|
||||
|
|
@ -2312,14 +2523,20 @@ static int it6505_process_hpd_irq(struct it6505 *it6505)
|
|||
DRM_DEV_DEBUG_DRIVER(dev, "dp_irq_vector = 0x%02x", dp_irq_vector);
|
||||
|
||||
if (dp_irq_vector & DP_CP_IRQ) {
|
||||
it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_CPIRQ,
|
||||
HDCP_TRIGGER_CPIRQ);
|
||||
|
||||
bstatus = it6505_dpcd_read(it6505, DP_AUX_HDCP_BSTATUS);
|
||||
if (bstatus < 0)
|
||||
return bstatus;
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "Bstatus = 0x%02x", bstatus);
|
||||
|
||||
/*Check BSTATUS when recive CP_IRQ */
|
||||
if (bstatus & DP_BSTATUS_R0_PRIME_READY &&
|
||||
it6505->hdcp_status == HDCP_AUTH_GOING)
|
||||
it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_CPIRQ,
|
||||
HDCP_TRIGGER_CPIRQ);
|
||||
else if (bstatus & (DP_BSTATUS_REAUTH_REQ | DP_BSTATUS_LINK_FAILURE) &&
|
||||
it6505->hdcp_status == HDCP_AUTH_DONE)
|
||||
it6505_start_hdcp(it6505);
|
||||
}
|
||||
|
||||
ret = drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
|
||||
|
|
@ -2456,7 +2673,11 @@ static void it6505_irq_hdcp_ksv_check(struct it6505 *it6505)
|
|||
{
|
||||
struct device *dev = it6505->dev;
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "HDCP event Interrupt");
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "HDCP repeater R0 event Interrupt");
|
||||
/* 1B01 HDCP encription should start when R0 is ready*/
|
||||
it6505_set_bits(it6505, REG_HDCP_TRIGGER,
|
||||
HDCP_TRIGGER_KSV_DONE, HDCP_TRIGGER_KSV_DONE);
|
||||
|
||||
schedule_work(&it6505->hdcp_wait_ksv_list);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1466,7 +1466,6 @@ static const struct hdmi_codec_ops it66121_audio_codec_ops = {
|
|||
.audio_shutdown = it66121_audio_shutdown,
|
||||
.mute_stream = it66121_audio_mute,
|
||||
.get_eld = it66121_audio_get_eld,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev)
|
||||
|
|
@ -1476,11 +1475,12 @@ static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev)
|
|||
.i2s = 1, /* Only i2s support for now */
|
||||
.spdif = 0,
|
||||
.max_i2s_channels = 8,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
if (!of_property_read_bool(dev->of_node, "#sound-dai-cells")) {
|
||||
if (!of_property_present(dev->of_node, "#sound-dai-cells")) {
|
||||
dev_info(dev, "No \"#sound-dai-cells\", no audio\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ struct lt9611 {
|
|||
struct device_node *dsi1_node;
|
||||
struct mipi_dsi_device *dsi0;
|
||||
struct mipi_dsi_device *dsi1;
|
||||
struct platform_device *audio_pdev;
|
||||
|
||||
bool ac_mode;
|
||||
|
||||
|
|
@ -767,15 +766,6 @@ static enum drm_mode_status lt9611_bridge_mode_valid(struct drm_bridge *bridge,
|
|||
return MODE_OK;
|
||||
}
|
||||
|
||||
static int lt9611_bridge_atomic_check(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
return drm_atomic_helper_connector_hdmi_check(conn_state->connector,
|
||||
conn_state->state);
|
||||
}
|
||||
|
||||
static void lt9611_bridge_atomic_pre_enable(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *old_bridge_state)
|
||||
{
|
||||
|
|
@ -864,6 +854,10 @@ static int lt9611_hdmi_clear_infoframe(struct drm_bridge *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;
|
||||
|
|
@ -897,6 +891,11 @@ static int lt9611_hdmi_write_infoframe(struct drm_bridge *bridge,
|
|||
int i;
|
||||
|
||||
switch (type) {
|
||||
case HDMI_INFOFRAME_TYPE_AUDIO:
|
||||
mask = LT9611_INFOFRAME_AUDIO;
|
||||
addr = 0x84b2;
|
||||
break;
|
||||
|
||||
case HDMI_INFOFRAME_TYPE_AVI:
|
||||
mask = LT9611_INFOFRAME_AVI;
|
||||
addr = 0x8440;
|
||||
|
|
@ -940,6 +939,55 @@ lt9611_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
|
|||
return MODE_OK;
|
||||
}
|
||||
|
||||
static int lt9611_hdmi_audio_startup(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge)
|
||||
{
|
||||
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x82d6, 0x8c);
|
||||
regmap_write(lt9611->regmap, 0x82d7, 0x04);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8406, 0x08);
|
||||
regmap_write(lt9611->regmap, 0x8407, 0x10);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8434, 0xd5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lt9611_hdmi_audio_prepare(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
|
||||
|
||||
if (hparms->sample_rate == 48000)
|
||||
regmap_write(lt9611->regmap, 0x840f, 0x2b);
|
||||
else if (hparms->sample_rate == 96000)
|
||||
regmap_write(lt9611->regmap, 0x840f, 0xab);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8435, 0x00);
|
||||
regmap_write(lt9611->regmap, 0x8436, 0x18);
|
||||
regmap_write(lt9611->regmap, 0x8437, 0x00);
|
||||
|
||||
return drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
|
||||
&hparms->cea);
|
||||
}
|
||||
|
||||
static void lt9611_hdmi_audio_shutdown(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge)
|
||||
{
|
||||
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
|
||||
|
||||
drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8406, 0x00);
|
||||
regmap_write(lt9611->regmap, 0x8407, 0x00);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs lt9611_bridge_funcs = {
|
||||
.attach = lt9611_bridge_attach,
|
||||
.mode_valid = lt9611_bridge_mode_valid,
|
||||
|
|
@ -947,7 +995,6 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
|
|||
.edid_read = lt9611_bridge_edid_read,
|
||||
.hpd_enable = lt9611_bridge_hpd_enable,
|
||||
|
||||
.atomic_check = lt9611_bridge_atomic_check,
|
||||
.atomic_pre_enable = lt9611_bridge_atomic_pre_enable,
|
||||
.atomic_enable = lt9611_bridge_atomic_enable,
|
||||
.atomic_disable = lt9611_bridge_atomic_disable,
|
||||
|
|
@ -960,6 +1007,10 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
|
|||
.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_audio_startup = lt9611_hdmi_audio_startup,
|
||||
.hdmi_audio_prepare = lt9611_hdmi_audio_prepare,
|
||||
.hdmi_audio_shutdown = lt9611_hdmi_audio_shutdown,
|
||||
};
|
||||
|
||||
static int lt9611_parse_dt(struct device *dev,
|
||||
|
|
@ -1013,101 +1064,6 @@ static int lt9611_read_device_rev(struct lt9611 *lt9611)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int lt9611_hdmi_hw_params(struct device *dev, void *data,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
struct lt9611 *lt9611 = data;
|
||||
|
||||
if (hparms->sample_rate == 48000)
|
||||
regmap_write(lt9611->regmap, 0x840f, 0x2b);
|
||||
else if (hparms->sample_rate == 96000)
|
||||
regmap_write(lt9611->regmap, 0x840f, 0xab);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8435, 0x00);
|
||||
regmap_write(lt9611->regmap, 0x8436, 0x18);
|
||||
regmap_write(lt9611->regmap, 0x8437, 0x00);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lt9611_audio_startup(struct device *dev, void *data)
|
||||
{
|
||||
struct lt9611 *lt9611 = data;
|
||||
|
||||
regmap_write(lt9611->regmap, 0x82d6, 0x8c);
|
||||
regmap_write(lt9611->regmap, 0x82d7, 0x04);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8406, 0x08);
|
||||
regmap_write(lt9611->regmap, 0x8407, 0x10);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8434, 0xd5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lt9611_audio_shutdown(struct device *dev, void *data)
|
||||
{
|
||||
struct lt9611 *lt9611 = data;
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8406, 0x00);
|
||||
regmap_write(lt9611->regmap, 0x8407, 0x00);
|
||||
}
|
||||
|
||||
static int lt9611_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
||||
ret = of_graph_parse_endpoint(endpoint, &of_ep);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* HDMI sound should be located as reg = <2>
|
||||
* Then, it is sound port 0
|
||||
*/
|
||||
if (of_ep.port == 2)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct hdmi_codec_ops lt9611_codec_ops = {
|
||||
.hw_params = lt9611_hdmi_hw_params,
|
||||
.audio_shutdown = lt9611_audio_shutdown,
|
||||
.audio_startup = lt9611_audio_startup,
|
||||
.get_dai_id = lt9611_hdmi_i2s_get_dai_id,
|
||||
};
|
||||
|
||||
static struct hdmi_codec_pdata codec_data = {
|
||||
.ops = <9611_codec_ops,
|
||||
.max_i2s_channels = 8,
|
||||
.i2s = 1,
|
||||
};
|
||||
|
||||
static int lt9611_audio_init(struct device *dev, struct lt9611 *lt9611)
|
||||
{
|
||||
codec_data.data = lt9611;
|
||||
lt9611->audio_pdev =
|
||||
platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
|
||||
PLATFORM_DEVID_AUTO,
|
||||
&codec_data, sizeof(codec_data));
|
||||
|
||||
return PTR_ERR_OR_ZERO(lt9611->audio_pdev);
|
||||
}
|
||||
|
||||
static void lt9611_audio_exit(struct lt9611 *lt9611)
|
||||
{
|
||||
if (lt9611->audio_pdev) {
|
||||
platform_device_unregister(lt9611->audio_pdev);
|
||||
lt9611->audio_pdev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int lt9611_probe(struct i2c_client *client)
|
||||
{
|
||||
struct lt9611 *lt9611;
|
||||
|
|
@ -1171,6 +1127,9 @@ static int lt9611_probe(struct i2c_client *client)
|
|||
|
||||
i2c_set_clientdata(client, lt9611);
|
||||
|
||||
/* Disable Audio InfoFrame, enabled by default */
|
||||
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, 0);
|
||||
|
||||
lt9611->bridge.funcs = <9611_bridge_funcs;
|
||||
lt9611->bridge.of_node = client->dev.of_node;
|
||||
lt9611->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
|
||||
|
|
@ -1179,6 +1138,9 @@ static int lt9611_probe(struct i2c_client *client)
|
|||
lt9611->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
lt9611->bridge.vendor = "Lontium";
|
||||
lt9611->bridge.product = "LT9611";
|
||||
lt9611->bridge.hdmi_audio_dev = dev;
|
||||
lt9611->bridge.hdmi_audio_max_i2s_playback_channels = 8;
|
||||
lt9611->bridge.hdmi_audio_dai_port = 2;
|
||||
|
||||
drm_bridge_add(<9611->bridge);
|
||||
|
||||
|
|
@ -1200,10 +1162,6 @@ static int lt9611_probe(struct i2c_client *client)
|
|||
|
||||
lt9611_enable_hpd_interrupts(lt9611);
|
||||
|
||||
ret = lt9611_audio_init(dev, lt9611);
|
||||
if (ret)
|
||||
goto err_remove_bridge;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_bridge:
|
||||
|
|
@ -1224,7 +1182,6 @@ static void lt9611_remove(struct i2c_client *client)
|
|||
struct lt9611 *lt9611 = i2c_get_clientdata(client);
|
||||
|
||||
disable_irq(client->irq);
|
||||
lt9611_audio_exit(lt9611);
|
||||
drm_bridge_remove(<9611->bridge);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(lt9611->supplies), lt9611->supplies);
|
||||
|
|
|
|||
|
|
@ -522,7 +522,8 @@ static void lt9611uxc_audio_shutdown(struct device *dev, void *data)
|
|||
}
|
||||
|
||||
static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
struct device_node *endpoint,
|
||||
void *data)
|
||||
{
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
|
|
|||
|
|
@ -815,7 +815,8 @@ static int sii902x_audio_get_eld(struct device *dev, void *data,
|
|||
}
|
||||
|
||||
static int sii902x_audio_get_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
struct device_node *endpoint,
|
||||
void *data)
|
||||
{
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
|
@ -840,7 +841,6 @@ static const struct hdmi_codec_ops sii902x_audio_codec_ops = {
|
|||
.mute_stream = sii902x_audio_mute,
|
||||
.get_eld = sii902x_audio_get_eld,
|
||||
.get_dai_id = sii902x_audio_get_dai_id,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static int sii902x_audio_codec_init(struct sii902x *sii902x,
|
||||
|
|
@ -863,11 +863,12 @@ static int sii902x_audio_codec_init(struct sii902x *sii902x,
|
|||
.i2s = 1, /* Only i2s support for now. */
|
||||
.spdif = 0,
|
||||
.max_i2s_channels = 0,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
u8 lanes[4];
|
||||
int num_lanes, i;
|
||||
|
||||
if (!of_property_read_bool(dev->of_node, "#sound-dai-cells")) {
|
||||
if (!of_property_present(dev->of_node, "#sound-dai-cells")) {
|
||||
dev_dbg(dev, "%s: No \"#sound-dai-cells\", no audio\n",
|
||||
__func__);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -148,7 +148,8 @@ static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf,
|
|||
}
|
||||
|
||||
static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
struct device_node *endpoint,
|
||||
void *data)
|
||||
{
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
|
|
|||
|
|
@ -361,22 +361,6 @@ static int dw_hdmi_qp_config_drm_infoframe(struct dw_hdmi_qp *hdmi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_bridge_atomic_check(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
||||
int ret;
|
||||
|
||||
ret = drm_atomic_helper_connector_hdmi_check(conn_state->connector,
|
||||
conn_state->state);
|
||||
if (ret)
|
||||
dev_dbg(hdmi->dev, "%s failed: %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *old_state)
|
||||
{
|
||||
|
|
@ -503,7 +487,6 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
|
|||
.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,
|
||||
.atomic_check = dw_hdmi_qp_bridge_atomic_check,
|
||||
.atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
|
||||
.atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
|
||||
.detect = dw_hdmi_qp_bridge_detect,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ if DRM_DISPLAY_HELPER
|
|||
|
||||
config DRM_BRIDGE_CONNECTOR
|
||||
bool
|
||||
select DRM_DISPLAY_HDMI_AUDIO_HELPER
|
||||
select DRM_DISPLAY_HDMI_STATE_HELPER
|
||||
help
|
||||
DRM connector implementation terminating DRM bridge chains.
|
||||
|
|
@ -75,6 +76,12 @@ config DRM_DISPLAY_HDCP_HELPER
|
|||
help
|
||||
DRM display helpers for HDCP.
|
||||
|
||||
config DRM_DISPLAY_HDMI_AUDIO_HELPER
|
||||
bool
|
||||
help
|
||||
DRM display helpers for HDMI Audio functionality (generic HDMI Codec
|
||||
implementation).
|
||||
|
||||
config DRM_DISPLAY_HDMI_HELPER
|
||||
bool
|
||||
help
|
||||
|
|
@ -82,6 +89,7 @@ config DRM_DISPLAY_HDMI_HELPER
|
|||
|
||||
config DRM_DISPLAY_HDMI_STATE_HELPER
|
||||
bool
|
||||
select DRM_DISPLAY_HDMI_AUDIO_HELPER
|
||||
select DRM_DISPLAY_HDMI_HELPER
|
||||
help
|
||||
DRM KMS state helpers for HDMI.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_TUNNEL) += \
|
|||
drm_display_helper-$(CONFIG_DRM_DISPLAY_DSC_HELPER) += \
|
||||
drm_dsc_helper.o
|
||||
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDCP_HELPER) += drm_hdcp_helper.o
|
||||
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_AUDIO_HELPER) += \
|
||||
drm_hdmi_audio_helper.o
|
||||
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_HELPER) += \
|
||||
drm_hdmi_helper.o \
|
||||
drm_scdc_helper.o
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@
|
|||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/display/drm_hdmi_audio_helper.h>
|
||||
#include <drm/display/drm_hdmi_helper.h>
|
||||
#include <drm/display/drm_hdmi_state_helper.h>
|
||||
|
||||
|
|
@ -180,11 +182,15 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force)
|
|||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *detect = bridge_connector->bridge_detect;
|
||||
struct drm_bridge *hdmi = bridge_connector->bridge_hdmi;
|
||||
enum drm_connector_status status;
|
||||
|
||||
if (detect) {
|
||||
status = detect->funcs->detect(detect);
|
||||
|
||||
if (hdmi)
|
||||
drm_atomic_helper_connector_hdmi_hotplug(connector, status);
|
||||
|
||||
drm_bridge_connector_hpd_notify(connector, status);
|
||||
} else {
|
||||
switch (connector->connector_type) {
|
||||
|
|
@ -203,6 +209,16 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force)
|
|||
return status;
|
||||
}
|
||||
|
||||
static void drm_bridge_connector_force(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *hdmi = bridge_connector->bridge_hdmi;
|
||||
|
||||
if (hdmi)
|
||||
drm_atomic_helper_connector_hdmi_force(connector);
|
||||
}
|
||||
|
||||
static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
|
||||
struct dentry *root)
|
||||
{
|
||||
|
|
@ -231,6 +247,7 @@ static void drm_bridge_connector_reset(struct drm_connector *connector)
|
|||
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
|
||||
.reset = drm_bridge_connector_reset,
|
||||
.detect = drm_bridge_connector_detect,
|
||||
.force = drm_bridge_connector_force,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
|
|
@ -276,6 +293,14 @@ static int drm_bridge_connector_get_modes(struct drm_connector *connector)
|
|||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
/*
|
||||
* If there is a HDMI bridge, EDID has been updated as a part of
|
||||
* the .detect(). Just update the modes here.
|
||||
*/
|
||||
bridge = bridge_connector->bridge_hdmi;
|
||||
if (bridge)
|
||||
return drm_edid_connector_add_modes(connector);
|
||||
|
||||
/*
|
||||
* If display exposes EDID, then we parse that in the normal way to
|
||||
* build table of supported modes.
|
||||
|
|
@ -313,11 +338,24 @@ drm_bridge_connector_mode_valid(struct drm_connector *connector,
|
|||
return MODE_OK;
|
||||
}
|
||||
|
||||
static int drm_bridge_connector_atomic_check(struct drm_connector *connector,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
|
||||
if (bridge_connector->bridge_hdmi)
|
||||
return drm_atomic_helper_connector_hdmi_check(connector, state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = {
|
||||
.get_modes = drm_bridge_connector_get_modes,
|
||||
.mode_valid = drm_bridge_connector_mode_valid,
|
||||
.enable_hpd = drm_bridge_connector_enable_hpd,
|
||||
.disable_hpd = drm_bridge_connector_disable_hpd,
|
||||
.atomic_check = drm_bridge_connector_atomic_check,
|
||||
};
|
||||
|
||||
static enum drm_mode_status
|
||||
|
|
@ -368,10 +406,94 @@ static int drm_bridge_connector_write_infoframe(struct drm_connector *connector,
|
|||
return bridge->funcs->hdmi_write_infoframe(bridge, type, buffer, len);
|
||||
}
|
||||
|
||||
static const struct drm_edid *
|
||||
drm_bridge_connector_read_edid(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
bridge = bridge_connector->bridge_edid;
|
||||
if (!bridge)
|
||||
return NULL;
|
||||
|
||||
return drm_bridge_edid_read(bridge, 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,
|
||||
};
|
||||
|
||||
static int drm_bridge_connector_audio_startup(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;
|
||||
|
||||
if (!bridge->funcs->hdmi_audio_startup)
|
||||
return 0;
|
||||
|
||||
return bridge->funcs->hdmi_audio_startup(connector, bridge);
|
||||
}
|
||||
|
||||
static int drm_bridge_connector_audio_prepare(struct drm_connector *connector,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
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_audio_prepare(connector, bridge, fmt, hparms);
|
||||
}
|
||||
|
||||
static void drm_bridge_connector_audio_shutdown(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;
|
||||
|
||||
bridge->funcs->hdmi_audio_shutdown(connector, bridge);
|
||||
}
|
||||
|
||||
static int drm_bridge_connector_audio_mute_stream(struct drm_connector *connector,
|
||||
bool enable, int direction)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
bridge = bridge_connector->bridge_hdmi;
|
||||
if (!bridge)
|
||||
return -EINVAL;
|
||||
|
||||
if (bridge->funcs->hdmi_audio_mute_stream)
|
||||
return bridge->funcs->hdmi_audio_mute_stream(connector, bridge,
|
||||
enable, direction);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static const struct drm_connector_hdmi_audio_funcs drm_bridge_connector_hdmi_audio_funcs = {
|
||||
.startup = drm_bridge_connector_audio_startup,
|
||||
.prepare = drm_bridge_connector_audio_prepare,
|
||||
.shutdown = drm_bridge_connector_audio_shutdown,
|
||||
.mute_stream = drm_bridge_connector_audio_mute_stream,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
|
|
@ -473,7 +595,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
|||
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (bridge_connector->bridge_hdmi)
|
||||
if (bridge_connector->bridge_hdmi) {
|
||||
bridge = bridge_connector->bridge_hdmi;
|
||||
|
||||
ret = drmm_connector_hdmi_init(drm, connector,
|
||||
bridge_connector->bridge_hdmi->vendor,
|
||||
bridge_connector->bridge_hdmi->product,
|
||||
|
|
@ -482,12 +606,31 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
|||
connector_type, ddc,
|
||||
supported_formats,
|
||||
max_bpc);
|
||||
else
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (bridge->hdmi_audio_max_i2s_playback_channels ||
|
||||
bridge->hdmi_audio_spdif_playback) {
|
||||
if (!bridge->funcs->hdmi_audio_prepare ||
|
||||
!bridge->funcs->hdmi_audio_shutdown)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ret = drm_connector_hdmi_audio_init(connector,
|
||||
bridge->hdmi_audio_dev,
|
||||
&drm_bridge_connector_hdmi_audio_funcs,
|
||||
bridge->hdmi_audio_max_i2s_playback_channels,
|
||||
bridge->hdmi_audio_spdif_playback,
|
||||
bridge->hdmi_audio_dai_port);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
} else {
|
||||
ret = drmm_connector_init(drm, connector,
|
||||
&drm_bridge_connector_funcs,
|
||||
connector_type, ddc);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
|
||||
|
||||
|
|
|
|||
190
drivers/gpu/drm/display/drm_hdmi_audio_helper.c
Normal file
190
drivers/gpu/drm/display/drm_hdmi_audio_helper.c
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (c) 2024 Linaro Ltd
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/display/drm_hdmi_audio_helper.h>
|
||||
|
||||
#include <sound/hdmi-codec.h>
|
||||
|
||||
static int drm_connector_hdmi_audio_startup(struct device *dev, void *data)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
const struct drm_connector_hdmi_audio_funcs *funcs =
|
||||
connector->hdmi_audio.funcs;
|
||||
|
||||
if (funcs->startup)
|
||||
return funcs->startup(connector);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_connector_hdmi_audio_prepare(struct device *dev, void *data,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
const struct drm_connector_hdmi_audio_funcs *funcs =
|
||||
connector->hdmi_audio.funcs;
|
||||
|
||||
return funcs->prepare(connector, fmt, hparms);
|
||||
}
|
||||
|
||||
static void drm_connector_hdmi_audio_shutdown(struct device *dev, void *data)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
const struct drm_connector_hdmi_audio_funcs *funcs =
|
||||
connector->hdmi_audio.funcs;
|
||||
|
||||
return funcs->shutdown(connector);
|
||||
}
|
||||
|
||||
static int drm_connector_hdmi_audio_mute_stream(struct device *dev, void *data,
|
||||
bool enable, int direction)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
const struct drm_connector_hdmi_audio_funcs *funcs =
|
||||
connector->hdmi_audio.funcs;
|
||||
|
||||
if (funcs->mute_stream)
|
||||
return funcs->mute_stream(connector, enable, direction);
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int drm_connector_hdmi_audio_get_dai_id(struct snd_soc_component *comment,
|
||||
struct device_node *endpoint,
|
||||
void *data)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
||||
if (connector->hdmi_audio.dai_port < 0)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = of_graph_parse_endpoint(endpoint, &of_ep);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (of_ep.port == connector->hdmi_audio.dai_port)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int drm_connector_hdmi_audio_get_eld(struct device *dev, void *data,
|
||||
uint8_t *buf, size_t len)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
|
||||
mutex_lock(&connector->eld_mutex);
|
||||
memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
|
||||
mutex_unlock(&connector->eld_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_connector_hdmi_audio_hook_plugged_cb(struct device *dev,
|
||||
void *data,
|
||||
hdmi_codec_plugged_cb fn,
|
||||
struct device *codec_dev)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
|
||||
mutex_lock(&connector->hdmi_audio.lock);
|
||||
|
||||
connector->hdmi_audio.plugged_cb = fn;
|
||||
connector->hdmi_audio.plugged_cb_dev = codec_dev;
|
||||
|
||||
fn(codec_dev, connector->hdmi_audio.last_state);
|
||||
|
||||
mutex_unlock(&connector->hdmi_audio.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector,
|
||||
bool plugged)
|
||||
{
|
||||
mutex_lock(&connector->hdmi_audio.lock);
|
||||
|
||||
connector->hdmi_audio.last_state = plugged;
|
||||
|
||||
if (connector->hdmi_audio.plugged_cb &&
|
||||
connector->hdmi_audio.plugged_cb_dev)
|
||||
connector->hdmi_audio.plugged_cb(connector->hdmi_audio.plugged_cb_dev,
|
||||
connector->hdmi_audio.last_state);
|
||||
|
||||
mutex_unlock(&connector->hdmi_audio.lock);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_hdmi_audio_plugged_notify);
|
||||
|
||||
static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = {
|
||||
.audio_startup = drm_connector_hdmi_audio_startup,
|
||||
.prepare = drm_connector_hdmi_audio_prepare,
|
||||
.audio_shutdown = drm_connector_hdmi_audio_shutdown,
|
||||
.mute_stream = drm_connector_hdmi_audio_mute_stream,
|
||||
.get_eld = drm_connector_hdmi_audio_get_eld,
|
||||
.get_dai_id = drm_connector_hdmi_audio_get_dai_id,
|
||||
.hook_plugged_cb = drm_connector_hdmi_audio_hook_plugged_cb,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_connector_hdmi_audio_init - Initialize HDMI Codec device for the DRM connector
|
||||
* @connector: A pointer to the connector to allocate codec for
|
||||
* @hdmi_codec_dev: device to be used as a parent for the HDMI Codec
|
||||
* @funcs: callbacks for this HDMI Codec
|
||||
* @max_i2s_playback_channels: maximum number of playback I2S channels
|
||||
* @spdif_playback: set if HDMI codec has S/PDIF playback port
|
||||
* @dai_port: sound DAI port, -1 if it is not enabled
|
||||
*
|
||||
* Create a HDMI codec device to be used with the specified connector.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, error code on failure.
|
||||
*/
|
||||
int drm_connector_hdmi_audio_init(struct drm_connector *connector,
|
||||
struct device *hdmi_codec_dev,
|
||||
const struct drm_connector_hdmi_audio_funcs *funcs,
|
||||
unsigned int max_i2s_playback_channels,
|
||||
bool spdif_playback,
|
||||
int dai_port)
|
||||
{
|
||||
struct hdmi_codec_pdata codec_pdata = {
|
||||
.ops = &drm_connector_hdmi_audio_ops,
|
||||
.max_i2s_channels = max_i2s_playback_channels,
|
||||
.i2s = !!max_i2s_playback_channels,
|
||||
.spdif = spdif_playback,
|
||||
.no_i2s_capture = true,
|
||||
.no_spdif_capture = true,
|
||||
.data = connector,
|
||||
};
|
||||
struct platform_device *pdev;
|
||||
|
||||
if (!funcs ||
|
||||
!funcs->prepare ||
|
||||
!funcs->shutdown)
|
||||
return -EINVAL;
|
||||
|
||||
connector->hdmi_audio.funcs = funcs;
|
||||
connector->hdmi_audio.dai_port = dai_port;
|
||||
|
||||
pdev = platform_device_register_data(hdmi_codec_dev,
|
||||
HDMI_CODEC_DRV_NAME,
|
||||
PLATFORM_DEVID_AUTO,
|
||||
&codec_pdata, sizeof(codec_pdata));
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
connector->hdmi_audio.codec_pdev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_hdmi_audio_init);
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include <drm/display/drm_hdmi_audio_helper.h>
|
||||
#include <drm/display/drm_hdmi_helper.h>
|
||||
#include <drm/display/drm_hdmi_state_helper.h>
|
||||
|
||||
|
|
@ -777,3 +778,59 @@ drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *con
|
|||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_clear_audio_infoframe);
|
||||
|
||||
static void
|
||||
drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector,
|
||||
enum drm_connector_status status)
|
||||
{
|
||||
const struct drm_edid *drm_edid;
|
||||
|
||||
if (status == connector_status_disconnected) {
|
||||
// TODO: also handle CEC and scramber, HDMI sink disconnected.
|
||||
drm_connector_hdmi_audio_plugged_notify(connector, false);
|
||||
}
|
||||
|
||||
if (connector->hdmi.funcs->read_edid)
|
||||
drm_edid = connector->hdmi.funcs->read_edid(connector);
|
||||
else
|
||||
drm_edid = drm_edid_read(connector);
|
||||
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
if (status == connector_status_connected) {
|
||||
// TODO: also handle CEC and scramber, HDMI sink is now connected.
|
||||
drm_connector_hdmi_audio_plugged_notify(connector, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_connector_hdmi_hotplug - Handle the hotplug event for the HDMI connector
|
||||
* @connector: A pointer to the HDMI connector
|
||||
* @status: Connection status
|
||||
*
|
||||
* This function should be called as a part of the .detect() / .detect_ctx()
|
||||
* callbacks, updating the HDMI-specific connector's data.
|
||||
*/
|
||||
void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
|
||||
enum drm_connector_status status)
|
||||
{
|
||||
drm_atomic_helper_connector_hdmi_update(connector, status);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_hotplug);
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_connector_hdmi_force - HDMI Connector implementation of the force callback
|
||||
* @connector: A pointer to the HDMI connector
|
||||
*
|
||||
* This function implements the .force() callback for the HDMI connectors. It
|
||||
* can either be used directly as the callback or should be called from within
|
||||
* the .force() callback implementation to maintain the HDMI-specific
|
||||
* connector's data.
|
||||
*/
|
||||
void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector)
|
||||
{
|
||||
drm_atomic_helper_connector_hdmi_update(connector, connector->status);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_force);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <drm/drm_sysfs.h>
|
||||
#include <drm/drm_utils.h>
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
|
|
@ -281,6 +282,7 @@ static int drm_connector_init_only(struct drm_device *dev,
|
|||
mutex_init(&connector->eld_mutex);
|
||||
mutex_init(&connector->edid_override_mutex);
|
||||
mutex_init(&connector->hdmi.infoframes.lock);
|
||||
mutex_init(&connector->hdmi_audio.lock);
|
||||
connector->edid_blob_ptr = NULL;
|
||||
connector->epoch_counter = 0;
|
||||
connector->tile_blob_ptr = NULL;
|
||||
|
|
@ -714,6 +716,8 @@ void drm_connector_cleanup(struct drm_connector *connector)
|
|||
DRM_CONNECTOR_REGISTERED))
|
||||
drm_connector_unregister(connector);
|
||||
|
||||
platform_device_unregister(connector->hdmi_audio.codec_pdev);
|
||||
|
||||
if (connector->privacy_screen) {
|
||||
drm_privacy_screen_put(connector->privacy_screen);
|
||||
connector->privacy_screen = NULL;
|
||||
|
|
@ -750,6 +754,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
|
|||
connector->funcs->atomic_destroy_state(connector,
|
||||
connector->state);
|
||||
|
||||
mutex_destroy(&connector->hdmi_audio.lock);
|
||||
mutex_destroy(&connector->hdmi.infoframes.lock);
|
||||
mutex_destroy(&connector->mutex);
|
||||
|
||||
|
|
|
|||
|
|
@ -845,6 +845,16 @@ static void print_size(struct drm_printer *p, const char *stat,
|
|||
drm_printf(p, "drm-%s-%s:\t%llu%s\n", stat, region, sz, units[u]);
|
||||
}
|
||||
|
||||
int drm_memory_stats_is_zero(const struct drm_memory_stats *stats)
|
||||
{
|
||||
return (stats->shared == 0 &&
|
||||
stats->private == 0 &&
|
||||
stats->resident == 0 &&
|
||||
stats->purgeable == 0 &&
|
||||
stats->active == 0);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_memory_stats_is_zero);
|
||||
|
||||
/**
|
||||
* drm_print_memory_stats - A helper to print memory stats
|
||||
* @p: The printer to print output to
|
||||
|
|
@ -860,7 +870,9 @@ void drm_print_memory_stats(struct drm_printer *p,
|
|||
{
|
||||
print_size(p, "total", region, stats->private + stats->shared);
|
||||
print_size(p, "shared", region, stats->shared);
|
||||
print_size(p, "active", region, stats->active);
|
||||
|
||||
if (supported_status & DRM_GEM_OBJECT_ACTIVE)
|
||||
print_size(p, "active", region, stats->active);
|
||||
|
||||
if (supported_status & DRM_GEM_OBJECT_RESIDENT)
|
||||
print_size(p, "resident", region, stats->resident);
|
||||
|
|
@ -893,15 +905,13 @@ void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file)
|
|||
|
||||
if (obj->funcs && obj->funcs->status) {
|
||||
s = obj->funcs->status(obj);
|
||||
supported_status = DRM_GEM_OBJECT_RESIDENT |
|
||||
DRM_GEM_OBJECT_PURGEABLE;
|
||||
supported_status |= s;
|
||||
}
|
||||
|
||||
if (drm_gem_object_is_shared_for_memory_stats(obj)) {
|
||||
if (drm_gem_object_is_shared_for_memory_stats(obj))
|
||||
status.shared += obj->size;
|
||||
} else {
|
||||
else
|
||||
status.private += obj->size;
|
||||
}
|
||||
|
||||
if (s & DRM_GEM_OBJECT_RESIDENT) {
|
||||
status.resident += add_size;
|
||||
|
|
@ -914,6 +924,7 @@ void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file)
|
|||
|
||||
if (!dma_resv_test_signaled(obj->resv, dma_resv_usage_rw(true))) {
|
||||
status.active += add_size;
|
||||
supported_status |= DRM_GEM_OBJECT_ACTIVE;
|
||||
|
||||
/* If still active, don't count as purgeable: */
|
||||
s &= ~DRM_GEM_OBJECT_PURGEABLE;
|
||||
|
|
|
|||
|
|
@ -414,7 +414,7 @@ bool drm_is_panel_follower(struct device *dev)
|
|||
* don't bother trying to parse it here. We just need to know if the
|
||||
* property is there.
|
||||
*/
|
||||
return of_property_read_bool(dev->of_node, "panel");
|
||||
return of_property_present(dev->of_node, "panel");
|
||||
}
|
||||
EXPORT_SYMBOL(drm_is_panel_follower);
|
||||
|
||||
|
|
|
|||
|
|
@ -1660,7 +1660,6 @@ static const struct hdmi_codec_ops audio_codec_ops = {
|
|||
.audio_shutdown = hdmi_audio_shutdown,
|
||||
.mute_stream = hdmi_audio_mute,
|
||||
.get_eld = hdmi_audio_get_eld,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static int hdmi_register_audio_device(struct hdmi_context *hdata)
|
||||
|
|
@ -1669,6 +1668,7 @@ static int hdmi_register_audio_device(struct hdmi_context *hdata)
|
|||
.ops = &audio_codec_ops,
|
||||
.max_i2s_channels = 6,
|
||||
.i2s = 1,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
hdata->audio.pdev = platform_device_register_data(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o
|
||||
hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \
|
||||
dp/dp_aux.o dp/dp_link.o dp/dp_hw.o hibmc_drm_dp.o
|
||||
|
||||
obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
|
||||
|
|
|
|||
164
drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c
Normal file
164
drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (c) 2024 Hisilicon Limited.
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include "dp_comm.h"
|
||||
#include "dp_reg.h"
|
||||
|
||||
#define HIBMC_AUX_CMD_REQ_LEN GENMASK(7, 4)
|
||||
#define HIBMC_AUX_CMD_ADDR GENMASK(27, 8)
|
||||
#define HIBMC_AUX_CMD_I2C_ADDR_ONLY BIT(28)
|
||||
#define HIBMC_BYTES_IN_U32 4
|
||||
#define HIBMC_AUX_I2C_WRITE_SUCCESS 0x1
|
||||
#define HIBMC_DP_MIN_PULSE_NUM 0x9
|
||||
#define BITS_IN_U8 8
|
||||
|
||||
static inline void hibmc_dp_aux_reset(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_RST_CTRL, HIBMC_DP_CFG_AUX_RST_N, 0x0);
|
||||
usleep_range(10, 15);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_RST_CTRL, HIBMC_DP_CFG_AUX_RST_N, 0x1);
|
||||
}
|
||||
|
||||
static void hibmc_dp_aux_read_data(struct hibmc_dp_dev *dp, u8 *buf, u8 size)
|
||||
{
|
||||
u32 reg_num;
|
||||
u32 value;
|
||||
u32 num;
|
||||
u8 i, j;
|
||||
|
||||
reg_num = DIV_ROUND_UP(size, HIBMC_BYTES_IN_U32);
|
||||
for (i = 0; i < reg_num; i++) {
|
||||
/* number of bytes read from a single register */
|
||||
num = min(size - i * HIBMC_BYTES_IN_U32, HIBMC_BYTES_IN_U32);
|
||||
value = readl(dp->base + HIBMC_DP_AUX_RD_DATA0 + i * HIBMC_BYTES_IN_U32);
|
||||
/* convert the 32-bit value of the register to the buffer. */
|
||||
for (j = 0; j < num; j++)
|
||||
buf[i * HIBMC_BYTES_IN_U32 + j] = value >> (j * BITS_IN_U8);
|
||||
}
|
||||
}
|
||||
|
||||
static void hibmc_dp_aux_write_data(struct hibmc_dp_dev *dp, u8 *buf, u8 size)
|
||||
{
|
||||
u32 reg_num;
|
||||
u32 value;
|
||||
u32 num;
|
||||
u8 i, j;
|
||||
|
||||
reg_num = DIV_ROUND_UP(size, HIBMC_BYTES_IN_U32);
|
||||
for (i = 0; i < reg_num; i++) {
|
||||
/* number of bytes written to a single register */
|
||||
num = min_t(u8, size - i * HIBMC_BYTES_IN_U32, HIBMC_BYTES_IN_U32);
|
||||
value = 0;
|
||||
/* obtain the 32-bit value written to a single register. */
|
||||
for (j = 0; j < num; j++)
|
||||
value |= buf[i * HIBMC_BYTES_IN_U32 + j] << (j * BITS_IN_U8);
|
||||
/* writing data to a single register */
|
||||
writel(value, dp->base + HIBMC_DP_AUX_WR_DATA0 + i * HIBMC_BYTES_IN_U32);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 hibmc_dp_aux_build_cmd(const struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
u32 aux_cmd = msg->request;
|
||||
|
||||
if (msg->size)
|
||||
aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_REQ_LEN, (msg->size - 1));
|
||||
else
|
||||
aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_I2C_ADDR_ONLY, 1);
|
||||
|
||||
aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_ADDR, msg->address);
|
||||
|
||||
return aux_cmd;
|
||||
}
|
||||
|
||||
/* ret >= 0, ret is size; ret < 0, ret is err code */
|
||||
static int hibmc_dp_aux_parse_xfer(struct hibmc_dp_dev *dp, struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
u32 buf_data_cnt;
|
||||
u32 aux_status;
|
||||
|
||||
aux_status = readl(dp->base + HIBMC_DP_AUX_STATUS);
|
||||
msg->reply = FIELD_GET(HIBMC_DP_CFG_AUX_STATUS, aux_status);
|
||||
|
||||
if (aux_status & HIBMC_DP_CFG_AUX_TIMEOUT)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* only address */
|
||||
if (!msg->size)
|
||||
return 0;
|
||||
|
||||
if (msg->reply != DP_AUX_NATIVE_REPLY_ACK)
|
||||
return -EIO;
|
||||
|
||||
buf_data_cnt = FIELD_GET(HIBMC_DP_CFG_AUX_READY_DATA_BYTE, aux_status);
|
||||
|
||||
switch (msg->request) {
|
||||
case DP_AUX_NATIVE_WRITE:
|
||||
return msg->size;
|
||||
case DP_AUX_I2C_WRITE | DP_AUX_I2C_MOT:
|
||||
if (buf_data_cnt == HIBMC_AUX_I2C_WRITE_SUCCESS)
|
||||
return msg->size;
|
||||
else
|
||||
return FIELD_GET(HIBMC_DP_CFG_AUX, aux_status);
|
||||
case DP_AUX_NATIVE_READ:
|
||||
case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
|
||||
buf_data_cnt--;
|
||||
if (buf_data_cnt != msg->size) {
|
||||
/* only the successful part of data is read */
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* all data is successfully read */
|
||||
hibmc_dp_aux_read_data(dp, msg->buffer, msg->size);
|
||||
return msg->size;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* ret >= 0 ,ret is size; ret < 0, ret is err code */
|
||||
static ssize_t hibmc_dp_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
struct hibmc_dp_dev *dp = container_of(aux, struct hibmc_dp_dev, aux);
|
||||
u32 aux_cmd;
|
||||
int ret;
|
||||
u32 val; /* val will be assigned at the beginning of readl_poll_timeout function */
|
||||
|
||||
writel(0, dp->base + HIBMC_DP_AUX_WR_DATA0);
|
||||
writel(0, dp->base + HIBMC_DP_AUX_WR_DATA1);
|
||||
writel(0, dp->base + HIBMC_DP_AUX_WR_DATA2);
|
||||
writel(0, dp->base + HIBMC_DP_AUX_WR_DATA3);
|
||||
|
||||
hibmc_dp_aux_write_data(dp, msg->buffer, msg->size);
|
||||
|
||||
aux_cmd = hibmc_dp_aux_build_cmd(msg);
|
||||
writel(aux_cmd, dp->base + HIBMC_DP_AUX_CMD_ADDR);
|
||||
|
||||
/* enable aux transfer */
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_REQ, 0x1);
|
||||
ret = readl_poll_timeout(dp->base + HIBMC_DP_AUX_REQ, val,
|
||||
!(val & HIBMC_DP_CFG_AUX_REQ), 50, 5000);
|
||||
if (ret) {
|
||||
hibmc_dp_aux_reset(dp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return hibmc_dp_aux_parse_xfer(dp, msg);
|
||||
}
|
||||
|
||||
void hibmc_dp_aux_init(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_MIN_PULSE_NUM,
|
||||
HIBMC_DP_MIN_PULSE_NUM);
|
||||
|
||||
dp->aux.transfer = hibmc_dp_aux_xfer;
|
||||
dp->aux.is_remote = 0;
|
||||
drm_dp_aux_init(&dp->aux);
|
||||
}
|
||||
63
drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h
Normal file
63
drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/* Copyright (c) 2024 Hisilicon Limited. */
|
||||
|
||||
#ifndef DP_COMM_H
|
||||
#define DP_COMM_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/io.h>
|
||||
#include <drm/display/drm_dp_helper.h>
|
||||
|
||||
#define HIBMC_DP_LANE_NUM_MAX 2
|
||||
|
||||
struct hibmc_link_status {
|
||||
bool clock_recovered;
|
||||
bool channel_equalized;
|
||||
};
|
||||
|
||||
struct hibmc_link_cap {
|
||||
u8 link_rate;
|
||||
u8 lanes;
|
||||
};
|
||||
|
||||
struct hibmc_dp_link {
|
||||
struct hibmc_link_status status;
|
||||
u8 train_set[HIBMC_DP_LANE_NUM_MAX];
|
||||
struct hibmc_link_cap cap;
|
||||
};
|
||||
|
||||
struct hibmc_dp_dev {
|
||||
struct drm_dp_aux aux;
|
||||
struct drm_device *dev;
|
||||
void __iomem *base;
|
||||
struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */
|
||||
struct hibmc_dp_link link;
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
};
|
||||
|
||||
#define dp_field_modify(reg_value, mask, val) \
|
||||
do { \
|
||||
(reg_value) &= ~(mask); \
|
||||
(reg_value) |= FIELD_PREP(mask, val); \
|
||||
} while (0) \
|
||||
|
||||
#define hibmc_dp_reg_write_field(dp, offset, mask, val) \
|
||||
do { \
|
||||
typeof(dp) _dp = dp; \
|
||||
typeof(_dp->base) addr = (_dp->base + (offset)); \
|
||||
mutex_lock(&_dp->lock); \
|
||||
u32 reg_value = readl(addr); \
|
||||
dp_field_modify(reg_value, mask, val); \
|
||||
writel(reg_value, addr); \
|
||||
mutex_unlock(&_dp->lock); \
|
||||
} while (0)
|
||||
|
||||
void hibmc_dp_aux_init(struct hibmc_dp_dev *dp);
|
||||
int hibmc_dp_link_training(struct hibmc_dp_dev *dp);
|
||||
|
||||
#endif
|
||||
19
drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h
Normal file
19
drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/* Copyright (c) 2024 Hisilicon Limited. */
|
||||
|
||||
#ifndef DP_CONFIG_H
|
||||
#define DP_CONFIG_H
|
||||
|
||||
#define HIBMC_DP_BPP 24
|
||||
#define HIBMC_DP_SYMBOL_PER_FCLK 4
|
||||
#define HIBMC_DP_MSA1 0x20
|
||||
#define HIBMC_DP_MSA2 0x845c00
|
||||
#define HIBMC_DP_OFFSET 0x1e0000
|
||||
#define HIBMC_DP_HDCP 0x2
|
||||
#define HIBMC_DP_INT_RST 0xffff
|
||||
#define HIBMC_DP_DPTX_RST 0x3ff
|
||||
#define HIBMC_DP_CLK_EN 0x7
|
||||
#define HIBMC_DP_SYNC_EN_MASK 0x3
|
||||
#define HIBMC_DP_LINK_RATE_CAL 27
|
||||
|
||||
#endif
|
||||
220
drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c
Normal file
220
drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (c) 2024 Hisilicon Limited.
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include "dp_config.h"
|
||||
#include "dp_comm.h"
|
||||
#include "dp_reg.h"
|
||||
#include "dp_hw.h"
|
||||
|
||||
static void hibmc_dp_set_tu(struct hibmc_dp_dev *dp, struct drm_display_mode *mode)
|
||||
{
|
||||
u32 tu_symbol_frac_size;
|
||||
u32 tu_symbol_size;
|
||||
u32 rate_ks;
|
||||
u8 lane_num;
|
||||
u32 value;
|
||||
u32 bpp;
|
||||
|
||||
lane_num = dp->link.cap.lanes;
|
||||
if (lane_num == 0) {
|
||||
drm_err(dp->dev, "set tu failed, lane num cannot be 0!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bpp = HIBMC_DP_BPP;
|
||||
rate_ks = dp->link.cap.link_rate * HIBMC_DP_LINK_RATE_CAL;
|
||||
value = (mode->clock * bpp * 5) / (61 * lane_num * rate_ks);
|
||||
|
||||
if (value % 10 == 9) { /* 9 carry */
|
||||
tu_symbol_size = value / 10 + 1;
|
||||
tu_symbol_frac_size = 0;
|
||||
} else {
|
||||
tu_symbol_size = value / 10;
|
||||
tu_symbol_frac_size = value % 10 + 1;
|
||||
}
|
||||
|
||||
drm_dbg_dp(dp->dev, "tu value: %u.%u value: %u\n",
|
||||
tu_symbol_size, tu_symbol_frac_size, value);
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET,
|
||||
HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE, tu_symbol_size);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET,
|
||||
HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE, tu_symbol_frac_size);
|
||||
}
|
||||
|
||||
static void hibmc_dp_set_sst(struct hibmc_dp_dev *dp, struct drm_display_mode *mode)
|
||||
{
|
||||
u32 hblank_size;
|
||||
u32 htotal_size;
|
||||
u32 htotal_int;
|
||||
u32 hblank_int;
|
||||
u32 fclk; /* flink_clock */
|
||||
|
||||
fclk = dp->link.cap.link_rate * HIBMC_DP_LINK_RATE_CAL;
|
||||
|
||||
/* Considering the effect of spread spectrum, the value may be deviated.
|
||||
* The coefficient (0.9947) is used to offset the deviation.
|
||||
*/
|
||||
htotal_int = mode->htotal * 9947 / 10000;
|
||||
htotal_size = htotal_int * fclk / (HIBMC_DP_SYMBOL_PER_FCLK * (mode->clock / 1000));
|
||||
|
||||
hblank_int = mode->htotal - mode->hdisplay - mode->hdisplay * 53 / 10000;
|
||||
hblank_size = hblank_int * fclk * 9947 /
|
||||
(mode->clock * 10 * HIBMC_DP_SYMBOL_PER_FCLK);
|
||||
|
||||
drm_dbg_dp(dp->dev, "h_active %u v_active %u htotal_size %u hblank_size %u",
|
||||
mode->hdisplay, mode->vdisplay, htotal_size, hblank_size);
|
||||
drm_dbg_dp(dp->dev, "flink_clock %u pixel_clock %d", fclk, mode->clock / 1000);
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE,
|
||||
HIBMC_DP_CFG_STREAM_HTOTAL_SIZE, htotal_size);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE,
|
||||
HIBMC_DP_CFG_STREAM_HBLANK_SIZE, hblank_size);
|
||||
}
|
||||
|
||||
static void hibmc_dp_link_cfg(struct hibmc_dp_dev *dp, struct drm_display_mode *mode)
|
||||
{
|
||||
u32 timing_delay;
|
||||
u32 vblank;
|
||||
u32 hstart;
|
||||
u32 vstart;
|
||||
|
||||
vblank = mode->vtotal - mode->vdisplay;
|
||||
timing_delay = mode->htotal - mode->hsync_start;
|
||||
hstart = mode->htotal - mode->hsync_start;
|
||||
vstart = mode->vtotal - mode->vsync_start;
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG0,
|
||||
HIBMC_DP_CFG_TIMING_GEN0_HBLANK, mode->htotal - mode->hdisplay);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG0,
|
||||
HIBMC_DP_CFG_TIMING_GEN0_HACTIVE, mode->hdisplay);
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG2,
|
||||
HIBMC_DP_CFG_TIMING_GEN0_VBLANK, vblank);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG2,
|
||||
HIBMC_DP_CFG_TIMING_GEN0_VACTIVE, mode->vdisplay);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG3,
|
||||
HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH,
|
||||
mode->vsync_start - mode->vdisplay);
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG0,
|
||||
HIBMC_DP_CFG_STREAM_HACTIVE, mode->hdisplay);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG0,
|
||||
HIBMC_DP_CFG_STREAM_HBLANK, mode->htotal - mode->hdisplay);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG2,
|
||||
HIBMC_DP_CFG_STREAM_HSYNC_WIDTH,
|
||||
mode->hsync_end - mode->hsync_start);
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG1,
|
||||
HIBMC_DP_CFG_STREAM_VACTIVE, mode->vdisplay);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG1,
|
||||
HIBMC_DP_CFG_STREAM_VBLANK, vblank);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG3,
|
||||
HIBMC_DP_CFG_STREAM_VFRONT_PORCH,
|
||||
mode->vsync_start - mode->vdisplay);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG3,
|
||||
HIBMC_DP_CFG_STREAM_VSYNC_WIDTH,
|
||||
mode->vsync_end - mode->vsync_start);
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_MSA0,
|
||||
HIBMC_DP_CFG_STREAM_VSTART, vstart);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_MSA0,
|
||||
HIBMC_DP_CFG_STREAM_HSTART, hstart);
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_VSYNC_POLARITY,
|
||||
mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_HSYNC_POLARITY,
|
||||
mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0);
|
||||
|
||||
/* MSA mic 0 and 1 */
|
||||
writel(HIBMC_DP_MSA1, dp->base + HIBMC_DP_VIDEO_MSA1);
|
||||
writel(HIBMC_DP_MSA2, dp->base + HIBMC_DP_VIDEO_MSA2);
|
||||
|
||||
hibmc_dp_set_tu(dp, mode);
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_RGB_ENABLE, 0x1);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_VIDEO_MAPPING, 0);
|
||||
|
||||
/* divide 2: up even */
|
||||
if (timing_delay % 2)
|
||||
timing_delay++;
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_MODEL_CTRL,
|
||||
HIBMC_DP_CFG_PIXEL_NUM_TIMING_MODE_SEL1, timing_delay);
|
||||
|
||||
hibmc_dp_set_sst(dp, mode);
|
||||
}
|
||||
|
||||
int hibmc_dp_hw_init(struct hibmc_dp *dp)
|
||||
{
|
||||
struct drm_device *drm_dev = dp->drm_dev;
|
||||
struct hibmc_dp_dev *dp_dev;
|
||||
|
||||
dp_dev = devm_kzalloc(drm_dev->dev, sizeof(struct hibmc_dp_dev), GFP_KERNEL);
|
||||
if (!dp_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&dp_dev->lock);
|
||||
|
||||
dp->dp_dev = dp_dev;
|
||||
|
||||
dp_dev->dev = drm_dev;
|
||||
dp_dev->base = dp->mmio + HIBMC_DP_OFFSET;
|
||||
|
||||
hibmc_dp_aux_init(dp_dev);
|
||||
|
||||
dp_dev->link.cap.lanes = 0x2;
|
||||
dp_dev->link.cap.link_rate = DP_LINK_BW_2_7;
|
||||
|
||||
/* 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);
|
||||
/* rst */
|
||||
writel(HIBMC_DP_DPTX_RST, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL);
|
||||
/* clock enable */
|
||||
writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable)
|
||||
{
|
||||
struct hibmc_dp_dev *dp_dev = dp->dp_dev;
|
||||
|
||||
if (enable) {
|
||||
hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, BIT(0), 0x1);
|
||||
writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
|
||||
hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, BIT(10), 0x1);
|
||||
writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
|
||||
} else {
|
||||
hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, BIT(10), 0);
|
||||
writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
|
||||
hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, BIT(0), 0);
|
||||
writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
|
||||
}
|
||||
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode)
|
||||
{
|
||||
struct hibmc_dp_dev *dp_dev = dp->dp_dev;
|
||||
int ret;
|
||||
|
||||
if (!dp_dev->link.status.channel_equalized) {
|
||||
ret = hibmc_dp_link_training(dp_dev);
|
||||
if (ret) {
|
||||
drm_err(dp->drm_dev, "dp link training failed, ret: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
hibmc_dp_display_en(dp, false);
|
||||
hibmc_dp_link_cfg(dp_dev, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h
Normal file
28
drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/* Copyright (c) 2024 Hisilicon Limited. */
|
||||
|
||||
#ifndef DP_KAPI_H
|
||||
#define DP_KAPI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
struct hibmc_dp_dev;
|
||||
|
||||
struct hibmc_dp {
|
||||
struct hibmc_dp_dev *dp_dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
void __iomem *mmio;
|
||||
};
|
||||
|
||||
int hibmc_dp_hw_init(struct hibmc_dp *dp);
|
||||
int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode);
|
||||
void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable);
|
||||
|
||||
#endif
|
||||
332
drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c
Normal file
332
drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (c) 2024 Hisilicon Limited.
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include "dp_comm.h"
|
||||
#include "dp_reg.h"
|
||||
|
||||
#define HIBMC_EQ_MAX_RETRY 5
|
||||
|
||||
static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
u8 buf[2];
|
||||
int ret;
|
||||
|
||||
/* DP 2 lane */
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_LANE_DATA_EN,
|
||||
dp->link.cap.lanes == 0x2 ? 0x3 : 0x1);
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_GCTL0, HIBMC_DP_CFG_PHY_LANE_NUM,
|
||||
dp->link.cap.lanes == 0x2 ? 0x1 : 0);
|
||||
|
||||
/* enhanced frame */
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_FRAME_MODE, 0x1);
|
||||
|
||||
/* set rate and lane count */
|
||||
buf[0] = dp->link.cap.link_rate;
|
||||
buf[1] = DP_LANE_COUNT_ENHANCED_FRAME_EN | dp->link.cap.lanes;
|
||||
ret = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, sizeof(buf));
|
||||
if (ret != sizeof(buf)) {
|
||||
drm_dbg_dp(dp->dev, "dp aux write link rate and lanes failed, ret: %d\n", ret);
|
||||
return ret >= 0 ? -EIO : ret;
|
||||
}
|
||||
|
||||
/* set 8b/10b and downspread */
|
||||
buf[0] = DP_SPREAD_AMP_0_5;
|
||||
buf[1] = DP_SET_ANSI_8B10B;
|
||||
ret = drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, buf, sizeof(buf));
|
||||
if (ret != sizeof(buf)) {
|
||||
drm_dbg_dp(dp->dev, "dp aux write 8b/10b and downspread failed, ret: %d\n", ret);
|
||||
return ret >= 0 ? -EIO : ret;
|
||||
}
|
||||
|
||||
ret = drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd);
|
||||
if (ret)
|
||||
drm_err(dp->dev, "dp aux read dpcd failed, ret: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hibmc_dp_link_set_pattern(struct hibmc_dp_dev *dp, int pattern)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
u8 buf;
|
||||
|
||||
buf = (u8)pattern;
|
||||
if (pattern != DP_TRAINING_PATTERN_DISABLE && pattern != DP_TRAINING_PATTERN_4) {
|
||||
buf |= DP_LINK_SCRAMBLING_DISABLE;
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_SCRAMBLE_EN, 0x1);
|
||||
} else {
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_SCRAMBLE_EN, 0);
|
||||
}
|
||||
|
||||
switch (pattern) {
|
||||
case DP_TRAINING_PATTERN_DISABLE:
|
||||
val = 0;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_1:
|
||||
val = 1;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_2:
|
||||
val = 2;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_3:
|
||||
val = 3;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_4:
|
||||
val = 4;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_PAT_SEL, val);
|
||||
|
||||
ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET, &buf, sizeof(buf));
|
||||
if (ret != sizeof(buf)) {
|
||||
drm_dbg_dp(dp->dev, "dp aux write training pattern set failed\n");
|
||||
return ret >= 0 ? -EIO : ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hibmc_dp_link_training_cr_pre(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
u8 *train_set = dp->link.train_set;
|
||||
int ret;
|
||||
u8 i;
|
||||
|
||||
ret = hibmc_dp_link_training_configure(dp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < dp->link.cap.lanes; i++)
|
||||
train_set[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
|
||||
|
||||
ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->link.cap.lanes);
|
||||
if (ret != dp->link.cap.lanes) {
|
||||
drm_dbg_dp(dp->dev, "dp aux write training lane set failed\n");
|
||||
return ret >= 0 ? -EIO : ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool hibmc_dp_link_get_adjust_train(struct hibmc_dp_dev *dp,
|
||||
u8 lane_status[DP_LINK_STATUS_SIZE])
|
||||
{
|
||||
u8 train_set[HIBMC_DP_LANE_NUM_MAX] = {0};
|
||||
u8 lane;
|
||||
|
||||
for (lane = 0; lane < dp->link.cap.lanes; lane++)
|
||||
train_set[lane] = drm_dp_get_adjust_request_voltage(lane_status, lane) |
|
||||
drm_dp_get_adjust_request_pre_emphasis(lane_status, lane);
|
||||
|
||||
if (memcmp(dp->link.train_set, train_set, HIBMC_DP_LANE_NUM_MAX)) {
|
||||
memcpy(dp->link.train_set, train_set, HIBMC_DP_LANE_NUM_MAX);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int hibmc_dp_link_reduce_rate(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
switch (dp->link.cap.link_rate) {
|
||||
case DP_LINK_BW_2_7:
|
||||
dp->link.cap.link_rate = DP_LINK_BW_1_62;
|
||||
return 0;
|
||||
case DP_LINK_BW_5_4:
|
||||
dp->link.cap.link_rate = DP_LINK_BW_2_7;
|
||||
return 0;
|
||||
case DP_LINK_BW_8_1:
|
||||
dp->link.cap.link_rate = DP_LINK_BW_5_4;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int hibmc_dp_link_reduce_lane(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
switch (dp->link.cap.lanes) {
|
||||
case 0x2:
|
||||
dp->link.cap.lanes--;
|
||||
break;
|
||||
case 0x1:
|
||||
drm_err(dp->dev, "dp link training reduce lane failed, already reach minimum\n");
|
||||
return -EIO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
u8 lane_status[DP_LINK_STATUS_SIZE] = {0};
|
||||
bool level_changed;
|
||||
u32 voltage_tries;
|
||||
u32 cr_tries;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* DP 1.4 spec define 10 for maxtries value, for pre DP 1.4 version set a limit of 80
|
||||
* (4 voltage levels x 4 preemphasis levels x 5 identical voltage retries)
|
||||
*/
|
||||
|
||||
voltage_tries = 1;
|
||||
for (cr_tries = 0; cr_tries < 80; cr_tries++) {
|
||||
drm_dp_link_train_clock_recovery_delay(&dp->aux, dp->dpcd);
|
||||
|
||||
ret = drm_dp_dpcd_read_link_status(&dp->aux, lane_status);
|
||||
if (ret != DP_LINK_STATUS_SIZE) {
|
||||
drm_err(dp->dev, "Get lane status failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) {
|
||||
drm_dbg_dp(dp->dev, "dp link training cr done\n");
|
||||
dp->link.status.clock_recovered = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (voltage_tries == 5) {
|
||||
drm_dbg_dp(dp->dev, "same voltage tries 5 times\n");
|
||||
dp->link.status.clock_recovered = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
level_changed = hibmc_dp_link_get_adjust_train(dp, lane_status);
|
||||
ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set,
|
||||
dp->link.cap.lanes);
|
||||
if (ret != dp->link.cap.lanes) {
|
||||
drm_dbg_dp(dp->dev, "Update link training failed\n");
|
||||
return ret >= 0 ? -EIO : ret;
|
||||
}
|
||||
|
||||
voltage_tries = level_changed ? 1 : voltage_tries + 1;
|
||||
}
|
||||
|
||||
drm_err(dp->dev, "dp link training clock recovery 80 times failed\n");
|
||||
dp->link.status.clock_recovered = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
u8 lane_status[DP_LINK_STATUS_SIZE] = {0};
|
||||
u8 eq_tries;
|
||||
int ret;
|
||||
|
||||
ret = hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (eq_tries = 0; eq_tries < HIBMC_EQ_MAX_RETRY; eq_tries++) {
|
||||
drm_dp_link_train_channel_eq_delay(&dp->aux, dp->dpcd);
|
||||
|
||||
ret = drm_dp_dpcd_read_link_status(&dp->aux, lane_status);
|
||||
if (ret != DP_LINK_STATUS_SIZE) {
|
||||
drm_err(dp->dev, "get lane status failed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) {
|
||||
drm_dbg_dp(dp->dev, "clock recovery check failed\n");
|
||||
drm_dbg_dp(dp->dev, "cannot continue channel equalization\n");
|
||||
dp->link.status.clock_recovered = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (drm_dp_channel_eq_ok(lane_status, dp->link.cap.lanes)) {
|
||||
dp->link.status.channel_equalized = true;
|
||||
drm_dbg_dp(dp->dev, "dp link training eq done\n");
|
||||
break;
|
||||
}
|
||||
|
||||
hibmc_dp_link_get_adjust_train(dp, lane_status);
|
||||
ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
|
||||
dp->link.train_set, dp->link.cap.lanes);
|
||||
if (ret != dp->link.cap.lanes) {
|
||||
drm_dbg_dp(dp->dev, "Update link training failed\n");
|
||||
ret = (ret >= 0) ? -EIO : ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (eq_tries == HIBMC_EQ_MAX_RETRY)
|
||||
drm_err(dp->dev, "channel equalization failed %u times\n", eq_tries);
|
||||
|
||||
hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static int hibmc_dp_link_downgrade_training_cr(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
if (hibmc_dp_link_reduce_rate(dp))
|
||||
return hibmc_dp_link_reduce_lane(dp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hibmc_dp_link_downgrade_training_eq(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
if ((dp->link.status.clock_recovered && !dp->link.status.channel_equalized)) {
|
||||
if (!hibmc_dp_link_reduce_lane(dp))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hibmc_dp_link_reduce_rate(dp);
|
||||
}
|
||||
|
||||
int hibmc_dp_link_training(struct hibmc_dp_dev *dp)
|
||||
{
|
||||
struct hibmc_dp_link *link = &dp->link;
|
||||
int ret;
|
||||
|
||||
while (true) {
|
||||
ret = hibmc_dp_link_training_cr_pre(dp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = hibmc_dp_link_training_cr(dp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!link->status.clock_recovered) {
|
||||
ret = hibmc_dp_link_downgrade_training_cr(dp);
|
||||
if (ret)
|
||||
goto err;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = hibmc_dp_link_training_channel_eq(dp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!link->status.channel_equalized) {
|
||||
ret = hibmc_dp_link_downgrade_training_eq(dp);
|
||||
if (ret)
|
||||
goto err;
|
||||
continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err:
|
||||
hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
76
drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h
Normal file
76
drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/* Copyright (c) 2024 Hisilicon Limited. */
|
||||
|
||||
#ifndef DP_REG_H
|
||||
#define DP_REG_H
|
||||
|
||||
#define HIBMC_DP_AUX_CMD_ADDR 0x50
|
||||
#define HIBMC_DP_AUX_WR_DATA0 0x54
|
||||
#define HIBMC_DP_AUX_WR_DATA1 0x58
|
||||
#define HIBMC_DP_AUX_WR_DATA2 0x5c
|
||||
#define HIBMC_DP_AUX_WR_DATA3 0x60
|
||||
#define HIBMC_DP_AUX_RD_DATA0 0x64
|
||||
#define HIBMC_DP_AUX_REQ 0x74
|
||||
#define HIBMC_DP_AUX_STATUS 0x78
|
||||
#define HIBMC_DP_PHYIF_CTRL0 0xa0
|
||||
#define HIBMC_DP_VIDEO_CTRL 0x100
|
||||
#define HIBMC_DP_VIDEO_CONFIG0 0x104
|
||||
#define HIBMC_DP_VIDEO_CONFIG1 0x108
|
||||
#define HIBMC_DP_VIDEO_CONFIG2 0x10c
|
||||
#define HIBMC_DP_VIDEO_CONFIG3 0x110
|
||||
#define HIBMC_DP_VIDEO_PACKET 0x114
|
||||
#define HIBMC_DP_VIDEO_MSA0 0x118
|
||||
#define HIBMC_DP_VIDEO_MSA1 0x11c
|
||||
#define HIBMC_DP_VIDEO_MSA2 0x120
|
||||
#define HIBMC_DP_VIDEO_HORIZONTAL_SIZE 0X124
|
||||
#define HIBMC_DP_TIMING_GEN_CONFIG0 0x26c
|
||||
#define HIBMC_DP_TIMING_GEN_CONFIG2 0x274
|
||||
#define HIBMC_DP_TIMING_GEN_CONFIG3 0x278
|
||||
#define HIBMC_DP_HDCP_CFG 0x600
|
||||
#define HIBMC_DP_DPTX_RST_CTRL 0x700
|
||||
#define HIBMC_DP_DPTX_CLK_CTRL 0x704
|
||||
#define HIBMC_DP_DPTX_GCTL0 0x708
|
||||
#define HIBMC_DP_INTR_ENABLE 0x720
|
||||
#define HIBMC_DP_INTR_ORIGINAL_STATUS 0x728
|
||||
#define HIBMC_DP_TIMING_MODEL_CTRL 0x884
|
||||
#define HIBMC_DP_TIMING_SYNC_CTRL 0xFF0
|
||||
|
||||
#define HIBMC_DP_CFG_AUX_SYNC_LEN_SEL BIT(1)
|
||||
#define HIBMC_DP_CFG_AUX_TIMER_TIMEOUT BIT(2)
|
||||
#define HIBMC_DP_CFG_STREAM_FRAME_MODE BIT(6)
|
||||
#define HIBMC_DP_CFG_AUX_MIN_PULSE_NUM GENMASK(13, 9)
|
||||
#define HIBMC_DP_CFG_LANE_DATA_EN GENMASK(11, 8)
|
||||
#define HIBMC_DP_CFG_PHY_LANE_NUM GENMASK(2, 1)
|
||||
#define HIBMC_DP_CFG_AUX_REQ BIT(0)
|
||||
#define HIBMC_DP_CFG_AUX_RST_N BIT(4)
|
||||
#define HIBMC_DP_CFG_AUX_TIMEOUT BIT(0)
|
||||
#define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12)
|
||||
#define HIBMC_DP_CFG_AUX GENMASK(24, 17)
|
||||
#define HIBMC_DP_CFG_AUX_STATUS GENMASK(11, 4)
|
||||
#define HIBMC_DP_CFG_SCRAMBLE_EN BIT(0)
|
||||
#define HIBMC_DP_CFG_PAT_SEL GENMASK(7, 4)
|
||||
#define HIBMC_DP_CFG_TIMING_GEN0_HACTIVE GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_TIMING_GEN0_HBLANK GENMASK(15, 0)
|
||||
#define HIBMC_DP_CFG_TIMING_GEN0_VACTIVE GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_TIMING_GEN0_VBLANK GENMASK(15, 0)
|
||||
#define HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_STREAM_HACTIVE GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_STREAM_HBLANK GENMASK(15, 0)
|
||||
#define HIBMC_DP_CFG_STREAM_HSYNC_WIDTH GENMASK(15, 0)
|
||||
#define HIBMC_DP_CFG_STREAM_VACTIVE GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_STREAM_VBLANK GENMASK(15, 0)
|
||||
#define HIBMC_DP_CFG_STREAM_VFRONT_PORCH GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_STREAM_VSYNC_WIDTH GENMASK(15, 0)
|
||||
#define HIBMC_DP_CFG_STREAM_VSTART GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_STREAM_HSTART GENMASK(15, 0)
|
||||
#define HIBMC_DP_CFG_STREAM_VSYNC_POLARITY BIT(8)
|
||||
#define HIBMC_DP_CFG_STREAM_HSYNC_POLARITY BIT(7)
|
||||
#define HIBMC_DP_CFG_STREAM_RGB_ENABLE BIT(1)
|
||||
#define HIBMC_DP_CFG_STREAM_VIDEO_MAPPING GENMASK(5, 2)
|
||||
#define HIBMC_DP_CFG_PIXEL_NUM_TIMING_MODE_SEL1 GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE GENMASK(5, 0)
|
||||
#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE GENMASK(9, 6)
|
||||
#define HIBMC_DP_CFG_STREAM_HTOTAL_SIZE GENMASK(31, 16)
|
||||
#define HIBMC_DP_CFG_STREAM_HBLANK_SIZE GENMASK(15, 0)
|
||||
|
||||
#endif
|
||||
118
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c
Normal file
118
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (c) 2024 Hisilicon Limited.
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include "hibmc_drm_drv.h"
|
||||
#include "dp/dp_hw.h"
|
||||
|
||||
static int hibmc_dp_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = drm_add_modes_noedid(connector, connector->dev->mode_config.max_width,
|
||||
connector->dev->mode_config.max_height);
|
||||
drm_set_preferred_mode(connector, 1024, 768); // temporary implementation
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = {
|
||||
.get_modes = hibmc_dp_connector_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs hibmc_dp_conn_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static inline int hibmc_dp_prepare(struct hibmc_dp *dp, struct drm_display_mode *mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
hibmc_dp_display_en(dp, false);
|
||||
|
||||
ret = hibmc_dp_mode_set(dp, mode);
|
||||
if (ret)
|
||||
drm_err(dp->drm_dev, "hibmc dp mode set failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hibmc_dp_encoder_enable(struct drm_encoder *drm_encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct hibmc_dp *dp = container_of(drm_encoder, struct hibmc_dp, encoder);
|
||||
struct drm_display_mode *mode = &drm_encoder->crtc->state->mode;
|
||||
|
||||
if (hibmc_dp_prepare(dp, mode))
|
||||
return;
|
||||
|
||||
hibmc_dp_display_en(dp, true);
|
||||
}
|
||||
|
||||
static void hibmc_dp_encoder_disable(struct drm_encoder *drm_encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct hibmc_dp *dp = container_of(drm_encoder, struct hibmc_dp, encoder);
|
||||
|
||||
hibmc_dp_display_en(dp, false);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs hibmc_dp_encoder_helper_funcs = {
|
||||
.atomic_enable = hibmc_dp_encoder_enable,
|
||||
.atomic_disable = hibmc_dp_encoder_disable,
|
||||
};
|
||||
|
||||
int hibmc_dp_init(struct hibmc_drm_private *priv)
|
||||
{
|
||||
struct drm_device *dev = &priv->dev;
|
||||
struct drm_crtc *crtc = &priv->crtc;
|
||||
struct hibmc_dp *dp = &priv->dp;
|
||||
struct drm_connector *connector = &dp->connector;
|
||||
struct drm_encoder *encoder = &dp->encoder;
|
||||
int ret;
|
||||
|
||||
dp->mmio = priv->mmio;
|
||||
dp->drm_dev = dev;
|
||||
|
||||
ret = hibmc_dp_hw_init(&priv->dp);
|
||||
if (ret) {
|
||||
drm_err(dev, "hibmc dp hw init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hibmc_dp_display_en(&priv->dp, false);
|
||||
|
||||
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
||||
ret = drmm_encoder_init(dev, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
|
||||
if (ret) {
|
||||
drm_err(dev, "init dp encoder failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_encoder_helper_add(encoder, &hibmc_dp_encoder_helper_funcs);
|
||||
|
||||
ret = drm_connector_init(dev, connector, &hibmc_dp_conn_funcs,
|
||||
DRM_MODE_CONNECTOR_DisplayPort);
|
||||
if (ret) {
|
||||
drm_err(dev, "init dp connector failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &hibmc_dp_conn_helper_funcs);
|
||||
|
||||
drm_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -28,6 +28,10 @@
|
|||
#include "hibmc_drm_drv.h"
|
||||
#include "hibmc_drm_regs.h"
|
||||
|
||||
#define HIBMC_DP_HOST_SERDES_CTRL 0x1f001c
|
||||
#define HIBMC_DP_HOST_SERDES_CTRL_VAL 0x8a00
|
||||
#define HIBMC_DP_HOST_SERDES_CTRL_MASK 0x7ffff
|
||||
|
||||
DEFINE_DRM_GEM_FOPS(hibmc_fops);
|
||||
|
||||
static irqreturn_t hibmc_interrupt(int irq, void *arg)
|
||||
|
|
@ -117,6 +121,14 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* if DP existed, init DP */
|
||||
if ((readl(priv->mmio + HIBMC_DP_HOST_SERDES_CTRL) &
|
||||
HIBMC_DP_HOST_SERDES_CTRL_MASK) == HIBMC_DP_HOST_SERDES_CTRL_VAL) {
|
||||
ret = hibmc_dp_init(priv);
|
||||
if (ret)
|
||||
drm_err(dev, "failed to init dp: %d\n", ret);
|
||||
}
|
||||
|
||||
ret = hibmc_vdac_init(priv);
|
||||
if (ret) {
|
||||
drm_err(dev, "failed to init vdac: %d\n", ret);
|
||||
|
|
@ -327,6 +339,8 @@ static int hibmc_pci_probe(struct pci_dev *pdev,
|
|||
goto err_return;
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
ret = hibmc_load(dev);
|
||||
if (ret) {
|
||||
drm_err(dev, "failed to load hibmc: %d\n", ret);
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@
|
|||
|
||||
#include <drm/drm_framebuffer.h>
|
||||
|
||||
struct hibmc_connector {
|
||||
struct drm_connector base;
|
||||
#include "dp/dp_hw.h"
|
||||
|
||||
struct hibmc_vdac {
|
||||
struct drm_device *dev;
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_algo_bit_data bit_data;
|
||||
};
|
||||
|
|
@ -35,13 +38,13 @@ struct hibmc_drm_private {
|
|||
struct drm_device dev;
|
||||
struct drm_plane primary_plane;
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder encoder;
|
||||
struct hibmc_connector connector;
|
||||
struct hibmc_vdac vdac;
|
||||
struct hibmc_dp dp;
|
||||
};
|
||||
|
||||
static inline struct hibmc_connector *to_hibmc_connector(struct drm_connector *connector)
|
||||
static inline struct hibmc_vdac *to_hibmc_vdac(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct hibmc_connector, base);
|
||||
return container_of(connector, struct hibmc_vdac, connector);
|
||||
}
|
||||
|
||||
static inline struct hibmc_drm_private *to_hibmc_drm_private(struct drm_device *dev)
|
||||
|
|
@ -57,6 +60,8 @@ void hibmc_set_current_gate(struct hibmc_drm_private *priv,
|
|||
int hibmc_de_init(struct hibmc_drm_private *priv);
|
||||
int hibmc_vdac_init(struct hibmc_drm_private *priv);
|
||||
|
||||
int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_connector *connector);
|
||||
int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *connector);
|
||||
|
||||
int hibmc_dp_init(struct hibmc_drm_private *priv);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@
|
|||
|
||||
static void hibmc_set_i2c_signal(void *data, u32 mask, int value)
|
||||
{
|
||||
struct hibmc_connector *hibmc_connector = data;
|
||||
struct hibmc_drm_private *priv = to_hibmc_drm_private(hibmc_connector->base.dev);
|
||||
struct hibmc_vdac *vdac = data;
|
||||
struct hibmc_drm_private *priv = to_hibmc_drm_private(vdac->connector.dev);
|
||||
u32 tmp_dir = readl(priv->mmio + GPIO_DATA_DIRECTION);
|
||||
|
||||
if (value) {
|
||||
|
|
@ -45,8 +45,8 @@ static void hibmc_set_i2c_signal(void *data, u32 mask, int value)
|
|||
|
||||
static int hibmc_get_i2c_signal(void *data, u32 mask)
|
||||
{
|
||||
struct hibmc_connector *hibmc_connector = data;
|
||||
struct hibmc_drm_private *priv = to_hibmc_drm_private(hibmc_connector->base.dev);
|
||||
struct hibmc_vdac *vdac = data;
|
||||
struct hibmc_drm_private *priv = to_hibmc_drm_private(vdac->connector.dev);
|
||||
u32 tmp_dir = readl(priv->mmio + GPIO_DATA_DIRECTION);
|
||||
|
||||
if ((tmp_dir & mask) != mask) {
|
||||
|
|
@ -77,22 +77,21 @@ static int hibmc_ddc_getscl(void *data)
|
|||
return hibmc_get_i2c_signal(data, I2C_SCL_MASK);
|
||||
}
|
||||
|
||||
int hibmc_ddc_create(struct drm_device *drm_dev,
|
||||
struct hibmc_connector *connector)
|
||||
int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *vdac)
|
||||
{
|
||||
connector->adapter.owner = THIS_MODULE;
|
||||
snprintf(connector->adapter.name, I2C_NAME_SIZE, "HIS i2c bit bus");
|
||||
connector->adapter.dev.parent = drm_dev->dev;
|
||||
i2c_set_adapdata(&connector->adapter, connector);
|
||||
connector->adapter.algo_data = &connector->bit_data;
|
||||
vdac->adapter.owner = THIS_MODULE;
|
||||
snprintf(vdac->adapter.name, I2C_NAME_SIZE, "HIS i2c bit bus");
|
||||
vdac->adapter.dev.parent = drm_dev->dev;
|
||||
i2c_set_adapdata(&vdac->adapter, vdac);
|
||||
vdac->adapter.algo_data = &vdac->bit_data;
|
||||
|
||||
connector->bit_data.udelay = 20;
|
||||
connector->bit_data.timeout = usecs_to_jiffies(2000);
|
||||
connector->bit_data.data = connector;
|
||||
connector->bit_data.setsda = hibmc_ddc_setsda;
|
||||
connector->bit_data.setscl = hibmc_ddc_setscl;
|
||||
connector->bit_data.getsda = hibmc_ddc_getsda;
|
||||
connector->bit_data.getscl = hibmc_ddc_getscl;
|
||||
vdac->bit_data.udelay = 20;
|
||||
vdac->bit_data.timeout = usecs_to_jiffies(2000);
|
||||
vdac->bit_data.data = vdac;
|
||||
vdac->bit_data.setsda = hibmc_ddc_setsda;
|
||||
vdac->bit_data.setscl = hibmc_ddc_setscl;
|
||||
vdac->bit_data.getsda = hibmc_ddc_getsda;
|
||||
vdac->bit_data.getscl = hibmc_ddc_getscl;
|
||||
|
||||
return i2c_bit_add_bus(&connector->adapter);
|
||||
return i2c_bit_add_bus(&vdac->adapter);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@
|
|||
|
||||
static int hibmc_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct hibmc_connector *hibmc_connector = to_hibmc_connector(connector);
|
||||
struct hibmc_vdac *vdac = to_hibmc_vdac(connector);
|
||||
const struct drm_edid *drm_edid;
|
||||
int count;
|
||||
|
||||
drm_edid = drm_edid_read_ddc(connector, &hibmc_connector->adapter);
|
||||
drm_edid = drm_edid_read_ddc(connector, &vdac->adapter);
|
||||
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
|
||||
|
|
@ -51,9 +51,9 @@ static int hibmc_connector_get_modes(struct drm_connector *connector)
|
|||
|
||||
static void hibmc_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct hibmc_connector *hibmc_connector = to_hibmc_connector(connector);
|
||||
struct hibmc_vdac *vdac = to_hibmc_vdac(connector);
|
||||
|
||||
i2c_del_adapter(&hibmc_connector->adapter);
|
||||
i2c_del_adapter(&vdac->adapter);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
|
|
@ -93,20 +93,20 @@ static const struct drm_encoder_helper_funcs hibmc_encoder_helper_funcs = {
|
|||
int hibmc_vdac_init(struct hibmc_drm_private *priv)
|
||||
{
|
||||
struct drm_device *dev = &priv->dev;
|
||||
struct hibmc_connector *hibmc_connector = &priv->connector;
|
||||
struct drm_encoder *encoder = &priv->encoder;
|
||||
struct hibmc_vdac *vdac = &priv->vdac;
|
||||
struct drm_encoder *encoder = &vdac->encoder;
|
||||
struct drm_crtc *crtc = &priv->crtc;
|
||||
struct drm_connector *connector = &hibmc_connector->base;
|
||||
struct drm_connector *connector = &vdac->connector;
|
||||
int ret;
|
||||
|
||||
ret = hibmc_ddc_create(dev, hibmc_connector);
|
||||
ret = hibmc_ddc_create(dev, vdac);
|
||||
if (ret) {
|
||||
drm_err(dev, "failed to create ddc: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
||||
ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC);
|
||||
ret = drmm_encoder_init(dev, encoder, NULL, DRM_MODE_ENCODER_DAC, NULL);
|
||||
if (ret) {
|
||||
drm_err(dev, "failed to init encoder: %d\n", ret);
|
||||
return ret;
|
||||
|
|
@ -117,7 +117,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv)
|
|||
ret = drm_connector_init_with_ddc(dev, connector,
|
||||
&hibmc_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VGA,
|
||||
&hibmc_connector->adapter);
|
||||
&vdac->adapter);
|
||||
if (ret) {
|
||||
drm_err(dev, "failed to init connector: %d\n", ret);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -1165,7 +1165,6 @@ static const struct hdmi_codec_ops audio_codec_ops = {
|
|||
.audio_shutdown = tda998x_audio_shutdown,
|
||||
.mute_stream = tda998x_audio_mute_stream,
|
||||
.get_eld = tda998x_audio_get_eld,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static int tda998x_audio_codec_init(struct tda998x_priv *priv,
|
||||
|
|
@ -1176,6 +1175,7 @@ static int tda998x_audio_codec_init(struct tda998x_priv *priv,
|
|||
.max_i2s_channels = 2,
|
||||
.no_i2s_capture = 1,
|
||||
.no_spdif_capture = 1,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
if (priv->audio_port_enable[AUDIO_ROUTE_I2S])
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ static void show_meminfo(struct drm_printer *p, struct drm_file *file)
|
|||
for_each_memory_region(mr, i915, id)
|
||||
drm_print_memory_stats(p,
|
||||
&stats[id],
|
||||
DRM_GEM_OBJECT_ACTIVE |
|
||||
DRM_GEM_OBJECT_RESIDENT |
|
||||
DRM_GEM_OBJECT_PURGEABLE,
|
||||
mr->uabi_name);
|
||||
|
|
|
|||
|
|
@ -2638,7 +2638,6 @@ static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
|
|||
.audio_shutdown = mtk_dp_audio_shutdown,
|
||||
.get_eld = mtk_dp_audio_get_eld,
|
||||
.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static int mtk_dp_register_audio_driver(struct device *dev)
|
||||
|
|
@ -2649,6 +2648,7 @@ static int mtk_dp_register_audio_driver(struct device *dev)
|
|||
.max_i2s_channels = 8,
|
||||
.i2s = 1,
|
||||
.data = mtk_dp,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
mtk_dp->audio_pdev = platform_device_register_data(dev,
|
||||
|
|
|
|||
|
|
@ -1660,7 +1660,6 @@ static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = {
|
|||
.mute_stream = mtk_hdmi_audio_mute,
|
||||
.get_eld = mtk_hdmi_audio_get_eld,
|
||||
.hook_plugged_cb = mtk_hdmi_audio_hook_plugged_cb,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static int mtk_hdmi_register_audio_driver(struct device *dev)
|
||||
|
|
@ -1671,6 +1670,7 @@ static int mtk_hdmi_register_audio_driver(struct device *dev)
|
|||
.max_i2s_channels = 2,
|
||||
.i2s = 1,
|
||||
.data = hdmi,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
struct platform_device *pdev;
|
||||
|
||||
|
|
|
|||
|
|
@ -1831,7 +1831,7 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host)
|
|||
msm_dsi->te_source = devm_kstrdup(dev, te_source, GFP_KERNEL);
|
||||
ret = 0;
|
||||
|
||||
if (of_property_read_bool(np, "syscon-sfpb")) {
|
||||
if (of_property_present(np, "syscon-sfpb")) {
|
||||
msm_host->sfpb = syscon_regmap_lookup_by_phandle(np,
|
||||
"syscon-sfpb");
|
||||
if (IS_ERR(msm_host->sfpb)) {
|
||||
|
|
|
|||
|
|
@ -4454,6 +4454,37 @@ static const struct panel_desc ti_nspire_classic_lcd_panel = {
|
|||
.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
|
||||
};
|
||||
|
||||
static const struct display_timing topland_tian_g07017_01_timing = {
|
||||
.pixelclock = { 44900000, 51200000, 63000000 },
|
||||
.hactive = { 1024, 1024, 1024 },
|
||||
.hfront_porch = { 16, 160, 216 },
|
||||
.hback_porch = { 160, 160, 160 },
|
||||
.hsync_len = { 1, 1, 140 },
|
||||
.vactive = { 600, 600, 600 },
|
||||
.vfront_porch = { 1, 12, 127 },
|
||||
.vback_porch = { 23, 23, 23 },
|
||||
.vsync_len = { 1, 1, 20 },
|
||||
};
|
||||
|
||||
static const struct panel_desc topland_tian_g07017_01 = {
|
||||
.timings = &topland_tian_g07017_01_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 154,
|
||||
.height = 86,
|
||||
},
|
||||
.delay = {
|
||||
.prepare = 1, /* 6.5 - 150µs PLL wake-up time */
|
||||
.enable = 100, /* 6.4 - Power on: 6 VSyncs */
|
||||
.disable = 84, /* 6.4 - Power off: 5 Vsyncs */
|
||||
.unprepare = 50, /* 6.4 - Power off: 3 Vsyncs */
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode toshiba_lt089ac29000_mode = {
|
||||
.clock = 79500,
|
||||
.hdisplay = 1280,
|
||||
|
|
@ -5139,6 +5170,9 @@ static const struct of_device_id platform_of_match[] = {
|
|||
}, {
|
||||
.compatible = "toshiba,lt089ac29000",
|
||||
.data = &toshiba_lt089ac29000,
|
||||
}, {
|
||||
.compatible = "topland,tian-g07017-01",
|
||||
.data = &topland_tian_g07017_01,
|
||||
}, {
|
||||
.compatible = "tpk,f07a-0102",
|
||||
.data = &tpk_f07a_0102,
|
||||
|
|
|
|||
|
|
@ -885,7 +885,6 @@ static const struct hdmi_codec_ops audio_codec_ops = {
|
|||
.mute_stream = cdn_dp_audio_mute_stream,
|
||||
.get_eld = cdn_dp_audio_get_eld,
|
||||
.hook_plugged_cb = cdn_dp_audio_hook_plugged_cb,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
|
||||
|
|
@ -896,6 +895,7 @@ static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
|
|||
.spdif = 1,
|
||||
.ops = &audio_codec_ops,
|
||||
.max_i2s_channels = 8,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
dp->audio_pdev = platform_device_register_data(
|
||||
|
|
|
|||
|
|
@ -1454,16 +1454,18 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
|
|||
vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en);
|
||||
|
||||
if (afbc_en) {
|
||||
u32 stride;
|
||||
u32 stride, block_w;
|
||||
|
||||
/* the afbc superblock is 16 x 16 or 32 x 8 */
|
||||
block_w = fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 ? 32 : 16;
|
||||
|
||||
/* the afbc superblock is 16 x 16 */
|
||||
afbc_format = vop2_convert_afbc_format(fb->format->format);
|
||||
|
||||
/* Enable color transform for YTR */
|
||||
if (fb->modifier & AFBC_FORMAT_MOD_YTR)
|
||||
afbc_format |= (1 << 4);
|
||||
|
||||
afbc_tile_num = ALIGN(actual_w, 16) >> 4;
|
||||
afbc_tile_num = ALIGN(actual_w, block_w) / block_w;
|
||||
|
||||
/*
|
||||
* AFBC pic_vir_width is count by pixel, this is different
|
||||
|
|
@ -1474,6 +1476,9 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
|
|||
drm_dbg_kms(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
|
||||
vp->id, win->data->name, stride);
|
||||
|
||||
/* It's for head stride, each head size is 16 byte */
|
||||
stride = ALIGN(stride, block_w) / block_w * 16;
|
||||
|
||||
uv_swap = vop2_afbc_uv_swap(fb->format->format);
|
||||
/*
|
||||
* This is a workaround for crazy IC design, Cluster
|
||||
|
|
@ -1504,7 +1509,11 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
|
|||
else
|
||||
vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1);
|
||||
|
||||
vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
|
||||
if (fb->modifier & AFBC_FORMAT_MOD_SPLIT)
|
||||
vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 1);
|
||||
else
|
||||
vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
|
||||
|
||||
transform_offset = vop2_afbc_transform_offset(pstate, half_block_en);
|
||||
vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
|
||||
vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
|
||||
|
|
|
|||
|
|
@ -1355,7 +1355,8 @@ EXPORT_SYMBOL(drm_sched_init);
|
|||
* drm_sched_backend_ops.run_job(). Consequently, drm_sched_backend_ops.free_job()
|
||||
* will not be called for all jobs still in drm_gpu_scheduler.pending_list.
|
||||
* There is no solution for this currently. Thus, it is up to the driver to make
|
||||
* sure that
|
||||
* sure that:
|
||||
*
|
||||
* a) drm_sched_fini() is only called after for all submitted jobs
|
||||
* drm_sched_backend_ops.free_job() has been called or that
|
||||
* b) the jobs for which drm_sched_backend_ops.free_job() has not been called
|
||||
|
|
|
|||
|
|
@ -1237,7 +1237,6 @@ static const struct hdmi_codec_ops audio_codec_ops = {
|
|||
.audio_shutdown = hdmi_audio_shutdown,
|
||||
.mute_stream = hdmi_audio_mute,
|
||||
.get_eld = hdmi_audio_get_eld,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
static int sti_hdmi_register_audio_driver(struct device *dev,
|
||||
|
|
@ -1247,6 +1246,7 @@ static int sti_hdmi_register_audio_driver(struct device *dev,
|
|||
.ops = &audio_codec_ops,
|
||||
.max_i2s_channels = 8,
|
||||
.i2s = 1,
|
||||
.no_capture_mute = 1,
|
||||
};
|
||||
|
||||
DRM_DEBUG_DRIVER("\n");
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ config DRM_VC4
|
|||
depends on COMMON_CLK
|
||||
depends on PM
|
||||
select DRM_CLIENT_SELECTION
|
||||
select DRM_DISPLAY_HDMI_AUDIO_HELPER
|
||||
select DRM_DISPLAY_HDMI_HELPER
|
||||
select DRM_DISPLAY_HDMI_STATE_HELPER
|
||||
select DRM_DISPLAY_HELPER
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
* encoder block has CEC support.
|
||||
*/
|
||||
|
||||
#include <drm/display/drm_hdmi_audio_helper.h>
|
||||
#include <drm/display/drm_hdmi_helper.h>
|
||||
#include <drm/display/drm_hdmi_state_helper.h>
|
||||
#include <drm/display/drm_scdc_helper.h>
|
||||
|
|
@ -383,7 +384,6 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
|
|||
enum drm_connector_status status)
|
||||
{
|
||||
struct drm_connector *connector = &vc4_hdmi->connector;
|
||||
const struct drm_edid *drm_edid;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
|
|
@ -405,17 +405,14 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
|
|||
return;
|
||||
}
|
||||
|
||||
drm_edid = drm_edid_read_ddc(connector, vc4_hdmi->ddc);
|
||||
drm_atomic_helper_connector_hdmi_hotplug(connector, status);
|
||||
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
cec_s_phys_addr(vc4_hdmi->cec_adap,
|
||||
connector->display_info.source_physical_address, false);
|
||||
|
||||
if (!drm_edid)
|
||||
if (status != connector_status_connected)
|
||||
return;
|
||||
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
for (;;) {
|
||||
ret = vc4_hdmi_reset_link(connector, ctx);
|
||||
if (ret == -EDEADLK) {
|
||||
|
|
@ -470,31 +467,10 @@ static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
|
|||
|
||||
static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
|
||||
const struct drm_edid *drm_edid;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* NOTE: This function should really take vc4_hdmi->mutex, but doing so
|
||||
* results in reentrancy issues since cec_s_phys_addr() might call
|
||||
* .adap_enable, which leads to that funtion being called with our mutex
|
||||
* held.
|
||||
*
|
||||
* Concurrency isn't an issue at the moment since we don't share
|
||||
* any state with any of the other frameworks so we can ignore
|
||||
* the lock for now.
|
||||
*/
|
||||
|
||||
drm_edid = drm_edid_read_ddc(connector, vc4_hdmi->ddc);
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
cec_s_phys_addr(vc4_hdmi->cec_adap,
|
||||
connector->display_info.source_physical_address, false);
|
||||
if (!drm_edid)
|
||||
return 0;
|
||||
|
||||
ret = drm_edid_connector_add_modes(connector);
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
if (!vc4->hvs->vc5_hdmi_enable_hdmi_20) {
|
||||
struct drm_device *drm = connector->dev;
|
||||
|
|
@ -570,6 +546,7 @@ static void vc4_hdmi_connector_reset(struct drm_connector *connector)
|
|||
}
|
||||
|
||||
static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
|
||||
.force = drm_atomic_helper_connector_hdmi_force,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.reset = vc4_hdmi_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
|
|
@ -584,6 +561,7 @@ static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs =
|
|||
};
|
||||
|
||||
static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs;
|
||||
static const struct drm_connector_hdmi_audio_funcs vc4_hdmi_audio_funcs;
|
||||
|
||||
static int vc4_hdmi_connector_init(struct drm_device *dev,
|
||||
struct vc4_hdmi *vc4_hdmi)
|
||||
|
|
@ -609,6 +587,12 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_connector_hdmi_audio_init(connector, dev->dev,
|
||||
&vc4_hdmi_audio_funcs,
|
||||
8, false, -1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
|
||||
|
||||
/*
|
||||
|
|
@ -1921,9 +1905,9 @@ static bool vc4_hdmi_audio_can_stream(struct vc4_hdmi *vc4_hdmi)
|
|||
return true;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_audio_startup(struct device *dev, void *data)
|
||||
static int vc4_hdmi_audio_startup(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct drm_device *drm = vc4_hdmi->connector.dev;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
|
@ -1985,9 +1969,9 @@ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
|
|||
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
|
||||
}
|
||||
|
||||
static void vc4_hdmi_audio_shutdown(struct device *dev, void *data)
|
||||
static void vc4_hdmi_audio_shutdown(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct drm_device *drm = vc4_hdmi->connector.dev;
|
||||
unsigned long flags;
|
||||
int idx;
|
||||
|
|
@ -2057,13 +2041,12 @@ static int sample_rate_to_mai_fmt(int samplerate)
|
|||
}
|
||||
|
||||
/* HDMI audio codec callbacks */
|
||||
static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
|
||||
static int vc4_hdmi_audio_prepare(struct drm_connector *connector,
|
||||
struct hdmi_codec_daifmt *daifmt,
|
||||
struct hdmi_codec_params *params)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct drm_device *drm = vc4_hdmi->connector.dev;
|
||||
struct drm_connector *connector = &vc4_hdmi->connector;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
unsigned int sample_rate = params->sample_rate;
|
||||
unsigned int channels = params->channels;
|
||||
|
|
@ -2075,7 +2058,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
|
|||
int ret = 0;
|
||||
int idx;
|
||||
|
||||
dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
|
||||
dev_dbg(&vc4_hdmi->pdev->dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
|
||||
sample_rate, params->sample_width, channels);
|
||||
|
||||
mutex_lock(&vc4_hdmi->mutex);
|
||||
|
|
@ -2214,40 +2197,12 @@ static const struct snd_dmaengine_pcm_config pcm_conf = {
|
|||
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
|
||||
};
|
||||
|
||||
static int vc4_hdmi_audio_get_eld(struct device *dev, void *data,
|
||||
uint8_t *buf, size_t len)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
|
||||
struct drm_connector *connector = &vc4_hdmi->connector;
|
||||
|
||||
mutex_lock(&connector->eld_mutex);
|
||||
memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
|
||||
mutex_unlock(&connector->eld_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hdmi_codec_ops vc4_hdmi_codec_ops = {
|
||||
.get_eld = vc4_hdmi_audio_get_eld,
|
||||
static const struct drm_connector_hdmi_audio_funcs vc4_hdmi_audio_funcs = {
|
||||
.startup = vc4_hdmi_audio_startup,
|
||||
.prepare = vc4_hdmi_audio_prepare,
|
||||
.audio_shutdown = vc4_hdmi_audio_shutdown,
|
||||
.audio_startup = vc4_hdmi_audio_startup,
|
||||
.shutdown = vc4_hdmi_audio_shutdown,
|
||||
};
|
||||
|
||||
static struct hdmi_codec_pdata vc4_hdmi_codec_pdata = {
|
||||
.ops = &vc4_hdmi_codec_ops,
|
||||
.max_i2s_channels = 8,
|
||||
.i2s = 1,
|
||||
};
|
||||
|
||||
static void vc4_hdmi_audio_codec_release(void *ptr)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = ptr;
|
||||
|
||||
platform_device_unregister(vc4_hdmi->audio.codec_pdev);
|
||||
vc4_hdmi->audio.codec_pdev = NULL;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
|
||||
{
|
||||
const struct vc4_hdmi_register *mai_data =
|
||||
|
|
@ -2255,7 +2210,6 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
|
|||
struct snd_soc_dai_link *dai_link = &vc4_hdmi->audio.link;
|
||||
struct snd_soc_card *card = &vc4_hdmi->audio.card;
|
||||
struct device *dev = &vc4_hdmi->pdev->dev;
|
||||
struct platform_device *codec_pdev;
|
||||
const __be32 *addr;
|
||||
int index, len;
|
||||
int ret;
|
||||
|
|
@ -2348,20 +2302,6 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
|
|||
return ret;
|
||||
}
|
||||
|
||||
codec_pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
|
||||
PLATFORM_DEVID_AUTO,
|
||||
&vc4_hdmi_codec_pdata,
|
||||
sizeof(vc4_hdmi_codec_pdata));
|
||||
if (IS_ERR(codec_pdev)) {
|
||||
dev_err(dev, "Couldn't register the HDMI codec: %ld\n", PTR_ERR(codec_pdev));
|
||||
return PTR_ERR(codec_pdev);
|
||||
}
|
||||
vc4_hdmi->audio.codec_pdev = codec_pdev;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, vc4_hdmi_audio_codec_release, vc4_hdmi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dai_link->cpus = &vc4_hdmi->audio.cpu;
|
||||
dai_link->codecs = &vc4_hdmi->audio.codec;
|
||||
dai_link->platforms = &vc4_hdmi->audio.platform;
|
||||
|
|
@ -2374,7 +2314,7 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
|
|||
dai_link->stream_name = "MAI PCM";
|
||||
dai_link->codecs->dai_name = "i2s-hifi";
|
||||
dai_link->cpus->dai_name = dev_name(dev);
|
||||
dai_link->codecs->name = dev_name(&codec_pdev->dev);
|
||||
dai_link->codecs->name = dev_name(&vc4_hdmi->connector.hdmi_audio.codec_pdev->dev);
|
||||
dai_link->platforms->name = dev_name(dev);
|
||||
|
||||
card->dai_link = dai_link;
|
||||
|
|
|
|||
|
|
@ -104,8 +104,6 @@ struct vc4_hdmi_audio {
|
|||
struct snd_soc_dai_link_component codec;
|
||||
struct snd_soc_dai_link_component platform;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct hdmi_audio_infoframe infoframe;
|
||||
struct platform_device *codec_pdev;
|
||||
bool streaming;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ static void show_meminfo(struct drm_printer *p, struct drm_file *file)
|
|||
if (man) {
|
||||
drm_print_memory_stats(p,
|
||||
&stats[mem_type],
|
||||
DRM_GEM_OBJECT_ACTIVE |
|
||||
DRM_GEM_OBJECT_RESIDENT |
|
||||
(mem_type != XE_PL_SYSTEM ? 0 :
|
||||
DRM_GEM_OBJECT_PURGEABLE),
|
||||
|
|
|
|||
22
include/drm/display/drm_hdmi_audio_helper.h
Normal file
22
include/drm/display/drm_hdmi_audio_helper.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef DRM_DISPLAY_HDMI_AUDIO_HELPER_H_
|
||||
#define DRM_DISPLAY_HDMI_AUDIO_HELPER_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct drm_connector;
|
||||
struct drm_connector_hdmi_audio_funcs;
|
||||
|
||||
struct device;
|
||||
|
||||
int drm_connector_hdmi_audio_init(struct drm_connector *connector,
|
||||
struct device *hdmi_codec_dev,
|
||||
const struct drm_connector_hdmi_audio_funcs *funcs,
|
||||
unsigned int max_i2s_playback_channels,
|
||||
bool spdif_playback,
|
||||
int sound_dai_port);
|
||||
void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector,
|
||||
bool plugged);
|
||||
|
||||
#endif
|
||||
|
|
@ -8,6 +8,8 @@ struct drm_connector;
|
|||
struct drm_connector_state;
|
||||
struct hdmi_audio_infoframe;
|
||||
|
||||
enum drm_connector_status;
|
||||
|
||||
void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
|
||||
struct drm_connector_state *new_conn_state);
|
||||
|
||||
|
|
@ -19,6 +21,9 @@ int drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector
|
|||
int drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *connector);
|
||||
int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector,
|
||||
struct drm_atomic_state *state);
|
||||
void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
|
||||
enum drm_connector_status status);
|
||||
void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector);
|
||||
|
||||
enum drm_mode_status
|
||||
drm_hdmi_connector_mode_valid(struct drm_connector *connector,
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ struct drm_display_info;
|
|||
struct drm_minor;
|
||||
struct drm_panel;
|
||||
struct edid;
|
||||
struct hdmi_codec_daifmt;
|
||||
struct hdmi_codec_params;
|
||||
struct i2c_adapter;
|
||||
|
||||
/**
|
||||
|
|
@ -676,6 +678,57 @@ struct drm_bridge_funcs {
|
|||
enum hdmi_infoframe_type type,
|
||||
const u8 *buffer, size_t len);
|
||||
|
||||
/**
|
||||
* @hdmi_audio_startup:
|
||||
*
|
||||
* Called when ASoC starts an audio stream setup. The
|
||||
* @hdmi_audio_startup() is optional.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int (*hdmi_audio_startup)(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge);
|
||||
|
||||
/**
|
||||
* @prepare:
|
||||
* Configures HDMI-encoder for audio stream. Can be called multiple
|
||||
* times for each setup. Mandatory if HDMI audio is enabled in the
|
||||
* bridge's configuration.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int (*hdmi_audio_prepare)(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms);
|
||||
|
||||
/**
|
||||
* @hdmi_audio_shutdown:
|
||||
*
|
||||
* Shut down the audio stream. Mandatory if HDMI audio is enabled in
|
||||
* the bridge's configuration.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, a negative error code otherwise
|
||||
*/
|
||||
void (*hdmi_audio_shutdown)(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge);
|
||||
|
||||
/**
|
||||
* @hdmi_audio_mute_stream:
|
||||
*
|
||||
* Mute/unmute HDMI audio stream. The @hdmi_audio_mute_stream callback
|
||||
* is optional.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int (*hdmi_audio_mute_stream)(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge,
|
||||
bool enable, int direction);
|
||||
|
||||
/**
|
||||
* @debugfs_init:
|
||||
*
|
||||
|
|
@ -859,6 +912,27 @@ struct drm_bridge {
|
|||
* @DRM_BRIDGE_OP_HDMI is set.
|
||||
*/
|
||||
unsigned int max_bpc;
|
||||
|
||||
/**
|
||||
* @hdmi_audio_dev: device to be used as a parent for the HDMI Codec
|
||||
*/
|
||||
struct device *hdmi_audio_dev;
|
||||
|
||||
/**
|
||||
* @hdmi_audio_max_i2s_playback_channels: maximum number of playback
|
||||
* I2S channels for the HDMI codec
|
||||
*/
|
||||
int hdmi_audio_max_i2s_playback_channels;
|
||||
|
||||
/**
|
||||
* @hdmi_audio_spdif_playback: set if HDMI codec has S/PDIF playback port
|
||||
*/
|
||||
unsigned int hdmi_audio_spdif_playback : 1;
|
||||
|
||||
/**
|
||||
* @hdmi_audio_dai_port: sound DAI port, -1 if it is not enabled
|
||||
*/
|
||||
int hdmi_audio_dai_port;
|
||||
};
|
||||
|
||||
static inline struct drm_bridge *
|
||||
|
|
|
|||
|
|
@ -45,7 +45,10 @@ struct drm_property;
|
|||
struct drm_property_blob;
|
||||
struct drm_printer;
|
||||
struct drm_privacy_screen;
|
||||
struct drm_edid;
|
||||
struct edid;
|
||||
struct hdmi_codec_daifmt;
|
||||
struct hdmi_codec_params;
|
||||
struct i2c_adapter;
|
||||
|
||||
enum drm_connector_force {
|
||||
|
|
@ -1141,6 +1144,53 @@ struct drm_connector_state {
|
|||
struct drm_connector_hdmi_state hdmi;
|
||||
};
|
||||
|
||||
struct drm_connector_hdmi_audio_funcs {
|
||||
/**
|
||||
* @startup:
|
||||
*
|
||||
* Called when ASoC starts an audio stream setup. The
|
||||
* @startup() is optional.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int (*startup)(struct drm_connector *connector);
|
||||
|
||||
/**
|
||||
* @prepare:
|
||||
* Configures HDMI-encoder for audio stream. Can be called
|
||||
* multiple times for each setup. Mandatory.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int (*prepare)(struct drm_connector *connector,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms);
|
||||
|
||||
/**
|
||||
* @shutdown:
|
||||
*
|
||||
* Shut down the audio stream. Mandatory.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, a negative error code otherwise
|
||||
*/
|
||||
void (*shutdown)(struct drm_connector *connector);
|
||||
|
||||
/**
|
||||
* @mute_stream:
|
||||
*
|
||||
* Mute/unmute HDMI audio stream. The @mute_stream callback is
|
||||
* optional.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int (*mute_stream)(struct drm_connector *connector,
|
||||
bool enable, int direction);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions
|
||||
*/
|
||||
|
|
@ -1198,6 +1248,21 @@ struct drm_connector_hdmi_funcs {
|
|||
int (*write_infoframe)(struct drm_connector *connector,
|
||||
enum hdmi_infoframe_type type,
|
||||
const u8 *buffer, size_t len);
|
||||
|
||||
/**
|
||||
* @read_edid:
|
||||
*
|
||||
* This callback is used by the framework as a replacement for reading
|
||||
* the EDID from connector->ddc. It is still recommended to provide
|
||||
* connector->ddc instead of implementing this callback. Returned EDID
|
||||
* should be freed via the drm_edid_free().
|
||||
*
|
||||
* The @read_edid callback is optional.
|
||||
*
|
||||
* Returns:
|
||||
* Valid EDID on success, NULL in case of failure.
|
||||
*/
|
||||
const struct drm_edid *(*read_edid)(struct drm_connector *connector);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1660,6 +1725,68 @@ struct drm_cmdline_mode {
|
|||
bool tv_mode_specified;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_connector_hdmi_audio - DRM gemeric HDMI Codec-related structure
|
||||
*
|
||||
* HDMI drivers usually incorporate a HDMI Codec. This structure expresses the
|
||||
* generic HDMI Codec as used by the DRM HDMI Codec framework.
|
||||
*/
|
||||
struct drm_connector_hdmi_audio {
|
||||
/**
|
||||
* @funcs:
|
||||
*
|
||||
* Implementation of the HDMI codec functionality to be used by the DRM
|
||||
* HDMI Codec framework.
|
||||
*/
|
||||
const struct drm_connector_hdmi_audio_funcs *funcs;
|
||||
|
||||
/**
|
||||
* @codec_pdev:
|
||||
*
|
||||
* Platform device created to hold the HDMI Codec. It will be
|
||||
* automatically unregistered during drm_connector_cleanup().
|
||||
*/
|
||||
struct platform_device *codec_pdev;
|
||||
|
||||
/**
|
||||
* @lock:
|
||||
*
|
||||
* Mutex to protect @last_state, @plugged_cb and @plugged_cb_dev.
|
||||
*/
|
||||
struct mutex lock;
|
||||
|
||||
/**
|
||||
* @plugged_cb:
|
||||
*
|
||||
* Callback to be called when the HDMI sink get plugged to or unplugged
|
||||
* from this connector. This is assigned by the framework when
|
||||
* requested by the ASoC code.
|
||||
*/
|
||||
void (*plugged_cb)(struct device *dev, bool plugged);
|
||||
|
||||
/**
|
||||
* @plugged_cb_dev:
|
||||
*
|
||||
* The data for @plugged_cb(). It is being provided by the ASoC.
|
||||
*/
|
||||
struct device *plugged_cb_dev;
|
||||
|
||||
/**
|
||||
* @last_state:
|
||||
*
|
||||
* Last plugged state recored by the framework. It is used to correctly
|
||||
* report the state to @plugged_cb().
|
||||
*/
|
||||
bool last_state;
|
||||
|
||||
/**
|
||||
* @dai_port:
|
||||
*
|
||||
* The port in DT that is used for the Codec DAI.
|
||||
*/
|
||||
int dai_port;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct drm_connector_hdmi - DRM Connector HDMI-related structure
|
||||
*/
|
||||
|
|
@ -2121,6 +2248,11 @@ struct drm_connector {
|
|||
* @hdmi: HDMI-related variable and properties.
|
||||
*/
|
||||
struct drm_connector_hdmi hdmi;
|
||||
|
||||
/**
|
||||
* @hdmi_audio: HDMI codec properties and non-DRM state.
|
||||
*/
|
||||
struct drm_connector_hdmi_audio hdmi_audio;
|
||||
};
|
||||
|
||||
#define obj_to_connector(x) container_of(x, struct drm_connector, base)
|
||||
|
|
|
|||
|
|
@ -494,6 +494,7 @@ struct drm_memory_stats {
|
|||
|
||||
enum drm_gem_object_status;
|
||||
|
||||
int drm_memory_stats_is_zero(const struct drm_memory_stats *stats);
|
||||
void drm_print_memory_stats(struct drm_printer *p,
|
||||
const struct drm_memory_stats *stats,
|
||||
enum drm_gem_object_status supported_status,
|
||||
|
|
|
|||
|
|
@ -48,19 +48,21 @@ struct drm_gem_object;
|
|||
* enum drm_gem_object_status - bitmask of object state for fdinfo reporting
|
||||
* @DRM_GEM_OBJECT_RESIDENT: object is resident in memory (ie. not unpinned)
|
||||
* @DRM_GEM_OBJECT_PURGEABLE: object marked as purgeable by userspace
|
||||
* @DRM_GEM_OBJECT_ACTIVE: object is currently used by an active submission
|
||||
*
|
||||
* Bitmask of status used for fdinfo memory stats, see &drm_gem_object_funcs.status
|
||||
* and drm_show_fdinfo(). Note that an object can DRM_GEM_OBJECT_PURGEABLE if
|
||||
* it still active or not resident, in which case drm_show_fdinfo() will not
|
||||
* and drm_show_fdinfo(). Note that an object can report DRM_GEM_OBJECT_PURGEABLE
|
||||
* and be active or not resident, in which case drm_show_fdinfo() will not
|
||||
* account for it as purgeable. So drivers do not need to check if the buffer
|
||||
* is idle and resident to return this bit. (Ie. userspace can mark a buffer
|
||||
* as purgeable even while it is still busy on the GPU.. it does not _actually_
|
||||
* become puregeable until it becomes idle. The status gem object func does
|
||||
* not need to consider this.)
|
||||
* is idle and resident to return this bit, i.e. userspace can mark a buffer as
|
||||
* purgeable even while it is still busy on the GPU. It will not get reported in
|
||||
* the puregeable stats until it becomes idle. The status gem object func does
|
||||
* not need to consider this.
|
||||
*/
|
||||
enum drm_gem_object_status {
|
||||
DRM_GEM_OBJECT_RESIDENT = BIT(0),
|
||||
DRM_GEM_OBJECT_PURGEABLE = BIT(1),
|
||||
DRM_GEM_OBJECT_ACTIVE = BIT(2),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ struct hdmi_codec_ops {
|
|||
* Optional
|
||||
*/
|
||||
int (*get_dai_id)(struct snd_soc_component *comment,
|
||||
struct device_node *endpoint);
|
||||
struct device_node *endpoint,
|
||||
void *data);
|
||||
|
||||
/*
|
||||
* Hook callback function to handle connector plug event.
|
||||
|
|
@ -114,9 +115,6 @@ struct hdmi_codec_ops {
|
|||
int (*hook_plugged_cb)(struct device *dev, void *data,
|
||||
hdmi_codec_plugged_cb fn,
|
||||
struct device *codec_dev);
|
||||
|
||||
/* bit field */
|
||||
unsigned int no_capture_mute:1;
|
||||
};
|
||||
|
||||
/* HDMI codec initalization data */
|
||||
|
|
@ -128,6 +126,7 @@ struct hdmi_codec_pdata {
|
|||
uint spdif:1;
|
||||
uint no_spdif_playback:1;
|
||||
uint no_spdif_capture:1;
|
||||
uint no_capture_mute:1;
|
||||
int max_i2s_channels;
|
||||
void *data;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -714,7 +714,7 @@ static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction)
|
|||
*/
|
||||
if (hcp->hcd.ops->mute_stream &&
|
||||
(direction == SNDRV_PCM_STREAM_PLAYBACK ||
|
||||
!hcp->hcd.ops->no_capture_mute))
|
||||
!hcp->hcd.no_capture_mute))
|
||||
return hcp->hcd.ops->mute_stream(dai->dev->parent,
|
||||
hcp->hcd.data,
|
||||
mute, direction);
|
||||
|
|
@ -995,7 +995,7 @@ static int hdmi_of_xlate_dai_id(struct snd_soc_component *component,
|
|||
int ret = -ENOTSUPP; /* see snd_soc_get_dai_id() */
|
||||
|
||||
if (hcp->hcd.ops->get_dai_id)
|
||||
ret = hcp->hcd.ops->get_dai_id(component, endpoint);
|
||||
ret = hcp->hcd.ops->get_dai_id(component, endpoint, hcp->hcd.data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user