summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/Kconfig2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c20
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device.c1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c31
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c11
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c14
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c6
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_priv.h4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process.c5
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c5
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_topology.c40
-rw-r--r--drivers/gpu/drm/drm_edid.c43
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.c132
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.h5
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h5
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dpi.c42
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c257
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h83
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c127
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.h2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c266
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_g2d.c9
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_iommu.h1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c150
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c65
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c126
-rw-r--r--drivers/gpu/drm/i2c/Kconfig6
-rw-r--r--drivers/gpu/drm/i2c/Makefile2
-rw-r--r--drivers/gpu/drm/i2c/adv7511.c1010
-rw-r--r--drivers/gpu/drm/i2c/adv7511.h289
-rw-r--r--drivers/gpu/drm/imx/Kconfig (renamed from drivers/staging/imx-drm/Kconfig)0
-rw-r--r--drivers/gpu/drm/imx/Makefile (renamed from drivers/staging/imx-drm/Makefile)0
-rw-r--r--drivers/gpu/drm/imx/imx-drm-core.c (renamed from drivers/staging/imx-drm/imx-drm-core.c)0
-rw-r--r--drivers/gpu/drm/imx/imx-drm.h (renamed from drivers/staging/imx-drm/imx-drm.h)0
-rw-r--r--drivers/gpu/drm/imx/imx-hdmi.c (renamed from drivers/staging/imx-drm/imx-hdmi.c)0
-rw-r--r--drivers/gpu/drm/imx/imx-hdmi.h (renamed from drivers/staging/imx-drm/imx-hdmi.h)0
-rw-r--r--drivers/gpu/drm/imx/imx-ldb.c (renamed from drivers/staging/imx-drm/imx-ldb.c)0
-rw-r--r--drivers/gpu/drm/imx/imx-tve.c (renamed from drivers/staging/imx-drm/imx-tve.c)0
-rw-r--r--drivers/gpu/drm/imx/ipuv3-crtc.c (renamed from drivers/staging/imx-drm/ipuv3-crtc.c)0
-rw-r--r--drivers/gpu/drm/imx/ipuv3-plane.c (renamed from drivers/staging/imx-drm/ipuv3-plane.c)0
-rw-r--r--drivers/gpu/drm/imx/ipuv3-plane.h (renamed from drivers/staging/imx-drm/ipuv3-plane.h)0
-rw-r--r--drivers/gpu/drm/imx/parallel-display.c (renamed from drivers/staging/imx-drm/parallel-display.c)0
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig11
-rw-r--r--drivers/gpu/drm/rcar-du/Makefile2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.h10
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c4
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.c45
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.h23
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c118
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h31
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c151
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h35
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c53
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c31
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h1
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_vgacon.c5
-rw-r--r--drivers/gpu/drm/udl/udl_dmabuf.c13
-rw-r--r--drivers/gpu/drm/udl/udl_drv.h2
-rw-r--r--drivers/gpu/drm/udl/udl_gem.c4
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/imx-drm/TODO17
69 files changed, 2467 insertions, 864 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 37c5a6ea5bdf..24c2d7caedd5 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -202,3 +202,5 @@ source "drivers/gpu/drm/panel/Kconfig"
source "drivers/gpu/drm/sti/Kconfig"
source "drivers/gpu/drm/amd/amdkfd/Kconfig"
+
+source "drivers/gpu/drm/imx/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index dd9d35bfa690..47d89869c5df 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs/
obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
obj-$(CONFIG_DRM_STI) += sti/
+obj-$(CONFIG_DRM_IMX) += imx/
obj-y += i2c/
obj-y += panel/
obj-y += bridge/
diff --git a/drivers/gpu/drm/amd/amdkfd/Kconfig b/drivers/gpu/drm/amd/amdkfd/Kconfig
index e13c67c8d2c0..8dfac37ff327 100644
--- a/drivers/gpu/drm/amd/amdkfd/Kconfig
+++ b/drivers/gpu/drm/amd/amdkfd/Kconfig
@@ -4,6 +4,6 @@
config HSA_AMD
tristate "HSA kernel driver for AMD GPU devices"
- depends on (DRM_RADEON || DRM_AMDGPU) && AMD_IOMMU_V2 && X86_64
+ depends on DRM_RADEON && AMD_IOMMU_V2 && X86_64
help
Enable this if you want to use HSA features on AMD GPU devices.
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index 64c73bae960d..102cd36799b1 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -149,7 +149,9 @@ static int set_queue_properties_from_user(struct queue_properties *q_properties,
}
if ((args->ring_base_address) &&
- (!access_ok(VERIFY_WRITE, args->ring_base_address, sizeof(uint64_t)))) {
+ (!access_ok(VERIFY_WRITE,
+ (const void __user *) args->ring_base_address,
+ sizeof(uint64_t)))) {
pr_err("kfd: can't access ring base address\n");
return -EFAULT;
}
@@ -159,12 +161,16 @@ static int set_queue_properties_from_user(struct queue_properties *q_properties,
return -EINVAL;
}
- if (!access_ok(VERIFY_WRITE, args->read_pointer_address, sizeof(uint32_t))) {
+ if (!access_ok(VERIFY_WRITE,
+ (const void __user *) args->read_pointer_address,
+ sizeof(uint32_t))) {
pr_err("kfd: can't access read pointer\n");
return -EFAULT;
}
- if (!access_ok(VERIFY_WRITE, args->write_pointer_address, sizeof(uint32_t))) {
+ if (!access_ok(VERIFY_WRITE,
+ (const void __user *) args->write_pointer_address,
+ sizeof(uint32_t))) {
pr_err("kfd: can't access write pointer\n");
return -EFAULT;
}
@@ -236,7 +242,7 @@ static long kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p,
mutex_lock(&p->mutex);
pdd = kfd_bind_process_to_device(dev, p);
- if (IS_ERR(pdd) < 0) {
+ if (IS_ERR(pdd)) {
err = PTR_ERR(pdd);
goto err_bind_process;
}
@@ -325,7 +331,9 @@ static int kfd_ioctl_update_queue(struct file *filp, struct kfd_process *p,
}
if ((args.ring_base_address) &&
- (!access_ok(VERIFY_WRITE, args.ring_base_address, sizeof(uint64_t)))) {
+ (!access_ok(VERIFY_WRITE,
+ (const void __user *) args.ring_base_address,
+ sizeof(uint64_t)))) {
pr_err("kfd: can't access ring base address\n");
return -EFAULT;
}
@@ -381,7 +389,7 @@ static long kfd_ioctl_set_memory_policy(struct file *filep,
mutex_lock(&p->mutex);
pdd = kfd_bind_process_to_device(dev, p);
- if (IS_ERR(pdd) < 0) {
+ if (IS_ERR(pdd)) {
err = PTR_ERR(pdd);
goto out;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
index 9beb6f7bba9d..43884ebd4303 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
@@ -267,6 +267,7 @@ void kgd2kfd_suspend(struct kfd_dev *kfd)
if (kfd->init_complete) {
kfd->dqm->stop(kfd->dqm);
+ amd_iommu_set_invalidate_ctx_cb(kfd->pdev, NULL);
amd_iommu_free_device(kfd->pdev);
}
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 8c40d04932b2..924e90c072e5 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -67,26 +67,21 @@ static inline unsigned int get_pipes_num_cpsch(void)
return PIPE_PER_ME_CP_SCHEDULING;
}
-static unsigned int get_sh_mem_bases_nybble_64(struct kfd_process *process,
- struct kfd_dev *dev)
+static inline unsigned int
+get_sh_mem_bases_nybble_64(struct kfd_process_device *pdd)
{
- struct kfd_process_device *pdd;
uint32_t nybble;
- pdd = kfd_get_process_device_data(dev, process, 1);
nybble = (pdd->lds_base >> 60) & 0x0E;
return nybble;
}
-static unsigned int get_sh_mem_bases_32(struct kfd_process *process,
- struct kfd_dev *dev)
+static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd)
{
- struct kfd_process_device *pdd;
unsigned int shared_base;
- pdd = kfd_get_process_device_data(dev, process, 1);
shared_base = (pdd->lds_base >> 16) & 0xFF;
return shared_base;
@@ -96,10 +91,13 @@ static uint32_t compute_sh_mem_bases_64bit(unsigned int top_address_nybble);
static void init_process_memory(struct device_queue_manager *dqm,
struct qcm_process_device *qpd)
{
+ struct kfd_process_device *pdd;
unsigned int temp;
BUG_ON(!dqm || !qpd);
+ pdd = qpd_to_pdd(qpd);
+
/* check if sh_mem_config register already configured */
if (qpd->sh_mem_config == 0) {
qpd->sh_mem_config =
@@ -111,11 +109,11 @@ static void init_process_memory(struct device_queue_manager *dqm,
}
if (qpd->pqm->process->is_32bit_user_mode) {
- temp = get_sh_mem_bases_32(qpd->pqm->process, dqm->dev);
+ temp = get_sh_mem_bases_32(pdd);
qpd->sh_mem_bases = SHARED_BASE(temp);
qpd->sh_mem_config |= PTR32;
} else {
- temp = get_sh_mem_bases_nybble_64(qpd->pqm->process, dqm->dev);
+ temp = get_sh_mem_bases_nybble_64(pdd);
qpd->sh_mem_bases = compute_sh_mem_bases_64bit(temp);
}
@@ -409,6 +407,7 @@ static int unregister_process_nocpsch(struct device_queue_manager *dqm,
list_for_each_entry_safe(cur, next, &dqm->queues, list) {
if (qpd == cur->qpd) {
list_del(&cur->list);
+ kfree(cur);
dqm->processes_count--;
goto out;
}
@@ -576,11 +575,15 @@ static int initialize_nocpsch(struct device_queue_manager *dqm)
static void uninitialize_nocpsch(struct device_queue_manager *dqm)
{
+ int i;
+
BUG_ON(!dqm);
BUG_ON(dqm->queue_count > 0 || dqm->processes_count > 0);
kfree(dqm->allocated_queues);
+ for (i = 0 ; i < KFD_MQD_TYPE_MAX ; i++)
+ kfree(dqm->mqds[i]);
mutex_destroy(&dqm->lock);
kfd2kgd->free_mem(dqm->dev->kgd,
(struct kgd_mem *) dqm->pipeline_mem);
@@ -706,8 +709,7 @@ static int stop_cpsch(struct device_queue_manager *dqm)
destroy_queues_cpsch(dqm, true);
list_for_each_entry(node, &dqm->queues, list) {
- pdd = kfd_get_process_device_data(dqm->dev,
- node->qpd->pqm->process, 1);
+ pdd = qpd_to_pdd(node->qpd);
pdd->bound = false;
}
kfd2kgd->free_mem(dqm->dev->kgd,
@@ -789,8 +791,9 @@ out:
return retval;
}
-int fence_wait_timeout(unsigned int *fence_addr, unsigned int fence_value,
- unsigned long timeout)
+static int fence_wait_timeout(unsigned int *fence_addr,
+ unsigned int fence_value,
+ unsigned long timeout)
{
BUG_ON(!fence_addr);
timeout += jiffies;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
index 0dcb78755686..b5791a5c7c06 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
@@ -23,6 +23,7 @@
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/slab.h>
+#include <linux/io.h>
/*
* This extension supports a kernel level doorbells management for
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c b/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c
index 2dfc4c0e85a4..66df4da01c29 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c
@@ -276,21 +276,22 @@
*/
#define MAKE_GPUVM_APP_BASE(gpu_num) \
- (((uint64_t)(gpu_num) << 61) + 0x1000000000000)
+ (((uint64_t)(gpu_num) << 61) + 0x1000000000000L)
#define MAKE_GPUVM_APP_LIMIT(base) \
- (((uint64_t)(base) & 0xFFFFFF0000000000) | 0xFFFFFFFFFF)
+ (((uint64_t)(base) & \
+ 0xFFFFFF0000000000UL) | 0xFFFFFFFFFFL)
#define MAKE_SCRATCH_APP_BASE(gpu_num) \
- (((uint64_t)(gpu_num) << 61) + 0x100000000)
+ (((uint64_t)(gpu_num) << 61) + 0x100000000L)
#define MAKE_SCRATCH_APP_LIMIT(base) \
- (((uint64_t)base & 0xFFFFFFFF00000000) | 0xFFFFFFFF)
+ (((uint64_t)base & 0xFFFFFFFF00000000UL) | 0xFFFFFFFF)
#define MAKE_LDS_APP_BASE(gpu_num) \
(((uint64_t)(gpu_num) << 61) + 0x0)
#define MAKE_LDS_APP_LIMIT(base) \
- (((uint64_t)(base) & 0xFFFFFFFF00000000) | 0xFFFFFFFF)
+ (((uint64_t)(base) & 0xFFFFFFFF00000000UL) | 0xFFFFFFFF)
int kfd_init_apertures(struct kfd_process *process)
{
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
index 555af4514237..9abac48de499 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
@@ -25,6 +25,7 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/printk.h>
+#include <linux/sched.h>
#include "kfd_kernel_queue.h"
#include "kfd_priv.h"
#include "kfd_device_queue_manager.h"
@@ -66,8 +67,7 @@ static bool initialize(struct kernel_queue *kq, struct kfd_dev *dev,
if (kq->mqd == NULL)
return false;
- prop.doorbell_ptr =
- (uint32_t *)kfd_get_kernel_doorbell(dev, &prop.doorbell_off);
+ prop.doorbell_ptr = kfd_get_kernel_doorbell(dev, &prop.doorbell_off);
if (prop.doorbell_ptr == NULL)
goto err_get_kernel_doorbell;
@@ -172,7 +172,7 @@ err_rptr_allocate_vidmem:
kfd2kgd->free_mem(dev->kgd, (struct kgd_mem *) kq->pq);
err_pq_allocate_vidmem:
pr_err("kfd: error init pq\n");
- kfd_release_kernel_doorbell(dev, (u32 *)prop.doorbell_ptr);
+ kfd_release_kernel_doorbell(dev, prop.doorbell_ptr);
err_get_kernel_doorbell:
pr_err("kfd: error init doorbell");
return false;
@@ -195,7 +195,7 @@ static void uninitialize(struct kernel_queue *kq)
kfd2kgd->free_mem(kq->dev->kgd, (struct kgd_mem *) kq->wptr_mem);
kfd2kgd->free_mem(kq->dev->kgd, (struct kgd_mem *) kq->pq);
kfd_release_kernel_doorbell(kq->dev,
- (u32 *)kq->queue->properties.doorbell_ptr);
+ kq->queue->properties.doorbell_ptr);
uninit_queue(kq->queue);
}
@@ -255,7 +255,7 @@ static void submit_packet(struct kernel_queue *kq)
#endif
*kq->wptr_kernel = kq->pending_wptr;
- write_kernel_doorbell((u32 *)kq->queue->properties.doorbell_ptr,
+ write_kernel_doorbell(kq->queue->properties.doorbell_ptr,
kq->pending_wptr);
}
@@ -275,7 +275,7 @@ static int sync_with_hw(struct kernel_queue *kq, unsigned long timeout_ms)
*kq->wptr_kernel, *kq->rptr_kernel);
return -ETIME;
}
- cpu_relax();
+ schedule();
}
return 0;
@@ -321,7 +321,7 @@ void kernel_queue_uninit(struct kernel_queue *kq)
kfree(kq);
}
-void test_kq(struct kfd_dev *dev)
+static __attribute__((unused)) void test_kq(struct kfd_dev *dev)
{
struct kernel_queue *kq;
uint32_t *buffer, i;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c
index 59d240719a08..adc31474e786 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c
@@ -179,9 +179,9 @@ static int destroy_mqd(struct mqd_manager *mm, void *mqd,
pipe_id, queue_id);
}
-bool is_occupied(struct mqd_manager *mm, void *mqd,
- uint64_t queue_address, uint32_t pipe_id,
- uint32_t queue_id)
+static bool is_occupied(struct mqd_manager *mm, void *mqd,
+ uint64_t queue_address, uint32_t pipe_id,
+ uint32_t queue_id)
{
return kfd2kgd->hqd_is_occupies(mm->dev->kgd, queue_address,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index 41e608d684b1..f9fb81e3bb09 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -279,7 +279,7 @@ struct queue_properties {
uint32_t queue_percent;
uint32_t *read_ptr;
uint32_t *write_ptr;
- uint32_t *doorbell_ptr;
+ uint32_t __iomem *doorbell_ptr;
uint32_t doorbell_off;
bool is_interop;
bool is_active;
@@ -414,6 +414,8 @@ struct kfd_process_device {
bool bound;
};
+#define qpd_to_pdd(x) container_of(x, struct kfd_process_device, qpd)
+
/* Process data */
struct kfd_process {
/*
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index a17e2dd0f489..b4f49ac13334 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -348,11 +348,6 @@ struct kfd_process_device *kfd_bind_process_to_device(struct kfd_dev *dev,
if (err < 0)
return ERR_PTR(err);
- if (err < 0) {
- amd_iommu_unbind_pasid(dev->pdev, p->pasid);
- return ERR_PTR(err);
- }
-
pdd->bound = true;
return pdd;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index c7859fcc25ce..47526780d736 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -208,7 +208,7 @@ int pqm_create_queue(struct process_queue_manager *pqm,
case KFD_QUEUE_TYPE_DIQ:
kq = kernel_queue_init(dev, KFD_QUEUE_TYPE_DIQ);
if (kq == NULL) {
- kernel_queue_uninit(kq);
+ retval = -ENOMEM;
goto err_create_queue;
}
kq->queue->properties.queue_id = *qid;
@@ -325,7 +325,8 @@ int pqm_update_queue(struct process_queue_manager *pqm, unsigned int qid,
return 0;
}
-struct kernel_queue *pqm_get_kernel_queue(struct process_queue_manager *pqm,
+static __attribute__((unused)) struct kernel_queue *pqm_get_kernel_queue(
+ struct process_queue_manager *pqm,
unsigned int qid)
{
struct process_queue_node *pqn;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
index 77cd7d55b39d..5733e2859e8a 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
@@ -96,7 +96,7 @@ static int kfd_topology_get_crat_acpi(void *crat_image, size_t *size)
return -EINVAL;
}
- if (*size >= crat_table->length && crat_image != 0)
+ if (*size >= crat_table->length && crat_image != NULL)
memcpy(crat_image, crat_table, crat_table->length);
*size = crat_table->length;
@@ -183,7 +183,7 @@ static int kfd_parse_subtype_mem(struct crat_subtype_memory *mem)
list_for_each_entry(dev, &topology_device_list, list) {
if (mem->promixity_domain == i) {
props = kfd_alloc_struct(props);
- if (props == 0)
+ if (props == NULL)
return -ENOMEM;
if (dev->node_props.cpu_cores_count == 0)
@@ -231,7 +231,7 @@ static int kfd_parse_subtype_cache(struct crat_subtype_cache *cache)
if (id == dev->node_props.cpu_core_id_base ||
id == dev->node_props.simd_id_base) {
props = kfd_alloc_struct(props);
- if (props == 0)
+ if (props == NULL)
return -ENOMEM;
props->processor_id_low = id;
@@ -282,7 +282,7 @@ static int kfd_parse_subtype_iolink(struct crat_subtype_iolink *iolink)
list_for_each_entry(dev, &topology_device_list, list) {
if (id_from == i) {
props = kfd_alloc_struct(props);
- if (props == 0)
+ if (props == NULL)
return -ENOMEM;
props->node_from = id_from;
@@ -415,9 +415,9 @@ static struct kfd_topology_device *kfd_create_topology_device(void)
struct kfd_topology_device *dev;
dev = kfd_alloc_struct(dev);
- if (dev == 0) {
+ if (dev == NULL) {
pr_err("No memory to allocate a topology device");
- return 0;
+ return NULL;
}
INIT_LIST_HEAD(&dev->mem_props);
@@ -428,7 +428,7 @@ static struct kfd_topology_device *kfd_create_topology_device(void)
sys_props.num_devices++;
return dev;
- }
+}
static int kfd_parse_crat_table(void *crat_image)
{
@@ -752,11 +752,11 @@ static void kfd_remove_sysfs_node_entry(struct kfd_topology_device *dev)
if (iolink->kobj) {
kfd_remove_sysfs_file(iolink->kobj,
&iolink->attr);
- iolink->kobj = 0;
+ iolink->kobj = NULL;
}
kobject_del(dev->kobj_iolink);
kobject_put(dev->kobj_iolink);
- dev->kobj_iolink = 0;
+ dev->kobj_iolink = NULL;
}
if (dev->kobj_cache) {
@@ -764,22 +764,22 @@ static void kfd_remove_sysfs_node_entry(struct kfd_topology_device *dev)
if (cache->kobj) {
kfd_remove_sysfs_file(cache->kobj,
&cache->attr);
- cache->kobj = 0;
+ cache->kobj = NULL;
}
kobject_del(dev->kobj_cache);
kobject_put(dev->kobj_cache);
- dev->kobj_cache = 0;
+ dev->kobj_cache = NULL;
}
if (dev->kobj_mem) {
list_for_each_entry(mem, &dev->mem_props, list)
if (mem->kobj) {
kfd_remove_sysfs_file(mem->kobj, &mem->attr);
- mem->kobj = 0;
+ mem->kobj = NULL;
}
kobject_del(dev->kobj_mem);
kobject_put(dev->kobj_mem);
- dev->kobj_mem = 0;
+ dev->kobj_mem = NULL;
}
if (dev->kobj_node) {
@@ -788,7 +788,7 @@ static void kfd_remove_sysfs_node_entry(struct kfd_topology_device *dev)
sysfs_remove_file(dev->kobj_node, &dev->attr_props);
kobject_del(dev->kobj_node);
kobject_put(dev->kobj_node);
- dev->kobj_node = 0;
+ dev->kobj_node = NULL;
}
}
@@ -939,7 +939,7 @@ static int kfd_topology_update_sysfs(void)
int ret;
pr_info("Creating topology SYSFS entries\n");
- if (sys_props.kobj_topology == 0) {
+ if (sys_props.kobj_topology == NULL) {
sys_props.kobj_topology =
kfd_alloc_struct(sys_props.kobj_topology);
if (!sys_props.kobj_topology)
@@ -989,17 +989,17 @@ static void kfd_topology_release_sysfs(void)
if (sys_props.kobj_nodes) {
kobject_del(sys_props.kobj_nodes);
kobject_put(sys_props.kobj_nodes);
- sys_props.kobj_nodes = 0;
+ sys_props.kobj_nodes = NULL;
}
kobject_del(sys_props.kobj_topology);
kobject_put(sys_props.kobj_topology);
- sys_props.kobj_topology = 0;
+ sys_props.kobj_topology = NULL;
}
}
int kfd_topology_init(void)
{
- void *crat_image = 0;
+ void *crat_image = NULL;
size_t image_size = 0;
int ret;
@@ -1094,12 +1094,12 @@ static uint32_t kfd_generate_gpu_id(struct kfd_dev *gpu)
static struct kfd_topology_device *kfd_assign_gpu(struct kfd_dev *gpu)
{
struct kfd_topology_device *dev;
- struct kfd_topology_device *out_dev = 0;
+ struct kfd_topology_device *out_dev = NULL;
BUG_ON(!gpu);
list_for_each_entry(dev, &topology_device_list, list)
- if (dev->gpu == 0 && dev->node_props.simd_count > 0) {
+ if (dev->gpu == NULL && dev->node_props.simd_count > 0) {
dev->gpu = gpu;
out_dev = dev;
break;
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 3bf999134bcc..1a77a49d2695 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1125,9 +1125,9 @@ EXPORT_SYMBOL(drm_edid_is_valid);
* Return: 0 on success or -1 on failure.
*/
static int
-drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
- int block, int len)
+drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
{
+ struct i2c_adapter *adapter = data;
unsigned char start = block * EDID_LENGTH;
unsigned char segment = block >> 1;
unsigned char xfers = segment ? 3 : 2;
@@ -1184,8 +1184,26 @@ static bool drm_edid_is_zero(u8 *in_edid, int length)
return true;
}
-static u8 *
-drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
+/**
+ * drm_do_get_edid - get EDID data using a custom EDID block read function
+ * @connector: connector we're probing
+ * @get_edid_block: EDID block read function
+ * @data: private data passed to the block read function
+ *
+ * When the I2C adapter connected to the DDC bus is hidden behind a device that
+ * exposes a different interface to read EDID blocks this function can be used
+ * to get EDID data using a custom block read function.
+ *
+ * As in the general case the DDC bus is accessible by the kernel at the I2C
+ * level, drivers must make all reasonable efforts to expose it as an I2C
+ * adapter and use drm_get_edid() instead of abusing this function.
+ *
+ * Return: Pointer to valid EDID or NULL if we couldn't find any.
+ */
+struct edid *drm_do_get_edid(struct drm_connector *connector,
+ int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
+ size_t len),
+ void *data)
{
int i, j = 0, valid_extensions = 0;
u8 *block, *new;
@@ -1196,7 +1214,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
/* base block fetch */
for (i = 0; i < 4; i++) {
- if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH))
+ if (get_edid_block(data, block, 0, EDID_LENGTH))
goto out;
if (drm_edid_block_valid(block, 0, print_bad_edid))
break;
@@ -1210,7 +1228,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
/* if there's no extensions, we're done */
if (block[0x7e] == 0)
- return block;
+ return (struct edid *)block;
new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL);
if (!new)
@@ -1219,7 +1237,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
for (j = 1; j <= block[0x7e]; j++) {
for (i = 0; i < 4; i++) {
- if (drm_do_probe_ddc_edid(adapter,
+ if (get_edid_block(data,
block + (valid_extensions + 1) * EDID_LENGTH,
j, EDID_LENGTH))
goto out;
@@ -1247,7 +1265,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
block = new;
}
- return block;
+ return (struct edid *)block;
carp:
if (print_bad_edid) {
@@ -1260,6 +1278,7 @@ out:
kfree(block);
return NULL;
}
+EXPORT_SYMBOL_GPL(drm_do_get_edid);
/**
* drm_probe_ddc() - probe DDC presence
@@ -1289,12 +1308,10 @@ EXPORT_SYMBOL(drm_probe_ddc);
struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter)
{
- struct edid *edid = NULL;
-
- if (drm_probe_ddc(adapter))
- edid = (struct edid *)drm_do_get_edid(connector, adapter);
+ if (!drm_probe_ddc(adapter))
+ return NULL;
- return edid;
+ return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
}
EXPORT_SYMBOL(drm_get_edid);
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 6adb1e5cfb08..34d46aa75416 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -30,12 +30,17 @@
#include <drm/drm_panel.h>
#include <drm/bridge/ptn3460.h>
-#include "exynos_drm_drv.h"
#include "exynos_dp_core.h"
#define ctx_from_connector(c) container_of(c, struct exynos_dp_device, \
connector)
+static inline struct exynos_dp_device *
+display_to_dp(struct exynos_drm_display *d)
+{
+ return container_of(d, struct exynos_dp_device, display);
+}
+
struct bridge_init {
struct i2c_client *client;
struct device_node *node;
@@ -882,7 +887,7 @@ static void exynos_dp_hotplug(struct work_struct *work)
static void exynos_dp_commit(struct exynos_drm_display *display)
{
- struct exynos_dp_device *dp = display->ctx;
+ struct exynos_dp_device *dp = display_to_dp(display);
int ret;
/* Keep the panel disabled while we configure video */
@@ -1020,7 +1025,7 @@ static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
static int exynos_dp_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
- struct exynos_dp_device *dp = display->ctx;
+ struct exynos_dp_device *dp = display_to_dp(display);
struct drm_connector *connector = &dp->connector;
int ret;
@@ -1052,33 +1057,19 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
static void exynos_dp_phy_init(struct exynos_dp_device *dp)
{
- if (dp->phy) {
+ if (dp->phy)
phy_power_on(dp->phy);
- } else if (dp->phy_addr) {
- u32 reg;
-
- reg = __raw_readl(dp->phy_addr);
- reg |= dp->enable_mask;
- __raw_writel(reg, dp->phy_addr);
- }
}
static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
{
- if (dp->phy) {
+ if (dp->phy)
phy_power_off(dp->phy);
- } else if (dp->phy_addr) {
- u32 reg;
-
- reg = __raw_readl(dp->phy_addr);
- reg &= ~(dp->enable_mask);
- __raw_writel(reg, dp->phy_addr);
- }
}
static void exynos_dp_poweron(struct exynos_drm_display *display)
{
- struct exynos_dp_device *dp = display->ctx;
+ struct exynos_dp_device *dp = display_to_dp(display);
if (dp->dpms_mode == DRM_MODE_DPMS_ON)
return;
@@ -1099,7 +1090,7 @@ static void exynos_dp_poweron(struct exynos_drm_display *display)
static void exynos_dp_poweroff(struct exynos_drm_display *display)
{
- struct exynos_dp_device *dp = display->ctx;
+ struct exynos_dp_device *dp = display_to_dp(display);
if (dp->dpms_mode != DRM_MODE_DPMS_ON)
return;
@@ -1124,7 +1115,7 @@ static void exynos_dp_poweroff(struct exynos_drm_display *display)
static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
{
- struct exynos_dp_device *dp = display->ctx;
+ struct exynos_dp_device *dp = display_to_dp(display);
switch (mode) {
case DRM_MODE_DPMS_ON:
@@ -1147,11 +1138,6 @@ static struct exynos_drm_display_ops exynos_dp_display_ops = {
.commit = exynos_dp_commit,
};
-static struct exynos_drm_display exynos_dp_display = {
- .type = EXYNOS_DISPLAY_TYPE_LCD,
- .ops = &exynos_dp_display_ops,
-};
-
static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
{
struct device_node *dp_node = dev->of_node;
@@ -1210,44 +1196,6 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
return dp_video_config;
}
-static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
-{
- struct device_node *dp_phy_node = of_node_get(dp->dev->of_node);
- u32 phy_base;
- int ret = 0;
-
- dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
- if (!dp_phy_node) {
- dp->phy = devm_phy_get(dp->dev, "dp");
- return PTR_ERR_OR_ZERO(dp->phy);
- }
-
- if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
- dev_err(dp->dev, "failed to get reg for dptx-phy\n");
- ret = -EINVAL;
- goto err;
- }
-
- if (of_property_read_u32(dp_phy_node, "samsung,enable-mask",
- &dp->enable_mask)) {
- dev_err(dp->dev, "failed to get enable-mask for dptx-phy\n");
- ret = -EINVAL;
- goto err;
- }
-
- dp->phy_addr = ioremap(phy_base, SZ_4);
- if (!dp->phy_addr) {
- dev_err(dp->dev, "failed to ioremap dp-phy\n");
- ret = -ENOMEM;
- goto err;
- }
-
-err:
- of_node_put(dp_phy_node);
-
- return ret;
-}
-
static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
{
int ret;
@@ -1263,10 +1211,10 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
{
+ struct exynos_dp_device *dp = dev_get_drvdata(dev);
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm_dev = data;
struct resource *res;
- struct exynos_dp_device *dp = exynos_dp_display.ctx;
unsigned int irq_flags;
int ret = 0;
@@ -1277,9 +1225,21 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
if (IS_ERR(dp->video_info))
return PTR_ERR(dp->video_info);
- ret = exynos_dp_dt_parse_phydata(dp);
- if (ret)
- return ret;
+ dp->phy = devm_phy_get(dp->dev, "dp");
+ if (IS_ERR(dp->phy)) {
+ dev_err(dp->dev, "no DP phy configured\n");
+ ret = PTR_ERR(dp->phy);
+ if (ret) {
+ /*
+ * phy itself is not enabled, so we can move forward
+ * assigning NULL to phy pointer.
+ */
+ if (ret == -ENOSYS || ret == -ENODEV)
+ dp->phy = NULL;
+ else
+ return ret;
+ }
+ }
if (!dp->panel) {
ret = exynos_dp_dt_parse_panel(dp);
@@ -1346,17 +1306,15 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
dp->drm_dev = drm_dev;
- platform_set_drvdata(pdev, &exynos_dp_display);
-
- return exynos_drm_create_enc_conn(drm_dev, &exynos_dp_display);
+ return exynos_drm_create_enc_conn(drm_dev, &dp->display);
}
static void exynos_dp_unbind(struct device *dev, struct device *master,
void *data)
{
- struct exynos_drm_display *display = dev_get_drvdata(dev);
+ struct exynos_dp_device *dp = dev_get_drvdata(dev);
- exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
+ exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_OFF);
}
static const struct component_ops exynos_dp_ops = {
@@ -1371,16 +1329,20 @@ static int exynos_dp_probe(struct platform_device *pdev)
struct exynos_dp_device *dp;
int ret;
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
- exynos_dp_display.type);
- if (ret)
- return ret;
-
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL);
if (!dp)
return -ENOMEM;
+ dp->display.type = EXYNOS_DISPLAY_TYPE_LCD;
+ dp->display.ops = &exynos_dp_display_ops;
+ platform_set_drvdata(pdev, dp);
+
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+ dp->display.type);
+ if (ret)
+ return ret;
+
panel_node = of_parse_phandle(dev->of_node, "panel", 0);
if (panel_node) {
dp->panel = of_drm_find_panel(panel_node);
@@ -1389,8 +1351,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
- exynos_dp_display.ctx = dp;
-
ret = component_add(&pdev->dev, &exynos_dp_ops);
if (ret)
exynos_drm_component_del(&pdev->dev,
@@ -1410,19 +1370,17 @@ static int exynos_dp_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int exynos_dp_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct exynos_drm_display *display = platform_get_drvdata(pdev);
+ struct exynos_dp_device *dp = dev_get_drvdata(dev);
- exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
+ exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_OFF);
return 0;
}
static int exynos_dp_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct exynos_drm_display *display = platform_get_drvdata(pdev);
+ struct exynos_dp_device *dp = dev_get_drvdata(dev);
- exynos_dp_dpms(display, DRM_MODE_DPMS_ON);
+ exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_ON);
return 0;
}
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
index a1aee6931bd7..164f171168e7 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.h
@@ -17,6 +17,8 @@
#include <drm/drm_dp_helper.h>
#include <drm/exynos_drm.h>
+#include "exynos_drm_drv.h"
+
#define DP_TIMEOUT_LOOP_COUNT 100
#define MAX_CR_LOOP 5
#define MAX_EQ_LOOP 5
@@ -145,6 +147,7 @@ struct link_train {
};
struct exynos_dp_device {
+ struct exynos_drm_display display;
struct device *dev;
struct drm_device *drm_dev;
struct drm_connector connector;
@@ -153,8 +156,6 @@ struct exynos_dp_device {
struct clk *clock;
unsigned int irq;
void __iomem *reg_base;
- void __iomem *phy_addr;
- unsigned int enable_mask;
struct video_info *video_info;
struct link_train link_train;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
index 690dcddab725..e353d353836f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -15,10 +15,7 @@
#ifndef _EXYNOS_DRM_CRTC_H_
#define _EXYNOS_DRM_CRTC_H_
-struct drm_device;
-struct drm_crtc;
-struct exynos_drm_manager;
-struct exynos_drm_overlay;
+#include "exynos_drm_drv.h"
int exynos_drm_crtc_create(struct exynos_drm_manager *manager);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 3dc678ed9949..37678cf4425a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -22,6 +22,7 @@
#include "exynos_drm_drv.h"
struct exynos_dpi {
+ struct exynos_drm_display display;
struct device *dev;
struct device_node *panel_node;
@@ -35,6 +36,11 @@ struct exynos_dpi {
#define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
+static inline struct exynos_dpi *display_to_dpi(struct exynos_drm_display *d)
+{
+ return container_of(d, struct exynos_dpi, display);
+}
+
static enum drm_connector_status
exynos_dpi_detect(struct drm_connector *connector, bool force)
{
@@ -100,7 +106,7 @@ static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
static int exynos_dpi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
- struct exynos_dpi *ctx = display->ctx;
+ struct exynos_dpi *ctx = display_to_dpi(display);
struct drm_connector *connector = &ctx->connector;
int ret;
@@ -141,7 +147,7 @@ static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
{
- struct exynos_dpi *ctx = display->ctx;
+ struct exynos_dpi *ctx = display_to_dpi(display);
switch (mode) {
case DRM_MODE_DPMS_ON:
@@ -165,11 +171,6 @@ static struct exynos_drm_display_ops exynos_dpi_display_ops = {
.dpms = exynos_dpi_dpms
};
-static struct exynos_drm_display exynos_dpi_display = {
- .type = EXYNOS_DISPLAY_TYPE_LCD,
- .ops = &exynos_dpi_display_ops,
-};
-
/* of_* functions will be removed after merge of of_graph patches */
static struct device_node *
of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg)
@@ -299,20 +300,21 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
struct exynos_dpi *ctx;
int ret;
- ret = exynos_drm_component_add(dev,
- EXYNOS_DEVICE_TYPE_CONNECTOR,
- exynos_dpi_display.type);
- if (ret)
- return ERR_PTR(ret);
-
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
- goto err_del_component;
+ return ERR_PTR(-ENOMEM);
+ ctx->display.type = EXYNOS_DISPLAY_TYPE_LCD;
+ ctx->display.ops = &exynos_dpi_display_ops;
ctx->dev = dev;
- exynos_dpi_display.ctx = ctx;
ctx->dpms_mode = DRM_MODE_DPMS_OFF;
+ ret = exynos_drm_component_add(dev,
+ EXYNOS_DEVICE_TYPE_CONNECTOR,
+ ctx->display.type);
+ if (ret)
+ return ERR_PTR(ret);
+
ret = exynos_dpi_parse_dt(ctx);
if (ret < 0) {
devm_kfree(dev, ctx);
@@ -328,7 +330,7 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
}
}
- return &exynos_dpi_display;
+ return &ctx->display;
err_del_component:
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
@@ -336,16 +338,16 @@ err_del_component:
return NULL;
}
-int exynos_dpi_remove(struct device *dev)
+int exynos_dpi_remove(struct exynos_drm_display *display)
{
- struct exynos_dpi *ctx = exynos_dpi_display.ctx;
+ struct exynos_dpi *ctx = display_to_dpi(display);
- exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF);
+ exynos_dpi_dpms(&ctx->display, DRM_MODE_DPMS_OFF);
if (ctx->panel)
drm_panel_detach(ctx->panel);
- exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+ exynos_drm_component_del(ctx->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index c57466edf45b..d71fb54582d2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -203,8 +203,6 @@ static int exynos_drm_resume(struct drm_device *dev)
}
drm_modeset_unlock_all(dev);
- drm_helper_resume_force_mode(dev);
-
return 0;
}
@@ -475,8 +473,6 @@ void exynos_drm_component_del(struct device *dev,
list_del(&cdev->list);
kfree(cdev);
}
-
- break;
}
mutex_unlock(&drm_component_lock);
@@ -495,6 +491,12 @@ static struct component_match *exynos_drm_match_add(struct device *dev)
mutex_lock(&drm_component_lock);
+ /* Do not retry to probe if there is no any kms driver regitered. */
+ if (list_empty(&drm_component_list)) {
+ mutex_unlock(&drm_component_lock);
+ return ERR_PTR(-ENODEV);
+ }
+
list_for_each_entry(cdev, &drm_component_list, list) {
/*
* Add components to master only in case that crtc and
@@ -550,183 +552,68 @@ static const struct component_master_ops exynos_drm_ops = {
.unbind = exynos_drm_unbind,
};
-static int exynos_drm_platform_probe(struct platform_device *pdev)
-{
- struct component_match *match;
- int ret;
-
- pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
-
+static struct platform_driver *const exynos_drm_kms_drivers[] = {
#ifdef CONFIG_DRM_EXYNOS_FIMD
- ret = platform_driver_register(&fimd_driver);
- if (ret < 0)
- return ret;
+ &fimd_driver,
#endif
-
#ifdef CONFIG_DRM_EXYNOS_DP
- ret = platform_driver_register(&dp_driver);
- if (ret < 0)
- goto err_unregister_fimd_drv;
+ &dp_driver,
#endif
-
#ifdef CONFIG_DRM_EXYNOS_DSI
- ret = platform_driver_register(&dsi_driver);
- if (ret < 0)
- goto err_unregister_dp_drv;
+ &dsi_driver,
#endif
-
#ifdef CONFIG_DRM_EXYNOS_HDMI
- ret = platform_driver_register(&mixer_driver);
- if (ret < 0)
- goto err_unregister_dsi_drv;
- ret = platform_driver_register(&hdmi_driver);
- if (ret < 0)
- goto err_unregister_mixer_drv;
+ &mixer_driver,
+ &hdmi_driver,
#endif
+};
+static struct platform_driver *const exynos_drm_non_kms_drivers[] = {
#ifdef CONFIG_DRM_EXYNOS_G2D
- ret = platform_driver_register(&g2d_driver);
- if (ret < 0)
- goto err_unregister_hdmi_drv;
+ &g2d_driver,
#endif
-
#ifdef CONFIG_DRM_EXYNOS_FIMC
- ret = platform_driver_register(&fimc_driver);
- if (ret < 0)
- goto err_unregister_g2d_drv;
+ &fimc_driver,
#endif
-
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
- ret = platform_driver_register(&rotator_driver);
- if (ret < 0)
- goto err_unregister_fimc_drv;
+ &rotator_driver,
#endif
-
#ifdef CONFIG_DRM_EXYNOS_GSC
- ret = platform_driver_register(&gsc_driver);
- if (ret < 0)
- goto err_unregister_rotator_drv;
+ &gsc_driver,
#endif
-
#ifdef CONFIG_DRM_EXYNOS_IPP
- ret = platform_driver_register(&ipp_driver);
- if (ret < 0)
- goto err_unregister_gsc_drv;
-
- ret = exynos_platform_device_ipp_register();
- if (ret < 0)
- goto err_unregister_ipp_drv;
+ &ipp_driver,
#endif
+};
+
+static int exynos_drm_platform_probe(struct platform_device *pdev)
+{
+ struct component_match *match;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
match = exynos_drm_match_add(&pdev->dev);
if (IS_ERR(match)) {
- ret = PTR_ERR(match);
- goto err_unregister_resources;
+ return PTR_ERR(match);
}
- ret = component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
- match);
- if (ret < 0)
- goto err_unregister_resources;
-
- return ret;
-
-err_unregister_resources:
-
-#ifdef CONFIG_DRM_EXYNOS_IPP
- exynos_platform_device_ipp_unregister();
-err_unregister_ipp_drv:
- platform_driver_unregister(&ipp_driver);
-err_unregister_gsc_drv:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_GSC
- platform_driver_unregister(&gsc_driver);
-err_unregister_rotator_drv:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_ROTATOR
- platform_driver_unregister(&rotator_driver);
-err_unregister_fimc_drv:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_FIMC
- platform_driver_unregister(&fimc_driver);
-err_unregister_g2d_drv:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_G2D
- platform_driver_unregister(&g2d_driver);
-err_unregister_hdmi_drv:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_HDMI
- platform_driver_unregister(&hdmi_driver);
-err_unregister_mixer_drv:
- platform_driver_unregister(&mixer_driver);
-err_unregister_dsi_drv:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_DSI
- platform_driver_unregister(&dsi_driver);
-err_unregister_dp_drv:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_DP
- platform_driver_unregister(&dp_driver);
-err_unregister_fimd_drv:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_FIMD
- platform_driver_unregister(&fimd_driver);
-#endif
- return ret;
+ return component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
+ match);
}
static int exynos_drm_platform_remove(struct platform_device *pdev)
{
-#ifdef CONFIG_DRM_EXYNOS_IPP
- exynos_platform_device_ipp_unregister();
- platform_driver_unregister(&ipp_driver);
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_GSC
- platform_driver_unregister(&gsc_driver);
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_ROTATOR
- platform_driver_unregister(&rotator_driver);
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_FIMC
- platform_driver_unregister(&fimc_driver);
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_G2D
- platform_driver_unregister(&g2d_driver);
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_HDMI
- platform_driver_unregister(&mixer_driver);
- platform_driver_unregister(&hdmi_driver);
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_FIMD
- platform_driver_unregister(&fimd_driver);
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_DSI
- platform_driver_unregister(&dsi_driver);
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_DP
- platform_driver_unregister(&dp_driver);
-#endif
component_master_del(&pdev->dev, &exynos_drm_ops);
return 0;
}
+static const char * const strings[] = {
+ "samsung,exynos3",
+ "samsung,exynos4",
+ "samsung,exynos5",
+};
+
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = exynos_drm_platform_remove,
@@ -739,31 +626,75 @@ static struct platform_driver exynos_drm_platform_driver = {
static int exynos_drm_init(void)
{
- int ret;
+ bool is_exynos = false;
+ int ret, i, j;
+
+ /*
+ * Register device object only in case of Exynos SoC.
+ *
+ * Below codes resolves temporarily infinite loop issue incurred
+ * by Exynos drm driver when using multi-platform kernel.
+ * So these codes will be replaced with more generic way later.
+ */
+ for (i = 0; i < ARRAY_SIZE(strings); i++) {
+ if (of_machine_is_compatible(strings[i])) {
+ is_exynos = true;
+ break;
+ }
+ }
+
+ if (!is_exynos)
+ return -ENODEV;
exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
NULL, 0);
if (IS_ERR(exynos_drm_pdev))
return PTR_ERR(exynos_drm_pdev);
-#ifdef CONFIG_DRM_EXYNOS_VIDI
ret = exynos_drm_probe_vidi();
if (ret < 0)
goto err_unregister_pd;
+
+ for (i = 0; i < ARRAY_SIZE(exynos_drm_kms_drivers); ++i) {
+ ret = platform_driver_register(exynos_drm_kms_drivers[i]);
+ if (ret < 0)
+ goto err_unregister_kms_drivers;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(exynos_drm_non_kms_drivers); ++j) {
+ ret = platform_driver_register(exynos_drm_non_kms_drivers[j]);
+ if (ret < 0)
+ goto err_unregister_non_kms_drivers;
+ }
+
+#ifdef CONFIG_DRM_EXYNOS_IPP
+ ret = exynos_platform_device_ipp_register();
+ if (ret < 0)
+ goto err_unregister_non_kms_drivers;
#endif
ret = platform_driver_register(&exynos_drm_platform_driver);
if (ret)
- goto err_remove_vidi;
+ goto err_unregister_resources;
return 0;
-err_remove_vidi:
-#ifdef CONFIG_DRM_EXYNOS_VIDI
+err_unregister_resources:
+#ifdef CONFIG_DRM_EXYNOS_IPP
+ exynos_platform_device_ipp_unregister();
+#endif
+
+err_unregister_non_kms_drivers:
+ while (--j >= 0)
+ platform_driver_unregister(exynos_drm_non_kms_drivers[j]);
+
+err_unregister_kms_drivers:
+ while (--i >= 0)
+ platform_driver_unregister(exynos_drm_kms_drivers[i]);
+
exynos_drm_remove_vidi();
err_unregister_pd:
-#endif
platform_device_unregister(exynos_drm_pdev);
return ret;
@@ -771,10 +702,22 @@ err_unregister_pd:
static void exynos_drm_exit(void)
{
+ int i;
+
+#ifdef CONFIG_DRM_EXYNOS_IPP
+ exynos_platform_device_ipp_unregister();
+#endif
+
+ for (i = ARRAY_SIZE(exynos_drm_non_kms_drivers) - 1; i >= 0; --i)
+ platform_driver_unregister(exynos_drm_non_kms_drivers[i]);
+
+ for (i = ARRAY_SIZE(exynos_drm_kms_drivers) - 1; i >= 0; --i)
+ platform_driver_unregister(exynos_drm_kms_drivers[i]);
+
platform_driver_unregister(&exynos_drm_platform_driver);
-#ifdef CONFIG_DRM_EXYNOS_VIDI
+
exynos_drm_remove_vidi();
-#endif
+
platform_device_unregister(exynos_drm_pdev);
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index d22e640f59a0..2e5063488c50 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -15,6 +15,7 @@
#ifndef _EXYNOS_DRM_DRV_H_
#define _EXYNOS_DRM_DRV_H_
+#include <drm/drmP.h>
#include <linux/module.h>
#define MAX_CRTC 3
@@ -22,24 +23,6 @@
#define MAX_FB_BUFFER 4
#define DEFAULT_ZPOS -1
-#define _wait_for(COND, MS) ({ \
- unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
- int ret__ = 0; \
- while (!(COND)) { \
- if (time_after(jiffies, timeout__)) { \
- ret__ = -ETIMEDOUT; \
- break; \
- } \
- } \
- ret__; \
-})
-
-#define wait_for(COND, MS) _wait_for(COND, MS)
-
-struct drm_device;
-struct exynos_drm_overlay;
-struct drm_connector;
-
/* This enumerates device type. */
enum exynos_drm_device_type {
EXYNOS_DEVICE_TYPE_NONE,
@@ -83,10 +66,10 @@ enum exynos_drm_output_type {
* @dma_addr: array of bus(accessed by dma) address to the memory region
* allocated for a overlay.
* @zpos: order of overlay layer(z position).
- * @default_win: a window to be enabled.
- * @color_key: color key on or off.
* @index_color: if using color key feature then this value would be used
* as index color.
+ * @default_win: a window to be enabled.
+ * @color_key: color key on or off.
* @local_path: in case of lcd type, local path mode on or off.
* @transparency: transparency on or off.
* @activated: activated or not.
@@ -114,19 +97,20 @@ struct exynos_drm_overlay {
uint32_t pixel_format;
dma_addr_t dma_addr[MAX_FB_BUFFER];
int zpos;
-
- bool default_win;
- bool color_key;
unsigned int index_color;
- bool local_path;
- bool transparency;
- bool activated;
+
+ bool default_win:1;
+ bool color_key:1;
+ bool local_path:1;
+ bool transparency:1;
+ bool activated:1;
};
/*
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
+ * @create_connector: initialize and register a new connector
* @remove: cleans up the display for removal
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
@@ -168,7 +152,6 @@ struct exynos_drm_display {
struct drm_encoder *encoder;
struct drm_connector *connector;
struct exynos_drm_display_ops *ops;
- void *ctx;
};
/*
@@ -227,7 +210,6 @@ struct exynos_drm_manager {
struct drm_crtc *crtc;
int pipe;
struct exynos_drm_manager_ops *ops;
- void *ctx;
};
struct exynos_drm_g2d_private {
@@ -279,8 +261,6 @@ struct exynos_drm_private {
* @dev: pointer to device object for subdrv device driver.
* @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register().
- * @manager: subdrv has its own manager to control a hardware appropriately
- * and we can access a hardware drawing on this manager.
* @probe: this callback would be called by exynos drm driver after
* subdrv is registered to it.
* @remove: this callback is used to release resources created
@@ -312,45 +292,34 @@ int exynos_drm_device_subdrv_remove(struct drm_device *dev);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
-/*
- * this function registers exynos drm hdmi platform device. It ensures only one
- * instance of the device is created.
- */
-int exynos_platform_device_hdmi_register(void);
-
-/*
- * this function unregisters exynos drm hdmi platform device if it exists.
- */
-void exynos_platform_device_hdmi_unregister(void);
-
-/*
- * this function registers exynos drm ipp platform device.
- */
+#ifdef CONFIG_DRM_EXYNOS_IPP
int exynos_platform_device_ipp_register(void);
-
-/*
- * this function unregisters exynos drm ipp platform device if it exists.
- */
void exynos_platform_device_ipp_unregister(void);
+#else
+static inline int exynos_platform_device_ipp_register(void) { return 0; }
+static inline void exynos_platform_device_ipp_unregister(void) {}
+#endif
+
#ifdef CONFIG_DRM_EXYNOS_DPI
struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
-int exynos_dpi_remove(struct device *dev);
+int exynos_dpi_remove(struct exynos_drm_display *display);
#else
static inline struct exynos_drm_display *
exynos_dpi_probe(struct device *dev) { return NULL; }
-static inline int exynos_dpi_remove(struct device *dev) { return 0; }
+static inline int exynos_dpi_remove(struct exynos_drm_display *display)
+{
+ return 0;
+}
#endif
-/*
- * this function registers exynos drm vidi platform device/driver.
- */
+#ifdef CONFIG_DRM_EXYNOS_VIDI
int exynos_drm_probe_vidi(void);
-
-/*
- * this function unregister exynos drm vidi platform device/driver.
- */
void exynos_drm_remove_vidi(void);
+#else
+static inline int exynos_drm_probe_vidi(void) { return 0; }
+static inline void exynos_drm_remove_vidi(void) {}
+#endif
/* This function creates a encoder and a connector, and initializes them. */
int exynos_drm_create_enc_conn(struct drm_device *dev,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index f43d25896f3b..05fe93dc57a8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -268,9 +268,9 @@ struct exynos_dsi_driver_data {
};
struct exynos_dsi {
+ struct exynos_drm_display display;
struct mipi_dsi_host dsi_host;
struct drm_connector connector;
- struct drm_encoder *encoder;
struct device_node *panel_node;
struct drm_panel *panel;
struct device *dev;
@@ -304,6 +304,11 @@ struct exynos_dsi {
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
+static inline struct exynos_dsi *display_to_dsi(struct exynos_drm_display *d)
+{
+ return container_of(d, struct exynos_dsi, display);
+}
+
static struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
.plltmr_reg = 0x50,
.has_freqband = 1,
@@ -316,6 +321,11 @@ static struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
.has_clklane_stop = 1,
};
+static struct exynos_dsi_driver_data exynos4415_dsi_driver_data = {
+ .plltmr_reg = 0x58,
+ .has_clklane_stop = 1,
+};
+
static struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
.plltmr_reg = 0x58,
};
@@ -325,6 +335,8 @@ static struct of_device_id exynos_dsi_of_match[] = {
.data = &exynos3_dsi_driver_data },
{ .compatible = "samsung,exynos4210-mipi-dsi",
.data = &exynos4_dsi_driver_data },
+ { .compatible = "samsung,exynos4415-mipi-dsi",
+ .data = &exynos4415_dsi_driver_data },
{ .compatible = "samsung,exynos5410-mipi-dsi",
.data = &exynos5_dsi_driver_data },
{ }
@@ -1104,7 +1116,7 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
{
struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
- struct drm_encoder *encoder = dsi->encoder;
+ struct drm_encoder *encoder = dsi->display.encoder;
if (dsi->state & DSIM_STATE_ENABLED)
exynos_drm_crtc_te_handler(encoder->crtc);
@@ -1143,6 +1155,7 @@ static int exynos_dsi_init(struct exynos_dsi *dsi)
static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
{
int ret;
+ int te_gpio_irq;
dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0);
if (!gpio_is_valid(dsi->te_gpio)) {
@@ -1157,14 +1170,10 @@ static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
goto out;
}
- /*
- * This TE GPIO IRQ should not be set to IRQ_NOAUTOEN, because panel
- * calls drm_panel_init() first then calls mipi_dsi_attach() in probe().
- * It means that te_gpio is invalid when exynos_dsi_enable_irq() is
- * called by drm_panel_init() before panel is attached.
- */
- ret = request_threaded_irq(gpio_to_irq(dsi->te_gpio),
- exynos_dsi_te_irq_handler, NULL,
+ te_gpio_irq = gpio_to_irq(dsi->te_gpio);
+
+ irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
+ ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL,
IRQF_TRIGGER_RISING, "TE", dsi);
if (ret) {
dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
@@ -1195,9 +1204,6 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
dsi->mode_flags = device->mode_flags;
dsi->panel_node = device->dev.of_node;
- if (dsi->connector.dev)
- drm_helper_hpd_irq_event(dsi->connector.dev);
-
/*
* This is a temporary solution and should be made by more generic way.
*
@@ -1211,6 +1217,9 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
return ret;
}
+ if (dsi->connector.dev)
+ drm_helper_hpd_irq_event(dsi->connector.dev);
+
return 0;
}
@@ -1369,16 +1378,17 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
exynos_dsi_set_display_mode(dsi);
exynos_dsi_set_display_enable(dsi, true);
+ dsi->state |= DSIM_STATE_ENABLED;
+
ret = drm_panel_enable(dsi->panel);
if (ret < 0) {
+ dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel);
exynos_dsi_poweroff(dsi);
return ret;
}
- dsi->state |= DSIM_STATE_ENABLED;
-
return 0;
}
@@ -1397,7 +1407,7 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
{
- struct exynos_dsi *dsi = display->ctx;
+ struct exynos_dsi *dsi = display_to_dsi(display);
if (dsi->panel) {
switch (mode) {
@@ -1474,7 +1484,7 @@ exynos_dsi_best_encoder(struct drm_connector *connector)
{
struct exynos_dsi *dsi = connector_to_dsi(connector);
- return dsi->encoder;
+ return dsi->display.encoder;
}
static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
@@ -1486,12 +1496,10 @@ static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
static int exynos_dsi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
- struct exynos_dsi *dsi = display->ctx;
+ struct exynos_dsi *dsi = display_to_dsi(display);
struct drm_connector *connector = &dsi->connector;
int ret;
- dsi->encoder = encoder;
-
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(encoder->dev, connector,
@@ -1512,7 +1520,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display,
static void exynos_dsi_mode_set(struct exynos_drm_display *display,
struct drm_display_mode *mode)
{
- struct exynos_dsi *dsi = display->ctx;
+ struct exynos_dsi *dsi = display_to_dsi(display);
struct videomode *vm = &dsi->vm;
vm->hactive = mode->hdisplay;
@@ -1531,10 +1539,6 @@ static struct exynos_drm_display_ops exynos_dsi_display_ops = {
.dpms = exynos_dsi_dpms
};
-static struct exynos_drm_display exynos_dsi_display = {
- .type = EXYNOS_DISPLAY_TYPE_LCD,
- .ops = &exynos_dsi_display_ops,
-};
MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
/* of_* functions will be removed after merge of of_graph patches */
@@ -1640,28 +1644,28 @@ end:
static int exynos_dsi_bind(struct device *dev, struct device *master,
void *data)
{
+ struct exynos_drm_display *display = dev_get_drvdata(dev);
+ struct exynos_dsi *dsi = display_to_dsi(display);
struct drm_device *drm_dev = data;
- struct exynos_dsi *dsi;
int ret;
- ret = exynos_drm_create_enc_conn(drm_dev, &exynos_dsi_display);
+ ret = exynos_drm_create_enc_conn(drm_dev, display);
if (ret) {
DRM_ERROR("Encoder create [%d] failed with %d\n",
- exynos_dsi_display.type, ret);
+ display->type, ret);
return ret;
}
- dsi = exynos_dsi_display.ctx;
-
return mipi_dsi_host_register(&dsi->dsi_host);
}
static void exynos_dsi_unbind(struct device *dev, struct device *master,
void *data)
{
- struct exynos_dsi *dsi = exynos_dsi_display.ctx;
+ struct exynos_drm_display *display = dev_get_drvdata(dev);
+ struct exynos_dsi *dsi = display_to_dsi(display);
- exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
+ exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF);
mipi_dsi_host_unregister(&dsi->dsi_host);
}
@@ -1673,22 +1677,23 @@ static const struct component_ops exynos_dsi_component_ops = {
static int exynos_dsi_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct resource *res;
struct exynos_dsi *dsi;
int ret;
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
- exynos_dsi_display.type);
+ dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+ if (!dsi)
+ return -ENOMEM;
+
+ dsi->display.type = EXYNOS_DISPLAY_TYPE_LCD;
+ dsi->display.ops = &exynos_dsi_display_ops;
+
+ ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+ dsi->display.type);
if (ret)
return ret;
- dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
- if (!dsi) {
- dev_err(&pdev->dev, "failed to allocate dsi object.\n");
- ret = -ENOMEM;
- goto err_del_component;
- }
-
/* To be checked as invalid one */
dsi->te_gpio = -ENOENT;
@@ -1697,9 +1702,9 @@ static int exynos_dsi_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&dsi->transfer_list);
dsi->dsi_host.ops = &exynos_dsi_ops;
- dsi->dsi_host.dev = &pdev->dev;
+ dsi->dsi_host.dev = dev;
- dsi->dev = &pdev->dev;
+ dsi->dev = dev;
dsi->driver_data = exynos_dsi_get_driver_data(pdev);
ret = exynos_dsi_parse_dt(dsi);
@@ -1708,70 +1713,68 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->supplies[0].supply = "vddcore";
dsi->supplies[1].supply = "vddio";
- ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(dsi->supplies),
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
dsi->supplies);
if (ret) {
- dev_info(&pdev->dev, "failed to get regulators: %d\n", ret);
+ dev_info(dev, "failed to get regulators: %d\n", ret);
return -EPROBE_DEFER;
}
- dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
+ dsi->pll_clk = devm_clk_get(dev, "pll_clk");
if (IS_ERR(dsi->pll_clk)) {
- dev_info(&pdev->dev, "failed to get dsi pll input clock\n");
+ dev_info(dev, "failed to get dsi pll input clock\n");
ret = PTR_ERR(dsi->pll_clk);
goto err_del_component;
}
- dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ dsi->bus_clk = devm_clk_get(dev, "bus_clk");
if (IS_ERR(dsi->bus_clk)) {
- dev_info(&pdev->dev, "failed to get dsi bus clock\n");
+ dev_info(dev, "failed to get dsi bus clock\n");
ret = PTR_ERR(dsi->bus_clk);
goto err_del_component;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ dsi->reg_base = devm_ioremap_resource(dev, res);
if (IS_ERR(dsi->reg_base)) {
- dev_err(&pdev->dev, "failed to remap io region\n");
+ dev_err(dev, "failed to remap io region\n");
ret = PTR_ERR(dsi->reg_base);
goto err_del_component;
}
- dsi->phy = devm_phy_get(&pdev->dev, "dsim");
+ dsi->phy = devm_phy_get(dev, "dsim");
if (IS_ERR(dsi->phy)) {
- dev_info(&pdev->dev, "failed to get dsim phy\n");
+ dev_info(dev, "failed to get dsim phy\n");
ret = PTR_ERR(dsi->phy);
goto err_del_component;
}
dsi->irq = platform_get_irq(pdev, 0);
if (dsi->irq < 0) {
- dev_err(&pdev->dev, "failed to request dsi irq resource\n");
+ dev_err(dev, "failed to request dsi irq resource\n");
ret = dsi->irq;
goto err_del_component;
}
irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
- ret = devm_request_threaded_irq(&pdev->dev, dsi->irq, NULL,
+ ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
exynos_dsi_irq, IRQF_ONESHOT,
- dev_name(&pdev->dev), dsi);
+ dev_name(dev), dsi);
if (ret) {
- dev_err(&pdev->dev, "failed to request dsi irq\n");
+ dev_err(dev, "failed to request dsi irq\n");
goto err_del_component;
}
- exynos_dsi_display.ctx = dsi;
-
- platform_set_drvdata(pdev, &exynos_dsi_display);
+ platform_set_drvdata(pdev, &dsi->display);
- ret = component_add(&pdev->dev, &exynos_dsi_component_ops);
+ ret = component_add(dev, &exynos_dsi_component_ops);
if (ret)
goto err_del_component;
return ret;
err_del_component:
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+ exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return ret;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
index b7a1620a7e79..26305d8dd93a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
@@ -14,8 +14,6 @@
#ifndef _EXYNOS_DRM_ENCODER_H_
#define _EXYNOS_DRM_ENCODER_H_
-struct exynos_drm_manager;
-
void exynos_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_display *mgr,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 085b066a9993..e5810d13bf9c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -84,8 +84,6 @@
/* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5
-#define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev))
-
struct fimd_driver_data {
unsigned int timing_base;
unsigned int lcdblk_offset;
@@ -96,6 +94,7 @@ struct fimd_driver_data {
unsigned int has_clksel:1;
unsigned int has_limited_fmt:1;
unsigned int has_vidoutcon:1;
+ unsigned int has_vtsel:1;
};
static struct fimd_driver_data s3c64xx_fimd_driver_data = {
@@ -118,6 +117,17 @@ static struct fimd_driver_data exynos4_fimd_driver_data = {
.lcdblk_vt_shift = 10,
.lcdblk_bypass_shift = 1,
.has_shadowcon = 1,
+ .has_vtsel = 1,
+};
+
+static struct fimd_driver_data exynos4415_fimd_driver_data = {
+ .timing_base = 0x20000,
+ .lcdblk_offset = 0x210,
+ .lcdblk_vt_shift = 10,
+ .lcdblk_bypass_shift = 1,
+ .has_shadowcon = 1,
+ .has_vidoutcon = 1,
+ .has_vtsel = 1,
};
static struct fimd_driver_data exynos5_fimd_driver_data = {
@@ -127,6 +137,7 @@ static struct fimd_driver_data exynos5_fimd_driver_data = {
.lcdblk_bypass_shift = 15,
.has_shadowcon = 1,
.has_vidoutcon = 1,
+ .has_vtsel = 1,
};
struct fimd_win_data {
@@ -146,6 +157,7 @@ struct fimd_win_data {
};
struct fimd_context {
+ struct exynos_drm_manager manager;
struct device *dev;
struct drm_device *drm_dev;
struct clk *bus_clk;
@@ -173,6 +185,11 @@ struct fimd_context {
struct exynos_drm_display *display;
};
+static inline struct fimd_context *mgr_to_fimd(struct exynos_drm_manager *mgr)
+{
+ return container_of(mgr, struct fimd_context, manager);
+}
+
static const struct of_device_id fimd_driver_dt_match[] = {
{ .compatible = "samsung,s3c6400-fimd",
.data = &s3c64xx_fimd_driver_data },
@@ -180,6 +197,8 @@ static const struct of_device_id fimd_driver_dt_match[] = {
.data = &exynos3_fimd_driver_data },
{ .compatible = "samsung,exynos4210-fimd",
.data = &exynos4_fimd_driver_data },
+ { .compatible = "samsung,exynos4415-fimd",
+ .data = &exynos4415_fimd_driver_data },
{ .compatible = "samsung,exynos5250-fimd",
.data = &exynos5_fimd_driver_data },
{},
@@ -197,7 +216,7 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
if (ctx->suspended)
return;
@@ -214,9 +233,35 @@ static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
+static void fimd_enable_video_output(struct fimd_context *ctx, int win,
+ bool enable)
+{
+ u32 val = readl(ctx->regs + WINCON(win));
+
+ if (enable)
+ val |= WINCONx_ENWIN;
+ else
+ val &= ~WINCONx_ENWIN;
+
+ writel(val, ctx->regs + WINCON(win));
+}
+
+static void fimd_enable_shadow_channel_path(struct fimd_context *ctx, int win,
+ bool enable)
+{
+ u32 val = readl(ctx->regs + SHADOWCON);
+
+ if (enable)
+ val |= SHADOWCON_CHx_ENABLE(win);
+ else
+ val &= ~SHADOWCON_CHx_ENABLE(win);
+
+ writel(val, ctx->regs + SHADOWCON);
+}
+
static void fimd_clear_channel(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -226,16 +271,12 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr)
u32 val = readl(ctx->regs + WINCON(win));
if (val & WINCONx_ENWIN) {
- /* wincon */
- val &= ~WINCONx_ENWIN;
- writel(val, ctx->regs + WINCON(win));
-
- /* unprotect windows */
- if (ctx->driver_data->has_shadowcon) {
- val = readl(ctx->regs + SHADOWCON);
- val &= ~SHADOWCON_CHx_ENABLE(win);
- writel(val, ctx->regs + SHADOWCON);
- }
+ fimd_enable_video_output(ctx, win, false);
+
+ if (ctx->driver_data->has_shadowcon)
+ fimd_enable_shadow_channel_path(ctx, win,
+ false);
+
ch_enabled = 1;
}
}
@@ -253,7 +294,7 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr)
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
struct exynos_drm_private *priv;
priv = drm_dev->dev_private;
@@ -275,7 +316,7 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
/* detach this sub driver from iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev))
@@ -315,14 +356,14 @@ static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
static void fimd_mode_set(struct exynos_drm_manager *mgr,
const struct drm_display_mode *in_mode)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
drm_mode_copy(&ctx->mode, in_mode);
}
static void fimd_commit(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
struct drm_display_mode *mode = &ctx->mode;
struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base;
@@ -343,7 +384,8 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
writel(0, timing_base + I80IFCONFBx(0));
/* set video type selection to I80 interface */
- if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
+ if (driver_data->has_vtsel && ctx->sysreg &&
+ regmap_update_bits(ctx->sysreg,
driver_data->lcdblk_offset,
0x3 << driver_data->lcdblk_vt_shift,
0x1 << driver_data->lcdblk_vt_shift)) {
@@ -421,7 +463,7 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
u32 val;
if (ctx->suspended)
@@ -431,12 +473,19 @@ static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
val = readl(ctx->regs + VIDINTCON0);
val |= VIDINTCON0_INT_ENABLE;
- val |= VIDINTCON0_INT_FRAME;
- val &= ~VIDINTCON0_FRAMESEL0_MASK;
- val |= VIDINTCON0_FRAMESEL0_VSYNC;
- val &= ~VIDINTCON0_FRAMESEL1_MASK;
- val |= VIDINTCON0_FRAMESEL1_NONE;
+ if (ctx->i80_if) {
+ val |= VIDINTCON0_INT_I80IFDONE;
+ val |= VIDINTCON0_INT_SYSMAINCON;
+ val &= ~VIDINTCON0_INT_SYSSUBCON;
+ } else {
+ val |= VIDINTCON0_INT_FRAME;
+
+ val &= ~VIDINTCON0_FRAMESEL0_MASK;
+ val |= VIDINTCON0_FRAMESEL0_VSYNC;
+ val &= ~VIDINTCON0_FRAMESEL1_MASK;
+ val |= VIDINTCON0_FRAMESEL1_NONE;
+ }
writel(val, ctx->regs + VIDINTCON0);
}
@@ -446,7 +495,7 @@ static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
u32 val;
if (ctx->suspended)
@@ -455,9 +504,15 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
if (test_and_clear_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
- val &= ~VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_INT_ENABLE;
+ if (ctx->i80_if) {
+ val &= ~VIDINTCON0_INT_I80IFDONE;
+ val &= ~VIDINTCON0_INT_SYSMAINCON;
+ val &= ~VIDINTCON0_INT_SYSSUBCON;
+ } else
+ val &= ~VIDINTCON0_INT_FRAME;
+
writel(val, ctx->regs + VIDINTCON0);
}
}
@@ -465,7 +520,7 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data;
int win;
unsigned long offset;
@@ -623,7 +678,7 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data;
int win = zpos;
unsigned long val, alpha, size;
@@ -730,20 +785,14 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
if (win != 0)
fimd_win_set_colkey(ctx, win);
- /* wincon */
- val = readl(ctx->regs + WINCON(win));
- val |= WINCONx_ENWIN;
- writel(val, ctx->regs + WINCON(win));
+ fimd_enable_video_output(ctx, win, true);
+
+ if (ctx->driver_data->has_shadowcon)
+ fimd_enable_shadow_channel_path(ctx, win, true);
/* Enable DMA channel and unprotect windows */
fimd_shadow_protect_win(ctx, win, false);
- if (ctx->driver_data->has_shadowcon) {
- val = readl(ctx->regs + SHADOWCON);
- val |= SHADOWCON_CHx_ENABLE(win);
- writel(val, ctx->regs + SHADOWCON);
- }
-
win_data->enabled = true;
if (ctx->i80_if)
@@ -752,10 +801,9 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data;
int win = zpos;
- u32 val;
if (win == DEFAULT_ZPOS)
win = ctx->default_win;
@@ -774,18 +822,12 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
/* protect windows */
fimd_shadow_protect_win(ctx, win, true);
- /* wincon */
- val = readl(ctx->regs + WINCON(win));
- val &= ~WINCONx_ENWIN;
- writel(val, ctx->regs + WINCON(win));
+ fimd_enable_video_output(ctx, win, false);
- /* unprotect windows */
- if (ctx->driver_data->has_shadowcon) {
- val = readl(ctx->regs + SHADOWCON);
- val &= ~SHADOWCON_CHx_ENABLE(win);
- writel(val, ctx->regs + SHADOWCON);
- }
+ if (ctx->driver_data->has_shadowcon)
+ fimd_enable_shadow_channel_path(ctx, win, false);
+ /* unprotect windows */
fimd_shadow_protect_win(ctx, win, false);
win_data->enabled = false;
@@ -793,7 +835,7 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
static void fimd_window_suspend(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data;
int i;
@@ -803,12 +845,11 @@ static void fimd_window_suspend(struct exynos_drm_manager *mgr)
if (win_data->enabled)
fimd_win_disable(mgr, i);
}
- fimd_wait_for_vblank(mgr);
}
static void fimd_window_resume(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data;
int i;
@@ -821,7 +862,7 @@ static void fimd_window_resume(struct exynos_drm_manager *mgr)
static void fimd_apply(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data;
int i;
@@ -838,7 +879,7 @@ static void fimd_apply(struct exynos_drm_manager *mgr)
static int fimd_poweron(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
int ret;
if (!ctx->suspended)
@@ -886,7 +927,7 @@ bus_clk_err:
static int fimd_poweroff(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
if (ctx->suspended)
return 0;
@@ -928,39 +969,41 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
static void fimd_trigger(struct device *dev)
{
- struct exynos_drm_manager *mgr = get_fimd_manager(dev);
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = dev_get_drvdata(dev);
struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base;
u32 reg;
- atomic_set(&ctx->triggering, 1);
+ /*
+ * Skips triggering if in triggering state, because multiple triggering
+ * requests can cause panel reset.
+ */
+ if (atomic_read(&ctx->triggering))
+ return;
- reg = readl(ctx->regs + VIDINTCON0);
- reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE |
- VIDINTCON0_INT_SYSMAINCON);
- writel(reg, ctx->regs + VIDINTCON0);
+ /* Enters triggering mode */
+ atomic_set(&ctx->triggering, 1);
reg = readl(timing_base + TRIGCON);
reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
writel(reg, timing_base + TRIGCON);
+
+ /*
+ * Exits triggering mode if vblank is not enabled yet, because when the
+ * VIDINTCON0 register is not set, it can not exit from triggering mode.
+ */
+ if (!test_bit(0, &ctx->irq_flags))
+ atomic_set(&ctx->triggering, 0);
}
static void fimd_te_handler(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = mgr->ctx;
+ struct fimd_context *ctx = mgr_to_fimd(mgr);
/* Checks the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev)
return;
- /*
- * Skips to trigger if in triggering state, because multiple triggering
- * requests can cause panel reset.
- */
- if (atomic_read(&ctx->triggering))
- return;
-
/*
* If there is a page flip request, triggers and handles the page flip
* event so that current fb can be updated into panel GRAM.
@@ -972,10 +1015,10 @@ static void fimd_te_handler(struct exynos_drm_manager *mgr)
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue);
-
- if (!atomic_read(&ctx->triggering))
- drm_handle_vblank(ctx->drm_dev, ctx->pipe);
}
+
+ if (test_bit(0, &ctx->irq_flags))
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
}
static struct exynos_drm_manager_ops fimd_manager_ops = {
@@ -992,11 +1035,6 @@ static struct exynos_drm_manager_ops fimd_manager_ops = {
.te_handler = fimd_te_handler,
};
-static struct exynos_drm_manager fimd_manager = {
- .type = EXYNOS_DISPLAY_TYPE_LCD,
- .ops = &fimd_manager_ops,
-};
-
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{
struct fimd_context *ctx = (struct fimd_context *)dev_id;
@@ -1013,16 +1051,10 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
goto out;
if (ctx->i80_if) {
- /* unset I80 frame done interrupt */
- val = readl(ctx->regs + VIDINTCON0);
- val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
- writel(val, ctx->regs + VIDINTCON0);
+ exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
- /* exit triggering mode */
+ /* Exits triggering mode */
atomic_set(&ctx->triggering, 0);
-
- drm_handle_vblank(ctx->drm_dev, ctx->pipe);
- exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
} else {
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
@@ -1040,11 +1072,11 @@ out:
static int fimd_bind(struct device *dev, struct device *master, void *data)
{
- struct fimd_context *ctx = fimd_manager.ctx;
+ struct fimd_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
- fimd_mgr_initialize(&fimd_manager, drm_dev);
- exynos_drm_crtc_create(&fimd_manager);
+ fimd_mgr_initialize(&ctx->manager, drm_dev);
+ exynos_drm_crtc_create(&ctx->manager);
if (ctx->display)
exynos_drm_create_enc_conn(drm_dev, ctx->display);
@@ -1055,15 +1087,14 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
static void fimd_unbind(struct device *dev, struct device *master,
void *data)
{
- struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
- struct fimd_context *ctx = fimd_manager.ctx;
+ struct fimd_context *ctx = dev_get_drvdata(dev);
- fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
+ fimd_dpms(&ctx->manager, DRM_MODE_DPMS_OFF);
if (ctx->display)
- exynos_dpi_remove(dev);
+ exynos_dpi_remove(ctx->display);
- fimd_mgr_remove(mgr);
+ fimd_mgr_remove(&ctx->manager);
}
static const struct component_ops fimd_component_ops = {
@@ -1079,21 +1110,20 @@ static int fimd_probe(struct platform_device *pdev)
struct resource *res;
int ret = -EINVAL;
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
- fimd_manager.type);
- if (ret)
- return ret;
-
- if (!dev->of_node) {
- ret = -ENODEV;
- goto err_del_component;
- }
+ if (!dev->of_node)
+ return -ENODEV;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- ret = -ENOMEM;
- goto err_del_component;
- }
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->manager.type = EXYNOS_DISPLAY_TYPE_LCD;
+ ctx->manager.ops = &fimd_manager_ops;
+
+ ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
+ ctx->manager.type);
+ if (ret)
+ return ret;
ctx->dev = dev;
ctx->suspended = true;
@@ -1182,27 +1212,27 @@ static int fimd_probe(struct platform_device *pdev)
init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0);
- platform_set_drvdata(pdev, &fimd_manager);
-
- fimd_manager.ctx = ctx;
+ platform_set_drvdata(pdev, ctx);
ctx->display = exynos_dpi_probe(dev);
- if (IS_ERR(ctx->display))
- return PTR_ERR(ctx->display);
+ if (IS_ERR(ctx->display)) {
+ ret = PTR_ERR(ctx->display);
+ goto err_del_component;
+ }
- pm_runtime_enable(&pdev->dev);
+ pm_runtime_enable(dev);
- ret = component_add(&pdev->dev, &fimd_component_ops);
+ ret = component_add(dev, &fimd_component_ops);
if (ret)
goto err_disable_pm_runtime;
return ret;
err_disable_pm_runtime:
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_disable(dev);
err_del_component:
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+ exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index df7a77d3eff8..6ff8599f6cbf 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -302,9 +302,12 @@ static void g2d_fini_cmdlist(struct g2d_data *g2d)
struct exynos_drm_subdrv *subdrv = &g2d->subdrv;
kfree(g2d->cmdlist_node);
- dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
- g2d->cmdlist_pool_virt,
- g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+
+ if (g2d->cmdlist_pool_virt && g2d->cmdlist_pool) {
+ dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
+ g2d->cmdlist_pool_virt,
+ g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+ }
}
static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
index 72376d41c512..35d25889b476 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
@@ -40,7 +40,6 @@ static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
#else
-struct dma_iommu_mapping;
static inline int drm_create_iommu_mapping(struct drm_device *drm_dev)
{
return 0;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
index 00d74b18f7cb..d5ad17dfc24d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
@@ -426,18 +426,21 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
c_node->start_work = ipp_create_cmd_work();
if (IS_ERR(c_node->start_work)) {
DRM_ERROR("failed to create start work.\n");
+ ret = PTR_ERR(c_node->start_work);
goto err_remove_id;
}
c_node->stop_work = ipp_create_cmd_work();
if (IS_ERR(c_node->stop_work)) {
DRM_ERROR("failed to create stop work.\n");
+ ret = PTR_ERR(c_node->stop_work);
goto err_free_start;
}
c_node->event_work = ipp_create_event_work();
if (IS_ERR(c_node->event_work)) {
DRM_ERROR("failed to create event work.\n");
+ ret = PTR_ERR(c_node->event_work);
goto err_free_stop;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 50faf913e574..45899fb63272 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/component.h>
#include <drm/exynos_drm.h>
@@ -28,7 +29,6 @@
/* vidi has totally three virtual windows. */
#define WINDOWS_NR 3
-#define get_vidi_mgr(dev) platform_get_drvdata(to_platform_device(dev))
#define ctx_from_connector(c) container_of(c, struct vidi_context, \
connector)
@@ -47,11 +47,13 @@ struct vidi_win_data {
};
struct vidi_context {
+ struct exynos_drm_manager manager;
+ struct exynos_drm_display display;
+ struct platform_device *pdev;
struct drm_device *drm_dev;
struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_connector connector;
- struct exynos_drm_subdrv subdrv;
struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid;
unsigned int clkdiv;
@@ -66,6 +68,16 @@ struct vidi_context {
int pipe;
};
+static inline struct vidi_context *manager_to_vidi(struct exynos_drm_manager *m)
+{
+ return container_of(m, struct vidi_context, manager);
+}
+
+static inline struct vidi_context *display_to_vidi(struct exynos_drm_display *d)
+{
+ return container_of(d, struct vidi_context, display);
+}
+
static const char fake_edid_info[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05,
0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
@@ -93,7 +105,7 @@ static const char fake_edid_info[] = {
static void vidi_apply(struct exynos_drm_manager *mgr)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
struct vidi_win_data *win_data;
int i;
@@ -110,7 +122,7 @@ static void vidi_apply(struct exynos_drm_manager *mgr)
static void vidi_commit(struct exynos_drm_manager *mgr)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
if (ctx->suspended)
return;
@@ -118,7 +130,7 @@ static void vidi_commit(struct exynos_drm_manager *mgr)
static int vidi_enable_vblank(struct exynos_drm_manager *mgr)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
if (ctx->suspended)
return -EPERM;
@@ -140,7 +152,7 @@ static int vidi_enable_vblank(struct exynos_drm_manager *mgr)
static void vidi_disable_vblank(struct exynos_drm_manager *mgr)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
if (ctx->suspended)
return;
@@ -152,7 +164,7 @@ static void vidi_disable_vblank(struct exynos_drm_manager *mgr)
static void vidi_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
struct vidi_win_data *win_data;
int win;
unsigned long offset;
@@ -204,7 +216,7 @@ static void vidi_win_mode_set(struct exynos_drm_manager *mgr,
static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
struct vidi_win_data *win_data;
int win = zpos;
@@ -229,7 +241,7 @@ static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos)
static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
struct vidi_win_data *win_data;
int win = zpos;
@@ -247,7 +259,7 @@ static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos)
static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -271,7 +283,7 @@ static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable)
static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
DRM_DEBUG_KMS("%d\n", mode);
@@ -297,7 +309,7 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev)
{
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = manager_to_vidi(mgr);
struct exynos_drm_private *priv = drm_dev->dev_private;
mgr->drm_dev = ctx->drm_dev = drm_dev;
@@ -316,11 +328,6 @@ static struct exynos_drm_manager_ops vidi_manager_ops = {
.win_disable = vidi_win_disable,
};
-static struct exynos_drm_manager vidi_manager = {
- .type = EXYNOS_DISPLAY_TYPE_VIDI,
- .ops = &vidi_manager_ops,
-};
-
static void vidi_fake_vblank_handler(struct work_struct *work)
{
struct vidi_context *ctx = container_of(work, struct vidi_context,
@@ -349,9 +356,8 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
static int vidi_show_connection(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct vidi_context *ctx = dev_get_drvdata(dev);
int rc;
- struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
- struct vidi_context *ctx = mgr->ctx;
mutex_lock(&ctx->lock);
@@ -366,8 +372,7 @@ static int vidi_store_connection(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
- struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = dev_get_drvdata(dev);
int ret;
ret = kstrtoint(buf, 0, &ctx->connected);
@@ -420,7 +425,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
display = exynos_drm_get_display(encoder);
if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) {
- ctx = display->ctx;
+ ctx = display_to_vidi(display);
break;
}
}
@@ -530,7 +535,7 @@ static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
static int vidi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
- struct vidi_context *ctx = display->ctx;
+ struct vidi_context *ctx = display_to_vidi(display);
struct drm_connector *connector = &ctx->connector;
int ret;
@@ -556,27 +561,22 @@ static struct exynos_drm_display_ops vidi_display_ops = {
.create_connector = vidi_create_connector,
};
-static struct exynos_drm_display vidi_display = {
- .type = EXYNOS_DISPLAY_TYPE_VIDI,
- .ops = &vidi_display_ops,
-};
-
-static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+static int vidi_bind(struct device *dev, struct device *master, void *data)
{
- struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
struct drm_crtc *crtc = ctx->crtc;
int ret;
- vidi_mgr_initialize(mgr, drm_dev);
+ vidi_mgr_initialize(&ctx->manager, drm_dev);
- ret = exynos_drm_crtc_create(&vidi_manager);
+ ret = exynos_drm_crtc_create(&ctx->manager);
if (ret) {
DRM_ERROR("failed to create crtc.\n");
return ret;
}
- ret = exynos_drm_create_enc_conn(drm_dev, &vidi_display);
+ ret = exynos_drm_create_enc_conn(drm_dev, &ctx->display);
if (ret) {
crtc->funcs->destroy(crtc);
DRM_ERROR("failed to create encoder and connector.\n");
@@ -586,9 +586,18 @@ static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
return 0;
}
+
+static void vidi_unbind(struct device *dev, struct device *master, void *data)
+{
+}
+
+static const struct component_ops vidi_component_ops = {
+ .bind = vidi_bind,
+ .unbind = vidi_unbind,
+};
+
static int vidi_probe(struct platform_device *pdev)
{
- struct exynos_drm_subdrv *subdrv;
struct vidi_context *ctx;
int ret;
@@ -596,40 +605,54 @@ static int vidi_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
+ ctx->manager.type = EXYNOS_DISPLAY_TYPE_VIDI;
+ ctx->manager.ops = &vidi_manager_ops;
+ ctx->display.type = EXYNOS_DISPLAY_TYPE_VIDI;
+ ctx->display.ops = &vidi_display_ops;
ctx->default_win = 0;
+ ctx->pdev = pdev;
- INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
-
- vidi_manager.ctx = ctx;
- vidi_display.ctx = ctx;
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
+ ctx->manager.type);
+ if (ret)
+ return ret;
- mutex_init(&ctx->lock);
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+ ctx->display.type);
+ if (ret)
+ goto err_del_crtc_component;
- platform_set_drvdata(pdev, &vidi_manager);
+ INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
- subdrv = &ctx->subdrv;
- subdrv->dev = &pdev->dev;
- subdrv->probe = vidi_subdrv_probe;
+ mutex_init(&ctx->lock);
- ret = exynos_drm_subdrv_register(subdrv);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register drm vidi device\n");
- return ret;
- }
+ platform_set_drvdata(pdev, ctx);
ret = device_create_file(&pdev->dev, &dev_attr_connection);
if (ret < 0) {
- exynos_drm_subdrv_unregister(subdrv);
- DRM_INFO("failed to create connection sysfs.\n");
+ DRM_ERROR("failed to create connection sysfs.\n");
+ goto err_del_conn_component;
}
- return 0;
+ ret = component_add(&pdev->dev, &vidi_component_ops);
+ if (ret)
+ goto err_remove_file;
+
+ return ret;
+
+err_remove_file:
+ device_remove_file(&pdev->dev, &dev_attr_connection);
+err_del_conn_component:
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+err_del_crtc_component:
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+
+ return ret;
}
static int vidi_remove(struct platform_device *pdev)
{
- struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
- struct vidi_context *ctx = mgr->ctx;
+ struct vidi_context *ctx = platform_get_drvdata(pdev);
if (ctx->raw_edid != (struct edid *)fake_edid_info) {
kfree(ctx->raw_edid);
@@ -638,6 +661,10 @@ static int vidi_remove(struct platform_device *pdev)
return -EINVAL;
}
+ component_del(&pdev->dev, &vidi_component_ops);
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+
return 0;
}
@@ -668,12 +695,19 @@ int exynos_drm_probe_vidi(void)
return ret;
}
+static int exynos_drm_remove_vidi_device(struct device *dev, void *data)
+{
+ platform_device_unregister(to_platform_device(dev));
+
+ return 0;
+}
+
void exynos_drm_remove_vidi(void)
{
- struct vidi_context *ctx = vidi_manager.ctx;
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct platform_device *pdev = to_platform_device(subdrv->dev);
+ int ret = driver_for_each_device(&vidi_driver.driver, NULL, NULL,
+ exynos_drm_remove_vidi_device);
+ /* silence compiler warning */
+ (void)ret;
platform_driver_unregister(&vidi_driver);
- platform_device_unregister(pdev);
}
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 563a19e62eb2..5765a161abdd 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -49,7 +49,6 @@
#include <linux/gpio.h>
#include <media/s5p_hdmi.h>
-#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev))
#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
#define HOTPLUG_DEBOUNCE_MS 1100
@@ -182,6 +181,7 @@ struct hdmi_conf_regs {
};
struct hdmi_context {
+ struct exynos_drm_display display;
struct device *dev;
struct drm_device *drm_dev;
struct drm_connector connector;
@@ -213,6 +213,11 @@ struct hdmi_context {
enum hdmi_type type;
};
+static inline struct hdmi_context *display_to_hdmi(struct exynos_drm_display *d)
+{
+ return container_of(d, struct hdmi_context, display);
+}
+
struct hdmiphy_config {
int pixel_clock;
u8 conf[32];
@@ -1123,7 +1128,7 @@ static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
static int hdmi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
- struct hdmi_context *hdata = display->ctx;
+ struct hdmi_context *hdata = display_to_hdmi(display);
struct drm_connector *connector = &hdata->connector;
int ret;
@@ -2000,7 +2005,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
static void hdmi_mode_set(struct exynos_drm_display *display,
struct drm_display_mode *mode)
{
- struct hdmi_context *hdata = display->ctx;
+ struct hdmi_context *hdata = display_to_hdmi(display);
struct drm_display_mode *m = mode;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
@@ -2019,7 +2024,7 @@ static void hdmi_mode_set(struct exynos_drm_display *display,
static void hdmi_commit(struct exynos_drm_display *display)
{
- struct hdmi_context *hdata = display->ctx;
+ struct hdmi_context *hdata = display_to_hdmi(display);
mutex_lock(&hdata->hdmi_mutex);
if (!hdata->powered) {
@@ -2033,7 +2038,7 @@ static void hdmi_commit(struct exynos_drm_display *display)
static void hdmi_poweron(struct exynos_drm_display *display)
{
- struct hdmi_context *hdata = display->ctx;
+ struct hdmi_context *hdata = display_to_hdmi(display);
struct hdmi_resources *res = &hdata->res;
mutex_lock(&hdata->hdmi_mutex);
@@ -2064,7 +2069,7 @@ static void hdmi_poweron(struct exynos_drm_display *display)
static void hdmi_poweroff(struct exynos_drm_display *display)
{
- struct hdmi_context *hdata = display->ctx;
+ struct hdmi_context *hdata = display_to_hdmi(display);
struct hdmi_resources *res = &hdata->res;
mutex_lock(&hdata->hdmi_mutex);
@@ -2099,7 +2104,7 @@ out:
static void hdmi_dpms(struct exynos_drm_display *display, int mode)
{
- struct hdmi_context *hdata = display->ctx;
+ struct hdmi_context *hdata = display_to_hdmi(display);
struct drm_encoder *encoder = hdata->encoder;
struct drm_crtc *crtc = encoder->crtc;
struct drm_crtc_helper_funcs *funcs = NULL;
@@ -2143,11 +2148,6 @@ static struct exynos_drm_display_ops hdmi_display_ops = {
.commit = hdmi_commit,
};
-static struct exynos_drm_display hdmi_display = {
- .type = EXYNOS_DISPLAY_TYPE_HDMI,
- .ops = &hdmi_display_ops,
-};
-
static void hdmi_hotplug_work_func(struct work_struct *work)
{
struct hdmi_context *hdata;
@@ -2302,12 +2302,11 @@ MODULE_DEVICE_TABLE (of, hdmi_match_types);
static int hdmi_bind(struct device *dev, struct device *master, void *data)
{
struct drm_device *drm_dev = data;
- struct hdmi_context *hdata;
+ struct hdmi_context *hdata = dev_get_drvdata(dev);
- hdata = hdmi_display.ctx;
hdata->drm_dev = drm_dev;
- return exynos_drm_create_enc_conn(drm_dev, &hdmi_display);
+ return exynos_drm_create_enc_conn(drm_dev, &hdata->display);
}
static void hdmi_unbind(struct device *dev, struct device *master, void *data)
@@ -2349,31 +2348,28 @@ static int hdmi_probe(struct platform_device *pdev)
struct resource *res;
int ret;
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
- hdmi_display.type);
- if (ret)
- return ret;
-
- if (!dev->of_node) {
- ret = -ENODEV;
- goto err_del_component;
- }
+ if (!dev->of_node)
+ return -ENODEV;
pdata = drm_hdmi_dt_parse_pdata(dev);
- if (!pdata) {
- ret = -EINVAL;
- goto err_del_component;
- }
+ if (!pdata)
+ return -EINVAL;
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
- if (!hdata) {
- ret = -ENOMEM;
- goto err_del_component;
- }
+ if (!hdata)
+ return -ENOMEM;
+
+ hdata->display.type = EXYNOS_DISPLAY_TYPE_HDMI;
+ hdata->display.ops = &hdmi_display_ops;
+
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+ hdata->display.type);
+ if (ret)
+ return ret;
mutex_init(&hdata->hdmi_mutex);
- platform_set_drvdata(pdev, &hdmi_display);
+ platform_set_drvdata(pdev, hdata);
match = of_match_node(hdmi_match_types, dev->of_node);
if (!match) {
@@ -2485,7 +2481,6 @@ out_get_phy_port:
}
pm_runtime_enable(dev);
- hdmi_display.ctx = hdata;
ret = component_add(&pdev->dev, &hdmi_component_ops);
if (ret)
@@ -2510,7 +2505,7 @@ err_del_component:
static int hdmi_remove(struct platform_device *pdev)
{
- struct hdmi_context *hdata = hdmi_display.ctx;
+ struct hdmi_context *hdata = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&hdata->hotplug_work);
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index a41c84ee3a2d..820b76234ef4 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -40,8 +40,6 @@
#include "exynos_drm_iommu.h"
#include "exynos_mixer.h"
-#define get_mixer_manager(dev) platform_get_drvdata(to_platform_device(dev))
-
#define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0
@@ -86,6 +84,7 @@ enum mixer_version_id {
};
struct mixer_context {
+ struct exynos_drm_manager manager;
struct platform_device *pdev;
struct device *dev;
struct drm_device *drm_dev;
@@ -104,6 +103,11 @@ struct mixer_context {
atomic_t wait_vsync_event;
};
+static inline struct mixer_context *mgr_to_mixer(struct exynos_drm_manager *mgr)
+{
+ return container_of(mgr, struct mixer_context, manager);
+}
+
struct mixer_drv_data {
enum mixer_version_id version;
bool is_vp_enabled;
@@ -854,7 +858,7 @@ static int mixer_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev)
{
int ret;
- struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct exynos_drm_private *priv;
priv = drm_dev->dev_private;
@@ -885,7 +889,7 @@ static int mixer_initialize(struct exynos_drm_manager *mgr,
static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
{
- struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
if (is_drm_iommu_supported(mixer_ctx->drm_dev))
drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
@@ -893,7 +897,7 @@ static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
{
- struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &mixer_ctx->mixer_res;
if (!mixer_ctx->powered) {
@@ -910,7 +914,7 @@ static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
{
- struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &mixer_ctx->mixer_res;
/* disable vsync interrupt */
@@ -920,7 +924,7 @@ static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay)
{
- struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct hdmi_win_data *win_data;
int win;
@@ -971,7 +975,7 @@ static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
- struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
DRM_DEBUG_KMS("win: %d\n", win);
@@ -993,7 +997,7 @@ static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
- struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &mixer_ctx->mixer_res;
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
unsigned long flags;
@@ -1021,7 +1025,7 @@ static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
{
- struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
mutex_lock(&mixer_ctx->mixer_mutex);
if (!mixer_ctx->powered) {
@@ -1048,7 +1052,7 @@ static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
static void mixer_window_suspend(struct exynos_drm_manager *mgr)
{
- struct mixer_context *ctx = mgr->ctx;
+ struct mixer_context *ctx = mgr_to_mixer(mgr);
struct hdmi_win_data *win_data;
int i;
@@ -1062,7 +1066,7 @@ static void mixer_window_suspend(struct exynos_drm_manager *mgr)
static void mixer_window_resume(struct exynos_drm_manager *mgr)
{
- struct mixer_context *ctx = mgr->ctx;
+ struct mixer_context *ctx = mgr_to_mixer(mgr);
struct hdmi_win_data *win_data;
int i;
@@ -1077,7 +1081,7 @@ static void mixer_window_resume(struct exynos_drm_manager *mgr)
static void mixer_poweron(struct exynos_drm_manager *mgr)
{
- struct mixer_context *ctx = mgr->ctx;
+ struct mixer_context *ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &ctx->mixer_res;
mutex_lock(&ctx->mixer_mutex);
@@ -1111,7 +1115,7 @@ static void mixer_poweron(struct exynos_drm_manager *mgr)
static void mixer_poweroff(struct exynos_drm_manager *mgr)
{
- struct mixer_context *ctx = mgr->ctx;
+ struct mixer_context *ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &ctx->mixer_res;
mutex_lock(&ctx->mixer_mutex);
@@ -1187,11 +1191,6 @@ static struct exynos_drm_manager_ops mixer_manager_ops = {
.win_disable = mixer_win_disable,
};
-static struct exynos_drm_manager mixer_manager = {
- .type = EXYNOS_DISPLAY_TYPE_HDMI,
- .ops = &mixer_manager_ops,
-};
-
static struct mixer_drv_data exynos5420_mxr_drv_data = {
.version = MXR_VER_128_0_0_184,
.is_vp_enabled = 0,
@@ -1249,48 +1248,17 @@ MODULE_DEVICE_TABLE(of, mixer_match_types);
static int mixer_bind(struct device *dev, struct device *manager, void *data)
{
- struct platform_device *pdev = to_platform_device(dev);
+ struct mixer_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
- struct mixer_context *ctx;
- struct mixer_drv_data *drv;
int ret;
- dev_info(dev, "probe start\n");
-
- ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- DRM_ERROR("failed to alloc mixer context.\n");
- return -ENOMEM;
- }
-
- mutex_init(&ctx->mixer_mutex);
-
- if (dev->of_node) {
- const struct of_device_id *match;
- match = of_match_node(mixer_match_types, dev->of_node);
- drv = (struct mixer_drv_data *)match->data;
- } else {
- drv = (struct mixer_drv_data *)
- platform_get_device_id(pdev)->driver_data;
- }
-
- ctx->pdev = pdev;
- ctx->dev = dev;
- ctx->vp_enabled = drv->is_vp_enabled;
- ctx->has_sclk = drv->has_sclk;
- ctx->mxr_ver = drv->version;
- init_waitqueue_head(&ctx->wait_vsync_queue);
- atomic_set(&ctx->wait_vsync_event, 0);
-
- mixer_manager.ctx = ctx;
- ret = mixer_initialize(&mixer_manager, drm_dev);
+ ret = mixer_initialize(&ctx->manager, drm_dev);
if (ret)
return ret;
- platform_set_drvdata(pdev, &mixer_manager);
- ret = exynos_drm_crtc_create(&mixer_manager);
+ ret = exynos_drm_crtc_create(&ctx->manager);
if (ret) {
- mixer_mgr_remove(&mixer_manager);
+ mixer_mgr_remove(&ctx->manager);
return ret;
}
@@ -1301,11 +1269,9 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
static void mixer_unbind(struct device *dev, struct device *master, void *data)
{
- struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
+ struct mixer_context *ctx = dev_get_drvdata(dev);
- dev_info(dev, "remove successful\n");
-
- mixer_mgr_remove(mgr);
+ mixer_mgr_remove(&ctx->manager);
pm_runtime_disable(dev);
}
@@ -1317,22 +1283,62 @@ static const struct component_ops mixer_component_ops = {
static int mixer_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct mixer_drv_data *drv;
+ struct mixer_context *ctx;
int ret;
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ DRM_ERROR("failed to alloc mixer context.\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&ctx->mixer_mutex);
+
+ ctx->manager.type = EXYNOS_DISPLAY_TYPE_HDMI;
+ ctx->manager.ops = &mixer_manager_ops;
+
+ if (dev->of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_node(mixer_match_types, dev->of_node);
+ drv = (struct mixer_drv_data *)match->data;
+ } else {
+ drv = (struct mixer_drv_data *)
+ platform_get_device_id(pdev)->driver_data;
+ }
+
+ ctx->pdev = pdev;
+ ctx->dev = dev;
+ ctx->vp_enabled = drv->is_vp_enabled;
+ ctx->has_sclk = drv->has_sclk;
+ ctx->mxr_ver = drv->version;
+ init_waitqueue_head(&ctx->wait_vsync_queue);
+ atomic_set(&ctx->wait_vsync_event, 0);
+
+ platform_set_drvdata(pdev, ctx);
+
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
- mixer_manager.type);
+ ctx->manager.type);
if (ret)
return ret;
ret = component_add(&pdev->dev, &mixer_component_ops);
- if (ret)
+ if (ret) {
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+ return ret;
+ }
+
+ pm_runtime_enable(dev);
return ret;
}
static int mixer_remove(struct platform_device *pdev)
{
+ pm_runtime_disable(&pdev->dev);
+
component_del(&pdev->dev, &mixer_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index 4d341db462a2..22c7ed63a001 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -1,6 +1,12 @@
menu "I2C encoder or helper chips"
depends on DRM && DRM_KMS_HELPER && I2C
+config DRM_I2C_ADV7511
+ tristate "AV7511 encoder"
+ select REGMAP_I2C
+ help
+ Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders.
+
config DRM_I2C_CH7006
tristate "Chrontel ch7006 TV encoder"
default m if DRM_NOUVEAU
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
index 43aa33baebed..2c72eb584ab7 100644
--- a/drivers/gpu/drm/i2c/Makefile
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -1,5 +1,7 @@
ccflags-y := -Iinclude/drm
+obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
+
ch7006-y := ch7006_drv.o ch7006_mode.o
obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c
new file mode 100644
index 000000000000..faf1c0c5ab2e
--- /dev/null
+++ b/drivers/gpu/drm/i2c/adv7511.c
@@ -0,0 +1,1010 @@
+/*
+ * Analog Devices ADV7511 HDMI transmitter driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "adv7511.h"
+
+struct adv7511 {
+ struct i2c_client *i2c_main;
+ struct i2c_client *i2c_edid;
+
+ struct regmap *regmap;
+ struct regmap *packet_memory_regmap;
+ enum drm_connector_status status;
+ int dpms_mode;
+
+ unsigned int f_tmds;
+
+ unsigned int current_edid_segment;
+ uint8_t edid_buf[256];
+
+ wait_queue_head_t wq;
+ struct drm_encoder *encoder;
+
+ bool embedded_sync;
+ enum adv7511_sync_polarity vsync_polarity;
+ enum adv7511_sync_polarity hsync_polarity;
+ bool rgb;
+
+ struct edid *edid;
+
+ struct gpio_desc *gpio_pd;
+};
+
+static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder)
+{
+ return to_encoder_slave(encoder)->slave_priv;
+}
+
+/* ADI recommended values for proper operation. */
+static const struct reg_default adv7511_fixed_registers[] = {
+ { 0x98, 0x03 },
+ { 0x9a, 0xe0 },
+ { 0x9c, 0x30 },
+ { 0x9d, 0x61 },
+ { 0xa2, 0xa4 },
+ { 0xa3, 0xa4 },
+ { 0xe0, 0xd0 },
+ { 0xf9, 0x00 },
+ { 0x55, 0x02 },
+};
+
+/* -----------------------------------------------------------------------------
+ * Register access
+ */
+
+static const uint8_t adv7511_register_defaults[] = {
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00 */
+ 0x00, 0x00, 0x01, 0x0e, 0xbc, 0x18, 0x01, 0x13,
+ 0x25, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10 */
+ 0x46, 0x62, 0x04, 0xa8, 0x00, 0x00, 0x1c, 0x84,
+ 0x1c, 0xbf, 0x04, 0xa8, 0x1e, 0x70, 0x02, 0x1e, /* 20 */
+ 0x00, 0x00, 0x04, 0xa8, 0x08, 0x12, 0x1b, 0xac,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 */
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb0,
+ 0x00, 0x50, 0x90, 0x7e, 0x79, 0x70, 0x00, 0x00, /* 40 */
+ 0x00, 0xa8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x00, 0x00, /* 50 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 70 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 90 */
+ 0x0b, 0x02, 0x00, 0x18, 0x5a, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x80, 0x08, 0x04, 0x00, 0x00, /* a0 */
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c0 */
+ 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x01, 0x04,
+ 0x30, 0xff, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, /* d0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01,
+ 0x80, 0x75, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, /* e0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x11, 0x00, /* f0 */
+ 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static bool adv7511_register_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADV7511_REG_CHIP_REVISION:
+ case ADV7511_REG_SPDIF_FREQ:
+ case ADV7511_REG_CTS_AUTOMATIC1:
+ case ADV7511_REG_CTS_AUTOMATIC2:
+ case ADV7511_REG_VIC_DETECTED:
+ case ADV7511_REG_VIC_SEND:
+ case ADV7511_REG_AUX_VIC_DETECTED:
+ case ADV7511_REG_STATUS:
+ case ADV7511_REG_GC(1):
+ case ADV7511_REG_INT(0):
+ case ADV7511_REG_INT(1):
+ case ADV7511_REG_PLL_STATUS:
+ case ADV7511_REG_AN(0):
+ case ADV7511_REG_AN(1):
+ case ADV7511_REG_AN(2):
+ case ADV7511_REG_AN(3):
+ case ADV7511_REG_AN(4):
+ case ADV7511_REG_AN(5):
+ case ADV7511_REG_AN(6):
+ case ADV7511_REG_AN(7):
+ case ADV7511_REG_HDCP_STATUS:
+ case ADV7511_REG_BCAPS:
+ case ADV7511_REG_BKSV(0):
+ case ADV7511_REG_BKSV(1):
+ case ADV7511_REG_BKSV(2):
+ case ADV7511_REG_BKSV(3):
+ case ADV7511_REG_BKSV(4):
+ case ADV7511_REG_DDC_STATUS:
+ case ADV7511_REG_BSTATUS(0):
+ case ADV7511_REG_BSTATUS(1):
+ case ADV7511_REG_CHIP_ID_HIGH:
+ case ADV7511_REG_CHIP_ID_LOW:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config adv7511_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults_raw = adv7511_register_defaults,
+ .num_reg_defaults_raw = ARRAY_SIZE(adv7511_register_defaults),
+
+ .volatile_reg = adv7511_register_volatile,
+};
+
+/* -----------------------------------------------------------------------------
+ * Hardware configuration
+ */
+
+static void adv7511_set_colormap(struct adv7511 *adv7511, bool enable,
+ const uint16_t *coeff,
+ unsigned int scaling_factor)
+{
+ unsigned int i;
+
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(1),
+ ADV7511_CSC_UPDATE_MODE, ADV7511_CSC_UPDATE_MODE);
+
+ if (enable) {
+ for (i = 0; i < 12; ++i) {
+ regmap_update_bits(adv7511->regmap,
+ ADV7511_REG_CSC_UPPER(i),
+ 0x1f, coeff[i] >> 8);
+ regmap_write(adv7511->regmap,
+ ADV7511_REG_CSC_LOWER(i),
+ coeff[i] & 0xff);
+ }
+ }
+
+ if (enable)
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(0),
+ 0xe0, 0x80 | (scaling_factor << 5));
+ else
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(0),
+ 0x80, 0x00);
+
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(1),
+ ADV7511_CSC_UPDATE_MODE, 0);
+}
+
+static int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet)
+{
+ if (packet & 0xff)
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0,
+ packet, 0xff);
+
+ if (packet & 0xff00) {
+ packet >>= 8;
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
+ packet, 0xff);
+ }
+
+ return 0;
+}
+
+static int adv7511_packet_disable(struct adv7511 *adv7511, unsigned int packet)
+{
+ if (packet & 0xff)
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0,
+ packet, 0x00);
+
+ if (packet & 0xff00) {
+ packet >>= 8;
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
+ packet, 0x00);
+ }
+
+ return 0;
+}
+
+/* Coefficients for adv7511 color space conversion */
+static const uint16_t adv7511_csc_ycbcr_to_rgb[] = {
+ 0x0734, 0x04ad, 0x0000, 0x1c1b,
+ 0x1ddc, 0x04ad, 0x1f24, 0x0135,
+ 0x0000, 0x04ad, 0x087c, 0x1b77,
+};
+
+static void adv7511_set_config_csc(struct adv7511 *adv7511,
+ struct drm_connector *connector,
+ bool rgb)
+{
+ struct adv7511_video_config config;
+ bool output_format_422, output_format_ycbcr;
+ unsigned int mode;
+ uint8_t infoframe[17];
+
+ if (adv7511->edid)
+ config.hdmi_mode = drm_detect_hdmi_monitor(adv7511->edid);
+ else
+ config.hdmi_mode = false;
+
+ hdmi_avi_infoframe_init(&config.avi_infoframe);
+
+ config.avi_infoframe.scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
+
+ if (rgb) {
+ config.csc_enable = false;
+ config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB;
+ } else {
+ config.csc_scaling_factor = ADV7511_CSC_SCALING_4;
+ config.csc_coefficents = adv7511_csc_ycbcr_to_rgb;
+
+ if ((connector->display_info.color_formats &
+ DRM_COLOR_FORMAT_YCRCB422) &&
+ config.hdmi_mode) {
+ config.csc_enable = false;
+ config.avi_infoframe.colorspace =
+ HDMI_COLORSPACE_YUV422;
+ } else {
+ config.csc_enable = true;
+ config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB;
+ }
+ }
+
+ if (config.hdmi_mode) {
+ mode = ADV7511_HDMI_CFG_MODE_HDMI;
+
+ switch (config.avi_infoframe.colorspace) {
+ case HDMI_COLORSPACE_YUV444:
+ output_format_422 = false;
+ output_format_ycbcr = true;
+ break;
+ case HDMI_COLORSPACE_YUV422:
+ output_format_422 = true;
+ output_format_ycbcr = true;
+ break;
+ default:
+ output_format_422 = false;
+ output_format_ycbcr = false;
+ break;
+ }
+ } else {
+ mode = ADV7511_HDMI_CFG_MODE_DVI;
+ output_format_422 = false;
+ output_format_ycbcr = false;
+ }
+
+ adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
+
+ adv7511_set_colormap(adv7511, config.csc_enable,
+ config.csc_coefficents,
+ config.csc_scaling_factor);
+
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG1, 0x81,
+ (output_format_422 << 7) | output_format_ycbcr);
+
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_HDCP_HDMI_CFG,
+ ADV7511_HDMI_CFG_MODE_MASK, mode);
+
+ hdmi_avi_infoframe_pack(&config.avi_infoframe, infoframe,
+ sizeof(infoframe));
+
+ /* The AVI infoframe id is not configurable */
+ regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION,
+ infoframe + 1, sizeof(infoframe) - 1);
+
+ adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
+}
+
+static void adv7511_set_link_config(struct adv7511 *adv7511,
+ const struct adv7511_link_config *config)
+{
+ /*
+ * The input style values documented in the datasheet don't match the
+ * hardware register field values :-(
+ */
+ static const unsigned int input_styles[4] = { 0, 2, 1, 3 };
+
+ unsigned int clock_delay;
+ unsigned int color_depth;
+ unsigned int input_id;
+
+ clock_delay = (config->clock_delay + 1200) / 400;
+ color_depth = config->input_color_depth == 8 ? 3
+ : (config->input_color_depth == 10 ? 1 : 2);
+
+ /* TODO Support input ID 6 */
+ if (config->input_colorspace != HDMI_COLORSPACE_YUV422)
+ input_id = config->input_clock == ADV7511_INPUT_CLOCK_DDR
+ ? 5 : 0;
+ else if (config->input_clock == ADV7511_INPUT_CLOCK_DDR)
+ input_id = config->embedded_sync ? 8 : 7;
+ else if (config->input_clock == ADV7511_INPUT_CLOCK_2X)
+ input_id = config->embedded_sync ? 4 : 3;
+ else
+ input_id = config->embedded_sync ? 2 : 1;
+
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG, 0xf,
+ input_id);
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG1, 0x7e,
+ (color_depth << 4) |
+ (input_styles[config->input_style] << 2));
+ regmap_write(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG2,
+ config->input_justification << 3);
+ regmap_write(adv7511->regmap, ADV7511_REG_TIMING_GEN_SEQ,
+ config->sync_pulse << 2);
+
+ regmap_write(adv7511->regmap, 0xba, clock_delay << 5);
+
+ adv7511->embedded_sync = config->embedded_sync;
+ adv7511->hsync_polarity = config->hsync_polarity;
+ adv7511->vsync_polarity = config->vsync_polarity;
+ adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt and hotplug detection
+ */
+
+static bool adv7511_hpd(struct adv7511 *adv7511)
+{
+ unsigned int irq0;
+ int ret;
+
+ ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0);
+ if (ret < 0)
+ return false;
+
+ if (irq0 & ADV7511_INT0_HDP) {
+ regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
+ ADV7511_INT0_HDP);
+ return true;
+ }
+
+ return false;
+}
+
+static irqreturn_t adv7511_irq_handler(int irq, void *devid)
+{
+ struct adv7511 *adv7511 = devid;
+
+ if (adv7511_hpd(adv7511))
+ drm_helper_hpd_irq_event(adv7511->encoder->dev);
+
+ wake_up_all(&adv7511->wq);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int adv7511_is_interrupt_pending(struct adv7511 *adv7511,
+ unsigned int irq)
+{
+ unsigned int irq0, irq1;
+ unsigned int pending;
+ int ret;
+
+ ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0);
+ if (ret < 0)
+ return 0;
+ ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(1), &irq1);
+ if (ret < 0)
+ return 0;
+
+ pending = (irq1 << 8) | irq0;
+
+ return pending & irq;
+}
+
+static int adv7511_wait_for_interrupt(struct adv7511 *adv7511, int irq,
+ int timeout)
+{
+ unsigned int pending;
+ int ret;
+
+ if (adv7511->i2c_main->irq) {
+ ret = wait_event_interruptible_timeout(adv7511->wq,
+ adv7511_is_interrupt_pending(adv7511, irq),
+ msecs_to_jiffies(timeout));
+ if (ret <= 0)
+ return 0;
+ pending = adv7511_is_interrupt_pending(adv7511, irq);
+ } else {
+ if (timeout < 25)
+ timeout = 25;
+ do {
+ pending = adv7511_is_interrupt_pending(adv7511, irq);
+ if (pending)
+ break;
+ msleep(25);
+ timeout -= 25;
+ } while (timeout >= 25);
+ }
+
+ return pending;
+}
+
+/* -----------------------------------------------------------------------------
+ * EDID retrieval
+ */
+
+static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
+ size_t len)
+{
+ struct adv7511 *adv7511 = data;
+ struct i2c_msg xfer[2];
+ uint8_t offset;
+ unsigned int i;
+ int ret;
+
+ if (len > 128)
+ return -EINVAL;
+
+ if (adv7511->current_edid_segment != block / 2) {
+ unsigned int status;
+
+ ret = regmap_read(adv7511->regmap, ADV7511_REG_DDC_STATUS,
+ &status);
+ if (ret < 0)
+ return ret;
+
+ if (status != 2) {
+ regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT,
+ block);
+ ret = adv7511_wait_for_interrupt(adv7511,
+ ADV7511_INT0_EDID_READY |
+ ADV7511_INT1_DDC_ERROR, 200);
+
+ if (!(ret & ADV7511_INT0_EDID_READY))
+ return -EIO;
+ }
+
+ regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
+ ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR);
+
+ /* Break this apart, hopefully more I2C controllers will
+ * support 64 byte transfers than 256 byte transfers
+ */
+
+ xfer[0].addr = adv7511->i2c_edid->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = &offset;
+ xfer[1].addr = adv7511->i2c_edid->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 64;
+ xfer[1].buf = adv7511->edid_buf;
+
+ offset = 0;
+
+ for (i = 0; i < 4; ++i) {
+ ret = i2c_transfer(adv7511->i2c_edid->adapter, xfer,
+ ARRAY_SIZE(xfer));
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+
+ xfer[1].buf += 64;
+ offset += 64;
+ }
+
+ adv7511->current_edid_segment = block / 2;
+ }
+
+ if (block % 2 == 0)
+ memcpy(buf, adv7511->edid_buf, len);
+ else
+ memcpy(buf, adv7511->edid_buf + 128, len);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder operations
+ */
+
+static int adv7511_get_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
+ struct edid *edid;
+ unsigned int count;
+
+ /* Reading the EDID only works if the device is powered */
+ if (adv7511->dpms_mode != DRM_MODE_DPMS_ON) {
+ regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
+ ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR);
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
+ ADV7511_POWER_POWER_DOWN, 0);
+ adv7511->current_edid_segment = -1;
+ }
+
+ edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511);
+
+ if (adv7511->dpms_mode != DRM_MODE_DPMS_ON)
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
+ ADV7511_POWER_POWER_DOWN,
+ ADV7511_POWER_POWER_DOWN);
+
+ kfree(adv7511->edid);
+ adv7511->edid = edid;
+ if (!edid)
+ return 0;
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ count = drm_add_edid_modes(connector, edid);
+
+ adv7511_set_config_csc(adv7511, connector, adv7511->rgb);
+
+ return count;
+}
+
+static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ adv7511->current_edid_segment = -1;
+
+ regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
+ ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR);
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
+ ADV7511_POWER_POWER_DOWN, 0);
+ /*
+ * Per spec it is allowed to pulse the HDP signal to indicate
+ * that the EDID information has changed. Some monitors do this
+ * when they wakeup from standby or are enabled. When the HDP
+ * goes low the adv7511 is reset and the outputs are disabled
+ * which might cause the monitor to go to standby again. To
+ * avoid this we ignore the HDP pin for the first few seconds
+ * after enabeling the output.
+ */
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+ ADV7511_REG_POWER2_HDP_SRC_MASK,
+ ADV7511_REG_POWER2_HDP_SRC_NONE);
+ /* Most of the registers are reset during power down or
+ * when HPD is low
+ */
+ regcache_sync(adv7511->regmap);
+ break;
+ default:
+ /* TODO: setup additional power down modes */
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
+ ADV7511_POWER_POWER_DOWN,
+ ADV7511_POWER_POWER_DOWN);
+ regcache_mark_dirty(adv7511->regmap);
+ break;
+ }
+
+ adv7511->dpms_mode = mode;
+}
+
+static enum drm_connector_status
+adv7511_encoder_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
+ enum drm_connector_status status;
+ unsigned int val;
+ bool hpd;
+ int ret;
+
+ ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val);
+ if (ret < 0)
+ return connector_status_disconnected;
+
+ if (val & ADV7511_STATUS_HPD)
+ status = connector_status_connected;
+ else
+ status = connector_status_disconnected;
+
+ hpd = adv7511_hpd(adv7511);
+
+ /* The chip resets itself when the cable is disconnected, so in case
+ * there is a pending HPD interrupt and the cable is connected there was
+ * at least one transition from disconnected to connected and the chip
+ * has to be reinitialized. */
+ if (status == connector_status_connected && hpd &&
+ adv7511->dpms_mode == DRM_MODE_DPMS_ON) {
+ regcache_mark_dirty(adv7511->regmap);
+ adv7511_encoder_dpms(encoder, adv7511->dpms_mode);
+ adv7511_get_modes(encoder, connector);
+ if (adv7511->status == connector_status_connected)
+ status = connector_status_disconnected;
+ } else {
+ /* Renable HDP sensing */
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+ ADV7511_REG_POWER2_HDP_SRC_MASK,
+ ADV7511_REG_POWER2_HDP_SRC_BOTH);
+ }
+
+ adv7511->status = status;
+ return status;
+}
+
+static int adv7511_encoder_mode_valid(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 165000)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ return MODE_NO_INTERLACE;
+
+ return MODE_OK;
+}
+
+static void adv7511_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
+ unsigned int low_refresh_rate;
+ unsigned int hsync_polarity = 0;
+ unsigned int vsync_polarity = 0;
+
+ if (adv7511->embedded_sync) {
+ unsigned int hsync_offset, hsync_len;
+ unsigned int vsync_offset, vsync_len;
+
+ hsync_offset = adj_mode->crtc_hsync_start -
+ adj_mode->crtc_hdisplay;
+ vsync_offset = adj_mode->crtc_vsync_start -
+ adj_mode->crtc_vdisplay;
+ hsync_len = adj_mode->crtc_hsync_end -
+ adj_mode->crtc_hsync_start;
+ vsync_len = adj_mode->crtc_vsync_end -
+ adj_mode->crtc_vsync_start;
+
+ /* The hardware vsync generator has a off-by-one bug */
+ vsync_offset += 1;
+
+ regmap_write(adv7511->regmap, ADV7511_REG_HSYNC_PLACEMENT_MSB,
+ ((hsync_offset >> 10) & 0x7) << 5);
+ regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(0),
+ (hsync_offset >> 2) & 0xff);
+ regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(1),
+ ((hsync_offset & 0x3) << 6) |
+ ((hsync_len >> 4) & 0x3f));
+ regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(2),
+ ((hsync_len & 0xf) << 4) |
+ ((vsync_offset >> 6) & 0xf));
+ regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(3),
+ ((vsync_offset & 0x3f) << 2) |
+ ((vsync_len >> 8) & 0x3));
+ regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(4),
+ vsync_len & 0xff);
+
+ hsync_polarity = !(adj_mode->flags & DRM_MODE_FLAG_PHSYNC);
+ vsync_polarity = !(adj_mode->flags & DRM_MODE_FLAG_PVSYNC);
+ } else {
+ enum adv7511_sync_polarity mode_hsync_polarity;
+ enum adv7511_sync_polarity mode_vsync_polarity;
+
+ /**
+ * If the input signal is always low or always high we want to
+ * invert or let it passthrough depending on the polarity of the
+ * current mode.
+ **/
+ if (adj_mode->flags & DRM_MODE_FLAG_NHSYNC)
+ mode_hsync_polarity = ADV7511_SYNC_POLARITY_LOW;
+ else
+ mode_hsync_polarity = ADV7511_SYNC_POLARITY_HIGH;
+
+ if (adj_mode->flags & DRM_MODE_FLAG_NVSYNC)
+ mode_vsync_polarity = ADV7511_SYNC_POLARITY_LOW;
+ else
+ mode_vsync_polarity = ADV7511_SYNC_POLARITY_HIGH;
+
+ if (adv7511->hsync_polarity != mode_hsync_polarity &&
+ adv7511->hsync_polarity !=
+ ADV7511_SYNC_POLARITY_PASSTHROUGH)
+ hsync_polarity = 1;
+
+ if (adv7511->vsync_polarity != mode_vsync_polarity &&
+ adv7511->vsync_polarity !=
+ ADV7511_SYNC_POLARITY_PASSTHROUGH)
+ vsync_polarity = 1;
+ }
+
+ if (mode->vrefresh <= 24000)
+ low_refresh_rate = ADV7511_LOW_REFRESH_RATE_24HZ;
+ else if (mode->vrefresh <= 25000)
+ low_refresh_rate = ADV7511_LOW_REFRESH_RATE_25HZ;
+ else if (mode->vrefresh <= 30000)
+ low_refresh_rate = ADV7511_LOW_REFRESH_RATE_30HZ;
+ else
+ low_refresh_rate = ADV7511_LOW_REFRESH_RATE_NONE;
+
+ regmap_update_bits(adv7511->regmap, 0xfb,
+ 0x6, low_refresh_rate << 1);
+ regmap_update_bits(adv7511->regmap, 0x17,
+ 0x60, (vsync_polarity << 6) | (hsync_polarity << 5));
+
+ /*
+ * TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is
+ * supposed to give better results.
+ */
+
+ adv7511->f_tmds = mode->clock;
+}
+
+static struct drm_encoder_slave_funcs adv7511_encoder_funcs = {
+ .dpms = adv7511_encoder_dpms,
+ .mode_valid = adv7511_encoder_mode_valid,
+ .mode_set = adv7511_encoder_mode_set,
+ .detect = adv7511_encoder_detect,
+ .get_modes = adv7511_get_modes,
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe & remove
+ */
+
+static int adv7511_parse_dt(struct device_node *np,
+ struct adv7511_link_config *config)
+{
+ const char *str;
+ int ret;
+
+ memset(config, 0, sizeof(*config));
+
+ of_property_read_u32(np, "adi,input-depth", &config->input_color_depth);
+ if (config->input_color_depth != 8 && config->input_color_depth != 10 &&
+ config->input_color_depth != 12)
+ return -EINVAL;
+
+ ret = of_property_read_string(np, "adi,input-colorspace", &str);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(str, "rgb"))
+ config->input_colorspace = HDMI_COLORSPACE_RGB;
+ else if (!strcmp(str, "yuv422"))
+ config->input_colorspace = HDMI_COLORSPACE_YUV422;
+ else if (!strcmp(str, "yuv444"))
+ config->input_colorspace = HDMI_COLORSPACE_YUV444;
+ else
+ return -EINVAL;
+
+ ret = of_property_read_string(np, "adi,input-clock", &str);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(str, "1x"))
+ config->input_clock = ADV7511_INPUT_CLOCK_1X;
+ else if (!strcmp(str, "2x"))
+ config->input_clock = ADV7511_INPUT_CLOCK_2X;
+ else if (!strcmp(str, "ddr"))
+ config->input_clock = ADV7511_INPUT_CLOCK_DDR;
+ else
+ return -EINVAL;
+
+ if (config->input_colorspace == HDMI_COLORSPACE_YUV422 ||
+ config->input_clock != ADV7511_INPUT_CLOCK_1X) {
+ ret = of_property_read_u32(np, "adi,input-style",
+ &config->input_style);
+ if (ret)
+ return ret;
+
+ if (config->input_style < 1 || config->input_style > 3)
+ return -EINVAL;
+
+ ret = of_property_read_string(np, "adi,input-justification",
+ &str);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(str, "left"))
+ config->input_justification =
+ ADV7511_INPUT_JUSTIFICATION_LEFT;
+ else if (!strcmp(str, "evenly"))
+ config->input_justification =
+ ADV7511_INPUT_JUSTIFICATION_EVENLY;
+ else if (!strcmp(str, "right"))
+ config->input_justification =
+ ADV7511_INPUT_JUSTIFICATION_RIGHT;
+ else
+ return -EINVAL;
+
+ } else {
+ config->input_style = 1;
+ config->input_justification = ADV7511_INPUT_JUSTIFICATION_LEFT;
+ }
+
+ of_property_read_u32(np, "adi,clock-delay", &config->clock_delay);
+ if (config->clock_delay < -1200 || config->clock_delay > 1600)
+ return -EINVAL;
+
+ config->embedded_sync = of_property_read_bool(np, "adi,embedded-sync");
+
+ /* Hardcode the sync pulse configurations for now. */
+ config->sync_pulse = ADV7511_INPUT_SYNC_PULSE_NONE;
+ config->vsync_polarity = ADV7511_SYNC_POLARITY_PASSTHROUGH;
+ config->hsync_polarity = ADV7511_SYNC_POLARITY_PASSTHROUGH;
+
+ return 0;
+}
+
+static const int edid_i2c_addr = 0x7e;
+static const int packet_i2c_addr = 0x70;
+static const int cec_i2c_addr = 0x78;
+
+static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+ struct adv7511_link_config link_config;
+ struct adv7511 *adv7511;
+ struct device *dev = &i2c->dev;
+ unsigned int val;
+ int ret;
+
+ if (!dev->of_node)
+ return -EINVAL;
+
+ adv7511 = devm_kzalloc(dev, sizeof(*adv7511), GFP_KERNEL);
+ if (!adv7511)
+ return -ENOMEM;
+
+ adv7511->dpms_mode = DRM_MODE_DPMS_OFF;
+ adv7511->status = connector_status_disconnected;
+
+ ret = adv7511_parse_dt(dev->of_node, &link_config);
+ if (ret)
+ return ret;
+
+ /*
+ * The power down GPIO is optional. If present, toggle it from active to
+ * inactive to wake up the encoder.
+ */
+ adv7511->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH);
+ if (IS_ERR(adv7511->gpio_pd))
+ return PTR_ERR(adv7511->gpio_pd);
+
+ if (adv7511->gpio_pd) {
+ mdelay(5);
+ gpiod_set_value_cansleep(adv7511->gpio_pd, 0);
+ }
+
+ adv7511->regmap = devm_regmap_init_i2c(i2c, &adv7511_regmap_config);
+ if (IS_ERR(adv7511->regmap))
+ return PTR_ERR(adv7511->regmap);
+
+ ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val);
+ if (ret)
+ return ret;
+ dev_dbg(dev, "Rev. %d\n", val);
+
+ ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers,
+ ARRAY_SIZE(adv7511_fixed_registers));
+ if (ret)
+ return ret;
+
+ regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr);
+ regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR,
+ packet_i2c_addr);
+ regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR, cec_i2c_addr);
+ adv7511_packet_disable(adv7511, 0xffff);
+
+ adv7511->i2c_main = i2c;
+ adv7511->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1);
+ if (!adv7511->i2c_edid)
+ return -ENOMEM;
+
+ if (i2c->irq) {
+ init_waitqueue_head(&adv7511->wq);
+
+ ret = devm_request_threaded_irq(dev, i2c->irq, NULL,
+ adv7511_irq_handler,
+ IRQF_ONESHOT, dev_name(dev),
+ adv7511);
+ if (ret)
+ goto err_i2c_unregister_device;
+ }
+
+ /* CEC is unused for now */
+ regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,
+ ADV7511_CEC_CTRL_POWER_DOWN);
+
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
+ ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN);
+
+ adv7511->current_edid_segment = -1;
+
+ i2c_set_clientdata(i2c, adv7511);
+
+ adv7511_set_link_config(adv7511, &link_config);
+
+ return 0;
+
+err_i2c_unregister_device:
+ i2c_unregister_device(adv7511->i2c_edid);
+
+ return ret;
+}
+
+static int adv7511_remove(struct i2c_client *i2c)
+{
+ struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
+
+ i2c_unregister_device(adv7511->i2c_edid);
+
+ kfree(adv7511->edid);
+
+ return 0;
+}
+
+static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev,
+ struct drm_encoder_slave *encoder)
+{
+
+ struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
+
+ encoder->slave_priv = adv7511;
+ encoder->slave_funcs = &adv7511_encoder_funcs;
+
+ adv7511->encoder = &encoder->base;
+
+ return 0;
+}
+
+static const struct i2c_device_id adv7511_i2c_ids[] = {
+ { "adv7511", 0 },
+ { "adv7511w", 0 },
+ { "adv7513", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids);
+
+static const struct of_device_id adv7511_of_ids[] = {
+ { .compatible = "adi,adv7511", },
+ { .compatible = "adi,adv7511w", },
+ { .compatible = "adi,adv7513", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adv7511_of_ids);
+
+static struct drm_i2c_encoder_driver adv7511_driver = {
+ .i2c_driver = {
+ .driver = {
+ .name = "adv7511",
+ .of_match_table = adv7511_of_ids,
+ },
+ .id_table = adv7511_i2c_ids,
+ .probe = adv7511_probe,
+ .remove = adv7511_remove,
+ },
+
+ .encoder_init = adv7511_encoder_init,
+};
+
+static int __init adv7511_init(void)
+{
+ return drm_i2c_encoder_register(THIS_MODULE, &adv7511_driver);
+}
+module_init(adv7511_init);
+
+static void __exit adv7511_exit(void)
+{
+ drm_i2c_encoder_unregister(&adv7511_driver);
+}
+module_exit(adv7511_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADV7511 HDMI transmitter driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/i2c/adv7511.h b/drivers/gpu/drm/i2c/adv7511.h
new file mode 100644
index 000000000000..6599ed538426
--- /dev/null
+++ b/drivers/gpu/drm/i2c/adv7511.h
@@ -0,0 +1,289 @@
+/*
+ * Analog Devices ADV7511 HDMI transmitter driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef __DRM_I2C_ADV7511_H__
+#define __DRM_I2C_ADV7511_H__
+
+#include <linux/hdmi.h>
+
+#define ADV7511_REG_CHIP_REVISION 0x00
+#define ADV7511_REG_N0 0x01
+#define ADV7511_REG_N1 0x02
+#define ADV7511_REG_N2 0x03
+#define ADV7511_REG_SPDIF_FREQ 0x04
+#define ADV7511_REG_CTS_AUTOMATIC1 0x05
+#define ADV7511_REG_CTS_AUTOMATIC2 0x06
+#define ADV7511_REG_CTS_MANUAL0 0x07
+#define ADV7511_REG_CTS_MANUAL1 0x08
+#define ADV7511_REG_CTS_MANUAL2 0x09
+#define ADV7511_REG_AUDIO_SOURCE 0x0a
+#define ADV7511_REG_AUDIO_CONFIG 0x0b
+#define ADV7511_REG_I2S_CONFIG 0x0c
+#define ADV7511_REG_I2S_WIDTH 0x0d
+#define ADV7511_REG_AUDIO_SUB_SRC0 0x0e
+#define ADV7511_REG_AUDIO_SUB_SRC1 0x0f
+#define ADV7511_REG_AUDIO_SUB_SRC2 0x10
+#define ADV7511_REG_AUDIO_SUB_SRC3 0x11
+#define ADV7511_REG_AUDIO_CFG1 0x12
+#define ADV7511_REG_AUDIO_CFG2 0x13
+#define ADV7511_REG_AUDIO_CFG3 0x14
+#define ADV7511_REG_I2C_FREQ_ID_CFG 0x15
+#define ADV7511_REG_VIDEO_INPUT_CFG1 0x16
+#define ADV7511_REG_CSC_UPPER(x) (0x18 + (x) * 2)
+#define ADV7511_REG_CSC_LOWER(x) (0x19 + (x) * 2)
+#define ADV7511_REG_SYNC_DECODER(x) (0x30 + (x))
+#define ADV7511_REG_DE_GENERATOR (0x35 + (x))
+#define ADV7511_REG_PIXEL_REPETITION 0x3b
+#define ADV7511_REG_VIC_MANUAL 0x3c
+#define ADV7511_REG_VIC_SEND 0x3d
+#define ADV7511_REG_VIC_DETECTED 0x3e
+#define ADV7511_REG_AUX_VIC_DETECTED 0x3f
+#define ADV7511_REG_PACKET_ENABLE0 0x40
+#define ADV7511_REG_POWER 0x41
+#define ADV7511_REG_STATUS 0x42
+#define ADV7511_REG_EDID_I2C_ADDR 0x43
+#define ADV7511_REG_PACKET_ENABLE1 0x44
+#define ADV7511_REG_PACKET_I2C_ADDR 0x45
+#define ADV7511_REG_DSD_ENABLE 0x46
+#define ADV7511_REG_VIDEO_INPUT_CFG2 0x48
+#define ADV7511_REG_INFOFRAME_UPDATE 0x4a
+#define ADV7511_REG_GC(x) (0x4b + (x)) /* 0x4b - 0x51 */
+#define ADV7511_REG_AVI_INFOFRAME_VERSION 0x52
+#define ADV7511_REG_AVI_INFOFRAME_LENGTH 0x53
+#define ADV7511_REG_AVI_INFOFRAME_CHECKSUM 0x54
+#define ADV7511_REG_AVI_INFOFRAME(x) (0x55 + (x)) /* 0x55 - 0x6f */
+#define ADV7511_REG_AUDIO_INFOFRAME_VERSION 0x70
+#define ADV7511_REG_AUDIO_INFOFRAME_LENGTH 0x71
+#define ADV7511_REG_AUDIO_INFOFRAME_CHECKSUM 0x72
+#define ADV7511_REG_AUDIO_INFOFRAME(x) (0x73 + (x)) /* 0x73 - 0x7c */
+#define ADV7511_REG_INT_ENABLE(x) (0x94 + (x))
+#define ADV7511_REG_INT(x) (0x96 + (x))
+#define ADV7511_REG_INPUT_CLK_DIV 0x9d
+#define ADV7511_REG_PLL_STATUS 0x9e
+#define ADV7511_REG_HDMI_POWER 0xa1
+#define ADV7511_REG_HDCP_HDMI_CFG 0xaf
+#define ADV7511_REG_AN(x) (0xb0 + (x)) /* 0xb0 - 0xb7 */
+#define ADV7511_REG_HDCP_STATUS 0xb8
+#define ADV7511_REG_BCAPS 0xbe
+#define ADV7511_REG_BKSV(x) (0xc0 + (x)) /* 0xc0 - 0xc3 */
+#define ADV7511_REG_EDID_SEGMENT 0xc4
+#define ADV7511_REG_DDC_STATUS 0xc8
+#define ADV7511_REG_EDID_READ_CTRL 0xc9
+#define ADV7511_REG_BSTATUS(x) (0xca + (x)) /* 0xca - 0xcb */
+#define ADV7511_REG_TIMING_GEN_SEQ 0xd0
+#define ADV7511_REG_POWER2 0xd6
+#define ADV7511_REG_HSYNC_PLACEMENT_MSB 0xfa
+
+#define ADV7511_REG_SYNC_ADJUSTMENT(x) (0xd7 + (x)) /* 0xd7 - 0xdc */
+#define ADV7511_REG_TMDS_CLOCK_INV 0xde
+#define ADV7511_REG_ARC_CTRL 0xdf
+#define ADV7511_REG_CEC_I2C_ADDR 0xe1
+#define ADV7511_REG_CEC_CTRL 0xe2
+#define ADV7511_REG_CHIP_ID_HIGH 0xf5
+#define ADV7511_REG_CHIP_ID_LOW 0xf6
+
+#define ADV7511_CSC_ENABLE BIT(7)
+#define ADV7511_CSC_UPDATE_MODE BIT(5)
+
+#define ADV7511_INT0_HDP BIT(7)
+#define ADV7511_INT0_VSYNC BIT(5)
+#define ADV7511_INT0_AUDIO_FIFO_FULL BIT(4)
+#define ADV7511_INT0_EDID_READY BIT(2)
+#define ADV7511_INT0_HDCP_AUTHENTICATED BIT(1)
+
+#define ADV7511_INT1_DDC_ERROR BIT(7)
+#define ADV7511_INT1_BKSV BIT(6)
+#define ADV7511_INT1_CEC_TX_READY BIT(5)
+#define ADV7511_INT1_CEC_TX_ARBIT_LOST BIT(4)
+#define ADV7511_INT1_CEC_TX_RETRY_TIMEOUT BIT(3)
+#define ADV7511_INT1_CEC_RX_READY3 BIT(2)
+#define ADV7511_INT1_CEC_RX_READY2 BIT(1)
+#define ADV7511_INT1_CEC_RX_READY1 BIT(0)
+
+#define ADV7511_ARC_CTRL_POWER_DOWN BIT(0)
+
+#define ADV7511_CEC_CTRL_POWER_DOWN BIT(0)
+
+#define ADV7511_POWER_POWER_DOWN BIT(6)
+
+#define ADV7511_HDMI_CFG_MODE_MASK 0x2
+#define ADV7511_HDMI_CFG_MODE_DVI 0x0
+#define ADV7511_HDMI_CFG_MODE_HDMI 0x2
+
+#define ADV7511_AUDIO_SELECT_I2C 0x0
+#define ADV7511_AUDIO_SELECT_SPDIF 0x1
+#define ADV7511_AUDIO_SELECT_DSD 0x2
+#define ADV7511_AUDIO_SELECT_HBR 0x3
+#define ADV7511_AUDIO_SELECT_DST 0x4
+
+#define ADV7511_I2S_SAMPLE_LEN_16 0x2
+#define ADV7511_I2S_SAMPLE_LEN_20 0x3
+#define ADV7511_I2S_SAMPLE_LEN_18 0x4
+#define ADV7511_I2S_SAMPLE_LEN_22 0x5
+#define ADV7511_I2S_SAMPLE_LEN_19 0x8
+#define ADV7511_I2S_SAMPLE_LEN_23 0x9
+#define ADV7511_I2S_SAMPLE_LEN_24 0xb
+#define ADV7511_I2S_SAMPLE_LEN_17 0xc
+#define ADV7511_I2S_SAMPLE_LEN_21 0xd
+
+#define ADV7511_SAMPLE_FREQ_44100 0x0
+#define ADV7511_SAMPLE_FREQ_48000 0x2
+#define ADV7511_SAMPLE_FREQ_32000 0x3
+#define ADV7511_SAMPLE_FREQ_88200 0x8
+#define ADV7511_SAMPLE_FREQ_96000 0xa
+#define ADV7511_SAMPLE_FREQ_176400 0xc
+#define ADV7511_SAMPLE_FREQ_192000 0xe
+
+#define ADV7511_STATUS_POWER_DOWN_POLARITY BIT(7)
+#define ADV7511_STATUS_HPD BIT(6)
+#define ADV7511_STATUS_MONITOR_SENSE BIT(5)
+#define ADV7511_STATUS_I2S_32BIT_MODE BIT(3)
+
+#define ADV7511_PACKET_ENABLE_N_CTS BIT(8+6)
+#define ADV7511_PACKET_ENABLE_AUDIO_SAMPLE BIT(8+5)
+#define ADV7511_PACKET_ENABLE_AVI_INFOFRAME BIT(8+4)
+#define ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME BIT(8+3)
+#define ADV7511_PACKET_ENABLE_GC BIT(7)
+#define ADV7511_PACKET_ENABLE_SPD BIT(6)
+#define ADV7511_PACKET_ENABLE_MPEG BIT(5)
+#define ADV7511_PACKET_ENABLE_ACP BIT(4)
+#define ADV7511_PACKET_ENABLE_ISRC BIT(3)
+#define ADV7511_PACKET_ENABLE_GM BIT(2)
+#define ADV7511_PACKET_ENABLE_SPARE2 BIT(1)
+#define ADV7511_PACKET_ENABLE_SPARE1 BIT(0)
+
+#define ADV7511_REG_POWER2_HDP_SRC_MASK 0xc0
+#define ADV7511_REG_POWER2_HDP_SRC_BOTH 0x00
+#define ADV7511_REG_POWER2_HDP_SRC_HDP 0x40
+#define ADV7511_REG_POWER2_HDP_SRC_CEC 0x80
+#define ADV7511_REG_POWER2_HDP_SRC_NONE 0xc0
+#define ADV7511_REG_POWER2_TDMS_ENABLE BIT(4)
+#define ADV7511_REG_POWER2_GATE_INPUT_CLK BIT(0)
+
+#define ADV7511_LOW_REFRESH_RATE_NONE 0x0
+#define ADV7511_LOW_REFRESH_RATE_24HZ 0x1
+#define ADV7511_LOW_REFRESH_RATE_25HZ 0x2
+#define ADV7511_LOW_REFRESH_RATE_30HZ 0x3
+
+#define ADV7511_AUDIO_CFG3_LEN_MASK 0x0f
+#define ADV7511_I2C_FREQ_ID_CFG_RATE_MASK 0xf0
+
+#define ADV7511_AUDIO_SOURCE_I2S 0
+#define ADV7511_AUDIO_SOURCE_SPDIF 1
+
+#define ADV7511_I2S_FORMAT_I2S 0
+#define ADV7511_I2S_FORMAT_RIGHT_J 1
+#define ADV7511_I2S_FORMAT_LEFT_J 2
+
+#define ADV7511_PACKET(p, x) ((p) * 0x20 + (x))
+#define ADV7511_PACKET_SDP(x) ADV7511_PACKET(0, x)
+#define ADV7511_PACKET_MPEG(x) ADV7511_PACKET(1, x)
+#define ADV7511_PACKET_ACP(x) ADV7511_PACKET(2, x)
+#define ADV7511_PACKET_ISRC1(x) ADV7511_PACKET(3, x)
+#define ADV7511_PACKET_ISRC2(x) ADV7511_PACKET(4, x)
+#define ADV7511_PACKET_GM(x) ADV7511_PACKET(5, x)
+#define ADV7511_PACKET_SPARE(x) ADV7511_PACKET(6, x)
+
+enum adv7511_input_clock {
+ ADV7511_INPUT_CLOCK_1X,
+ ADV7511_INPUT_CLOCK_2X,
+ ADV7511_INPUT_CLOCK_DDR,
+};
+
+enum adv7511_input_justification {
+ ADV7511_INPUT_JUSTIFICATION_EVENLY = 0,
+ ADV7511_INPUT_JUSTIFICATION_RIGHT = 1,
+ ADV7511_INPUT_JUSTIFICATION_LEFT = 2,
+};
+
+enum adv7511_input_sync_pulse {
+ ADV7511_INPUT_SYNC_PULSE_DE = 0,
+ ADV7511_INPUT_SYNC_PULSE_HSYNC = 1,
+ ADV7511_INPUT_SYNC_PULSE_VSYNC = 2,
+ ADV7511_INPUT_SYNC_PULSE_NONE = 3,
+};
+
+/**
+ * enum adv7511_sync_polarity - Polarity for the input sync signals
+ * @ADV7511_SYNC_POLARITY_PASSTHROUGH: Sync polarity matches that of
+ * the currently configured mode.
+ * @ADV7511_SYNC_POLARITY_LOW: Sync polarity is low
+ * @ADV7511_SYNC_POLARITY_HIGH: Sync polarity is high
+ *
+ * If the polarity is set to either LOW or HIGH the driver will configure the
+ * ADV7511 to internally invert the sync signal if required to match the sync
+ * polarity setting for the currently selected output mode.
+ *
+ * If the polarity is set to PASSTHROUGH, the ADV7511 will route the signal
+ * unchanged. This is used when the upstream graphics core already generates
+ * the sync signals with the correct polarity.
+ */
+enum adv7511_sync_polarity {
+ ADV7511_SYNC_POLARITY_PASSTHROUGH,
+ ADV7511_SYNC_POLARITY_LOW,
+ ADV7511_SYNC_POLARITY_HIGH,
+};
+
+/**
+ * struct adv7511_link_config - Describes adv7511 hardware configuration
+ * @input_color_depth: Number of bits per color component (8, 10 or 12)
+ * @input_colorspace: The input colorspace (RGB, YUV444, YUV422)
+ * @input_clock: The input video clock style (1x, 2x, DDR)
+ * @input_style: The input component arrangement variant
+ * @input_justification: Video input format bit justification
+ * @clock_delay: Clock delay for the input clock (in ps)
+ * @embedded_sync: Video input uses BT.656-style embedded sync
+ * @sync_pulse: Select the sync pulse
+ * @vsync_polarity: vsync input signal configuration
+ * @hsync_polarity: hsync input signal configuration
+ */
+struct adv7511_link_config {
+ unsigned int input_color_depth;
+ enum hdmi_colorspace input_colorspace;
+ enum adv7511_input_clock input_clock;
+ unsigned int input_style;
+ enum adv7511_input_justification input_justification;
+
+ int clock_delay;
+
+ bool embedded_sync;
+ enum adv7511_input_sync_pulse sync_pulse;
+ enum adv7511_sync_polarity vsync_polarity;
+ enum adv7511_sync_polarity hsync_polarity;
+};
+
+/**
+ * enum adv7511_csc_scaling - Scaling factor for the ADV7511 CSC
+ * @ADV7511_CSC_SCALING_1: CSC results are not scaled
+ * @ADV7511_CSC_SCALING_2: CSC results are scaled by a factor of two
+ * @ADV7511_CSC_SCALING_4: CSC results are scalled by a factor of four
+ */
+enum adv7511_csc_scaling {
+ ADV7511_CSC_SCALING_1 = 0,
+ ADV7511_CSC_SCALING_2 = 1,
+ ADV7511_CSC_SCALING_4 = 2,
+};
+
+/**
+ * struct adv7511_video_config - Describes adv7511 hardware configuration
+ * @csc_enable: Whether to enable color space conversion
+ * @csc_scaling_factor: Color space conversion scaling factor
+ * @csc_coefficents: Color space conversion coefficents
+ * @hdmi_mode: Whether to use HDMI or DVI output mode
+ * @avi_infoframe: HDMI infoframe
+ */
+struct adv7511_video_config {
+ bool csc_enable;
+ enum adv7511_csc_scaling csc_scaling_factor;
+ const uint16_t *csc_coefficents;
+
+ bool hdmi_mode;
+ struct hdmi_avi_infoframe avi_infoframe;
+};
+
+#endif /* __DRM_I2C_ADV7511_H__ */
diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/gpu/drm/imx/Kconfig
index 82fb758a29bc..82fb758a29bc 100644
--- a/drivers/staging/imx-drm/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/gpu/drm/imx/Makefile
index 582c438d8cbd..582c438d8cbd 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/gpu/drm/imx/Makefile
diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
index 2f8007241734..2f8007241734 100644
--- a/drivers/staging/imx-drm/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h
index 7453ae00c412..7453ae00c412 100644
--- a/drivers/staging/imx-drm/imx-drm.h
+++ b/drivers/gpu/drm/imx/imx-drm.h
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/gpu/drm/imx/imx-hdmi.c
index aaec6b2cdf56..aaec6b2cdf56 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/gpu/drm/imx/imx-hdmi.c
diff --git a/drivers/staging/imx-drm/imx-hdmi.h b/drivers/gpu/drm/imx/imx-hdmi.h
index 39b677689db6..39b677689db6 100644
--- a/drivers/staging/imx-drm/imx-hdmi.h
+++ b/drivers/gpu/drm/imx/imx-hdmi.h
diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c
index 4662e00b456a..4662e00b456a 100644
--- a/drivers/staging/imx-drm/imx-ldb.c
+++ b/drivers/gpu/drm/imx/imx-ldb.c
diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c
index 42c651be6c20..42c651be6c20 100644
--- a/drivers/staging/imx-drm/imx-tve.c
+++ b/drivers/gpu/drm/imx/imx-tve.c
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c
index 11e84a251773..11e84a251773 100644
--- a/drivers/staging/imx-drm/ipuv3-crtc.c
+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c
diff --git a/drivers/staging/imx-drm/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c
index 944962b692bb..944962b692bb 100644
--- a/drivers/staging/imx-drm/ipuv3-plane.c
+++ b/drivers/gpu/drm/imx/ipuv3-plane.c
diff --git a/drivers/staging/imx-drm/ipuv3-plane.h b/drivers/gpu/drm/imx/ipuv3-plane.h
index c0aae5bcb5d4..c0aae5bcb5d4 100644
--- a/drivers/staging/imx-drm/ipuv3-plane.h
+++ b/drivers/gpu/drm/imx/ipuv3-plane.h
diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c
index 015a454b87e1..015a454b87e1 100644
--- a/drivers/staging/imx-drm/parallel-display.c
+++ b/drivers/gpu/drm/imx/parallel-display.c
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index c96f6089f8bf..2324a526de65 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -11,10 +11,17 @@ config DRM_RCAR_DU
Choose this option if you have an R-Car chipset.
If M is selected the module will be called rcar-du-drm.
+config DRM_RCAR_HDMI
+ bool "R-Car DU HDMI Encoder Support"
+ depends on DRM_RCAR_DU
+ depends on OF
+ help
+ Enable support for external HDMI encoders.
+
config DRM_RCAR_LVDS
bool "R-Car DU LVDS Encoder Support"
depends on DRM_RCAR_DU
depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
help
- Enable support the R-Car Display Unit embedded LVDS encoders
- (currently only on R8A7790).
+ Enable support for the R-Car Display Unit embedded LVDS encoders
+ (currently only on R8A7790 and R8A7791).
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 12b8d4477835..05de1c4097af 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -7,6 +7,8 @@ rcar-du-drm-y := rcar_du_crtc.o \
rcar_du_plane.o \
rcar_du_vgacon.o
+rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmicon.o \
+ rcar_du_hdmienc.o
rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 088bfd875d29..23cc910951f4 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -586,7 +586,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
if (irq < 0) {
dev_err(rcdu->dev, "no IRQ for CRTC %u\n", index);
- return ret;
+ return irq;
}
ret = devm_request_irq(rcdu->dev, irq, rcar_du_crtc_irq, irqflags,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index e97ae502dec5..984e6083699f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -15,7 +15,6 @@
#define __RCAR_DU_CRTC_H__
#include <linux/mutex.h>
-#include <linux/platform_data/rcar-du.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
@@ -41,6 +40,15 @@ struct rcar_du_crtc {
#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc)
+enum rcar_du_output {
+ RCAR_DU_OUTPUT_DPAD0,
+ RCAR_DU_OUTPUT_DPAD1,
+ RCAR_DU_OUTPUT_LVDS0,
+ RCAR_DU_OUTPUT_LVDS1,
+ RCAR_DU_OUTPUT_TCON,
+ RCAR_DU_OUTPUT_MAX,
+};
+
int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index);
void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable);
void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index d212efa6a495..967ae8f20233 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -146,12 +146,11 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
{
struct platform_device *pdev = dev->platformdev;
struct device_node *np = pdev->dev.of_node;
- struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
struct rcar_du_device *rcdu;
struct resource *mem;
int ret;
- if (pdata == NULL && np == NULL) {
+ if (np == NULL) {
dev_err(dev->dev, "no platform data\n");
return -ENODEV;
}
@@ -163,7 +162,6 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
}
rcdu->dev = &pdev->dev;
- rcdu->pdata = pdata;
rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data
: (void *)platform_get_device_id(pdev)->driver_data;
rcdu->ddev = dev;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index 8e494633c3b3..0a724669f02d 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -15,7 +15,6 @@
#define __RCAR_DU_DRV_H__
#include <linux/kernel.h>
-#include <linux/platform_data/rcar-du.h>
#include "rcar_du_crtc.h"
#include "rcar_du_group.h"
@@ -67,7 +66,6 @@ struct rcar_du_device_info {
struct rcar_du_device {
struct device *dev;
- const struct rcar_du_platform_data *pdata;
const struct rcar_du_device_info *info;
void __iomem *mmio;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index 7c0ec95915ef..34a122a39664 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -19,6 +19,8 @@
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
+#include "rcar_du_hdmicon.h"
+#include "rcar_du_hdmienc.h"
#include "rcar_du_kms.h"
#include "rcar_du_lvdscon.h"
#include "rcar_du_lvdsenc.h"
@@ -33,7 +35,7 @@ rcar_du_connector_best_encoder(struct drm_connector *connector)
{
struct rcar_du_connector *rcon = to_rcar_connector(connector);
- return &rcon->encoder->encoder;
+ return rcar_encoder_to_drm_encoder(rcon->encoder);
}
/* -----------------------------------------------------------------------------
@@ -142,10 +144,11 @@ static const struct drm_encoder_funcs encoder_funcs = {
int rcar_du_encoder_init(struct rcar_du_device *rcdu,
enum rcar_du_encoder_type type,
enum rcar_du_output output,
- const struct rcar_du_encoder_data *data,
- struct device_node *np)
+ struct device_node *enc_node,
+ struct device_node *con_node)
{
struct rcar_du_encoder *renc;
+ struct drm_encoder *encoder;
unsigned int encoder_type;
int ret;
@@ -154,6 +157,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
return -ENOMEM;
renc->output = output;
+ encoder = rcar_encoder_to_drm_encoder(renc);
switch (output) {
case RCAR_DU_OUTPUT_LVDS0:
@@ -175,6 +179,9 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
case RCAR_DU_ENCODER_LVDS:
encoder_type = DRM_MODE_ENCODER_LVDS;
break;
+ case RCAR_DU_ENCODER_HDMI:
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ break;
case RCAR_DU_ENCODER_NONE:
default:
/* No external encoder, use the internal encoder type. */
@@ -182,23 +189,35 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
break;
}
- ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
- encoder_type);
- if (ret < 0)
- return ret;
+ if (type == RCAR_DU_ENCODER_HDMI) {
+ if (renc->lvds) {
+ dev_err(rcdu->dev,
+ "Chaining LVDS and HDMI encoders not supported\n");
+ return -EINVAL;
+ }
- drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
+ ret = rcar_du_hdmienc_init(rcdu, renc, enc_node);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
+ encoder_type);
+ if (ret < 0)
+ return ret;
- switch (encoder_type) {
- case DRM_MODE_ENCODER_LVDS: {
- const struct rcar_du_panel_data *pdata =
- data ? &data->connector.lvds.panel : NULL;
- return rcar_du_lvds_connector_init(rcdu, renc, pdata, np);
+ drm_encoder_helper_add(encoder, &encoder_helper_funcs);
}
+ switch (encoder_type) {
+ case DRM_MODE_ENCODER_LVDS:
+ return rcar_du_lvds_connector_init(rcdu, renc, con_node);
+
case DRM_MODE_ENCODER_DAC:
return rcar_du_vga_connector_init(rcdu, renc);
+ case DRM_MODE_ENCODER_TMDS:
+ return rcar_du_hdmi_connector_init(rcdu, renc);
+
default:
return -EINVAL;
}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index bd624135ef1f..719b6f2a031c 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -14,21 +14,32 @@
#ifndef __RCAR_DU_ENCODER_H__
#define __RCAR_DU_ENCODER_H__
-#include <linux/platform_data/rcar-du.h>
-
#include <drm/drm_crtc.h>
+#include <drm/drm_encoder_slave.h>
struct rcar_du_device;
+struct rcar_du_hdmienc;
struct rcar_du_lvdsenc;
+enum rcar_du_encoder_type {
+ RCAR_DU_ENCODER_UNUSED = 0,
+ RCAR_DU_ENCODER_NONE,
+ RCAR_DU_ENCODER_VGA,
+ RCAR_DU_ENCODER_LVDS,
+ RCAR_DU_ENCODER_HDMI,
+};
+
struct rcar_du_encoder {
- struct drm_encoder encoder;
+ struct drm_encoder_slave slave;
enum rcar_du_output output;
+ struct rcar_du_hdmienc *hdmi;
struct rcar_du_lvdsenc *lvds;
};
#define to_rcar_encoder(e) \
- container_of(e, struct rcar_du_encoder, encoder)
+ container_of(e, struct rcar_du_encoder, slave.base)
+
+#define rcar_encoder_to_drm_encoder(e) (&(e)->slave.base)
struct rcar_du_connector {
struct drm_connector connector;
@@ -44,7 +55,7 @@ rcar_du_connector_best_encoder(struct drm_connector *connector);
int rcar_du_encoder_init(struct rcar_du_device *rcdu,
enum rcar_du_encoder_type type,
enum rcar_du_output output,
- const struct rcar_du_encoder_data *data,
- struct device_node *np);
+ struct device_node *enc_node,
+ struct device_node *con_node);
#endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
new file mode 100644
index 000000000000..8abaaf258f45
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
@@ -0,0 +1,118 @@
+/*
+ * R-Car Display Unit HDMI Connector
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_encoder.h"
+#include "rcar_du_hdmicon.h"
+#include "rcar_du_kms.h"
+
+#define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs)
+
+static int rcar_du_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct drm_encoder *encoder = connector->encoder;
+ struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
+
+ if (sfuncs->get_modes == NULL)
+ return 0;
+
+ return sfuncs->get_modes(encoder, connector);
+}
+
+static int rcar_du_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct drm_encoder *encoder = connector->encoder;
+ struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
+
+ if (sfuncs->mode_valid == NULL)
+ return MODE_OK;
+
+ return sfuncs->mode_valid(encoder, mode);
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+ .get_modes = rcar_du_hdmi_connector_get_modes,
+ .mode_valid = rcar_du_hdmi_connector_mode_valid,
+ .best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_hdmi_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct drm_encoder *encoder = connector->encoder;
+ struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
+
+ if (sfuncs->detect == NULL)
+ return connector_status_unknown;
+
+ return sfuncs->detect(encoder, connector);
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = rcar_du_hdmi_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = rcar_du_hdmi_connector_destroy,
+};
+
+int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc)
+{
+ struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
+ struct rcar_du_connector *rcon;
+ struct drm_connector *connector;
+ int ret;
+
+ rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
+ if (rcon == NULL)
+ return -ENOMEM;
+
+ connector = &rcon->connector;
+ connector->display_info.width_mm = 0;
+ connector->display_info.height_mm = 0;
+
+ ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret < 0)
+ return ret;
+
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+ ret = drm_connector_register(connector);
+ if (ret < 0)
+ return ret;
+
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ drm_object_property_set_value(&connector->base,
+ rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+ ret = drm_mode_connector_attach_encoder(connector, encoder);
+ if (ret < 0)
+ return ret;
+
+ connector->encoder = encoder;
+ rcon->encoder = renc;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h
new file mode 100644
index 000000000000..87daa949227f
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h
@@ -0,0 +1,31 @@
+/*
+ * R-Car Display Unit HDMI Connector
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_HDMICON_H__
+#define __RCAR_DU_HDMICON_H__
+
+struct rcar_du_device;
+struct rcar_du_encoder;
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
+int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc);
+#else
+static inline int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif /* __RCAR_DU_HDMICON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
new file mode 100644
index 000000000000..359bc999a9c8
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
@@ -0,0 +1,151 @@
+/*
+ * R-Car Display Unit HDMI Encoder
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_encoder.h"
+#include "rcar_du_hdmienc.h"
+
+struct rcar_du_hdmienc {
+ struct rcar_du_encoder *renc;
+ struct device *dev;
+ int dpms;
+};
+
+#define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi)
+#define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs)
+
+static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+ struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
+
+ if (hdmienc->dpms == mode)
+ return;
+
+ if (sfuncs->dpms)
+ sfuncs->dpms(encoder, mode);
+
+ hdmienc->dpms = mode;
+}
+
+static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
+
+ if (sfuncs->mode_fixup == NULL)
+ return true;
+
+ return sfuncs->mode_fixup(encoder, mode, adjusted_mode);
+}
+
+static void rcar_du_hdmienc_mode_prepare(struct drm_encoder *encoder)
+{
+ rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void rcar_du_hdmienc_mode_commit(struct drm_encoder *encoder)
+{
+ rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+ struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
+
+ if (sfuncs->mode_set)
+ sfuncs->mode_set(encoder, mode, adjusted_mode);
+
+ rcar_du_crtc_route_output(encoder->crtc, hdmienc->renc->output);
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+ .dpms = rcar_du_hdmienc_dpms,
+ .mode_fixup = rcar_du_hdmienc_mode_fixup,
+ .prepare = rcar_du_hdmienc_mode_prepare,
+ .commit = rcar_du_hdmienc_mode_commit,
+ .mode_set = rcar_du_hdmienc_mode_set,
+};
+
+static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder)
+{
+ struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+
+ rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ drm_encoder_cleanup(encoder);
+ put_device(hdmienc->dev);
+}
+
+static const struct drm_encoder_funcs encoder_funcs = {
+ .destroy = rcar_du_hdmienc_cleanup,
+};
+
+int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc, struct device_node *np)
+{
+ struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
+ struct drm_i2c_encoder_driver *driver;
+ struct i2c_client *i2c_slave;
+ struct rcar_du_hdmienc *hdmienc;
+ int ret;
+
+ hdmienc = devm_kzalloc(rcdu->dev, sizeof(*hdmienc), GFP_KERNEL);
+ if (hdmienc == NULL)
+ return -ENOMEM;
+
+ /* Locate the slave I2C device and driver. */
+ i2c_slave = of_find_i2c_device_by_node(np);
+ if (!i2c_slave || !i2c_get_clientdata(i2c_slave))
+ return -EPROBE_DEFER;
+
+ hdmienc->dev = &i2c_slave->dev;
+
+ if (hdmienc->dev->driver == NULL) {
+ ret = -EPROBE_DEFER;
+ goto error;
+ }
+
+ /* Initialize the slave encoder. */
+ driver = to_drm_i2c_encoder_driver(to_i2c_driver(hdmienc->dev->driver));
+ ret = driver->encoder_init(i2c_slave, rcdu->ddev, &renc->slave);
+ if (ret < 0)
+ goto error;
+
+ ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+ if (ret < 0)
+ goto error;
+
+ drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+ renc->hdmi = hdmienc;
+ hdmienc->renc = renc;
+
+ return 0;
+
+error:
+ put_device(hdmienc->dev);
+ return ret;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
new file mode 100644
index 000000000000..2ff0128ac8e1
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
@@ -0,0 +1,35 @@
+/*
+ * R-Car Display Unit HDMI Encoder
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_HDMIENC_H__
+#define __RCAR_DU_HDMIENC_H__
+
+#include <linux/module.h>
+
+struct device_node;
+struct rcar_du_device;
+struct rcar_du_encoder;
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
+int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc, struct device_node *np);
+#else
+static inline int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc,
+ struct device_node *np)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif /* __RCAR_DU_HDMIENC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 6289e3797bc5..0c5ee616b5a3 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -190,49 +190,16 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
.output_poll_changed = rcar_du_output_poll_changed,
};
-static int rcar_du_encoders_init_pdata(struct rcar_du_device *rcdu)
-{
- unsigned int num_encoders = 0;
- unsigned int i;
- int ret;
-
- for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
- const struct rcar_du_encoder_data *pdata =
- &rcdu->pdata->encoders[i];
- const struct rcar_du_output_routing *route =
- &rcdu->info->routes[pdata->output];
-
- if (pdata->type == RCAR_DU_ENCODER_UNUSED)
- continue;
-
- if (pdata->output >= RCAR_DU_OUTPUT_MAX ||
- route->possible_crtcs == 0) {
- dev_warn(rcdu->dev,
- "encoder %u references unexisting output %u, skipping\n",
- i, pdata->output);
- continue;
- }
-
- ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output,
- pdata, NULL);
- if (ret < 0)
- return ret;
-
- num_encoders++;
- }
-
- return num_encoders;
-}
-
-static int rcar_du_encoders_init_dt_one(struct rcar_du_device *rcdu,
- enum rcar_du_output output,
- struct of_endpoint *ep)
+static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
+ enum rcar_du_output output,
+ struct of_endpoint *ep)
{
static const struct {
const char *compatible;
enum rcar_du_encoder_type type;
} encoders[] = {
{ "adi,adv7123", RCAR_DU_ENCODER_VGA },
+ { "adi,adv7511w", RCAR_DU_ENCODER_HDMI },
{ "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS },
};
@@ -323,14 +290,14 @@ static int rcar_du_encoders_init_dt_one(struct rcar_du_device *rcdu,
connector = entity;
}
- ret = rcar_du_encoder_init(rcdu, enc_type, output, NULL, connector);
+ ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector);
of_node_put(encoder);
of_node_put(connector);
return ret < 0 ? ret : 1;
}
-static int rcar_du_encoders_init_dt(struct rcar_du_device *rcdu)
+static int rcar_du_encoders_init(struct rcar_du_device *rcdu)
{
struct device_node *np = rcdu->dev->of_node;
struct device_node *prev = NULL;
@@ -377,7 +344,7 @@ static int rcar_du_encoders_init_dt(struct rcar_du_device *rcdu)
}
/* Process the output pipeline. */
- ret = rcar_du_encoders_init_dt_one(rcdu, output, &ep);
+ ret = rcar_du_encoders_init_one(rcdu, output, &ep);
if (ret < 0) {
of_node_put(ep_node);
return ret;
@@ -442,11 +409,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
if (ret < 0)
return ret;
- if (rcdu->pdata)
- ret = rcar_du_encoders_init_pdata(rcdu);
- else
- ret = rcar_du_encoders_init_dt(rcdu);
-
+ ret = rcar_du_encoders_init(rcdu);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
index 115eed20db12..6d9811c052c4 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -27,7 +27,11 @@
struct rcar_du_lvds_connector {
struct rcar_du_connector connector;
- struct rcar_du_panel_data panel;
+ struct {
+ unsigned int width_mm; /* Panel width in mm */
+ unsigned int height_mm; /* Panel height in mm */
+ struct videomode mode;
+ } panel;
};
#define to_rcar_lvds_connector(c) \
@@ -78,31 +82,26 @@ static const struct drm_connector_funcs connector_funcs = {
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
struct rcar_du_encoder *renc,
- const struct rcar_du_panel_data *panel,
/* TODO const */ struct device_node *np)
{
+ struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
struct rcar_du_lvds_connector *lvdscon;
struct drm_connector *connector;
+ struct display_timing timing;
int ret;
lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
if (lvdscon == NULL)
return -ENOMEM;
- if (panel) {
- lvdscon->panel = *panel;
- } else {
- struct display_timing timing;
-
- ret = of_get_display_timing(np, "panel-timing", &timing);
- if (ret < 0)
- return ret;
+ ret = of_get_display_timing(np, "panel-timing", &timing);
+ if (ret < 0)
+ return ret;
- videomode_from_timing(&timing, &lvdscon->panel.mode);
+ videomode_from_timing(&timing, &lvdscon->panel.mode);
- of_property_read_u32(np, "width-mm", &lvdscon->panel.width_mm);
- of_property_read_u32(np, "height-mm", &lvdscon->panel.height_mm);
- }
+ of_property_read_u32(np, "width-mm", &lvdscon->panel.width_mm);
+ of_property_read_u32(np, "height-mm", &lvdscon->panel.height_mm);
connector = &lvdscon->connector.connector;
connector->display_info.width_mm = lvdscon->panel.width_mm;
@@ -122,11 +121,11 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
drm_object_property_set_value(&connector->base,
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
- ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+ ret = drm_mode_connector_attach_encoder(connector, encoder);
if (ret < 0)
return ret;
- connector->encoder = &renc->encoder;
+ connector->encoder = encoder;
lvdscon->connector.encoder = renc;
return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
index d11424d537f9..d4881ee0be7e 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
@@ -16,11 +16,9 @@
struct rcar_du_device;
struct rcar_du_encoder;
-struct rcar_du_panel_data;
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
struct rcar_du_encoder *renc,
- const struct rcar_du_panel_data *panel,
struct device_node *np);
#endif /* __RCAR_DU_LVDSCON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
index 3303a55cec79..f65aabda0796 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
@@ -16,7 +16,6 @@
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/platform_data/rcar-du.h>
struct rcar_drm_crtc;
struct rcar_du_lvdsenc;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index 564a723ede03..752747a5e920 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -52,6 +52,7 @@ static const struct drm_connector_funcs connector_funcs = {
int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
struct rcar_du_encoder *renc)
{
+ struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
struct rcar_du_connector *rcon;
struct drm_connector *connector;
int ret;
@@ -78,11 +79,11 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
drm_object_property_set_value(&connector->base,
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
- ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+ ret = drm_mode_connector_attach_encoder(connector, encoder);
if (ret < 0)
return ret;
- connector->encoder = &renc->encoder;
+ connector->encoder = encoder;
rcon->encoder = renc;
return 0;
diff --git a/drivers/gpu/drm/udl/udl_dmabuf.c b/drivers/gpu/drm/udl/udl_dmabuf.c
index 1d85c3a9151d..ac8a66b4dfc2 100644
--- a/drivers/gpu/drm/udl/udl_dmabuf.c
+++ b/drivers/gpu/drm/udl/udl_dmabuf.c
@@ -90,15 +90,18 @@ static struct sg_table *udl_map_dma_buf(struct dma_buf_attachment *attach,
return &udl_attach->sgt;
if (!obj->pages) {
- DRM_ERROR("pages is null.\n");
- return ERR_PTR(-ENOMEM);
+ ret = udl_gem_get_pages(obj);
+ if (ret) {
+ DRM_ERROR("failed to map pages.\n");
+ return ERR_PTR(ret);
+ }
}
page_count = obj->base.size / PAGE_SIZE;
obj->sg = drm_prime_pages_to_sg(obj->pages, page_count);
- if (!obj->sg) {
- DRM_ERROR("sg is null.\n");
- return ERR_PTR(-ENOMEM);
+ if (IS_ERR(obj->sg)) {
+ DRM_ERROR("failed to allocate sgt.\n");
+ return ERR_CAST(obj->sg);
}
sgt = &udl_attach->sgt;
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h
index 1b132d779621..80adbac82bde 100644
--- a/drivers/gpu/drm/udl/udl_drv.h
+++ b/drivers/gpu/drm/udl/udl_drv.h
@@ -129,6 +129,8 @@ struct dma_buf *udl_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *udl_gem_prime_import(struct drm_device *dev,
struct dma_buf *dma_buf);
+int udl_gem_get_pages(struct udl_gem_object *obj);
+void udl_gem_put_pages(struct udl_gem_object *obj);
int udl_gem_vmap(struct udl_gem_object *obj);
void udl_gem_vunmap(struct udl_gem_object *obj);
int udl_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index cd3482de5cab..2a0a784ab6ee 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -127,7 +127,7 @@ int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
}
}
-static int udl_gem_get_pages(struct udl_gem_object *obj)
+int udl_gem_get_pages(struct udl_gem_object *obj)
{
struct page **pages;
@@ -143,7 +143,7 @@ static int udl_gem_get_pages(struct udl_gem_object *obj)
return 0;
}
-static void udl_gem_put_pages(struct udl_gem_object *obj)
+void udl_gem_put_pages(struct udl_gem_object *obj)
{
if (obj->base.import_attach) {
drm_free_large(obj->pages);
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 4690ae9a267f..9425728b7eb5 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -86,8 +86,6 @@ source "drivers/staging/gdm72xx/Kconfig"
source "drivers/staging/gdm724x/Kconfig"
-source "drivers/staging/imx-drm/Kconfig"
-
source "drivers/staging/fwserial/Kconfig"
source "drivers/staging/goldfish/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index c780a0e70e15..bc233dd98a95 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -36,7 +36,6 @@ obj-$(CONFIG_STAGING_BOARD) += board/
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
-obj-$(CONFIG_DRM_IMX) += imx-drm/
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_LUSTRE_FS) += lustre/
diff --git a/drivers/staging/imx-drm/TODO b/drivers/staging/imx-drm/TODO
deleted file mode 100644
index 29636fb13959..000000000000
--- a/drivers/staging/imx-drm/TODO
+++ /dev/null
@@ -1,17 +0,0 @@
-TODO:
-- get DRM Maintainer review for this code
-- decide where to put the base driver. It is not specific to a subsystem
- and would be used by DRM/KMS and media/V4L2
-
-Missing features (not necessarily for moving out of staging):
-
-- Add support for IC (Image converter)
-- Add support for CSI (CMOS Sensor interface)
-- Add support for VDIC (Video Deinterlacer)
-
-Many work-in-progress patches for the above features exist. Contact
-Sascha Hauer <kernel@pengutronix.de> if you are interested in working
-on a specific feature.
-
-Please send any patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org> and
-Sascha Hauer <kernel@pengutronix.de>
OpenPOWER on IntegriCloud