summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dma-buf/dma-buf.c44
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_priv.h3
-rw-r--r--drivers/gpu/drm/arc/arcpgu_drv.c8
-rw-r--r--drivers/gpu/drm/arm/Kconfig16
-rw-r--r--drivers/gpu/drm/arm/Makefile2
-rw-r--r--drivers/gpu/drm/arm/malidp_crtc.c216
-rw-r--r--drivers/gpu/drm/arm/malidp_drv.c512
-rw-r--r--drivers/gpu/drm/arm/malidp_drv.h54
-rw-r--r--drivers/gpu/drm/arm/malidp_hw.c691
-rw-r--r--drivers/gpu/drm/arm/malidp_hw.h241
-rw-r--r--drivers/gpu/drm/arm/malidp_planes.c298
-rw-r--r--drivers/gpu/drm/arm/malidp_regs.h172
-rw-r--r--drivers/gpu/drm/armada/armada_drv.c1
-rw-r--r--drivers/gpu/drm/armada/armada_overlay.c1
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c15
-rw-r--r--drivers/gpu/drm/bridge/Kconfig8
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c467
-rw-r--r--drivers/gpu/drm/drm_auth.c285
-rw-r--r--drivers/gpu/drm/drm_bufs.c8
-rw-r--r--drivers/gpu/drm/drm_crtc.c164
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c3
-rw-r--r--drivers/gpu/drm/drm_crtc_internal.h88
-rw-r--r--drivers/gpu/drm/drm_debugfs.c3
-rw-r--r--drivers/gpu/drm/drm_dp_helper.c56
-rw-r--r--drivers/gpu/drm/drm_drv.c239
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c2
-rw-r--r--drivers/gpu/drm/drm_fops.c125
-rw-r--r--drivers/gpu/drm/drm_info.c117
-rw-r--r--drivers/gpu/drm/drm_internal.h21
-rw-r--r--drivers/gpu/drm/drm_ioctl.c124
-rw-r--r--drivers/gpu/drm/drm_irq.c4
-rw-r--r--drivers/gpu/drm/drm_legacy.h8
-rw-r--r--drivers/gpu/drm/drm_lock.c240
-rw-r--r--drivers/gpu/drm/drm_pci.c51
-rw-r--r--drivers/gpu/drm/drm_plane_helper.c28
-rw-r--r--drivers/gpu/drm/drm_platform.c18
-rw-r--r--drivers/gpu/drm/drm_simple_kms_helper.c1
-rw-r--r--drivers/gpu/drm/drm_vm.c54
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_drv.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c1
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c13
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c9
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h3
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c4
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c2
-rw-r--r--drivers/gpu/drm/i915/intel_display.c15
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c22
-rw-r--r--drivers/gpu/drm/i915/intel_dp_mst.c4
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h17
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.c2
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c2
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c2
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c2
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c13
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c26
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c2
-rw-r--r--drivers/gpu/drm/imx/imx-drm-core.c1
-rw-r--r--drivers/gpu/drm/mediatek/Kconfig9
-rw-r--r--drivers/gpu/drm/mediatek/Makefile7
-rw-r--r--drivers/gpu/drm/mediatek/mtk_cec.c265
-rw-r--r--drivers/gpu/drm/mediatek/mtk_cec.h26
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.c9
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_plane.c1
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi.c1828
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi.h23
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c358
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_regs.h238
-rw-r--r--drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c515
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c3
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c15
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_regs.h5
-rw-r--r--drivers/gpu/drm/rockchip/Kconfig1
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c21
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c1
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_drv.c1
-rw-r--r--drivers/gpu/drm/sis/sis_mm.c2
-rw-r--r--drivers/gpu/drm/sti/sti_compositor.c20
-rw-r--r--drivers/gpu/drm/sti/sti_compositor.h3
-rw-r--r--drivers/gpu/drm/sti/sti_crtc.c12
-rw-r--r--drivers/gpu/drm/sti/sti_cursor.c32
-rw-r--r--drivers/gpu/drm/sti/sti_drv.c138
-rw-r--r--drivers/gpu/drm/sti/sti_drv.h1
-rw-r--r--drivers/gpu/drm/sti/sti_dvo.c25
-rw-r--r--drivers/gpu/drm/sti/sti_gdp.c32
-rw-r--r--drivers/gpu/drm/sti/sti_hda.c26
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.c40
-rw-r--r--drivers/gpu/drm/sti/sti_hqvdp.c32
-rw-r--r--drivers/gpu/drm/sti/sti_mixer.c5
-rw-r--r--drivers/gpu/drm/sti/sti_mixer.h2
-rw-r--r--drivers/gpu/drm/sti/sti_plane.c24
-rw-r--r--drivers/gpu/drm/sti/sti_plane.h7
-rw-r--r--drivers/gpu/drm/sti/sti_tvout.c36
-rw-r--r--drivers/gpu/drm/sti/sti_vid.c5
-rw-r--r--drivers/gpu/drm/sti/sti_vid.h2
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c38
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c13
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.c2
-rw-r--r--drivers/gpu/drm/via/via_mm.c2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drm_bus.c10
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.h1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c5
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h1
109 files changed, 7226 insertions, 1163 deletions
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 6355ab38d630..20ce0687b111 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -824,7 +824,7 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
EXPORT_SYMBOL_GPL(dma_buf_vunmap);
#ifdef CONFIG_DEBUG_FS
-static int dma_buf_describe(struct seq_file *s)
+static int dma_buf_debug_show(struct seq_file *s, void *unused)
{
int ret;
struct dma_buf *buf_obj;
@@ -879,17 +879,9 @@ static int dma_buf_describe(struct seq_file *s)
return 0;
}
-static int dma_buf_show(struct seq_file *s, void *unused)
-{
- void (*func)(struct seq_file *) = s->private;
-
- func(s);
- return 0;
-}
-
static int dma_buf_debug_open(struct inode *inode, struct file *file)
{
- return single_open(file, dma_buf_show, inode->i_private);
+ return single_open(file, dma_buf_debug_show, NULL);
}
static const struct file_operations dma_buf_debug_fops = {
@@ -903,20 +895,23 @@ static struct dentry *dma_buf_debugfs_dir;
static int dma_buf_init_debugfs(void)
{
+ struct dentry *d;
int err = 0;
- dma_buf_debugfs_dir = debugfs_create_dir("dma_buf", NULL);
-
- if (IS_ERR(dma_buf_debugfs_dir)) {
- err = PTR_ERR(dma_buf_debugfs_dir);
- dma_buf_debugfs_dir = NULL;
- return err;
- }
+ d = debugfs_create_dir("dma_buf", NULL);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
- err = dma_buf_debugfs_create_file("bufinfo", dma_buf_describe);
+ dma_buf_debugfs_dir = d;
- if (err)
+ d = debugfs_create_file("bufinfo", S_IRUGO, dma_buf_debugfs_dir,
+ NULL, &dma_buf_debug_fops);
+ if (IS_ERR(d)) {
pr_debug("dma_buf: debugfs: failed to create node bufinfo\n");
+ debugfs_remove_recursive(dma_buf_debugfs_dir);
+ dma_buf_debugfs_dir = NULL;
+ err = PTR_ERR(d);
+ }
return err;
}
@@ -926,17 +921,6 @@ static void dma_buf_uninit_debugfs(void)
if (dma_buf_debugfs_dir)
debugfs_remove_recursive(dma_buf_debugfs_dir);
}
-
-int dma_buf_debugfs_create_file(const char *name,
- int (*write)(struct seq_file *))
-{
- struct dentry *d;
-
- d = debugfs_create_file(name, S_IRUGO, dma_buf_debugfs_dir,
- write, &dma_buf_debug_fops);
-
- return PTR_ERR_OR_ZERO(d);
-}
#else
static inline int dma_buf_init_debugfs(void)
{
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
index ec4036a09f3e..a625b9137da2 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
@@ -187,12 +187,12 @@ int init_pipelines(struct device_queue_manager *dqm,
unsigned int get_first_pipe(struct device_queue_manager *dqm);
unsigned int get_pipes_num(struct device_queue_manager *dqm);
-extern inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd)
+static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd)
{
return (pdd->lds_base >> 16) & 0xFF;
}
-extern inline unsigned int
+static inline unsigned int
get_sh_mem_bases_nybble_64(struct kfd_process_device *pdd)
{
return (pdd->lds_base >> 60) & 0x0E;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index d0d5f4baf72d..80113c335966 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -617,10 +617,7 @@ int kgd2kfd_resume(struct kfd_dev *kfd);
int kfd_init_apertures(struct kfd_process *process);
/* Queue Context Management */
-inline uint32_t lower_32(uint64_t x);
-inline uint32_t upper_32(uint64_t x);
struct cik_sdma_rlc_registers *get_sdma_mqd(void *mqd);
-inline uint32_t get_sdma_base_addr(struct cik_sdma_rlc_registers *m);
int init_queue(struct queue **q, struct queue_properties properties);
void uninit_queue(struct queue *q);
diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c
index 381c5fcbf903..ccbdadb108dc 100644
--- a/drivers/gpu/drm/arc/arcpgu_drv.c
+++ b/drivers/gpu/drm/arc/arcpgu_drv.c
@@ -211,15 +211,8 @@ static int arcpgu_probe(struct platform_device *pdev)
if (ret)
goto err_unload;
- ret = drm_connector_register_all(drm);
- if (ret)
- goto err_unregister;
-
return 0;
-err_unregister:
- drm_dev_unregister(drm);
-
err_unload:
arcpgu_unload(drm);
@@ -233,7 +226,6 @@ static int arcpgu_remove(struct platform_device *pdev)
{
struct drm_device *drm = platform_get_drvdata(pdev);
- drm_connector_unregister_all(drm);
drm_dev_unregister(drm);
arcpgu_unload(drm);
drm_dev_unref(drm);
diff --git a/drivers/gpu/drm/arm/Kconfig b/drivers/gpu/drm/arm/Kconfig
index eaed454e043c..1b2906568a48 100644
--- a/drivers/gpu/drm/arm/Kconfig
+++ b/drivers/gpu/drm/arm/Kconfig
@@ -25,3 +25,19 @@ config DRM_HDLCD_SHOW_UNDERRUN
Enable this option to show in red colour the pixels that the
HDLCD device did not fetch from framebuffer due to underrun
conditions.
+
+config DRM_MALI_DISPLAY
+ tristate "ARM Mali Display Processor"
+ depends on DRM && OF && (ARM || ARM64)
+ depends on COMMON_CLK
+ select DRM_ARM
+ select DRM_KMS_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_GEM_CMA_HELPER
+ select VIDEOMODE_HELPERS
+ help
+ Choose this option if you want to compile the ARM Mali Display
+ Processor driver. It supports the DP500, DP550 and DP650 variants
+ of the hardware.
+
+ If compiled as a module it will be called mali-dp.
diff --git a/drivers/gpu/drm/arm/Makefile b/drivers/gpu/drm/arm/Makefile
index 89dcb7bab93a..bb8b158ff90d 100644
--- a/drivers/gpu/drm/arm/Makefile
+++ b/drivers/gpu/drm/arm/Makefile
@@ -1,2 +1,4 @@
hdlcd-y := hdlcd_drv.o hdlcd_crtc.o
obj-$(CONFIG_DRM_HDLCD) += hdlcd.o
+mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o
+obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o
diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
new file mode 100644
index 000000000000..08e6a71f5d05
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_crtc.c
@@ -0,0 +1,216 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ * Author: Liviu Dudau <Liviu.Dudau@arm.com>
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * ARM Mali DP500/DP550/DP650 driver (crtc operations)
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/clk.h>
+#include <video/videomode.h>
+
+#include "malidp_drv.h"
+#include "malidp_hw.h"
+
+static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
+ struct malidp_hw_device *hwdev = malidp->dev;
+
+ /*
+ * check that the hardware can drive the required clock rate,
+ * but skip the check if the clock is meant to be disabled (req_rate = 0)
+ */
+ long rate, req_rate = mode->crtc_clock * 1000;
+
+ if (req_rate) {
+ rate = clk_round_rate(hwdev->mclk, req_rate);
+ if (rate < req_rate) {
+ DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n",
+ mode->crtc_clock);
+ return false;
+ }
+
+ rate = clk_round_rate(hwdev->pxlclk, req_rate);
+ if (rate != req_rate) {
+ DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
+ req_rate);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void malidp_crtc_enable(struct drm_crtc *crtc)
+{
+ struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
+ struct malidp_hw_device *hwdev = malidp->dev;
+ struct videomode vm;
+
+ drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
+
+ clk_prepare_enable(hwdev->pxlclk);
+
+ /* mclk needs to be set to the same or higher rate than pxlclk */
+ clk_set_rate(hwdev->mclk, crtc->state->adjusted_mode.crtc_clock * 1000);
+ clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000);
+
+ hwdev->modeset(hwdev, &vm);
+ hwdev->leave_config_mode(hwdev);
+ drm_crtc_vblank_on(crtc);
+}
+
+static void malidp_crtc_disable(struct drm_crtc *crtc)
+{
+ struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
+ struct malidp_hw_device *hwdev = malidp->dev;
+
+ drm_crtc_vblank_off(crtc);
+ hwdev->enter_config_mode(hwdev);
+ clk_disable_unprepare(hwdev->pxlclk);
+}
+
+static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
+ struct malidp_hw_device *hwdev = malidp->dev;
+ struct drm_plane *plane;
+ const struct drm_plane_state *pstate;
+ u32 rot_mem_free, rot_mem_usable;
+ int rotated_planes = 0;
+
+ /*
+ * check if there is enough rotation memory available for planes
+ * that need 90° and 270° rotation. Each plane has set its required
+ * memory size in the ->plane_check() callback, here we only make
+ * sure that the sums are less that the total usable memory.
+ *
+ * The rotation memory allocation algorithm (for each plane):
+ * a. If no more rotated planes exist, all remaining rotate
+ * memory in the bank is available for use by the plane.
+ * b. If other rotated planes exist, and plane's layer ID is
+ * DE_VIDEO1, it can use all the memory from first bank if
+ * secondary rotation memory bank is available, otherwise it can
+ * use up to half the bank's memory.
+ * c. If other rotated planes exist, and plane's layer ID is not
+ * DE_VIDEO1, it can use half of the available memory
+ *
+ * Note: this algorithm assumes that the order in which the planes are
+ * checked always has DE_VIDEO1 plane first in the list if it is
+ * rotated. Because that is how we create the planes in the first
+ * place, under current DRM version things work, but if ever the order
+ * in which drm_atomic_crtc_state_for_each_plane() iterates over planes
+ * changes, we need to pre-sort the planes before validation.
+ */
+
+ /* first count the number of rotated planes */
+ drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
+ if (pstate->rotation & MALIDP_ROTATED_MASK)
+ rotated_planes++;
+ }
+
+ rot_mem_free = hwdev->rotation_memory[0];
+ /*
+ * if we have more than 1 plane using rotation memory, use the second
+ * block of rotation memory as well
+ */
+ if (rotated_planes > 1)
+ rot_mem_free += hwdev->rotation_memory[1];
+
+ /* now validate the rotation memory requirements */
+ drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
+ struct malidp_plane *mp = to_malidp_plane(plane);
+ struct malidp_plane_state *ms = to_malidp_plane_state(pstate);
+
+ if (pstate->rotation & MALIDP_ROTATED_MASK) {
+ /* process current plane */
+ rotated_planes--;
+
+ if (!rotated_planes) {
+ /* no more rotated planes, we can use what's left */
+ rot_mem_usable = rot_mem_free;
+ } else {
+ if ((mp->layer->id != DE_VIDEO1) ||
+ (hwdev->rotation_memory[1] == 0))
+ rot_mem_usable = rot_mem_free / 2;
+ else
+ rot_mem_usable = hwdev->rotation_memory[0];
+ }
+
+ rot_mem_free -= rot_mem_usable;
+
+ if (ms->rotmem_size > rot_mem_usable)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
+ .mode_fixup = malidp_crtc_mode_fixup,
+ .enable = malidp_crtc_enable,
+ .disable = malidp_crtc_disable,
+ .atomic_check = malidp_crtc_atomic_check,
+};
+
+static const struct drm_crtc_funcs malidp_crtc_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+int malidp_crtc_init(struct drm_device *drm)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct drm_plane *primary = NULL, *plane;
+ int ret;
+
+ ret = malidp_de_planes_init(drm);
+ if (ret < 0) {
+ DRM_ERROR("Failed to initialise planes\n");
+ return ret;
+ }
+
+ drm_for_each_plane(plane, drm) {
+ if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+ primary = plane;
+ break;
+ }
+ }
+
+ if (!primary) {
+ DRM_ERROR("no primary plane found\n");
+ ret = -EINVAL;
+ goto crtc_cleanup_planes;
+ }
+
+ ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
+ &malidp_crtc_funcs, NULL);
+
+ if (!ret) {
+ drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
+ return 0;
+ }
+
+crtc_cleanup_planes:
+ malidp_de_planes_destroy(drm);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c
new file mode 100644
index 000000000000..e5b44e92f8cf
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_drv.c
@@ -0,0 +1,512 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ * Author: Liviu Dudau <Liviu.Dudau@arm.com>
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * ARM Mali DP500/DP550/DP650 KMS/DRM driver
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/of_reserved_mem.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_of.h>
+
+#include "malidp_drv.h"
+#include "malidp_regs.h"
+#include "malidp_hw.h"
+
+#define MALIDP_CONF_VALID_TIMEOUT 250
+
+/*
+ * set the "config valid" bit and wait until the hardware acts on it
+ */
+static int malidp_set_and_wait_config_valid(struct drm_device *drm)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+ int ret;
+
+ hwdev->set_config_valid(hwdev);
+ /* don't wait for config_valid flag if we are in config mode */
+ if (hwdev->in_config_mode(hwdev))
+ return 0;
+
+ ret = wait_event_interruptible_timeout(malidp->wq,
+ atomic_read(&malidp->config_valid) == 1,
+ msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT));
+
+ return (ret > 0) ? 0 : -ETIMEDOUT;
+}
+
+static void malidp_output_poll_changed(struct drm_device *drm)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+
+ drm_fbdev_cma_hotplug_event(malidp->fbdev);
+}
+
+static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
+{
+ struct drm_pending_vblank_event *event;
+ struct drm_device *drm = state->dev;
+ struct malidp_drm *malidp = drm->dev_private;
+ int ret = malidp_set_and_wait_config_valid(drm);
+
+ if (ret)
+ DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n");
+
+ event = malidp->crtc.state->event;
+ if (event) {
+ malidp->crtc.state->event = NULL;
+
+ spin_lock_irq(&drm->event_lock);
+ if (drm_crtc_vblank_get(&malidp->crtc) == 0)
+ drm_crtc_arm_vblank_event(&malidp->crtc, event);
+ else
+ drm_crtc_send_vblank_event(&malidp->crtc, event);
+ spin_unlock_irq(&drm->event_lock);
+ }
+ drm_atomic_helper_commit_hw_done(state);
+}
+
+static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
+{
+ struct drm_device *drm = state->dev;
+
+ drm_atomic_helper_commit_modeset_disables(drm, state);
+ drm_atomic_helper_commit_modeset_enables(drm, state);
+ drm_atomic_helper_commit_planes(drm, state, true);
+
+ malidp_atomic_commit_hw_done(state);
+
+ drm_atomic_helper_wait_for_vblanks(drm, state);
+
+ drm_atomic_helper_cleanup_planes(drm, state);
+}
+
+static struct drm_mode_config_helper_funcs malidp_mode_config_helpers = {
+ .atomic_commit_tail = malidp_atomic_commit_tail,
+};
+
+static const struct drm_mode_config_funcs malidp_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+ .output_poll_changed = malidp_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static int malidp_enable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+
+ malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK,
+ hwdev->map.de_irq_map.vsync_irq);
+ return 0;
+}
+
+static void malidp_disable_vblank(struct drm_device *drm, unsigned int pipe)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+
+ malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK,
+ hwdev->map.de_irq_map.vsync_irq);
+}
+
+static int malidp_init(struct drm_device *drm)
+{
+ int ret;
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+
+ drm_mode_config_init(drm);
+
+ drm->mode_config.min_width = hwdev->min_line_size;
+ drm->mode_config.min_height = hwdev->min_line_size;
+ drm->mode_config.max_width = hwdev->max_line_size;
+ drm->mode_config.max_height = hwdev->max_line_size;
+ drm->mode_config.funcs = &malidp_mode_config_funcs;
+ drm->mode_config.helper_private = &malidp_mode_config_helpers;
+
+ ret = malidp_crtc_init(drm);
+ if (ret) {
+ drm_mode_config_cleanup(drm);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int malidp_irq_init(struct platform_device *pdev)
+{
+ int irq_de, irq_se, ret = 0;
+ struct drm_device *drm = dev_get_drvdata(&pdev->dev);
+
+ /* fetch the interrupts from DT */
+ irq_de = platform_get_irq_byname(pdev, "DE");
+ if (irq_de < 0) {
+ DRM_ERROR("no 'DE' IRQ specified!\n");
+ return irq_de;
+ }
+ irq_se = platform_get_irq_byname(pdev, "SE");
+ if (irq_se < 0) {
+ DRM_ERROR("no 'SE' IRQ specified!\n");
+ return irq_se;
+ }
+
+ ret = malidp_de_irq_init(drm, irq_de);
+ if (ret)
+ return ret;
+
+ ret = malidp_se_irq_init(drm, irq_se);
+ if (ret) {
+ malidp_de_irq_fini(drm);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void malidp_lastclose(struct drm_device *drm)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+
+ drm_fbdev_cma_restore_mode(malidp->fbdev);
+}
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = noop_llseek,
+ .mmap = drm_gem_cma_mmap,
+};
+
+static struct drm_driver malidp_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
+ DRIVER_PRIME,
+ .lastclose = malidp_lastclose,
+ .get_vblank_counter = drm_vblank_no_hw_counter,
+ .enable_vblank = malidp_enable_vblank,
+ .disable_vblank = malidp_disable_vblank,
+ .gem_free_object_unlocked = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+ .fops = &fops,
+ .name = "mali-dp",
+ .desc = "ARM Mali Display Processor driver",
+ .date = "20160106",
+ .major = 1,
+ .minor = 0,
+};
+
+static const struct of_device_id malidp_drm_of_match[] = {
+ {
+ .compatible = "arm,mali-dp500",
+ .data = &malidp_device[MALIDP_500]
+ },
+ {
+ .compatible = "arm,mali-dp550",
+ .data = &malidp_device[MALIDP_550]
+ },
+ {
+ .compatible = "arm,mali-dp650",
+ .data = &malidp_device[MALIDP_650]
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, malidp_drm_of_match);
+
+#define MAX_OUTPUT_CHANNELS 3
+
+static int malidp_bind(struct device *dev)
+{
+ struct resource *res;
+ struct drm_device *drm;
+ struct malidp_drm *malidp;
+ struct malidp_hw_device *hwdev;
+ struct platform_device *pdev = to_platform_device(dev);
+ /* number of lines for the R, G and B output */
+ u8 output_width[MAX_OUTPUT_CHANNELS];
+ int ret = 0, i;
+ u32 version, out_depth = 0;
+
+ malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL);
+ if (!malidp)
+ return -ENOMEM;
+
+ hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL);
+ if (!hwdev)
+ return -ENOMEM;
+
+ /*
+ * copy the associated data from malidp_drm_of_match to avoid
+ * having to keep a reference to the OF node after binding
+ */
+ memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev));
+ malidp->dev = hwdev;
+
+ INIT_LIST_HEAD(&malidp->event_list);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hwdev->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(hwdev->regs)) {
+ DRM_ERROR("Failed to map control registers area\n");
+ return PTR_ERR(hwdev->regs);
+ }
+
+ hwdev->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(hwdev->pclk))
+ return PTR_ERR(hwdev->pclk);
+
+ hwdev->aclk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(hwdev->aclk))
+ return PTR_ERR(hwdev->aclk);
+
+ hwdev->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(hwdev->mclk))
+ return PTR_ERR(hwdev->mclk);
+
+ hwdev->pxlclk = devm_clk_get(dev, "pxlclk");
+ if (IS_ERR(hwdev->pxlclk))
+ return PTR_ERR(hwdev->pxlclk);
+
+ /* Get the optional framebuffer memory resource */
+ ret = of_reserved_mem_device_init(dev);
+ if (ret && ret != -ENODEV)
+ return ret;
+
+ drm = drm_dev_alloc(&malidp_driver, dev);
+ if (!drm) {
+ ret = -ENOMEM;
+ goto alloc_fail;
+ }
+
+ /* Enable APB clock in order to get access to the registers */
+ clk_prepare_enable(hwdev->pclk);
+ /*
+ * Enable AXI clock and main clock so that prefetch can start once
+ * the registers are set
+ */
+ clk_prepare_enable(hwdev->aclk);
+ clk_prepare_enable(hwdev->mclk);
+
+ ret = hwdev->query_hw(hwdev);
+ if (ret) {
+ DRM_ERROR("Invalid HW configuration\n");
+ goto query_hw_fail;
+ }
+
+ version = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_DE_CORE_ID);
+ DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16,
+ (version >> 12) & 0xf, (version >> 8) & 0xf);
+
+ /* set the number of lines used for output of RGB data */
+ ret = of_property_read_u8_array(dev->of_node,
+ "arm,malidp-output-port-lines",
+ output_width, MAX_OUTPUT_CHANNELS);
+ if (ret)
+ goto query_hw_fail;
+
+ for (i = 0; i < MAX_OUTPUT_CHANNELS; i++)
+ out_depth = (out_depth << 8) | (output_width[i] & 0xf);
+ malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base);
+
+ drm->dev_private = malidp;
+ dev_set_drvdata(dev, drm);
+ atomic_set(&malidp->config_valid, 0);
+ init_waitqueue_head(&malidp->wq);
+
+ ret = malidp_init(drm);
+ if (ret < 0)
+ goto init_fail;
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ goto register_fail;
+
+ /* Set the CRTC's port so that the encoder component can find it */
+ malidp->crtc.port = of_graph_get_next_endpoint(dev->of_node, NULL);
+
+ ret = component_bind_all(dev, drm);
+ of_node_put(malidp->crtc.port);
+
+ if (ret) {
+ DRM_ERROR("Failed to bind all components\n");
+ goto bind_fail;
+ }
+
+ ret = malidp_irq_init(pdev);
+ if (ret < 0)
+ goto irq_init_fail;
+
+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialise vblank\n");
+ goto vblank_fail;
+ }
+
+ drm_mode_config_reset(drm);
+
+ malidp->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+
+ if (IS_ERR(malidp->fbdev)) {
+ ret = PTR_ERR(malidp->fbdev);
+ malidp->fbdev = NULL;
+ goto fbdev_fail;
+ }
+
+ drm_kms_helper_poll_init(drm);
+ return 0;
+
+fbdev_fail:
+ drm_vblank_cleanup(drm);
+vblank_fail:
+ malidp_se_irq_fini(drm);
+ malidp_de_irq_fini(drm);
+irq_init_fail:
+ component_unbind_all(dev, drm);
+bind_fail:
+ drm_dev_unregister(drm);
+register_fail:
+ malidp_de_planes_destroy(drm);
+ drm_mode_config_cleanup(drm);
+init_fail:
+ drm->dev_private = NULL;
+ dev_set_drvdata(dev, NULL);
+query_hw_fail:
+ clk_disable_unprepare(hwdev->mclk);
+ clk_disable_unprepare(hwdev->aclk);
+ clk_disable_unprepare(hwdev->pclk);
+ drm_dev_unref(drm);
+alloc_fail:
+ of_reserved_mem_device_release(dev);
+
+ return ret;
+}
+
+static void malidp_unbind(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+
+ if (malidp->fbdev) {
+ drm_fbdev_cma_fini(malidp->fbdev);
+ malidp->fbdev = NULL;
+ }
+ drm_kms_helper_poll_fini(drm);
+ malidp_se_irq_fini(drm);
+ malidp_de_irq_fini(drm);
+ drm_vblank_cleanup(drm);
+ component_unbind_all(dev, drm);
+ drm_dev_unregister(drm);
+ malidp_de_planes_destroy(drm);
+ drm_mode_config_cleanup(drm);
+ drm->dev_private = NULL;
+ dev_set_drvdata(dev, NULL);
+ clk_disable_unprepare(hwdev->mclk);
+ clk_disable_unprepare(hwdev->aclk);
+ clk_disable_unprepare(hwdev->pclk);
+ drm_dev_unref(drm);
+ of_reserved_mem_device_release(dev);
+}
+
+static const struct component_master_ops malidp_master_ops = {
+ .bind = malidp_bind,
+ .unbind = malidp_unbind,
+};
+
+static int malidp_compare_dev(struct device *dev, void *data)
+{
+ struct device_node *np = data;
+
+ return dev->of_node == np;
+}
+
+static int malidp_platform_probe(struct platform_device *pdev)
+{
+ struct device_node *port, *ep;
+ struct component_match *match = NULL;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ /* there is only one output port inside each device, find it */
+ ep = of_graph_get_next_endpoint(pdev->dev.of_node, NULL);
+ if (!ep)
+ return -ENODEV;
+
+ if (!of_device_is_available(ep)) {
+ of_node_put(ep);
+ return -ENODEV;
+ }
+
+ /* add the remote encoder port as component */
+ port = of_graph_get_remote_port_parent(ep);
+ of_node_put(ep);
+ if (!port || !of_device_is_available(port)) {
+ of_node_put(port);
+ return -EAGAIN;
+ }
+
+ component_match_add(&pdev->dev, &match, malidp_compare_dev, port);
+ return component_master_add_with_match(&pdev->dev, &malidp_master_ops,
+ match);
+}
+
+static int malidp_platform_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &malidp_master_ops);
+ return 0;
+}
+
+static struct platform_driver malidp_platform_driver = {
+ .probe = malidp_platform_probe,
+ .remove = malidp_platform_remove,
+ .driver = {
+ .name = "mali-dp",
+ .of_match_table = malidp_drm_of_match,
+ },
+};
+
+module_platform_driver(malidp_platform_driver);
+
+MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>");
+MODULE_DESCRIPTION("ARM Mali DP DRM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h
new file mode 100644
index 000000000000..95558fde214b
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_drv.h
@@ -0,0 +1,54 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ * Author: Liviu Dudau <Liviu.Dudau@arm.com>
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * ARM Mali DP500/DP550/DP650 KMS/DRM driver structures
+ */
+
+#ifndef __MALIDP_DRV_H__
+#define __MALIDP_DRV_H__
+
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include "malidp_hw.h"
+
+struct malidp_drm {
+ struct malidp_hw_device *dev;
+ struct drm_fbdev_cma *fbdev;
+ struct list_head event_list;
+ struct drm_crtc crtc;
+ wait_queue_head_t wq;
+ atomic_t config_valid;
+};
+
+#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
+
+struct malidp_plane {
+ struct drm_plane base;
+ struct malidp_hw_device *hwdev;
+ const struct malidp_layer *layer;
+};
+
+struct malidp_plane_state {
+ struct drm_plane_state base;
+
+ /* size of the required rotation memory if plane is rotated */
+ u32 rotmem_size;
+};
+
+#define to_malidp_plane(x) container_of(x, struct malidp_plane, base)
+#define to_malidp_plane_state(x) container_of(x, struct malidp_plane_state, base)
+
+int malidp_de_planes_init(struct drm_device *drm);
+void malidp_de_planes_destroy(struct drm_device *drm);
+int malidp_crtc_init(struct drm_device *drm);
+
+/* often used combination of rotational bits */
+#define MALIDP_ROTATED_MASK (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))
+
+#endif /* __MALIDP_DRV_H__ */
diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
new file mode 100644
index 000000000000..a6132f1d58c1
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_hw.c
@@ -0,0 +1,691 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ * Author: Liviu Dudau <Liviu.Dudau@arm.com>
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * ARM Mali DP500/DP550/DP650 hardware manipulation routines. This is where
+ * the difference between various versions of the hardware is being dealt with
+ * in an attempt to provide to the rest of the driver code a unified view
+ */
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <drm/drmP.h>
+#include <video/videomode.h>
+#include <video/display_timing.h>
+
+#include "malidp_drv.h"
+#include "malidp_hw.h"
+
+static const struct malidp_input_format malidp500_de_formats[] = {
+ /* fourcc, layers supporting the format, internal id */
+ { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 0 },
+ { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 1 },
+ { DRM_FORMAT_ARGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 2 },
+ { DRM_FORMAT_ABGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 3 },
+ { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 4 },
+ { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 5 },
+ { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 6 },
+ { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 7 },
+ { DRM_FORMAT_RGBA5551, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 8 },
+ { DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 9 },
+ { DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 10 },
+ { DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 11 },
+ { DRM_FORMAT_UYVY, DE_VIDEO1, 12 },
+ { DRM_FORMAT_YUYV, DE_VIDEO1, 13 },
+ { DRM_FORMAT_NV12, DE_VIDEO1, 14 },
+ { DRM_FORMAT_YUV420, DE_VIDEO1, 15 },
+};
+
+#define MALIDP_ID(__group, __format) \
+ ((((__group) & 0x7) << 3) | ((__format) & 0x7))
+
+#define MALIDP_COMMON_FORMATS \
+ /* fourcc, layers supporting the format, internal id */ \
+ { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 0) }, \
+ { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 1) }, \
+ { DRM_FORMAT_RGBA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 2) }, \
+ { DRM_FORMAT_BGRA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 3) }, \
+ { DRM_FORMAT_ARGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 0) }, \
+ { DRM_FORMAT_ABGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 1) }, \
+ { DRM_FORMAT_RGBA8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 2) }, \
+ { DRM_FORMAT_BGRA8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 3) }, \
+ { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 0) }, \
+ { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 1) }, \
+ { DRM_FORMAT_RGBX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 2) }, \
+ { DRM_FORMAT_BGRX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 3) }, \
+ { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 0) }, \
+ { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 1) }, \
+ { DRM_FORMAT_RGBA5551, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 0) }, \
+ { DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 1) }, \
+ { DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 2) }, \
+ { DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 3) }, \
+ { DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) }, \
+ { DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) }, \
+ { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6) }, \
+ { DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) }
+
+static const struct malidp_input_format malidp550_de_formats[] = {
+ MALIDP_COMMON_FORMATS,
+};
+
+static const struct malidp_layer malidp500_layers[] = {
+ { DE_VIDEO1, MALIDP500_DE_LV_BASE, MALIDP500_DE_LV_PTR_BASE },
+ { DE_GRAPHICS1, MALIDP500_DE_LG1_BASE, MALIDP500_DE_LG1_PTR_BASE },
+ { DE_GRAPHICS2, MALIDP500_DE_LG2_BASE, MALIDP500_DE_LG2_PTR_BASE },
+};
+
+static const struct malidp_layer malidp550_layers[] = {
+ { DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE },
+ { DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE },
+ { DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE },
+ { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE },
+};
+
+#define MALIDP_DE_DEFAULT_PREFETCH_START 5
+
+static int malidp500_query_hw(struct malidp_hw_device *hwdev)
+{
+ u32 conf = malidp_hw_read(hwdev, MALIDP500_CONFIG_ID);
+ /* bit 4 of the CONFIG_ID register holds the line size multiplier */
+ u8 ln_size_mult = conf & 0x10 ? 2 : 1;
+
+ hwdev->min_line_size = 2;
+ hwdev->max_line_size = SZ_2K * ln_size_mult;
+ hwdev->rotation_memory[0] = SZ_1K * 64 * ln_size_mult;
+ hwdev->rotation_memory[1] = 0; /* no second rotation memory bank */
+
+ return 0;
+}
+
+static void malidp500_enter_config_mode(struct malidp_hw_device *hwdev)
+{
+ u32 status, count = 100;
+
+ malidp_hw_setbits(hwdev, MALIDP500_DC_CONFIG_REQ, MALIDP500_DC_CONTROL);
+ while (count) {
+ status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS);
+ if ((status & MALIDP500_DC_CONFIG_REQ) == MALIDP500_DC_CONFIG_REQ)
+ break;
+ /*
+ * entering config mode can take as long as the rendering
+ * of a full frame, hence the long sleep here
+ */
+ usleep_range(1000, 10000);
+ count--;
+ }
+ WARN(count == 0, "timeout while entering config mode");
+}
+
+static void malidp500_leave_config_mode(struct malidp_hw_device *hwdev)
+{
+ u32 status, count = 100;
+
+ malidp_hw_clearbits(hwdev, MALIDP500_DC_CONFIG_REQ, MALIDP500_DC_CONTROL);
+ while (count) {
+ status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS);
+ if ((status & MALIDP500_DC_CONFIG_REQ) == 0)
+ break;
+ usleep_range(100, 1000);
+ count--;
+ }
+ WARN(count == 0, "timeout while leaving config mode");
+}
+
+static bool malidp500_in_config_mode(struct malidp_hw_device *hwdev)
+{
+ u32 status;
+
+ status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS);
+ if ((status & MALIDP500_DC_CONFIG_REQ) == MALIDP500_DC_CONFIG_REQ)
+ return true;
+
+ return false;
+}
+
+static void malidp500_set_config_valid(struct malidp_hw_device *hwdev)
+{
+ malidp_hw_setbits(hwdev, MALIDP_CFG_VALID, MALIDP500_CONFIG_VALID);
+}
+
+static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode *mode)
+{
+ u32 val = 0;
+
+ malidp_hw_clearbits(hwdev, MALIDP500_DC_CLEAR_MASK, MALIDP500_DC_CONTROL);
+ if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+ val |= MALIDP500_HSYNCPOL;
+ if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ val |= MALIDP500_VSYNCPOL;
+ val |= MALIDP_DE_DEFAULT_PREFETCH_START;
+ malidp_hw_setbits(hwdev, val, MALIDP500_DC_CONTROL);
+
+ /*
+ * Mali-DP500 encodes the background color like this:
+ * - red @ MALIDP500_BGND_COLOR[12:0]
+ * - green @ MALIDP500_BGND_COLOR[27:16]
+ * - blue @ (MALIDP500_BGND_COLOR + 4)[12:0]
+ */
+ val = ((MALIDP_BGND_COLOR_G & 0xfff) << 16) |
+ (MALIDP_BGND_COLOR_R & 0xfff);
+ malidp_hw_write(hwdev, val, MALIDP500_BGND_COLOR);
+ malidp_hw_write(hwdev, MALIDP_BGND_COLOR_B, MALIDP500_BGND_COLOR + 4);
+
+ val = MALIDP_DE_H_FRONTPORCH(mode->hfront_porch) |
+ MALIDP_DE_H_BACKPORCH(mode->hback_porch);
+ malidp_hw_write(hwdev, val, MALIDP500_TIMINGS_BASE + MALIDP_DE_H_TIMINGS);
+
+ val = MALIDP500_DE_V_FRONTPORCH(mode->vfront_porch) |
+ MALIDP_DE_V_BACKPORCH(mode->vback_porch);
+ malidp_hw_write(hwdev, val, MALIDP500_TIMINGS_BASE + MALIDP_DE_V_TIMINGS);
+
+ val = MALIDP_DE_H_SYNCWIDTH(mode->hsync_len) |
+ MALIDP_DE_V_SYNCWIDTH(mode->vsync_len);
+ malidp_hw_write(hwdev, val, MALIDP500_TIMINGS_BASE + MALIDP_DE_SYNC_WIDTH);
+
+ val = MALIDP_DE_H_ACTIVE(mode->hactive) | MALIDP_DE_V_ACTIVE(mode->vactive);
+ malidp_hw_write(hwdev, val, MALIDP500_TIMINGS_BASE + MALIDP_DE_HV_ACTIVE);
+
+ if (mode->flags & DISPLAY_FLAGS_INTERLACED)
+ malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
+ else
+ malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
+}
+
+static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt)
+{
+ unsigned int depth;
+ int bpp;
+
+ /* RGB888 or BGR888 can't be rotated */
+ if ((fmt == DRM_FORMAT_RGB888) || (fmt == DRM_FORMAT_BGR888))
+ return -EINVAL;
+
+ /*
+ * Each layer needs enough rotation memory to fit 8 lines
+ * worth of pixel data. Required size is then:
+ * size = rotated_width * (bpp / 8) * 8;
+ */
+ drm_fb_get_bpp_depth(fmt, &depth, &bpp);
+
+ return w * bpp;
+}
+
+static int malidp550_query_hw(struct malidp_hw_device *hwdev)
+{
+ u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
+ u8 ln_size = (conf >> 4) & 0x3, rsize;
+
+ hwdev->min_line_size = 2;
+
+ switch (ln_size) {
+ case 0:
+ hwdev->max_line_size = SZ_2K;
+ /* two banks of 64KB for rotation memory */
+ rsize = 64;
+ break;
+ case 1:
+ hwdev->max_line_size = SZ_4K;
+ /* two banks of 128KB for rotation memory */
+ rsize = 128;
+ break;
+ case 2:
+ hwdev->max_line_size = 1280;
+ /* two banks of 40KB for rotation memory */
+ rsize = 40;
+ break;
+ case 3:
+ /* reserved value */
+ hwdev->max_line_size = 0;
+ return -EINVAL;
+ }
+
+ hwdev->rotation_memory[0] = hwdev->rotation_memory[1] = rsize * SZ_1K;
+ return 0;
+}
+
+static void malidp550_enter_config_mode(struct malidp_hw_device *hwdev)
+{
+ u32 status, count = 100;
+
+ malidp_hw_setbits(hwdev, MALIDP550_DC_CONFIG_REQ, MALIDP550_DC_CONTROL);
+ while (count) {
+ status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS);
+ if ((status & MALIDP550_DC_CONFIG_REQ) == MALIDP550_DC_CONFIG_REQ)
+ break;
+ /*
+ * entering config mode can take as long as the rendering
+ * of a full frame, hence the long sleep here
+ */
+ usleep_range(1000, 10000);
+ count--;
+ }
+ WARN(count == 0, "timeout while entering config mode");
+}
+
+static void malidp550_leave_config_mode(struct malidp_hw_device *hwdev)
+{
+ u32 status, count = 100;
+
+ malidp_hw_clearbits(hwdev, MALIDP550_DC_CONFIG_REQ, MALIDP550_DC_CONTROL);
+ while (count) {
+ status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS);
+ if ((status & MALIDP550_DC_CONFIG_REQ) == 0)
+ break;
+ usleep_range(100, 1000);
+ count--;
+ }
+ WARN(count == 0, "timeout while leaving config mode");
+}
+
+static bool malidp550_in_config_mode(struct malidp_hw_device *hwdev)
+{
+ u32 status;
+
+ status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS);
+ if ((status & MALIDP550_DC_CONFIG_REQ) == MALIDP550_DC_CONFIG_REQ)
+ return true;
+
+ return false;
+}
+
+static void malidp550_set_config_valid(struct malidp_hw_device *hwdev)
+{
+ malidp_hw_setbits(hwdev, MALIDP_CFG_VALID, MALIDP550_CONFIG_VALID);
+}
+
+static void malidp550_modeset(struct malidp_hw_device *hwdev, struct videomode *mode)
+{
+ u32 val = MALIDP_DE_DEFAULT_PREFETCH_START;
+
+ malidp_hw_write(hwdev, val, MALIDP550_DE_CONTROL);
+ /*
+ * Mali-DP550 and Mali-DP650 encode the background color like this:
+ * - red @ MALIDP550_DE_BGND_COLOR[23:16]
+ * - green @ MALIDP550_DE_BGND_COLOR[15:8]
+ * - blue @ MALIDP550_DE_BGND_COLOR[7:0]
+ *
+ * We need to truncate the least significant 4 bits from the default
+ * MALIDP_BGND_COLOR_x values
+ */
+ val = (((MALIDP_BGND_COLOR_R >> 4) & 0xff) << 16) |
+ (((MALIDP_BGND_COLOR_G >> 4) & 0xff) << 8) |
+ ((MALIDP_BGND_COLOR_B >> 4) & 0xff);
+ malidp_hw_write(hwdev, val, MALIDP550_DE_BGND_COLOR);
+
+ val = MALIDP_DE_H_FRONTPORCH(mode->hfront_porch) |
+ MALIDP_DE_H_BACKPORCH(mode->hback_porch);
+ malidp_hw_write(hwdev, val, MALIDP550_TIMINGS_BASE + MALIDP_DE_H_TIMINGS);
+
+ val = MALIDP550_DE_V_FRONTPORCH(mode->vfront_porch) |
+ MALIDP_DE_V_BACKPORCH(mode->vback_porch);
+ malidp_hw_write(hwdev, val, MALIDP550_TIMINGS_BASE + MALIDP_DE_V_TIMINGS);
+
+ val = MALIDP_DE_H_SYNCWIDTH(mode->hsync_len) |
+ MALIDP_DE_V_SYNCWIDTH(mode->vsync_len);
+ if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+ val |= MALIDP550_HSYNCPOL;
+ if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ val |= MALIDP550_VSYNCPOL;
+ malidp_hw_write(hwdev, val, MALIDP550_TIMINGS_BASE + MALIDP_DE_SYNC_WIDTH);
+
+ val = MALIDP_DE_H_ACTIVE(mode->hactive) | MALIDP_DE_V_ACTIVE(mode->vactive);
+ malidp_hw_write(hwdev, val, MALIDP550_TIMINGS_BASE + MALIDP_DE_HV_ACTIVE);
+
+ if (mode->flags & DISPLAY_FLAGS_INTERLACED)
+ malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
+ else
+ malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
+}
+
+static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt)
+{
+ u32 bytes_per_col;
+
+ /* raw RGB888 or BGR888 can't be rotated */
+ if ((fmt == DRM_FORMAT_RGB888) || (fmt == DRM_FORMAT_BGR888))
+ return -EINVAL;
+
+ switch (fmt) {
+ /* 8 lines at 4 bytes per pixel */
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ /* 16 lines at 2 bytes per pixel */
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_YUYV:
+ bytes_per_col = 32;
+ break;
+ /* 16 lines at 1.5 bytes per pixel */
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_YUV420:
+ bytes_per_col = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return w * bytes_per_col;
+}
+
+static int malidp650_query_hw(struct malidp_hw_device *hwdev)
+{
+ u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
+ u8 ln_size = (conf >> 4) & 0x3, rsize;
+
+ hwdev->min_line_size = 4;
+
+ switch (ln_size) {
+ case 0:
+ case 2:
+ /* reserved values */
+ hwdev->max_line_size = 0;
+ return -EINVAL;
+ case 1:
+ hwdev->max_line_size = SZ_4K;
+ /* two banks of 128KB for rotation memory */
+ rsize = 128;
+ break;
+ case 3:
+ hwdev->max_line_size = 2560;
+ /* two banks of 80KB for rotation memory */
+ rsize = 80;
+ }
+
+ hwdev->rotation_memory[0] = hwdev->rotation_memory[1] = rsize * SZ_1K;
+ return 0;
+}
+
+const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
+ [MALIDP_500] = {
+ .map = {
+ .se_base = MALIDP500_SE_BASE,
+ .dc_base = MALIDP500_DC_BASE,
+ .out_depth_base = MALIDP500_OUTPUT_DEPTH,
+ .features = 0, /* no CLEARIRQ register */
+ .n_layers = ARRAY_SIZE(malidp500_layers),
+ .layers = malidp500_layers,
+ .de_irq_map = {
+ .irq_mask = MALIDP_DE_IRQ_UNDERRUN |
+ MALIDP500_DE_IRQ_AXI_ERR |
+ MALIDP500_DE_IRQ_VSYNC |
+ MALIDP500_DE_IRQ_GLOBAL,
+ .vsync_irq = MALIDP500_DE_IRQ_VSYNC,
+ },
+ .se_irq_map = {
+ .irq_mask = MALIDP500_SE_IRQ_CONF_MODE,
+ .vsync_irq = 0,
+ },
+ .dc_irq_map = {
+ .irq_mask = MALIDP500_DE_IRQ_CONF_VALID,
+ .vsync_irq = MALIDP500_DE_IRQ_CONF_VALID,
+ },
+ .input_formats = malidp500_de_formats,
+ .n_input_formats = ARRAY_SIZE(malidp500_de_formats),
+ },
+ .query_hw = malidp500_query_hw,
+ .enter_config_mode = malidp500_enter_config_mode,
+ .leave_config_mode = malidp500_leave_config_mode,
+ .in_config_mode = malidp500_in_config_mode,
+ .set_config_valid = malidp500_set_config_valid,
+ .modeset = malidp500_modeset,
+ .rotmem_required = malidp500_rotmem_required,
+ },
+ [MALIDP_550] = {
+ .map = {
+ .se_base = MALIDP550_SE_BASE,
+ .dc_base = MALIDP550_DC_BASE,
+ .out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
+ .features = MALIDP_REGMAP_HAS_CLEARIRQ,
+ .n_layers = ARRAY_SIZE(malidp550_layers),
+ .layers = malidp550_layers,
+ .de_irq_map = {
+ .irq_mask = MALIDP_DE_IRQ_UNDERRUN |
+ MALIDP550_DE_IRQ_VSYNC,
+ .vsync_irq = MALIDP550_DE_IRQ_VSYNC,
+ },
+ .se_irq_map = {
+ .irq_mask = MALIDP550_SE_IRQ_EOW |
+ MALIDP550_SE_IRQ_AXI_ERR,
+ },
+ .dc_irq_map = {
+ .irq_mask = MALIDP550_DC_IRQ_CONF_VALID,
+ .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID,
+ },
+ .input_formats = malidp550_de_formats,
+ .n_input_formats = ARRAY_SIZE(malidp550_de_formats),
+ },
+ .query_hw = malidp550_query_hw,
+ .enter_config_mode = malidp550_enter_config_mode,
+ .leave_config_mode = malidp550_leave_config_mode,
+ .in_config_mode = malidp550_in_config_mode,
+ .set_config_valid = malidp550_set_config_valid,
+ .modeset = malidp550_modeset,
+ .rotmem_required = malidp550_rotmem_required,
+ },
+ [MALIDP_650] = {
+ .map = {
+ .se_base = MALIDP550_SE_BASE,
+ .dc_base = MALIDP550_DC_BASE,
+ .out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
+ .features = MALIDP_REGMAP_HAS_CLEARIRQ,
+ .n_layers = ARRAY_SIZE(malidp550_layers),
+ .layers = malidp550_layers,
+ .de_irq_map = {
+ .irq_mask = MALIDP_DE_IRQ_UNDERRUN |
+ MALIDP650_DE_IRQ_DRIFT |
+ MALIDP550_DE_IRQ_VSYNC,
+ .vsync_irq = MALIDP550_DE_IRQ_VSYNC,
+ },
+ .se_irq_map = {
+ .irq_mask = MALIDP550_SE_IRQ_EOW |
+ MALIDP550_SE_IRQ_AXI_ERR,
+ },
+ .dc_irq_map = {
+ .irq_mask = MALIDP550_DC_IRQ_CONF_VALID,
+ .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID,
+ },
+ .input_formats = malidp550_de_formats,
+ .n_input_formats = ARRAY_SIZE(malidp550_de_formats),
+ },
+ .query_hw = malidp650_query_hw,
+ .enter_config_mode = malidp550_enter_config_mode,
+ .leave_config_mode = malidp550_leave_config_mode,
+ .in_config_mode = malidp550_in_config_mode,
+ .set_config_valid = malidp550_set_config_valid,
+ .modeset = malidp550_modeset,
+ .rotmem_required = malidp550_rotmem_required,
+ },
+};
+
+u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
+ u8 layer_id, u32 format)
+{
+ unsigned int i;
+
+ for (i = 0; i < map->n_input_formats; i++) {
+ if (((map->input_formats[i].layer & layer_id) == layer_id) &&
+ (map->input_formats[i].format == format))
+ return map->input_formats[i].id;
+ }
+
+ return MALIDP_INVALID_FORMAT_ID;
+}
+
+static void malidp_hw_clear_irq(struct malidp_hw_device *hwdev, u8 block, u32 irq)
+{
+ u32 base = malidp_get_block_base(hwdev, block);
+
+ if (hwdev->map.features & MALIDP_REGMAP_HAS_CLEARIRQ)
+ malidp_hw_write(hwdev, irq, base + MALIDP_REG_CLEARIRQ);
+ else
+ malidp_hw_write(hwdev, irq, base + MALIDP_REG_STATUS);
+}
+
+static irqreturn_t malidp_de_irq(int irq, void *arg)
+{
+ struct drm_device *drm = arg;
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev;
+ const struct malidp_irq_map *de;
+ u32 status, mask, dc_status;
+ irqreturn_t ret = IRQ_NONE;
+
+ if (!drm->dev_private)
+ return IRQ_HANDLED;
+
+ hwdev = malidp->dev;
+ de = &hwdev->map.de_irq_map;
+
+ /* first handle the config valid IRQ */
+ dc_status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS);
+ if (dc_status & hwdev->map.dc_irq_map.vsync_irq) {
+ /* we have a page flip event */
+ atomic_set(&malidp->config_valid, 1);
+ malidp_hw_clear_irq(hwdev, MALIDP_DC_BLOCK, dc_status);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ status = malidp_hw_read(hwdev, MALIDP_REG_STATUS);
+ if (!(status & de->irq_mask))
+ return ret;
+
+ mask = malidp_hw_read(hwdev, MALIDP_REG_MASKIRQ);
+ status &= mask;
+ if (status & de->vsync_irq)
+ drm_crtc_handle_vblank(&malidp->crtc);
+
+ malidp_hw_clear_irq(hwdev, MALIDP_DE_BLOCK, status);
+
+ return (ret == IRQ_NONE) ? IRQ_HANDLED : ret;
+}
+
+static irqreturn_t malidp_de_irq_thread_handler(int irq, void *arg)
+{
+ struct drm_device *drm = arg;
+ struct malidp_drm *malidp = drm->dev_private;
+
+ wake_up(&malidp->wq);
+
+ return IRQ_HANDLED;
+}
+
+int malidp_de_irq_init(struct drm_device *drm, int irq)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+ int ret;
+
+ /* ensure interrupts are disabled */
+ malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK, 0xffffffff);
+ malidp_hw_clear_irq(hwdev, MALIDP_DE_BLOCK, 0xffffffff);
+ malidp_hw_disable_irq(hwdev, MALIDP_DC_BLOCK, 0xffffffff);
+ malidp_hw_clear_irq(hwdev, MALIDP_DC_BLOCK, 0xffffffff);
+
+ ret = devm_request_threaded_irq(drm->dev, irq, malidp_de_irq,
+ malidp_de_irq_thread_handler,
+ IRQF_SHARED, "malidp-de", drm);
+ if (ret < 0) {
+ DRM_ERROR("failed to install DE IRQ handler\n");
+ return ret;
+ }
+
+ /* first enable the DC block IRQs */
+ malidp_hw_enable_irq(hwdev, MALIDP_DC_BLOCK,
+ hwdev->map.dc_irq_map.irq_mask);
+
+ /* now enable the DE block IRQs */
+ malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK,
+ hwdev->map.de_irq_map.irq_mask);
+
+ return 0;
+}
+
+void malidp_de_irq_fini(struct drm_device *drm)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+
+ malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK,
+ hwdev->map.de_irq_map.irq_mask);
+ malidp_hw_disable_irq(hwdev, MALIDP_DC_BLOCK,
+ hwdev->map.dc_irq_map.irq_mask);
+}
+
+static irqreturn_t malidp_se_irq(int irq, void *arg)
+{
+ struct drm_device *drm = arg;
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+ u32 status, mask;
+
+ status = malidp_hw_read(hwdev, hwdev->map.se_base + MALIDP_REG_STATUS);
+ if (!(status & hwdev->map.se_irq_map.irq_mask))
+ return IRQ_NONE;
+
+ mask = malidp_hw_read(hwdev, hwdev->map.se_base + MALIDP_REG_MASKIRQ);
+ status = malidp_hw_read(hwdev, hwdev->map.se_base + MALIDP_REG_STATUS);
+ status &= mask;
+ /* ToDo: status decoding and firing up of VSYNC and page flip events */
+
+ malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, status);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t malidp_se_irq_thread_handler(int irq, void *arg)
+{
+ return IRQ_HANDLED;
+}
+
+int malidp_se_irq_init(struct drm_device *drm, int irq)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+ int ret;
+
+ /* ensure interrupts are disabled */
+ malidp_hw_disable_irq(hwdev, MALIDP_SE_BLOCK, 0xffffffff);
+ malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, 0xffffffff);
+
+ ret = devm_request_threaded_irq(drm->dev, irq, malidp_se_irq,
+ malidp_se_irq_thread_handler,
+ IRQF_SHARED, "malidp-se", drm);
+ if (ret < 0) {
+ DRM_ERROR("failed to install SE IRQ handler\n");
+ return ret;
+ }
+
+ malidp_hw_enable_irq(hwdev, MALIDP_SE_BLOCK,
+ hwdev->map.se_irq_map.irq_mask);
+
+ return 0;
+}
+
+void malidp_se_irq_fini(struct drm_device *drm)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct malidp_hw_device *hwdev = malidp->dev;
+
+ malidp_hw_disable_irq(hwdev, MALIDP_SE_BLOCK,
+ hwdev->map.se_irq_map.irq_mask);
+}
diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h
new file mode 100644
index 000000000000..141743e9f3a6
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_hw.h
@@ -0,0 +1,241 @@
+/*
+ *
+ * (C) COPYRIGHT 2013-2016 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * ARM Mali DP hardware manipulation routines.
+ */
+
+#ifndef __MALIDP_HW_H__
+#define __MALIDP_HW_H__
+
+#include <linux/bitops.h>
+#include "malidp_regs.h"
+
+struct videomode;
+struct clk;
+
+/* Mali DP IP blocks */
+enum {
+ MALIDP_DE_BLOCK = 0,
+ MALIDP_SE_BLOCK,
+ MALIDP_DC_BLOCK
+};
+
+/* Mali DP layer IDs */
+enum {
+ DE_VIDEO1 = BIT(0),
+ DE_GRAPHICS1 = BIT(1),
+ DE_GRAPHICS2 = BIT(2), /* used only in DP500 */
+ DE_VIDEO2 = BIT(3),
+ DE_SMART = BIT(4),
+};
+
+struct malidp_input_format {
+ u32 format; /* DRM fourcc */
+ u8 layer; /* bitmask of layers supporting it */
+ u8 id; /* used internally */
+};
+
+#define MALIDP_INVALID_FORMAT_ID 0xff
+
+/*
+ * hide the differences between register maps
+ * by using a common structure to hold the
+ * base register offsets
+ */
+
+struct malidp_irq_map {
+ u32 irq_mask; /* mask of IRQs that can be enabled in the block */
+ u32 vsync_irq; /* IRQ bit used for signaling during VSYNC */
+};
+
+struct malidp_layer {
+ u16 id; /* layer ID */
+ u16 base; /* address offset for the register bank */
+ u16 ptr; /* address offset for the pointer register */
+};
+
+/* regmap features */
+#define MALIDP_REGMAP_HAS_CLEARIRQ (1 << 0)
+
+struct malidp_hw_regmap {
+ /* address offset of the DE register bank */
+ /* is always 0x0000 */
+ /* address offset of the SE registers bank */
+ const u16 se_base;
+ /* address offset of the DC registers bank */
+ const u16 dc_base;
+
+ /* address offset for the output depth register */
+ const u16 out_depth_base;
+
+ /* bitmap with register map features */
+ const u8 features;
+
+ /* list of supported layers */
+ const u8 n_layers;
+ const struct malidp_layer *layers;
+
+ const struct malidp_irq_map de_irq_map;
+ const struct malidp_irq_map se_irq_map;
+ const struct malidp_irq_map dc_irq_map;
+
+ /* list of supported input formats for each layer */
+ const struct malidp_input_format *input_formats;
+ const u8 n_input_formats;
+};
+
+struct malidp_hw_device {
+ const struct malidp_hw_regmap map;
+ void __iomem *regs;
+
+ /* APB clock */
+ struct clk *pclk;
+ /* AXI clock */
+ struct clk *aclk;
+ /* main clock for display core */
+ struct clk *mclk;
+ /* pixel clock for display core */
+ struct clk *pxlclk;
+
+ /*
+ * Validate the driver instance against the hardware bits
+ */
+ int (*query_hw)(struct malidp_hw_device *hwdev);
+
+ /*
+ * Set the hardware into config mode, ready to accept mode changes
+ */
+ void (*enter_config_mode)(struct malidp_hw_device *hwdev);
+
+ /*
+ * Tell hardware to exit configuration mode
+ */
+ void (*leave_config_mode)(struct malidp_hw_device *hwdev);
+
+ /*
+ * Query if hardware is in configuration mode
+ */
+ bool (*in_config_mode)(struct malidp_hw_device *hwdev);
+
+ /*
+ * Set configuration valid flag for hardware parameters that can
+ * be changed outside the configuration mode. Hardware will use
+ * the new settings when config valid is set after the end of the
+ * current buffer scanout
+ */
+ void (*set_config_valid)(struct malidp_hw_device *hwdev);
+
+ /*
+ * Set a new mode in hardware. Requires the hardware to be in
+ * configuration mode before this function is called.
+ */
+ void (*modeset)(struct malidp_hw_device *hwdev, struct videomode *m);
+
+ /*
+ * Calculate the required rotation memory given the active area
+ * and the buffer format.
+ */
+ int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt);
+
+ u8 features;
+
+ u8 min_line_size;
+ u16 max_line_size;
+
+ /* size of memory used for rotating layers, up to two banks available */
+ u32 rotation_memory[2];
+};
+
+/* Supported variants of the hardware */
+enum {
+ MALIDP_500 = 0,
+ MALIDP_550,
+ MALIDP_650,
+ /* keep the next entry last */
+ MALIDP_MAX_DEVICES
+};
+
+extern const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES];
+
+static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg)
+{
+ return readl(hwdev->regs + reg);
+}
+
+static inline void malidp_hw_write(struct malidp_hw_device *hwdev,
+ u32 value, u32 reg)
+{
+ writel(value, hwdev->regs + reg);
+}
+
+static inline void malidp_hw_setbits(struct malidp_hw_device *hwdev,
+ u32 mask, u32 reg)
+{
+ u32 data = malidp_hw_read(hwdev, reg);
+
+ data |= mask;
+ malidp_hw_write(hwdev, data, reg);
+}
+
+static inline void malidp_hw_clearbits(struct malidp_hw_device *hwdev,
+ u32 mask, u32 reg)
+{
+ u32 data = malidp_hw_read(hwdev, reg);
+
+ data &= ~mask;
+ malidp_hw_write(hwdev, data, reg);
+}
+
+static inline u32 malidp_get_block_base(struct malidp_hw_device *hwdev,
+ u8 block)
+{
+ switch (block) {
+ case MALIDP_SE_BLOCK:
+ return hwdev->map.se_base;
+ case MALIDP_DC_BLOCK:
+ return hwdev->map.dc_base;
+ }
+
+ return 0;
+}
+
+static inline void malidp_hw_disable_irq(struct malidp_hw_device *hwdev,
+ u8 block, u32 irq)
+{
+ u32 base = malidp_get_block_base(hwdev, block);
+
+ malidp_hw_clearbits(hwdev, irq, base + MALIDP_REG_MASKIRQ);
+}
+
+static inline void malidp_hw_enable_irq(struct malidp_hw_device *hwdev,
+ u8 block, u32 irq)
+{
+ u32 base = malidp_get_block_base(hwdev, block);
+
+ malidp_hw_setbits(hwdev, irq, base + MALIDP_REG_MASKIRQ);
+}
+
+int malidp_de_irq_init(struct drm_device *drm, int irq);
+void malidp_de_irq_fini(struct drm_device *drm);
+int malidp_se_irq_init(struct drm_device *drm, int irq);
+void malidp_se_irq_fini(struct drm_device *drm);
+
+u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
+ u8 layer_id, u32 format);
+
+/*
+ * background color components are defined as 12bits values,
+ * they will be shifted right when stored on hardware that
+ * supports only 8bits per channel
+ */
+#define MALIDP_BGND_COLOR_R 0x000
+#define MALIDP_BGND_COLOR_G 0x000
+#define MALIDP_BGND_COLOR_B 0x000
+
+#endif /* __MALIDP_HW_H__ */
diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c
new file mode 100644
index 000000000000..725098d6179a
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_planes.c
@@ -0,0 +1,298 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ * Author: Liviu Dudau <Liviu.Dudau@arm.com>
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * ARM Mali DP plane manipulation routines.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "malidp_hw.h"
+#include "malidp_drv.h"
+
+/* Layer specific register offsets */
+#define MALIDP_LAYER_FORMAT 0x000
+#define MALIDP_LAYER_CONTROL 0x004
+#define LAYER_ENABLE (1 << 0)
+#define LAYER_ROT_OFFSET 8
+#define LAYER_H_FLIP (1 << 10)
+#define LAYER_V_FLIP (1 << 11)
+#define LAYER_ROT_MASK (0xf << 8)
+#define MALIDP_LAYER_SIZE 0x00c
+#define LAYER_H_VAL(x) (((x) & 0x1fff) << 0)
+#define LAYER_V_VAL(x) (((x) & 0x1fff) << 16)
+#define MALIDP_LAYER_COMP_SIZE 0x010
+#define MALIDP_LAYER_OFFSET 0x014
+#define MALIDP_LAYER_STRIDE 0x018
+
+static void malidp_de_plane_destroy(struct drm_plane *plane)
+{
+ struct malidp_plane *mp = to_malidp_plane(plane);
+
+ if (mp->base.fb)
+ drm_framebuffer_unreference(mp->base.fb);
+
+ drm_plane_helper_disable(plane);
+ drm_plane_cleanup(plane);
+ devm_kfree(plane->dev->dev, mp);
+}
+
+struct drm_plane_state *malidp_duplicate_plane_state(struct drm_plane *plane)
+{
+ struct malidp_plane_state *state, *m_state;
+
+ if (!plane->state)
+ return NULL;
+
+ state = kmalloc(sizeof(*state), GFP_KERNEL);
+ if (state) {
+ m_state = to_malidp_plane_state(plane->state);
+ __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
+ state->rotmem_size = m_state->rotmem_size;
+ }
+
+ return &state->base;
+}
+
+void malidp_destroy_plane_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct malidp_plane_state *m_state = to_malidp_plane_state(state);
+
+ __drm_atomic_helper_plane_destroy_state(state);
+ kfree(m_state);
+}
+
+static const struct drm_plane_funcs malidp_de_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = malidp_de_plane_destroy,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = malidp_duplicate_plane_state,
+ .atomic_destroy_state = malidp_destroy_plane_state,
+};
+
+static int malidp_de_plane_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct malidp_plane *mp = to_malidp_plane(plane);
+ struct malidp_plane_state *ms = to_malidp_plane_state(state);
+ u8 format_id;
+ u32 src_w, src_h;
+
+ if (!state->crtc || !state->fb)
+ return 0;
+
+ format_id = malidp_hw_get_format_id(&mp->hwdev->map, mp->layer->id,
+ state->fb->pixel_format);
+ if (format_id == MALIDP_INVALID_FORMAT_ID)
+ return -EINVAL;
+
+ src_w = state->src_w >> 16;
+ src_h = state->src_h >> 16;
+
+ if ((state->crtc_w > mp->hwdev->max_line_size) ||
+ (state->crtc_h > mp->hwdev->max_line_size) ||
+ (state->crtc_w < mp->hwdev->min_line_size) ||
+ (state->crtc_h < mp->hwdev->min_line_size) ||
+ (state->crtc_w != src_w) || (state->crtc_h != src_h))
+ return -EINVAL;
+
+ /* packed RGB888 / BGR888 can't be rotated or flipped */
+ if (state->rotation != BIT(DRM_ROTATE_0) &&
+ (state->fb->pixel_format == DRM_FORMAT_RGB888 ||
+ state->fb->pixel_format == DRM_FORMAT_BGR888))
+ return -EINVAL;
+
+ ms->rotmem_size = 0;
+ if (state->rotation & MALIDP_ROTATED_MASK) {
+ int val;
+
+ val = mp->hwdev->rotmem_required(mp->hwdev, state->crtc_h,
+ state->crtc_w,
+ state->fb->pixel_format);
+ if (val < 0)
+ return val;
+
+ ms->rotmem_size = val;
+ }
+
+ return 0;
+}
+
+static void malidp_de_plane_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_gem_cma_object *obj;
+ struct malidp_plane *mp;
+ const struct malidp_hw_regmap *map;
+ u8 format_id;
+ u16 ptr;
+ u32 format, src_w, src_h, dest_w, dest_h, val = 0;
+ int num_planes, i;
+
+ mp = to_malidp_plane(plane);
+
+ map = &mp->hwdev->map;
+ format = plane->state->fb->pixel_format;
+ format_id = malidp_hw_get_format_id(map, mp->layer->id, format);
+ num_planes = drm_format_num_planes(format);
+
+ /* convert src values from Q16 fixed point to integer */
+ src_w = plane->state->src_w >> 16;
+ src_h = plane->state->src_h >> 16;
+ if (plane->state->rotation & MALIDP_ROTATED_MASK) {
+ dest_w = plane->state->crtc_h;
+ dest_h = plane->state->crtc_w;
+ } else {
+ dest_w = plane->state->crtc_w;
+ dest_h = plane->state->crtc_h;
+ }
+
+ malidp_hw_write(mp->hwdev, format_id, mp->layer->base);
+
+ for (i = 0; i < num_planes; i++) {
+ /* calculate the offset for the layer's plane registers */
+ ptr = mp->layer->ptr + (i << 4);
+
+ obj = drm_fb_cma_get_gem_obj(plane->state->fb, i);
+ malidp_hw_write(mp->hwdev, lower_32_bits(obj->paddr), ptr);
+ malidp_hw_write(mp->hwdev, upper_32_bits(obj->paddr), ptr + 4);
+ malidp_hw_write(mp->hwdev, plane->state->fb->pitches[i],
+ mp->layer->base + MALIDP_LAYER_STRIDE);
+ }
+
+ malidp_hw_write(mp->hwdev, LAYER_H_VAL(src_w) | LAYER_V_VAL(src_h),
+ mp->layer->base + MALIDP_LAYER_SIZE);
+
+ malidp_hw_write(mp->hwdev, LAYER_H_VAL(dest_w) | LAYER_V_VAL(dest_h),
+ mp->layer->base + MALIDP_LAYER_COMP_SIZE);
+
+ malidp_hw_write(mp->hwdev, LAYER_H_VAL(plane->state->crtc_x) |
+ LAYER_V_VAL(plane->state->crtc_y),
+ mp->layer->base + MALIDP_LAYER_OFFSET);
+
+ /* first clear the rotation bits in the register */
+ malidp_hw_clearbits(mp->hwdev, LAYER_ROT_MASK,
+ mp->layer->base + MALIDP_LAYER_CONTROL);
+
+ /* setup the rotation and axis flip bits */
+ if (plane->state->rotation & DRM_ROTATE_MASK)
+ val = ilog2(plane->state->rotation & DRM_ROTATE_MASK) << LAYER_ROT_OFFSET;
+ if (plane->state->rotation & BIT(DRM_REFLECT_X))
+ val |= LAYER_V_FLIP;
+ if (plane->state->rotation & BIT(DRM_REFLECT_Y))
+ val |= LAYER_H_FLIP;
+
+ /* set the 'enable layer' bit */
+ val |= LAYER_ENABLE;
+
+ malidp_hw_setbits(mp->hwdev, val,
+ mp->layer->base + MALIDP_LAYER_CONTROL);
+}
+
+static void malidp_de_plane_disable(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct malidp_plane *mp = to_malidp_plane(plane);
+
+ malidp_hw_clearbits(mp->hwdev, LAYER_ENABLE,
+ mp->layer->base + MALIDP_LAYER_CONTROL);
+}
+
+static const struct drm_plane_helper_funcs malidp_de_plane_helper_funcs = {
+ .atomic_check = malidp_de_plane_check,
+ .atomic_update = malidp_de_plane_update,
+ .atomic_disable = malidp_de_plane_disable,
+};
+
+int malidp_de_planes_init(struct drm_device *drm)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ const struct malidp_hw_regmap *map = &malidp->dev->map;
+ struct malidp_plane *plane = NULL;
+ enum drm_plane_type plane_type;
+ unsigned long crtcs = 1 << drm->mode_config.num_crtc;
+ u32 *formats;
+ int ret, i, j, n;
+
+ formats = kcalloc(map->n_input_formats, sizeof(*formats), GFP_KERNEL);
+ if (!formats) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ for (i = 0; i < map->n_layers; i++) {
+ u8 id = map->layers[i].id;
+
+ plane = kzalloc(sizeof(*plane), GFP_KERNEL);
+ if (!plane) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ /* build the list of DRM supported formats based on the map */
+ for (n = 0, j = 0; j < map->n_input_formats; j++) {
+ if ((map->input_formats[j].layer & id) == id)
+ formats[n++] = map->input_formats[j].format;
+ }
+
+ plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY :
+ DRM_PLANE_TYPE_OVERLAY;
+ ret = drm_universal_plane_init(drm, &plane->base, crtcs,
+ &malidp_de_plane_funcs, formats,
+ n, plane_type, NULL);
+ if (ret < 0)
+ goto cleanup;
+
+ if (!drm->mode_config.rotation_property) {
+ unsigned long flags = BIT(DRM_ROTATE_0) |
+ BIT(DRM_ROTATE_90) |
+ BIT(DRM_ROTATE_180) |
+ BIT(DRM_ROTATE_270) |
+ BIT(DRM_REFLECT_X) |
+ BIT(DRM_REFLECT_Y);
+ drm->mode_config.rotation_property =
+ drm_mode_create_rotation_property(drm, flags);
+ }
+ /* SMART layer can't be rotated */
+ if (drm->mode_config.rotation_property && (id != DE_SMART))
+ drm_object_attach_property(&plane->base.base,
+ drm->mode_config.rotation_property,
+ BIT(DRM_ROTATE_0));
+
+ drm_plane_helper_add(&plane->base,
+ &malidp_de_plane_helper_funcs);
+ plane->hwdev = malidp->dev;
+ plane->layer = &map->layers[i];
+ }
+
+ kfree(formats);
+
+ return 0;
+
+cleanup:
+ malidp_de_planes_destroy(drm);
+ kfree(formats);
+
+ return ret;
+}
+
+void malidp_de_planes_destroy(struct drm_device *drm)
+{
+ struct drm_plane *p, *pt;
+
+ list_for_each_entry_safe(p, pt, &drm->mode_config.plane_list, head) {
+ drm_plane_cleanup(p);
+ kfree(p);
+ }
+}
diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h
new file mode 100644
index 000000000000..73fecb38f955
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_regs.h
@@ -0,0 +1,172 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ * Author: Liviu Dudau <Liviu.Dudau@arm.com>
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * ARM Mali DP500/DP550/DP650 registers definition.
+ */
+
+#ifndef __MALIDP_REGS_H__
+#define __MALIDP_REGS_H__
+
+/*
+ * abbreviations used:
+ * - DC - display core (general settings)
+ * - DE - display engine
+ * - SE - scaling engine
+ */
+
+/* interrupt bit masks */
+#define MALIDP_DE_IRQ_UNDERRUN (1 << 0)
+
+#define MALIDP500_DE_IRQ_AXI_ERR (1 << 4)
+#define MALIDP500_DE_IRQ_VSYNC (1 << 5)
+#define MALIDP500_DE_IRQ_PROG_LINE (1 << 6)
+#define MALIDP500_DE_IRQ_SATURATION (1 << 7)
+#define MALIDP500_DE_IRQ_CONF_VALID (1 << 8)
+#define MALIDP500_DE_IRQ_CONF_MODE (1 << 11)
+#define MALIDP500_DE_IRQ_CONF_ACTIVE (1 << 17)
+#define MALIDP500_DE_IRQ_PM_ACTIVE (1 << 18)
+#define MALIDP500_DE_IRQ_TESTMODE_ACTIVE (1 << 19)
+#define MALIDP500_DE_IRQ_FORCE_BLNK_ACTIVE (1 << 24)
+#define MALIDP500_DE_IRQ_AXI_BUSY (1 << 28)
+#define MALIDP500_DE_IRQ_GLOBAL (1 << 31)
+#define MALIDP500_SE_IRQ_CONF_MODE (1 << 0)
+#define MALIDP500_SE_IRQ_CONF_VALID (1 << 4)
+#define MALIDP500_SE_IRQ_INIT_BUSY (1 << 5)
+#define MALIDP500_SE_IRQ_AXI_ERROR (1 << 8)
+#define MALIDP500_SE_IRQ_OVERRUN (1 << 9)
+#define MALIDP500_SE_IRQ_PROG_LINE1 (1 << 12)
+#define MALIDP500_SE_IRQ_PROG_LINE2 (1 << 13)
+#define MALIDP500_SE_IRQ_CONF_ACTIVE (1 << 17)
+#define MALIDP500_SE_IRQ_PM_ACTIVE (1 << 18)
+#define MALIDP500_SE_IRQ_AXI_BUSY (1 << 28)
+#define MALIDP500_SE_IRQ_GLOBAL (1 << 31)
+
+#define MALIDP550_DE_IRQ_SATURATION (1 << 8)
+#define MALIDP550_DE_IRQ_VSYNC (1 << 12)
+#define MALIDP550_DE_IRQ_PROG_LINE (1 << 13)
+#define MALIDP550_DE_IRQ_AXI_ERR (1 << 16)
+#define MALIDP550_SE_IRQ_EOW (1 << 0)
+#define MALIDP550_SE_IRQ_AXI_ERR (1 << 16)
+#define MALIDP550_DC_IRQ_CONF_VALID (1 << 0)
+#define MALIDP550_DC_IRQ_CONF_MODE (1 << 4)
+#define MALIDP550_DC_IRQ_CONF_ACTIVE (1 << 16)
+#define MALIDP550_DC_IRQ_DE (1 << 20)
+#define MALIDP550_DC_IRQ_SE (1 << 24)
+
+#define MALIDP650_DE_IRQ_DRIFT (1 << 4)
+
+/* bit masks that are common between products */
+#define MALIDP_CFG_VALID (1 << 0)
+#define MALIDP_DISP_FUNC_ILACED (1 << 8)
+
+/* register offsets for IRQ management */
+#define MALIDP_REG_STATUS 0x00000
+#define MALIDP_REG_SETIRQ 0x00004
+#define MALIDP_REG_MASKIRQ 0x00008
+#define MALIDP_REG_CLEARIRQ 0x0000c
+
+/* register offsets */
+#define MALIDP_DE_CORE_ID 0x00018
+#define MALIDP_DE_DISPLAY_FUNC 0x00020
+
+/* these offsets are relative to MALIDP5x0_TIMINGS_BASE */
+#define MALIDP_DE_H_TIMINGS 0x0
+#define MALIDP_DE_V_TIMINGS 0x4
+#define MALIDP_DE_SYNC_WIDTH 0x8
+#define MALIDP_DE_HV_ACTIVE 0xc
+
+/* macros to set values into registers */
+#define MALIDP_DE_H_FRONTPORCH(x) (((x) & 0xfff) << 0)
+#define MALIDP_DE_H_BACKPORCH(x) (((x) & 0x3ff) << 16)
+#define MALIDP500_DE_V_FRONTPORCH(x) (((x) & 0xff) << 0)
+#define MALIDP550_DE_V_FRONTPORCH(x) (((x) & 0xfff) << 0)
+#define MALIDP_DE_V_BACKPORCH(x) (((x) & 0xff) << 16)
+#define MALIDP_DE_H_SYNCWIDTH(x) (((x) & 0x3ff) << 0)
+#define MALIDP_DE_V_SYNCWIDTH(x) (((x) & 0xff) << 16)
+#define MALIDP_DE_H_ACTIVE(x) (((x) & 0x1fff) << 0)
+#define MALIDP_DE_V_ACTIVE(x) (((x) & 0x1fff) << 16)
+
+/* register offsets and bits specific to DP500 */
+#define MALIDP500_DC_BASE 0x00000
+#define MALIDP500_DC_CONTROL 0x0000c
+#define MALIDP500_DC_CONFIG_REQ (1 << 17)
+#define MALIDP500_HSYNCPOL (1 << 20)
+#define MALIDP500_VSYNCPOL (1 << 21)
+#define MALIDP500_DC_CLEAR_MASK 0x300fff
+#define MALIDP500_DE_LINE_COUNTER 0x00010
+#define MALIDP500_DE_AXI_CONTROL 0x00014
+#define MALIDP500_DE_SECURE_CTRL 0x0001c
+#define MALIDP500_DE_CHROMA_KEY 0x00024
+#define MALIDP500_TIMINGS_BASE 0x00028
+
+#define MALIDP500_CONFIG_3D 0x00038
+#define MALIDP500_BGND_COLOR 0x0003c
+#define MALIDP500_OUTPUT_DEPTH 0x00044
+#define MALIDP500_YUV_RGB_COEF 0x00048
+#define MALIDP500_COLOR_ADJ_COEF 0x00078
+#define MALIDP500_COEF_TABLE_ADDR 0x000a8
+#define MALIDP500_COEF_TABLE_DATA 0x000ac
+#define MALIDP500_DE_LV_BASE 0x00100
+#define MALIDP500_DE_LV_PTR_BASE 0x00124
+#define MALIDP500_DE_LG1_BASE 0x00200
+#define MALIDP500_DE_LG1_PTR_BASE 0x0021c
+#define MALIDP500_DE_LG2_BASE 0x00300
+#define MALIDP500_DE_LG2_PTR_BASE 0x0031c
+#define MALIDP500_SE_BASE 0x00c00
+#define MALIDP500_SE_PTR_BASE 0x00e0c
+#define MALIDP500_DC_IRQ_BASE 0x00f00
+#define MALIDP500_CONFIG_VALID 0x00f00
+#define MALIDP500_CONFIG_ID 0x00fd4
+
+/* register offsets and bits specific to DP550/DP650 */
+#define MALIDP550_DE_CONTROL 0x00010
+#define MALIDP550_DE_LINE_COUNTER 0x00014
+#define MALIDP550_DE_AXI_CONTROL 0x00018
+#define MALIDP550_DE_QOS 0x0001c
+#define MALIDP550_TIMINGS_BASE 0x00030
+#define MALIDP550_HSYNCPOL (1 << 12)
+#define MALIDP550_VSYNCPOL (1 << 28)
+
+#define MALIDP550_DE_DISP_SIDEBAND 0x00040
+#define MALIDP550_DE_BGND_COLOR 0x00044
+#define MALIDP550_DE_OUTPUT_DEPTH 0x0004c
+#define MALIDP550_DE_COLOR_COEF 0x00050
+#define MALIDP550_DE_COEF_TABLE_ADDR 0x00080
+#define MALIDP550_DE_COEF_TABLE_DATA 0x00084
+#define MALIDP550_DE_LV1_BASE 0x00100
+#define MALIDP550_DE_LV1_PTR_BASE 0x00124
+#define MALIDP550_DE_LV2_BASE 0x00200
+#define MALIDP550_DE_LV2_PTR_BASE 0x00224
+#define MALIDP550_DE_LG_BASE 0x00300
+#define MALIDP550_DE_LG_PTR_BASE 0x0031c
+#define MALIDP550_DE_LS_BASE 0x00400
+#define MALIDP550_DE_LS_PTR_BASE 0x0042c
+#define MALIDP550_DE_PERF_BASE 0x00500
+#define MALIDP550_SE_BASE 0x08000
+#define MALIDP550_DC_BASE 0x0c000
+#define MALIDP550_DC_CONTROL 0x0c010
+#define MALIDP550_DC_CONFIG_REQ (1 << 16)
+#define MALIDP550_CONFIG_VALID 0x0c014
+#define MALIDP550_CONFIG_ID 0x0ffd4
+
+/*
+ * Starting with DP550 the register map blocks has been standardised to the
+ * following layout:
+ *
+ * Offset Block registers
+ * 0x00000 Display Engine
+ * 0x08000 Scaling Engine
+ * 0x0c000 Display Core
+ * 0x10000 Secure control
+ *
+ * The old DP500 IP mixes some DC with the DE registers, hence the need
+ * for a mapping structure.
+ */
+
+#endif /* __MALIDP_REGS_H__ */
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index cb21c0b6374a..f5ebdd681445 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -189,7 +189,6 @@ static struct drm_driver armada_drm_driver = {
.load = armada_drm_load,
.lastclose = armada_drm_lastclose,
.unload = armada_drm_unload,
- .set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = armada_drm_enable_vblank,
.disable_vblank = armada_drm_disable_vblank,
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 148e8a42b2c6..1ee707ef6b8d 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -121,6 +121,7 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
int ret;
ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip,
+ BIT(DRM_ROTATE_0),
0, INT_MAX, true, false, &visible);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
index 9ecf16c7911d..d4a3d61b7b06 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -691,13 +691,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev)
destroy_workqueue(dc->wq);
}
-static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev)
-{
- mutex_lock(&dev->mode_config.mutex);
- drm_connector_unregister_all(dev);
- mutex_unlock(&dev->mode_config.mutex);
-}
-
static void atmel_hlcdc_dc_lastclose(struct drm_device *dev)
{
struct atmel_hlcdc_dc *dc = dev->dev_private;
@@ -815,15 +808,8 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
if (ret)
goto err_unload;
- ret = drm_connector_register_all(ddev);
- if (ret)
- goto err_unregister;
-
return 0;
-err_unregister:
- drm_dev_unregister(ddev);
-
err_unload:
atmel_hlcdc_dc_unload(ddev);
@@ -837,7 +823,6 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
{
struct drm_device *ddev = platform_get_drvdata(pdev);
- atmel_hlcdc_dc_connector_unplug_all(ddev);
drm_dev_unregister(ddev);
atmel_hlcdc_dc_unload(ddev);
drm_dev_unref(ddev);
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 8f7423f18da5..a141921445f4 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -50,6 +50,14 @@ config DRM_PARADE_PS8622
---help---
Parade eDP-LVDS bridge chip driver.
+config DRM_SII902X
+ tristate "Silicon Image sii902x RGB/HDMI bridge"
+ depends on OF
+ select DRM_KMS_HELPER
+ select REGMAP_I2C
+ ---help---
+ Silicon Image sii902x bridge chip driver.
+
source "drivers/gpu/drm/bridge/analogix/Kconfig"
endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 96b13b30e6ab..bfec9f8cb9d2 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -5,4 +5,5 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
+obj-$(CONFIG_DRM_SII902X) += sii902x.o
obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
new file mode 100644
index 000000000000..9126d0306ab5
--- /dev/null
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2016 Atmel
+ * Bo Shen <voice.shen@atmel.com>
+ *
+ * Authors: Bo Shen <voice.shen@atmel.com>
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Wu, Songjun <Songjun.Wu@atmel.com>
+ *
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+#define SII902X_TPI_VIDEO_DATA 0x0
+
+#define SII902X_TPI_PIXEL_REPETITION 0x8
+#define SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT BIT(5)
+#define SII902X_TPI_AVI_PIXEL_REP_RISING_EDGE BIT(4)
+#define SII902X_TPI_AVI_PIXEL_REP_4X 3
+#define SII902X_TPI_AVI_PIXEL_REP_2X 1
+#define SII902X_TPI_AVI_PIXEL_REP_NONE 0
+#define SII902X_TPI_CLK_RATIO_HALF (0 << 6)
+#define SII902X_TPI_CLK_RATIO_1X (1 << 6)
+#define SII902X_TPI_CLK_RATIO_2X (2 << 6)
+#define SII902X_TPI_CLK_RATIO_4X (3 << 6)
+
+#define SII902X_TPI_AVI_IN_FORMAT 0x9
+#define SII902X_TPI_AVI_INPUT_BITMODE_12BIT BIT(7)
+#define SII902X_TPI_AVI_INPUT_DITHER BIT(6)
+#define SII902X_TPI_AVI_INPUT_RANGE_LIMITED (2 << 2)
+#define SII902X_TPI_AVI_INPUT_RANGE_FULL (1 << 2)
+#define SII902X_TPI_AVI_INPUT_RANGE_AUTO (0 << 2)
+#define SII902X_TPI_AVI_INPUT_COLORSPACE_BLACK (3 << 0)
+#define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV422 (2 << 0)
+#define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV444 (1 << 0)
+#define SII902X_TPI_AVI_INPUT_COLORSPACE_RGB (0 << 0)
+
+#define SII902X_TPI_AVI_INFOFRAME 0x0c
+
+#define SII902X_SYS_CTRL_DATA 0x1a
+#define SII902X_SYS_CTRL_PWR_DWN BIT(4)
+#define SII902X_SYS_CTRL_AV_MUTE BIT(3)
+#define SII902X_SYS_CTRL_DDC_BUS_REQ BIT(2)
+#define SII902X_SYS_CTRL_DDC_BUS_GRTD BIT(1)
+#define SII902X_SYS_CTRL_OUTPUT_MODE BIT(0)
+#define SII902X_SYS_CTRL_OUTPUT_HDMI 1
+#define SII902X_SYS_CTRL_OUTPUT_DVI 0
+
+#define SII902X_REG_CHIPID(n) (0x1b + (n))
+
+#define SII902X_PWR_STATE_CTRL 0x1e
+#define SII902X_AVI_POWER_STATE_MSK GENMASK(1, 0)
+#define SII902X_AVI_POWER_STATE_D(l) ((l) & SII902X_AVI_POWER_STATE_MSK)
+
+#define SII902X_INT_ENABLE 0x3c
+#define SII902X_INT_STATUS 0x3d
+#define SII902X_HOTPLUG_EVENT BIT(0)
+#define SII902X_PLUGGED_STATUS BIT(2)
+
+#define SII902X_REG_TPI_RQB 0xc7
+
+#define SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS 500
+
+struct sii902x {
+ struct i2c_client *i2c;
+ struct regmap *regmap;
+ struct drm_bridge bridge;
+ struct drm_connector connector;
+ struct gpio_desc *reset_gpio;
+};
+
+static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct sii902x, bridge);
+}
+
+static inline struct sii902x *connector_to_sii902x(struct drm_connector *con)
+{
+ return container_of(con, struct sii902x, connector);
+}
+
+static void sii902x_reset(struct sii902x *sii902x)
+{
+ if (!sii902x->reset_gpio)
+ return;
+
+ gpiod_set_value(sii902x->reset_gpio, 1);
+
+ /* The datasheet says treset-min = 100us. Make it 150us to be sure. */
+ usleep_range(150, 200);
+
+ gpiod_set_value(sii902x->reset_gpio, 0);
+}
+
+static enum drm_connector_status
+sii902x_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct sii902x *sii902x = connector_to_sii902x(connector);
+ unsigned int status;
+
+ regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);
+
+ return (status & SII902X_PLUGGED_STATUS) ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static const struct drm_connector_funcs sii902x_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .detect = sii902x_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int sii902x_get_modes(struct drm_connector *connector)
+{
+ struct sii902x *sii902x = connector_to_sii902x(connector);
+ struct regmap *regmap = sii902x->regmap;
+ u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ unsigned long timeout;
+ unsigned int status;
+ struct edid *edid;
+ int num = 0;
+ int ret;
+
+ ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
+ SII902X_SYS_CTRL_DDC_BUS_REQ,
+ SII902X_SYS_CTRL_DDC_BUS_REQ);
+ if (ret)
+ return ret;
+
+ timeout = jiffies +
+ msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
+ do {
+ ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
+ if (ret)
+ return ret;
+ } while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
+ time_before(jiffies, timeout));
+
+ if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
+ dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus");
+ return -ETIMEDOUT;
+ }
+
+ ret = regmap_write(regmap, SII902X_SYS_CTRL_DATA, status);
+ if (ret)
+ return ret;
+
+ edid = drm_get_edid(connector, sii902x->i2c->adapter);
+ drm_mode_connector_update_edid_property(connector, edid);
+ if (edid) {
+ num = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+ }
+
+ ret = drm_display_info_set_bus_formats(&connector->display_info,
+ &bus_format, 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
+ SII902X_SYS_CTRL_DDC_BUS_REQ |
+ SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);
+ if (ret)
+ return ret;
+
+ timeout = jiffies +
+ msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
+ do {
+ ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
+ if (ret)
+ return ret;
+ } while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
+ SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
+ time_before(jiffies, timeout));
+
+ if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
+ SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
+ dev_err(&sii902x->i2c->dev, "failed to release the i2c bus");
+ return -ETIMEDOUT;
+ }
+
+ return num;
+}
+
+static enum drm_mode_status sii902x_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ /* TODO: check mode */
+
+ return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs sii902x_connector_helper_funcs = {
+ .get_modes = sii902x_get_modes,
+ .mode_valid = sii902x_mode_valid,
+};
+
+static void sii902x_bridge_disable(struct drm_bridge *bridge)
+{
+ struct sii902x *sii902x = bridge_to_sii902x(bridge);
+
+ regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
+ SII902X_SYS_CTRL_PWR_DWN,
+ SII902X_SYS_CTRL_PWR_DWN);
+}
+
+static void sii902x_bridge_enable(struct drm_bridge *bridge)
+{
+ struct sii902x *sii902x = bridge_to_sii902x(bridge);
+
+ regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL,
+ SII902X_AVI_POWER_STATE_MSK,
+ SII902X_AVI_POWER_STATE_D(0));
+ regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
+ SII902X_SYS_CTRL_PWR_DWN, 0);
+}
+
+static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj)
+{
+ struct sii902x *sii902x = bridge_to_sii902x(bridge);
+ struct regmap *regmap = sii902x->regmap;
+ u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
+ struct hdmi_avi_infoframe frame;
+ int ret;
+
+ buf[0] = adj->clock;
+ buf[1] = adj->clock >> 8;
+ buf[2] = adj->vrefresh;
+ buf[3] = 0x00;
+ buf[4] = adj->hdisplay;
+ buf[5] = adj->hdisplay >> 8;
+ buf[6] = adj->vdisplay;
+ buf[7] = adj->vdisplay >> 8;
+ buf[8] = SII902X_TPI_CLK_RATIO_1X | SII902X_TPI_AVI_PIXEL_REP_NONE |
+ SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT;
+ buf[9] = SII902X_TPI_AVI_INPUT_RANGE_AUTO |
+ SII902X_TPI_AVI_INPUT_COLORSPACE_RGB;
+
+ ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10);
+ if (ret)
+ return;
+
+ ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, adj);
+ if (ret < 0) {
+ DRM_ERROR("couldn't fill AVI infoframe\n");
+ return;
+ }
+
+ ret = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));
+ if (ret < 0) {
+ DRM_ERROR("failed to pack AVI infoframe: %d\n", ret);
+ return;
+ }
+
+ /* Do not send the infoframe header, but keep the CRC field. */
+ regmap_bulk_write(regmap, SII902X_TPI_AVI_INFOFRAME,
+ buf + HDMI_INFOFRAME_HEADER_SIZE - 1,
+ HDMI_AVI_INFOFRAME_SIZE + 1);
+}
+
+static int sii902x_bridge_attach(struct drm_bridge *bridge)
+{
+ struct sii902x *sii902x = bridge_to_sii902x(bridge);
+ struct drm_device *drm = bridge->dev;
+ int ret;
+
+ drm_connector_helper_add(&sii902x->connector,
+ &sii902x_connector_helper_funcs);
+
+ if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
+ dev_err(&sii902x->i2c->dev,
+ "sii902x driver is only compatible with DRM devices supporting atomic updates");
+ return -ENOTSUPP;
+ }
+
+ ret = drm_connector_init(drm, &sii902x->connector,
+ &sii902x_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ return ret;
+
+ if (sii902x->i2c->irq > 0)
+ sii902x->connector.polled = DRM_CONNECTOR_POLL_HPD;
+ else
+ sii902x->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
+
+ drm_mode_connector_attach_encoder(&sii902x->connector, bridge->encoder);
+
+ return 0;
+}
+
+static const struct drm_bridge_funcs sii902x_bridge_funcs = {
+ .attach = sii902x_bridge_attach,
+ .mode_set = sii902x_bridge_mode_set,
+ .disable = sii902x_bridge_disable,
+ .enable = sii902x_bridge_enable,
+};
+
+static const struct regmap_range sii902x_volatile_ranges[] = {
+ { .range_min = 0, .range_max = 0xff },
+};
+
+static const struct regmap_access_table sii902x_volatile_table = {
+ .yes_ranges = sii902x_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(sii902x_volatile_ranges),
+};
+
+static const struct regmap_config sii902x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &sii902x_volatile_table,
+ .cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t sii902x_interrupt(int irq, void *data)
+{
+ struct sii902x *sii902x = data;
+ unsigned int status = 0;
+
+ regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);
+ regmap_write(sii902x->regmap, SII902X_INT_STATUS, status);
+
+ if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev)
+ drm_helper_hpd_irq_event(sii902x->bridge.dev);
+
+ return IRQ_HANDLED;
+}
+
+static int sii902x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ unsigned int status = 0;
+ struct sii902x *sii902x;
+ u8 chipid[4];
+ int ret;
+
+ sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL);
+ if (!sii902x)
+ return -ENOMEM;
+
+ sii902x->i2c = client;
+ sii902x->regmap = devm_regmap_init_i2c(client, &sii902x_regmap_config);
+ if (IS_ERR(sii902x->regmap))
+ return PTR_ERR(sii902x->regmap);
+
+ sii902x->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(sii902x->reset_gpio)) {
+ dev_err(dev, "Failed to retrieve/request reset gpio: %ld\n",
+ PTR_ERR(sii902x->reset_gpio));
+ return PTR_ERR(sii902x->reset_gpio);
+ }
+
+ sii902x_reset(sii902x);
+
+ ret = regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x0);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(sii902x->regmap, SII902X_REG_CHIPID(0),
+ &chipid, 4);
+ if (ret) {
+ dev_err(dev, "regmap_read failed %d\n", ret);
+ return ret;
+ }
+
+ if (chipid[0] != 0xb0) {
+ dev_err(dev, "Invalid chipid: %02x (expecting 0xb0)\n",
+ chipid[0]);
+ return -EINVAL;
+ }
+
+ /* Clear all pending interrupts */
+ regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);
+ regmap_write(sii902x->regmap, SII902X_INT_STATUS, status);
+
+ if (client->irq > 0) {
+ regmap_write(sii902x->regmap, SII902X_INT_ENABLE,
+ SII902X_HOTPLUG_EVENT);
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ sii902x_interrupt,
+ IRQF_ONESHOT, dev_name(dev),
+ sii902x);
+ if (ret)
+ return ret;
+ }
+
+ sii902x->bridge.funcs = &sii902x_bridge_funcs;
+ sii902x->bridge.of_node = dev->of_node;
+ ret = drm_bridge_add(&sii902x->bridge);
+ if (ret) {
+ dev_err(dev, "Failed to add drm_bridge\n");
+ return ret;
+ }
+
+ i2c_set_clientdata(client, sii902x);
+
+ return 0;
+}
+
+static int sii902x_remove(struct i2c_client *client)
+
+{
+ struct sii902x *sii902x = i2c_get_clientdata(client);
+
+ drm_bridge_remove(&sii902x->bridge);
+
+ return 0;
+}
+
+static const struct of_device_id sii902x_dt_ids[] = {
+ { .compatible = "sil,sii9022", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sii902x_dt_ids);
+
+static const struct i2c_device_id sii902x_i2c_ids[] = {
+ { "sii9022", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids);
+
+static struct i2c_driver sii902x_driver = {
+ .probe = sii902x_probe,
+ .remove = sii902x_remove,
+ .driver = {
+ .name = "sii902x",
+ .of_match_table = sii902x_dt_ids,
+ },
+ .id_table = sii902x_i2c_ids,
+};
+module_i2c_driver(sii902x_driver);
+
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("SII902x RGB -> HDMI bridges");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index 50d0baa06db0..4153e8a193af 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -30,25 +30,36 @@
#include <drm/drmP.h>
#include "drm_internal.h"
+#include "drm_legacy.h"
/**
- * drm_getmagic - Get unique magic of a client
- * @dev: DRM device to operate on
- * @data: ioctl data containing the drm_auth object
- * @file_priv: DRM file that performs the operation
+ * DOC: master and authentication
*
- * This looks up the unique magic of the passed client and returns it. If the
- * client did not have a magic assigned, yet, a new one is registered. The magic
- * is stored in the passed drm_auth object.
+ * struct &drm_master is used to track groups of clients with open
+ * primary/legacy device nodes. For every struct &drm_file which has had at
+ * least once successfully became the device master (either through the
+ * SET_MASTER IOCTL, or implicitly through opening the primary device node when
+ * no one else is the current master that time) there exists one &drm_master.
+ * This is noted in the is_master member of &drm_file. All other clients have
+ * just a pointer to the &drm_master they are associated with.
*
- * Returns: 0 on success, negative error code on failure.
+ * In addition only one &drm_master can be the current master for a &drm_device.
+ * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or
+ * implicitly through closing/openeing the primary device node. See also
+ * drm_is_current_master().
+ *
+ * Clients can authenticate against the current master (if it matches their own)
+ * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters,
+ * this allows controlled access to the device for an entire group of mutually
+ * trusted clients.
*/
+
int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_auth *auth = data;
int ret = 0;
- mutex_lock(&dev->struct_mutex);
+ mutex_lock(&dev->master_mutex);
if (!file_priv->magic) {
ret = idr_alloc(&file_priv->master->magic_map, file_priv,
1, 0, GFP_KERNEL);
@@ -56,23 +67,13 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
file_priv->magic = ret;
}
auth->magic = file_priv->magic;
- mutex_unlock(&dev->struct_mutex);
+ mutex_unlock(&dev->master_mutex);
DRM_DEBUG("%u\n", auth->magic);
return ret < 0 ? ret : 0;
}
-/**
- * drm_authmagic - Authenticate client with a magic
- * @dev: DRM device to operate on
- * @data: ioctl data containing the drm_auth object
- * @file_priv: DRM file that performs the operation
- *
- * This looks up a DRM client by the passed magic and authenticates it.
- *
- * Returns: 0 on success, negative error code on failure.
- */
int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -81,13 +82,253 @@ int drm_authmagic(struct drm_device *dev, void *data,
DRM_DEBUG("%u\n", auth->magic);
- mutex_lock(&dev->struct_mutex);
+ mutex_lock(&dev->master_mutex);
file = idr_find(&file_priv->master->magic_map, auth->magic);
if (file) {
file->authenticated = 1;
idr_replace(&file_priv->master->magic_map, NULL, auth->magic);
}
- mutex_unlock(&dev->struct_mutex);
+ mutex_unlock(&dev->master_mutex);
return file ? 0 : -EINVAL;
}
+
+static struct drm_master *drm_master_create(struct drm_device *dev)
+{
+ struct drm_master *master;
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return NULL;
+
+ kref_init(&master->refcount);
+ spin_lock_init(&master->lock.spinlock);
+ init_waitqueue_head(&master->lock.lock_queue);
+ idr_init(&master->magic_map);
+ master->dev = dev;
+
+ return master;
+}
+
+static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
+ bool new_master)
+{
+ int ret = 0;
+
+ dev->master = drm_master_get(fpriv->master);
+ if (dev->driver->master_set) {
+ ret = dev->driver->master_set(dev, fpriv, new_master);
+ if (unlikely(ret != 0)) {
+ drm_master_put(&dev->master);
+ }
+ }
+
+ return ret;
+}
+
+static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
+{
+ struct drm_master *old_master;
+ int ret;
+
+ lockdep_assert_held_once(&dev->master_mutex);
+
+ old_master = fpriv->master;
+ fpriv->master = drm_master_create(dev);
+ if (!fpriv->master) {
+ fpriv->master = old_master;
+ return -ENOMEM;
+ }
+
+ if (dev->driver->master_create) {
+ ret = dev->driver->master_create(dev, fpriv->master);
+ if (ret)
+ goto out_err;
+ }
+ fpriv->is_master = 1;
+ fpriv->authenticated = 1;
+
+ ret = drm_set_master(dev, fpriv, true);
+ if (ret)
+ goto out_err;
+
+ if (old_master)
+ drm_master_put(&old_master);
+
+ return 0;
+
+out_err:
+ /* drop references and restore old master on failure */
+ drm_master_put(&fpriv->master);
+ fpriv->master = old_master;
+
+ return ret;
+}
+
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ int ret = 0;
+
+ mutex_lock(&dev->master_mutex);
+ if (drm_is_current_master(file_priv))
+ goto out_unlock;
+
+ if (dev->master) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (!file_priv->master) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (!file_priv->is_master) {
+ ret = drm_new_set_master(dev, file_priv);
+ goto out_unlock;
+ }
+
+ ret = drm_set_master(dev, file_priv, false);
+out_unlock:
+ mutex_unlock(&dev->master_mutex);
+ return ret;
+}
+
+static void drm_drop_master(struct drm_device *dev,
+ struct drm_file *fpriv)
+{
+ if (dev->driver->master_drop)
+ dev->driver->master_drop(dev, fpriv);
+ drm_master_put(&dev->master);
+}
+
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&dev->master_mutex);
+ if (!drm_is_current_master(file_priv))
+ goto out_unlock;
+
+ if (!dev->master)
+ goto out_unlock;
+
+ ret = 0;
+ drm_drop_master(dev, file_priv);
+out_unlock:
+ mutex_unlock(&dev->master_mutex);
+ return ret;
+}
+
+int drm_master_open(struct drm_file *file_priv)
+{
+ struct drm_device *dev = file_priv->minor->dev;
+ int ret = 0;
+
+ /* if there is no current master make this fd it, but do not create
+ * any master object for render clients */
+ mutex_lock(&dev->master_mutex);
+ if (!dev->master)
+ ret = drm_new_set_master(dev, file_priv);
+ else
+ file_priv->master = drm_master_get(dev->master);
+ mutex_unlock(&dev->master_mutex);
+
+ return ret;
+}
+
+void drm_master_release(struct drm_file *file_priv)
+{
+ struct drm_device *dev = file_priv->minor->dev;
+ struct drm_master *master = file_priv->master;
+
+ mutex_lock(&dev->master_mutex);
+ if (file_priv->magic)
+ idr_remove(&file_priv->master->magic_map, file_priv->magic);
+
+ if (!drm_is_current_master(file_priv))
+ goto out;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
+ /*
+ * Since the master is disappearing, so is the
+ * possibility to lock.
+ */
+ mutex_lock(&dev->struct_mutex);
+ if (master->lock.hw_lock) {
+ if (dev->sigdata.lock == master->lock.hw_lock)
+ dev->sigdata.lock = NULL;
+ master->lock.hw_lock = NULL;
+ master->lock.file_priv = NULL;
+ wake_up_interruptible_all(&master->lock.lock_queue);
+ }
+ mutex_unlock(&dev->struct_mutex);
+ }
+
+ if (dev->master == file_priv->master)
+ drm_drop_master(dev, file_priv);
+out:
+ /* drop the master reference held by the file priv */
+ if (file_priv->master)
+ drm_master_put(&file_priv->master);
+ mutex_unlock(&dev->master_mutex);
+}
+
+/**
+ * drm_is_current_master - checks whether @priv is the current master
+ * @fpriv: DRM file private
+ *
+ * Checks whether @fpriv is current master on its device. This decides whether a
+ * client is allowed to run DRM_MASTER IOCTLs.
+ *
+ * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting
+ * - the current master is assumed to own the non-shareable display hardware.
+ */
+bool drm_is_current_master(struct drm_file *fpriv)
+{
+ return fpriv->is_master && fpriv->master == fpriv->minor->dev->master;
+}
+EXPORT_SYMBOL(drm_is_current_master);
+
+/**
+ * drm_master_get - reference a master pointer
+ * @master: struct &drm_master
+ *
+ * Increments the reference count of @master and returns a pointer to @master.
+ */
+struct drm_master *drm_master_get(struct drm_master *master)
+{
+ kref_get(&master->refcount);
+ return master;
+}
+EXPORT_SYMBOL(drm_master_get);
+
+static void drm_master_destroy(struct kref *kref)
+{
+ struct drm_master *master = container_of(kref, struct drm_master, refcount);
+ struct drm_device *dev = master->dev;
+
+ if (dev->driver->master_destroy)
+ dev->driver->master_destroy(dev, master);
+
+ drm_legacy_master_rmmaps(dev, master);
+
+ idr_destroy(&master->magic_map);
+ kfree(master->unique);
+ kfree(master);
+}
+
+/**
+ * drm_master_put - unreference and clear a master pointer
+ * @master: pointer to a pointer of struct &drm_master
+ *
+ * This decrements the &drm_master behind @master and sets it to NULL.
+ */
+void drm_master_put(struct drm_master **master)
+{
+ kref_put(&(*master)->refcount, drm_master_destroy);
+ *master = NULL;
+}
+EXPORT_SYMBOL(drm_master_put);
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index 9b34158c0f77..c3a12cd8bd0d 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -51,7 +51,7 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
*/
if (!entry->map ||
map->type != entry->map->type ||
- entry->master != dev->primary->master)
+ entry->master != dev->master)
continue;
switch (map->type) {
case _DRM_SHM:
@@ -245,12 +245,12 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
map->offset = (unsigned long)map->handle;
if (map->flags & _DRM_CONTAINS_LOCK) {
/* Prevent a 2nd X Server from creating a 2nd lock */
- if (dev->primary->master->lock.hw_lock != NULL) {
+ if (dev->master->lock.hw_lock != NULL) {
vfree(map->handle);
kfree(map);
return -EBUSY;
}
- dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */
+ dev->sigdata.lock = dev->master->lock.hw_lock = map->handle; /* Pointer to lock */
}
break;
case _DRM_AGP: {
@@ -356,7 +356,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
mutex_unlock(&dev->struct_mutex);
if (!(map->flags & _DRM_DRIVER))
- list->master = dev->primary->master;
+ list->master = dev->master;
*maplist = list;
return 0;
}
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 4ec35f9e6de5..fd93e9c79d28 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -39,6 +39,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_modeset_lock.h>
#include <drm/drm_atomic.h>
+#include <drm/drm_auth.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
@@ -608,6 +609,31 @@ static unsigned int drm_num_crtcs(struct drm_device *dev)
return num;
}
+static int drm_crtc_register_all(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ drm_for_each_crtc(crtc, dev) {
+ if (crtc->funcs->late_register)
+ ret = crtc->funcs->late_register(crtc);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void drm_crtc_unregister_all(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+
+ drm_for_each_crtc(crtc, dev) {
+ if (crtc->funcs->early_unregister)
+ crtc->funcs->early_unregister(crtc);
+ }
+}
+
/**
* drm_crtc_init_with_planes - Initialise a new CRTC object with
* specified primary and cursor planes.
@@ -938,6 +964,12 @@ void drm_connector_cleanup(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *t;
+ /* The connector should have been removed from userspace long before
+ * it is finally destroyed.
+ */
+ if (WARN_ON(connector->registered))
+ drm_connector_unregister(connector);
+
if (connector->tile_group) {
drm_mode_put_tile_group(dev, connector->tile_group);
connector->tile_group = NULL;
@@ -984,19 +1016,34 @@ int drm_connector_register(struct drm_connector *connector)
{
int ret;
+ if (connector->registered)
+ return 0;
+
ret = drm_sysfs_connector_add(connector);
if (ret)
return ret;
ret = drm_debugfs_connector_add(connector);
if (ret) {
- drm_sysfs_connector_remove(connector);
- return ret;
+ goto err_sysfs;
+ }
+
+ if (connector->funcs->late_register) {
+ ret = connector->funcs->late_register(connector);
+ if (ret)
+ goto err_debugfs;
}
drm_mode_object_register(connector->dev, &connector->base);
+ connector->registered = true;
return 0;
+
+err_debugfs:
+ drm_debugfs_connector_remove(connector);
+err_sysfs:
+ drm_sysfs_connector_remove(connector);
+ return ret;
}
EXPORT_SYMBOL(drm_connector_register);
@@ -1008,8 +1055,16 @@ EXPORT_SYMBOL(drm_connector_register);
*/
void drm_connector_unregister(struct drm_connector *connector)
{
+ if (!connector->registered)
+ return;
+
+ if (connector->funcs->early_unregister)
+ connector->funcs->early_unregister(connector);
+
drm_sysfs_connector_remove(connector);
drm_debugfs_connector_remove(connector);
+
+ connector->registered = false;
}
EXPORT_SYMBOL(drm_connector_unregister);
@@ -1018,9 +1073,9 @@ EXPORT_SYMBOL(drm_connector_unregister);
* @dev: drm device
*
* This function registers all connectors in sysfs and other places so that
- * userspace can start to access them. Drivers can call it after calling
- * drm_dev_register() to complete the device registration, if they don't call
- * drm_connector_register() on each connector individually.
+ * userspace can start to access them. drm_connector_register_all() is called
+ * automatically from drm_dev_register() to complete the device registration,
+ * if they don't call drm_connector_register() on each connector individually.
*
* When a device is unplugged and should be removed from userspace access,
* call drm_connector_unregister_all(), which is the inverse of this
@@ -1073,6 +1128,31 @@ void drm_connector_unregister_all(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_connector_unregister_all);
+static int drm_encoder_register_all(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+ int ret = 0;
+
+ drm_for_each_encoder(encoder, dev) {
+ if (encoder->funcs->late_register)
+ ret = encoder->funcs->late_register(encoder);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void drm_encoder_unregister_all(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ drm_for_each_encoder(encoder, dev) {
+ if (encoder->funcs->early_unregister)
+ encoder->funcs->early_unregister(encoder);
+ }
+}
+
/**
* drm_encoder_init - Init a preallocated encoder
* @dev: drm device
@@ -1261,6 +1341,31 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
}
EXPORT_SYMBOL(drm_universal_plane_init);
+static int drm_plane_register_all(struct drm_device *dev)
+{
+ struct drm_plane *plane;
+ int ret = 0;
+
+ drm_for_each_plane(plane, dev) {
+ if (plane->funcs->late_register)
+ ret = plane->funcs->late_register(plane);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void drm_plane_unregister_all(struct drm_device *dev)
+{
+ struct drm_plane *plane;
+
+ drm_for_each_plane(plane, dev) {
+ if (plane->funcs->early_unregister)
+ plane->funcs->early_unregister(plane);
+ }
+}
+
/**
* drm_plane_init - Initialize a legacy plane
* @dev: DRM device
@@ -1383,6 +1488,46 @@ void drm_plane_force_disable(struct drm_plane *plane)
}
EXPORT_SYMBOL(drm_plane_force_disable);
+int drm_modeset_register_all(struct drm_device *dev)
+{
+ int ret;
+
+ ret = drm_plane_register_all(dev);
+ if (ret)
+ goto err_plane;
+
+ ret = drm_crtc_register_all(dev);
+ if (ret)
+ goto err_crtc;
+
+ ret = drm_encoder_register_all(dev);
+ if (ret)
+ goto err_encoder;
+
+ ret = drm_connector_register_all(dev);
+ if (ret)
+ goto err_connector;
+
+ return 0;
+
+err_connector:
+ drm_encoder_unregister_all(dev);
+err_encoder:
+ drm_crtc_unregister_all(dev);
+err_crtc:
+ drm_plane_unregister_all(dev);
+err_plane:
+ return ret;
+}
+
+void drm_modeset_unregister_all(struct drm_device *dev)
+{
+ drm_connector_unregister_all(dev);
+ drm_encoder_unregister_all(dev);
+ drm_crtc_unregister_all(dev);
+ drm_plane_unregister_all(dev);
+}
+
static int drm_mode_create_standard_properties(struct drm_device *dev)
{
struct drm_property *prop;
@@ -3499,7 +3644,7 @@ int drm_mode_getfb(struct drm_device *dev,
r->bpp = fb->bits_per_pixel;
r->pitch = fb->pitches[0];
if (fb->funcs->create_handle) {
- if (file_priv->is_master || capable(CAP_SYS_ADMIN) ||
+ if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) ||
drm_is_control_client(file_priv)) {
ret = fb->funcs->create_handle(fb, file_priv,
&r->handle);
@@ -3656,6 +3801,13 @@ void drm_fb_release(struct drm_file *priv)
}
}
+static bool drm_property_type_valid(struct drm_property *property)
+{
+ if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+ return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+ return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+}
+
/**
* drm_property_create - create a new property type
* @dev: drm device
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index bf10d7046aa6..d61591274ff6 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -232,6 +232,9 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev)
*/
void drm_helper_disable_unused_functions(struct drm_device *dev)
{
+ if (drm_core_check_feature(dev, DRIVER_ATOMIC))
+ DRM_ERROR("Called for atomic driver, this is not what you want.\n");
+
drm_modeset_lock_all(dev);
__drm_helper_disable_unused_functions(dev);
drm_modeset_unlock_all(dev);
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index a78c138282ea..47a500b90fd7 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -31,14 +31,100 @@
* and are not exported to drivers.
*/
+
+/* drm_crtc.c */
+void drm_connector_ida_init(void);
+void drm_connector_ida_destroy(void);
int drm_mode_object_get(struct drm_device *dev,
struct drm_mode_object *obj, uint32_t obj_type);
void drm_mode_object_unregister(struct drm_device *dev,
struct drm_mode_object *object);
+bool drm_property_change_valid_get(struct drm_property *property,
+ uint64_t value,
+ struct drm_mode_object **ref);
+void drm_property_change_valid_put(struct drm_property *property,
+ struct drm_mode_object *ref);
+
+int drm_plane_check_pixel_format(const struct drm_plane *plane,
+ u32 format);
+int drm_crtc_check_viewport(const struct drm_crtc *crtc,
+ int x, int y,
+ const struct drm_display_mode *mode,
+ const struct drm_framebuffer *fb);
+
+void drm_fb_release(struct drm_file *file_priv);
+void drm_property_destroy_user_blobs(struct drm_device *dev,
+ struct drm_file *file_priv);
+
+/* dumb buffer support IOCTLs */
+int drm_mode_create_dumb_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+
+/* framebuffer IOCTLs */
+extern int drm_mode_addfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_addfb2(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_rmfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_getfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+
+/* IOCTLs */
+int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+int drm_mode_getresources(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_getplane_res(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_mode_getcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_getconnector(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_setcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_getplane(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_setplane(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_cursor2_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_getproperty_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_getblob_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_createblob_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_destroyblob_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_getencoder(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+
+int drm_mode_page_flip_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
/* drm_atomic.c */
int drm_atomic_get_property(struct drm_mode_object *obj,
- struct drm_property *property, uint64_t *val);
+ struct drm_property *property, uint64_t *val);
int drm_mode_atomic_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
+int drm_modeset_register_all(struct drm_device *dev);
+void drm_modeset_unregister_all(struct drm_device *dev);
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 3bcf8e6a85b3..fa10cef2ba37 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -46,11 +46,8 @@
static const struct drm_info_list drm_debugfs_list[] = {
{"name", drm_name_info, 0},
- {"vm", drm_vm_info, 0},
{"clients", drm_clients_info, 0},
- {"bufs", drm_bufs_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM},
- {"vma", drm_vma_info, 0},
};
#define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index eeaf5a7c3aa7..091053e995e5 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -708,8 +708,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
memset(&msg, 0, sizeof(msg));
- mutex_lock(&aux->hw_mutex);
-
for (i = 0; i < num; i++) {
msg.address = msgs[i].addr;
drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
@@ -764,8 +762,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
msg.size = 0;
(void)drm_dp_i2c_do_msg(aux, &msg);
- mutex_unlock(&aux->hw_mutex);
-
return err;
}
@@ -774,22 +770,64 @@ static const struct i2c_algorithm drm_dp_i2c_algo = {
.master_xfer = drm_dp_i2c_xfer,
};
+static struct drm_dp_aux *i2c_to_aux(struct i2c_adapter *i2c)
+{
+ return container_of(i2c, struct drm_dp_aux, ddc);
+}
+
+static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+ mutex_lock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
+static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+ return mutex_trylock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
+static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+ mutex_unlock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
/**
- * drm_dp_aux_register() - initialise and register aux channel
+ * drm_dp_aux_init() - minimally initialise an aux channel
* @aux: DisplayPort AUX channel
*
- * Returns 0 on success or a negative error code on failure.
+ * If you need to use the drm_dp_aux's i2c adapter prior to registering it
+ * with the outside world, call drm_dp_aux_init() first. You must still
+ * call drm_dp_aux_register() once the connector has been registered to
+ * allow userspace access to the auxiliary DP channel.
*/
-int drm_dp_aux_register(struct drm_dp_aux *aux)
+void drm_dp_aux_init(struct drm_dp_aux *aux)
{
- int ret;
-
mutex_init(&aux->hw_mutex);
aux->ddc.algo = &drm_dp_i2c_algo;
aux->ddc.algo_data = aux;
aux->ddc.retries = 3;
+ aux->ddc.lock_bus = lock_bus;
+ aux->ddc.trylock_bus = trylock_bus;
+ aux->ddc.unlock_bus = unlock_bus;
+}
+EXPORT_SYMBOL(drm_dp_aux_init);
+
+/**
+ * drm_dp_aux_register() - initialise and register aux channel
+ * @aux: DisplayPort AUX channel
+ *
+ * Automatically calls drm_dp_aux_init() if this hasn't been done yet.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_aux_register(struct drm_dp_aux *aux)
+{
+ int ret;
+
+ if (!aux->ddc.algo)
+ drm_dp_aux_init(aux);
+
aux->ddc.class = I2C_CLASS_DDC;
aux->ddc.owner = THIS_MODULE;
aux->ddc.dev.parent = aux->dev;
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 8b2582aeaab6..aead9ffcbe29 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,8 +34,10 @@
#include <linux/slab.h>
#include <drm/drmP.h>
#include <drm/drm_core.h>
+#include "drm_crtc_internal.h"
#include "drm_legacy.h"
#include "drm_internal.h"
+#include "drm_crtc_internal.h"
/*
* drm_debug: Enable debug output.
@@ -93,114 +95,6 @@ void drm_ut_debug_printk(const char *function_name, const char *format, ...)
}
EXPORT_SYMBOL(drm_ut_debug_printk);
-struct drm_master *drm_master_create(struct drm_minor *minor)
-{
- struct drm_master *master;
-
- master = kzalloc(sizeof(*master), GFP_KERNEL);
- if (!master)
- return NULL;
-
- kref_init(&master->refcount);
- spin_lock_init(&master->lock.spinlock);
- init_waitqueue_head(&master->lock.lock_queue);
- idr_init(&master->magic_map);
- master->minor = minor;
-
- return master;
-}
-
-struct drm_master *drm_master_get(struct drm_master *master)
-{
- kref_get(&master->refcount);
- return master;
-}
-EXPORT_SYMBOL(drm_master_get);
-
-static void drm_master_destroy(struct kref *kref)
-{
- struct drm_master *master = container_of(kref, struct drm_master, refcount);
- struct drm_device *dev = master->minor->dev;
-
- if (dev->driver->master_destroy)
- dev->driver->master_destroy(dev, master);
-
- drm_legacy_master_rmmaps(dev, master);
-
- idr_destroy(&master->magic_map);
- kfree(master->unique);
- kfree(master);
-}
-
-void drm_master_put(struct drm_master **master)
-{
- kref_put(&(*master)->refcount, drm_master_destroy);
- *master = NULL;
-}
-EXPORT_SYMBOL(drm_master_put);
-
-int drm_setmaster_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- int ret = 0;
-
- mutex_lock(&dev->master_mutex);
- if (file_priv->is_master)
- goto out_unlock;
-
- if (file_priv->minor->master) {
- ret = -EINVAL;
- goto out_unlock;
- }
-
- if (!file_priv->master) {
- ret = -EINVAL;
- goto out_unlock;
- }
-
- if (!file_priv->allowed_master) {
- ret = drm_new_set_master(dev, file_priv);
- goto out_unlock;
- }
-
- file_priv->minor->master = drm_master_get(file_priv->master);
- file_priv->is_master = 1;
- if (dev->driver->master_set) {
- ret = dev->driver->master_set(dev, file_priv, false);
- if (unlikely(ret != 0)) {
- file_priv->is_master = 0;
- drm_master_put(&file_priv->minor->master);
- }
- }
-
-out_unlock:
- mutex_unlock(&dev->master_mutex);
- return ret;
-}
-
-int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- int ret = -EINVAL;
-
- mutex_lock(&dev->master_mutex);
- if (!file_priv->is_master)
- goto out_unlock;
-
- if (!file_priv->minor->master)
- goto out_unlock;
-
- ret = 0;
- if (dev->driver->master_drop)
- dev->driver->master_drop(dev, file_priv, false);
- drm_master_put(&file_priv->minor->master);
- file_priv->is_master = 0;
-
-out_unlock:
- mutex_unlock(&dev->master_mutex);
- return ret;
-}
-
/*
* DRM Minors
* A DRM device can provide several char-dev interfaces on the DRM-Major. Each
@@ -405,10 +299,9 @@ void drm_minor_release(struct drm_minor *minor)
* callbacks implemented by the driver. The driver then needs to initialize all
* the various subsystems for the drm device like memory management, vblank
* handling, modesetting support and intial output configuration plus obviously
- * initialize all the corresponding hardware bits. An important part of this is
- * also calling drm_dev_set_unique() to set the userspace-visible unique name of
- * this device instance. Finally when everything is up and running and ready for
- * userspace the device instance can be published using drm_dev_register().
+ * initialize all the corresponding hardware bits. Finally when everything is up
+ * and running and ready for userspace the device instance can be published
+ * using drm_dev_register().
*
* There is also deprecated support for initalizing device instances using
* bus-specific helpers and the ->load() callback. But due to
@@ -430,6 +323,14 @@ void drm_minor_release(struct drm_minor *minor)
* dev_priv field of &drm_device.
*/
+static int drm_dev_set_unique(struct drm_device *dev, const char *name)
+{
+ kfree(dev->unique);
+ dev->unique = kstrdup(name, GFP_KERNEL);
+
+ return dev->unique ? 0 : -ENOMEM;
+}
+
/**
* drm_put_dev - Unregister and release a DRM device
* @dev: DRM device
@@ -549,11 +450,12 @@ static void drm_fs_inode_free(struct inode *inode)
}
/**
- * drm_dev_alloc - Allocate new DRM device
- * @driver: DRM driver to allocate device for
+ * drm_dev_init - Initialise new DRM device
+ * @dev: DRM device
+ * @driver: DRM driver
* @parent: Parent device object
*
- * Allocate and initialize a new DRM device. No device registration is done.
+ * Initialize a new DRM device. No device registration is done.
* Call drm_dev_register() to advertice the device to user space and register it
* with other core subsystems. This should be done last in the device
* initialization sequence to make sure userspace can't access an inconsistent
@@ -564,19 +466,18 @@ static void drm_fs_inode_free(struct inode *inode)
*
* Note that for purely virtual devices @parent can be NULL.
*
+ * Drivers that do not want to allocate their own device struct
+ * embedding struct &drm_device can call drm_dev_alloc() instead.
+ *
* RETURNS:
- * Pointer to new DRM device, or NULL if out of memory.
+ * 0 on success, or error code on failure.
*/
-struct drm_device *drm_dev_alloc(struct drm_driver *driver,
- struct device *parent)
+int drm_dev_init(struct drm_device *dev,
+ struct drm_driver *driver,
+ struct device *parent)
{
- struct drm_device *dev;
int ret;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return NULL;
-
kref_init(&dev->ref);
dev->dev = parent;
dev->driver = driver;
@@ -617,7 +518,8 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
if (ret)
goto err_minors;
- if (drm_ht_create(&dev->map_hash, 12))
+ ret = drm_ht_create(&dev->map_hash, 12);
+ if (ret)
goto err_minors;
drm_legacy_ctxbitmap_init(dev);
@@ -630,13 +532,13 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
}
}
- if (parent) {
- ret = drm_dev_set_unique(dev, dev_name(parent));
- if (ret)
- goto err_setunique;
- }
+ /* Use the parent device name as DRM device unique identifier, but fall
+ * back to the driver name for virtual devices like vgem. */
+ ret = drm_dev_set_unique(dev, parent ? dev_name(parent) : driver->name);
+ if (ret)
+ goto err_setunique;
- return dev;
+ return 0;
err_setunique:
if (drm_core_check_feature(dev, DRIVER_GEM))
@@ -651,8 +553,49 @@ err_minors:
drm_fs_inode_free(dev->anon_inode);
err_free:
mutex_destroy(&dev->master_mutex);
- kfree(dev);
- return NULL;
+ return ret;
+}
+EXPORT_SYMBOL(drm_dev_init);
+
+/**
+ * drm_dev_alloc - Allocate new DRM device
+ * @driver: DRM driver to allocate device for
+ * @parent: Parent device object
+ *
+ * Allocate and initialize a new DRM device. No device registration is done.
+ * Call drm_dev_register() to advertice the device to user space and register it
+ * with other core subsystems. This should be done last in the device
+ * initialization sequence to make sure userspace can't access an inconsistent
+ * state.
+ *
+ * The initial ref-count of the object is 1. Use drm_dev_ref() and
+ * drm_dev_unref() to take and drop further ref-counts.
+ *
+ * Note that for purely virtual devices @parent can be NULL.
+ *
+ * Drivers that wish to subclass or embed struct &drm_device into their
+ * own struct should look at using drm_dev_init() instead.
+ *
+ * RETURNS:
+ * Pointer to new DRM device, or NULL if out of memory.
+ */
+struct drm_device *drm_dev_alloc(struct drm_driver *driver,
+ struct device *parent)
+{
+ struct drm_device *dev;
+ int ret;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ ret = drm_dev_init(dev, driver, parent);
+ if (ret) {
+ kfree(dev);
+ return NULL;
+ }
+
+ return dev;
}
EXPORT_SYMBOL(drm_dev_alloc);
@@ -716,11 +659,7 @@ EXPORT_SYMBOL(drm_dev_unref);
*
* Register the DRM device @dev with the system, advertise device to user-space
* and start normal device operation. @dev must be allocated via drm_dev_alloc()
- * previously. Right after drm_dev_register() the driver should call
- * drm_connector_register_all() to register all connectors in sysfs. This is
- * a separate call for backward compatibility with drivers still using
- * the deprecated ->load() callback, where connectors are registered from within
- * the ->load() callback.
+ * previously.
*
* Never call this twice on any device!
*
@@ -757,6 +696,9 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
goto err_minors;
}
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_modeset_register_all(dev);
+
ret = 0;
goto out_unlock;
@@ -787,6 +729,9 @@ void drm_dev_unregister(struct drm_device *dev)
drm_lastclose(dev);
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_modeset_unregister_all(dev);
+
if (dev->driver->unload)
dev->driver->unload(dev);
@@ -804,26 +749,6 @@ void drm_dev_unregister(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_dev_unregister);
-/**
- * drm_dev_set_unique - Set the unique name of a DRM device
- * @dev: device of which to set the unique name
- * @name: unique name
- *
- * Sets the unique name of a DRM device using the specified string. Drivers
- * can use this at driver probe time if the unique name of the devices they
- * drive is static.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_dev_set_unique(struct drm_device *dev, const char *name)
-{
- kfree(dev->unique);
- dev->unique = kstrdup(name, GFP_KERNEL);
-
- return dev->unique ? 0 : -ENOMEM;
-}
-EXPORT_SYMBOL(drm_dev_set_unique);
-
/*
* DRM Core
* The DRM core module initializes all global DRM objects and makes them
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 0bac5246e5a7..ce54e985d91b 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -464,7 +464,7 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
/* Sometimes user space wants everything disabled, so don't steal the
* display if there's a master. */
- if (dev->primary->master)
+ if (lockless_dereference(dev->master))
return false;
drm_for_each_crtc(crtc, dev) {
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index a27bc7cda975..323c238fcac7 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -40,6 +40,7 @@
#include <linux/module.h>
#include "drm_legacy.h"
#include "drm_internal.h"
+#include "drm_crtc_internal.h"
/* from BKL pushdown */
DEFINE_MUTEX(drm_global_mutex);
@@ -168,60 +169,6 @@ static int drm_cpu_valid(void)
}
/*
- * drm_new_set_master - Allocate a new master object and become master for the
- * associated master realm.
- *
- * @dev: The associated device.
- * @fpriv: File private identifying the client.
- *
- * This function must be called with dev::struct_mutex held.
- * Returns negative error code on failure. Zero on success.
- */
-int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
-{
- struct drm_master *old_master;
- int ret;
-
- lockdep_assert_held_once(&dev->master_mutex);
-
- /* create a new master */
- fpriv->minor->master = drm_master_create(fpriv->minor);
- if (!fpriv->minor->master)
- return -ENOMEM;
-
- /* take another reference for the copy in the local file priv */
- old_master = fpriv->master;
- fpriv->master = drm_master_get(fpriv->minor->master);
-
- if (dev->driver->master_create) {
- ret = dev->driver->master_create(dev, fpriv->master);
- if (ret)
- goto out_err;
- }
- if (dev->driver->master_set) {
- ret = dev->driver->master_set(dev, fpriv, true);
- if (ret)
- goto out_err;
- }
-
- fpriv->is_master = 1;
- fpriv->allowed_master = 1;
- fpriv->authenticated = 1;
- if (old_master)
- drm_master_put(&old_master);
-
- return 0;
-
-out_err:
- /* drop both references and restore old master on failure */
- drm_master_put(&fpriv->minor->master);
- drm_master_put(&fpriv->master);
- fpriv->master = old_master;
-
- return ret;
-}
-
-/*
* Called whenever a process opens /dev/drm.
*
* \param filp file pointer.
@@ -283,19 +230,11 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
goto out_prime_destroy;
}
- /* if there is no current master make this fd it, but do not create
- * any master object for render clients */
- mutex_lock(&dev->master_mutex);
- if (drm_is_primary_client(priv) && !priv->minor->master) {
- /* create a new master */
- ret = drm_new_set_master(dev, priv);
+ if (drm_is_primary_client(priv)) {
+ ret = drm_master_open(priv);
if (ret)
goto out_close;
- } else if (drm_is_primary_client(priv)) {
- /* get a reference to the master */
- priv->master = drm_master_get(priv->minor->master);
}
- mutex_unlock(&dev->master_mutex);
mutex_lock(&dev->filelist_mutex);
list_add(&priv->lhead, &dev->filelist);
@@ -324,7 +263,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
return 0;
out_close:
- mutex_unlock(&dev->master_mutex);
if (dev->driver->postclose)
dev->driver->postclose(dev, priv);
out_prime_destroy:
@@ -338,18 +276,6 @@ out_prime_destroy:
return ret;
}
-static void drm_master_release(struct drm_device *dev, struct file *filp)
-{
- struct drm_file *file_priv = filp->private_data;
-
- if (drm_legacy_i_have_hw_lock(dev, file_priv)) {
- DRM_DEBUG("File %p released, freeing lock for context %d\n",
- filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
- drm_legacy_lock_free(&file_priv->master->lock,
- _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
- }
-}
-
static void drm_events_release(struct drm_file *file_priv)
{
struct drm_device *dev = file_priv->minor->dev;
@@ -451,11 +377,6 @@ int drm_release(struct inode *inode, struct file *filp)
list_del(&file_priv->lhead);
mutex_unlock(&dev->filelist_mutex);
- mutex_lock(&dev->struct_mutex);
- if (file_priv->magic)
- idr_remove(&file_priv->master->magic_map, file_priv->magic);
- mutex_unlock(&dev->struct_mutex);
-
if (dev->driver->preclose)
dev->driver->preclose(dev, file_priv);
@@ -468,9 +389,8 @@ int drm_release(struct inode *inode, struct file *filp)
(long)old_encode_dev(file_priv->minor->kdev->devt),
dev->open_count);
- /* if the master has gone away we can't do anything with the lock */
- if (file_priv->minor->master)
- drm_master_release(dev, filp);
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_legacy_lock_release(dev, filp);
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
drm_legacy_reclaim_buffers(dev, file_priv);
@@ -487,43 +407,12 @@ int drm_release(struct inode *inode, struct file *filp)
drm_legacy_ctxbitmap_flush(dev, file_priv);
- mutex_lock(&dev->master_mutex);
-
- if (file_priv->is_master) {
- struct drm_master *master = file_priv->master;
-
- /*
- * Since the master is disappearing, so is the
- * possibility to lock.
- */
- mutex_lock(&dev->struct_mutex);
- if (master->lock.hw_lock) {
- if (dev->sigdata.lock == master->lock.hw_lock)
- dev->sigdata.lock = NULL;
- master->lock.hw_lock = NULL;
- master->lock.file_priv = NULL;
- wake_up_interruptible_all(&master->lock.lock_queue);
- }
- mutex_unlock(&dev->struct_mutex);
-
- if (file_priv->minor->master == file_priv->master) {
- /* drop the reference held my the minor */
- if (dev->driver->master_drop)
- dev->driver->master_drop(dev, file_priv, true);
- drm_master_put(&file_priv->minor->master);
- }
- }
-
- /* drop the master reference held by the file priv */
- if (file_priv->master)
- drm_master_put(&file_priv->master);
- file_priv->is_master = 0;
- mutex_unlock(&dev->master_mutex);
+ if (drm_is_primary_client(file_priv))
+ drm_master_release(file_priv);
if (dev->driver->postclose)
dev->driver->postclose(dev, file_priv);
-
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_prime_destroy_file_private(&file_priv->prime);
diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
index 5d469b2f26f4..9ae353f4dd06 100644
--- a/drivers/gpu/drm/drm_info.c
+++ b/drivers/gpu/drm/drm_info.c
@@ -50,106 +50,24 @@ int drm_name_info(struct seq_file *m, void *data)
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_minor *minor = node->minor;
struct drm_device *dev = minor->dev;
- struct drm_master *master = minor->master;
- if (!master)
- return 0;
-
- if (master->unique) {
- seq_printf(m, "%s %s %s\n",
- dev->driver->name,
- dev_name(dev->dev), master->unique);
- } else {
- seq_printf(m, "%s %s\n",
- dev->driver->name, dev_name(dev->dev));
- }
- return 0;
-}
-
-/**
- * Called when "/proc/dri/.../vm" is read.
- *
- * Prints information about all mappings in drm_device::maplist.
- */
-int drm_vm_info(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_local_map *map;
- struct drm_map_list *r_list;
-
- /* Hardcoded from _DRM_FRAME_BUFFER,
- _DRM_REGISTERS, _DRM_SHM, _DRM_AGP, and
- _DRM_SCATTER_GATHER and _DRM_CONSISTENT */
- const char *types[] = { "FB", "REG", "SHM", "AGP", "SG", "PCI" };
- const char *type;
- int i;
-
- mutex_lock(&dev->struct_mutex);
- seq_printf(m, "slot offset size type flags address mtrr\n\n");
- i = 0;
- list_for_each_entry(r_list, &dev->maplist, head) {
- map = r_list->map;
- if (!map)
- continue;
- if (map->type < 0 || map->type > 5)
- type = "??";
- else
- type = types[map->type];
-
- seq_printf(m, "%4d 0x%016llx 0x%08lx %4.4s 0x%02x 0x%08lx ",
- i,
- (unsigned long long)map->offset,
- map->size, type, map->flags,
- (unsigned long) r_list->user_token);
- if (map->mtrr < 0)
- seq_printf(m, "none\n");
- else
- seq_printf(m, "%4d\n", map->mtrr);
- i++;
- }
- mutex_unlock(&dev->struct_mutex);
- return 0;
-}
+ struct drm_master *master;
-/**
- * Called when "/proc/dri/.../bufs" is read.
- */
-int drm_bufs_info(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_device_dma *dma;
- int i, seg_pages;
-
- mutex_lock(&dev->struct_mutex);
- dma = dev->dma;
- if (!dma) {
- mutex_unlock(&dev->struct_mutex);
- return 0;
- }
-
- seq_printf(m, " o size count free segs pages kB\n\n");
- for (i = 0; i <= DRM_MAX_ORDER; i++) {
- if (dma->bufs[i].buf_count) {
- seg_pages = dma->bufs[i].seg_count * (1 << dma->bufs[i].page_order);
- seq_printf(m, "%2d %8d %5d %5d %5d %5d %5ld\n",
- i,
- dma->bufs[i].buf_size,
- dma->bufs[i].buf_count,
- 0,
- dma->bufs[i].seg_count,
- seg_pages,
- seg_pages * PAGE_SIZE / 1024);
- }
- }
- seq_printf(m, "\n");
- for (i = 0; i < dma->buf_count; i++) {
- if (i && !(i % 32))
- seq_printf(m, "\n");
- seq_printf(m, " %d", dma->buflist[i]->list);
- }
+ mutex_lock(&dev->master_mutex);
+ master = dev->master;
+ if (!master)
+ goto out_unlock;
+
+ seq_printf(m, "%s", dev->driver->name);
+ if (dev->dev)
+ seq_printf(m, " dev=%s", dev_name(dev->dev));
+ if (master && master->unique)
+ seq_printf(m, " master=%s", master->unique);
+ if (dev->unique)
+ seq_printf(m, " unique=%s", dev->unique);
seq_printf(m, "\n");
- mutex_unlock(&dev->struct_mutex);
+out_unlock:
+ mutex_unlock(&dev->master_mutex);
+
return 0;
}
@@ -184,7 +102,7 @@ int drm_clients_info(struct seq_file *m, void *data)
task ? task->comm : "<unknown>",
pid_vnr(priv->pid),
priv->minor->index,
- priv->is_master ? 'y' : 'n',
+ drm_is_current_master(priv) ? 'y' : 'n',
priv->authenticated ? 'y' : 'n',
from_kuid_munged(seq_user_ns(m), priv->uid),
priv->magic);
@@ -194,7 +112,6 @@ int drm_clients_info(struct seq_file *m, void *data)
return 0;
}
-
static int drm_gem_one_name_info(int id, void *ptr, void *data)
{
struct drm_gem_object *obj = ptr;
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 902cf6a15212..b86dc9b921a5 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -29,15 +29,9 @@ extern struct mutex drm_global_mutex;
void drm_lastclose(struct drm_device *dev);
/* drm_pci.c */
-int drm_pci_set_unique(struct drm_device *dev,
- struct drm_master *master,
- struct drm_unique *u);
int drm_irq_by_busid(struct drm_device *dev, void *data,
struct drm_file *file_priv);
-/* drm_vm.c */
-int drm_vma_info(struct seq_file *m, void *data);
-
/* drm_prime.c */
int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
@@ -51,8 +45,6 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr
/* drm_info.c */
int drm_name_info(struct seq_file *m, void *data);
-int drm_vm_info(struct seq_file *m, void *data);
-int drm_bufs_info(struct seq_file *m, void *data);
int drm_clients_info(struct seq_file *m, void* data);
int drm_gem_name_info(struct seq_file *m, void *data);
@@ -67,6 +59,12 @@ int drm_getmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_master_open(struct drm_file *file_priv);
+void drm_master_release(struct drm_file *file_priv);
/* drm_sysfs.c */
extern struct class *drm_class;
@@ -92,13 +90,6 @@ int drm_gem_open_ioctl(struct drm_device *dev, void *data,
void drm_gem_open(struct drm_device *dev, struct drm_file *file_private);
void drm_gem_release(struct drm_device *dev, struct drm_file *file_private);
-/* drm_drv.c */
-int drm_setmaster_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-struct drm_master *drm_master_create(struct drm_minor *minor);
-
/* drm_debugfs.c */
#if defined(CONFIG_DEBUG_FS)
int drm_debugfs_init(struct drm_minor *minor, int minor_id,
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index b7a39771c152..1f84ff5f1bf8 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -30,6 +30,7 @@
#include <drm/drmP.h>
#include <drm/drm_core.h>
+#include <drm/drm_auth.h>
#include "drm_legacy.h"
#include "drm_internal.h"
#include "drm_crtc_internal.h"
@@ -37,6 +38,64 @@
#include <linux/pci.h>
#include <linux/export.h>
+/**
+ * DOC: getunique and setversion story
+ *
+ * BEWARE THE DRAGONS! MIND THE TRAPDOORS!
+ *
+ * In an attempt to warn anyone else who's trying to figure out what's going
+ * on here, I'll try to summarize the story. First things first, let's clear up
+ * the names, because the kernel internals, libdrm and the ioctls are all named
+ * differently:
+ *
+ * - GET_UNIQUE ioctl, implemented by drm_getunique is wrapped up in libdrm
+ * through the drmGetBusid function.
+ * - The libdrm drmSetBusid function is backed by the SET_UNIQUE ioctl. All
+ * that code is nerved in the kernel with drm_invalid_op().
+ * - The internal set_busid kernel functions and driver callbacks are
+ * exclusively use by the SET_VERSION ioctl, because only drm 1.0 (which is
+ * nerved) allowed userspace to set the busid through the above ioctl.
+ * - Other ioctls and functions involved are named consistently.
+ *
+ * For anyone wondering what's the difference between drm 1.1 and 1.4: Correctly
+ * handling pci domains in the busid on ppc. Doing this correctly was only
+ * implemented in libdrm in 2010, hence can't be nerved yet. No one knows what's
+ * special with drm 1.2 and 1.3.
+ *
+ * Now the actual horror story of how device lookup in drm works. At large,
+ * there's 2 different ways, either by busid, or by device driver name.
+ *
+ * Opening by busid is fairly simple:
+ *
+ * 1. First call SET_VERSION to make sure pci domains are handled properly. As a
+ * side-effect this fills out the unique name in the master structure.
+ * 2. Call GET_UNIQUE to read out the unique name from the master structure,
+ * which matches the busid thanks to step 1. If it doesn't, proceed to try
+ * the next device node.
+ *
+ * Opening by name is slightly different:
+ *
+ * 1. Directly call VERSION to get the version and to match against the driver
+ * name returned by that ioctl. Note that SET_VERSION is not called, which
+ * means the the unique name for the master node just opening is _not_ filled
+ * out. This despite that with current drm device nodes are always bound to
+ * one device, and can't be runtime assigned like with drm 1.0.
+ * 2. Match driver name. If it mismatches, proceed to the next device node.
+ * 3. Call GET_UNIQUE, and check whether the unique name has length zero (by
+ * checking that the first byte in the string is 0). If that's not the case
+ * libdrm skips and proceeds to the next device node. Probably this is just
+ * copypasta from drm 1.0 times where a set unique name meant that the driver
+ * was in use already, but that's just conjecture.
+ *
+ * Long story short: To keep the open by name logic working, GET_UNIQUE must
+ * _not_ return a unique string when SET_VERSION hasn't been called yet,
+ * otherwise libdrm breaks. Even when that unique string can't ever change, and
+ * is totally irrelevant for actually opening the device because runtime
+ * assignable device instances were only support in drm 1.0, which is long dead.
+ * But the libdrm code in drmOpenByName somehow survived, hence this can't be
+ * broken.
+ */
+
static int drm_version(struct drm_device *dev, void *data,
struct drm_file *file_priv);
@@ -75,51 +134,6 @@ drm_unset_busid(struct drm_device *dev,
master->unique_len = 0;
}
-/*
- * Set the bus id.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg user argument, pointing to a drm_unique structure.
- * \return zero on success or a negative number on failure.
- *
- * Copies the bus id from userspace into drm_device::unique, and verifies that
- * it matches the device this DRM is attached to (EINVAL otherwise). Deprecated
- * in interface version 1.1 and will return EBUSY when setversion has requested
- * version 1.1 or greater. Also note that KMS is all version 1.1 and later and
- * UMS was only ever supported on pci devices.
- */
-static int drm_setunique(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- struct drm_unique *u = data;
- struct drm_master *master = file_priv->master;
- int ret;
-
- if (master->unique_len || master->unique)
- return -EBUSY;
-
- if (!u->unique_len || u->unique_len > 1024)
- return -EINVAL;
-
- if (drm_core_check_feature(dev, DRIVER_MODESET))
- return 0;
-
- if (WARN_ON(!dev->pdev))
- return -EINVAL;
-
- ret = drm_pci_set_unique(dev, master, u);
- if (ret)
- goto err;
-
- return 0;
-
-err:
- drm_unset_busid(dev, master);
- return ret;
-}
-
static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
{
struct drm_master *master = file_priv->master;
@@ -135,12 +149,7 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
return ret;
}
} else {
- if (WARN(dev->unique == NULL,
- "No drm_driver.set_busid() implementation provided by "
- "%ps. Use drm_dev_set_unique() to set the unique "
- "name explicitly.", dev->driver))
- return -EINVAL;
-
+ WARN_ON(!dev->unique);
master->unique = kstrdup(dev->unique, GFP_KERNEL);
if (master->unique)
master->unique_len = strlen(dev->unique);
@@ -473,7 +482,8 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
return -EACCES;
/* MASTER is only for master or control clients */
- if (unlikely((flags & DRM_MASTER) && !file_priv->is_master &&
+ if (unlikely((flags & DRM_MASTER) &&
+ !drm_is_current_master(file_priv) &&
!drm_is_control_client(file_priv)))
return -EACCES;
@@ -504,7 +514,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version,
DRM_UNLOCKED|DRM_RENDER_ALLOW|DRM_CONTROL_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_legacy_getmap_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
@@ -513,10 +523,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0),
DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
- DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER),
+ DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_UNLOCKED|DRM_MASTER),
DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_legacy_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_legacy_rmmap_ioctl, DRM_AUTH),
@@ -524,8 +534,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_UNLOCKED|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_UNLOCKED|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 76e39c50c90c..8ca3d2bf2bda 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -994,10 +994,10 @@ static void send_vblank_event(struct drm_device *dev,
e->event.tv_sec = now->tv_sec;
e->event.tv_usec = now->tv_usec;
- drm_send_event_locked(dev, &e->base);
-
trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
e->event.sequence);
+
+ drm_send_event_locked(dev, &e->base);
}
/**
diff --git a/drivers/gpu/drm/drm_legacy.h b/drivers/gpu/drm/drm_legacy.h
index d3b6ee357a2b..c6f422e879dd 100644
--- a/drivers/gpu/drm/drm_legacy.h
+++ b/drivers/gpu/drm/drm_legacy.h
@@ -88,14 +88,10 @@ struct drm_agp_mem {
struct list_head head;
};
-/*
- * Generic Userspace Locking-API
- */
-
-int drm_legacy_i_have_hw_lock(struct drm_device *d, struct drm_file *f);
+/* drm_lock.c */
int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f);
int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f);
-int drm_legacy_lock_free(struct drm_lock_data *lock, unsigned int ctx);
+void drm_legacy_lock_release(struct drm_device *dev, struct file *filp);
/* DMA support */
int drm_legacy_dma_setup(struct drm_device *dev);
diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
index daa2ff12101b..48ac0ebbd663 100644
--- a/drivers/gpu/drm/drm_lock.c
+++ b/drivers/gpu/drm/drm_lock.c
@@ -41,6 +41,110 @@
static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context);
/**
+ * Take the heavyweight lock.
+ *
+ * \param lock lock pointer.
+ * \param context locking context.
+ * \return one if the lock is held, or zero otherwise.
+ *
+ * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
+ */
+static
+int drm_lock_take(struct drm_lock_data *lock_data,
+ unsigned int context)
+{
+ unsigned int old, new, prev;
+ volatile unsigned int *lock = &lock_data->hw_lock->lock;
+
+ spin_lock_bh(&lock_data->spinlock);
+ do {
+ old = *lock;
+ if (old & _DRM_LOCK_HELD)
+ new = old | _DRM_LOCK_CONT;
+ else {
+ new = context | _DRM_LOCK_HELD |
+ ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
+ _DRM_LOCK_CONT : 0);
+ }
+ prev = cmpxchg(lock, old, new);
+ } while (prev != old);
+ spin_unlock_bh(&lock_data->spinlock);
+
+ if (_DRM_LOCKING_CONTEXT(old) == context) {
+ if (old & _DRM_LOCK_HELD) {
+ if (context != DRM_KERNEL_CONTEXT) {
+ DRM_ERROR("%d holds heavyweight lock\n",
+ context);
+ }
+ return 0;
+ }
+ }
+
+ if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
+ /* Have lock */
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * This takes a lock forcibly and hands it to context. Should ONLY be used
+ * inside *_unlock to give lock to kernel before calling *_dma_schedule.
+ *
+ * \param dev DRM device.
+ * \param lock lock pointer.
+ * \param context locking context.
+ * \return always one.
+ *
+ * Resets the lock file pointer.
+ * Marks the lock as held by the given context, via the \p cmpxchg instruction.
+ */
+static int drm_lock_transfer(struct drm_lock_data *lock_data,
+ unsigned int context)
+{
+ unsigned int old, new, prev;
+ volatile unsigned int *lock = &lock_data->hw_lock->lock;
+
+ lock_data->file_priv = NULL;
+ do {
+ old = *lock;
+ new = context | _DRM_LOCK_HELD;
+ prev = cmpxchg(lock, old, new);
+ } while (prev != old);
+ return 1;
+}
+
+static int drm_legacy_lock_free(struct drm_lock_data *lock_data,
+ unsigned int context)
+{
+ unsigned int old, new, prev;
+ volatile unsigned int *lock = &lock_data->hw_lock->lock;
+
+ spin_lock_bh(&lock_data->spinlock);
+ if (lock_data->kernel_waiters != 0) {
+ drm_lock_transfer(lock_data, 0);
+ lock_data->idle_has_lock = 1;
+ spin_unlock_bh(&lock_data->spinlock);
+ return 1;
+ }
+ spin_unlock_bh(&lock_data->spinlock);
+
+ do {
+ old = *lock;
+ new = _DRM_LOCKING_CONTEXT(old);
+ prev = cmpxchg(lock, old, new);
+ } while (prev != old);
+
+ if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
+ DRM_ERROR("%d freed heavyweight lock held by %d\n",
+ context, _DRM_LOCKING_CONTEXT(old));
+ return 1;
+ }
+ wake_up_interruptible(&lock_data->lock_queue);
+ return 0;
+}
+
+/**
* Lock ioctl.
*
* \param inode device inode.
@@ -115,7 +219,7 @@ int drm_legacy_lock(struct drm_device *dev, void *data,
/* don't set the block all signals on the master process for now
* really probably not the correct answer but lets us debug xkb
* xserver for now */
- if (!file_priv->is_master) {
+ if (!drm_is_current_master(file_priv)) {
dev->sigdata.context = lock->context;
dev->sigdata.lock = master->lock.hw_lock;
}
@@ -165,120 +269,6 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_
}
/**
- * Take the heavyweight lock.
- *
- * \param lock lock pointer.
- * \param context locking context.
- * \return one if the lock is held, or zero otherwise.
- *
- * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
- */
-static
-int drm_lock_take(struct drm_lock_data *lock_data,
- unsigned int context)
-{
- unsigned int old, new, prev;
- volatile unsigned int *lock = &lock_data->hw_lock->lock;
-
- spin_lock_bh(&lock_data->spinlock);
- do {
- old = *lock;
- if (old & _DRM_LOCK_HELD)
- new = old | _DRM_LOCK_CONT;
- else {
- new = context | _DRM_LOCK_HELD |
- ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
- _DRM_LOCK_CONT : 0);
- }
- prev = cmpxchg(lock, old, new);
- } while (prev != old);
- spin_unlock_bh(&lock_data->spinlock);
-
- if (_DRM_LOCKING_CONTEXT(old) == context) {
- if (old & _DRM_LOCK_HELD) {
- if (context != DRM_KERNEL_CONTEXT) {
- DRM_ERROR("%d holds heavyweight lock\n",
- context);
- }
- return 0;
- }
- }
-
- if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
- /* Have lock */
- return 1;
- }
- return 0;
-}
-
-/**
- * This takes a lock forcibly and hands it to context. Should ONLY be used
- * inside *_unlock to give lock to kernel before calling *_dma_schedule.
- *
- * \param dev DRM device.
- * \param lock lock pointer.
- * \param context locking context.
- * \return always one.
- *
- * Resets the lock file pointer.
- * Marks the lock as held by the given context, via the \p cmpxchg instruction.
- */
-static int drm_lock_transfer(struct drm_lock_data *lock_data,
- unsigned int context)
-{
- unsigned int old, new, prev;
- volatile unsigned int *lock = &lock_data->hw_lock->lock;
-
- lock_data->file_priv = NULL;
- do {
- old = *lock;
- new = context | _DRM_LOCK_HELD;
- prev = cmpxchg(lock, old, new);
- } while (prev != old);
- return 1;
-}
-
-/**
- * Free lock.
- *
- * \param dev DRM device.
- * \param lock lock.
- * \param context context.
- *
- * Resets the lock file pointer.
- * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task
- * waiting on the lock queue.
- */
-int drm_legacy_lock_free(struct drm_lock_data *lock_data, unsigned int context)
-{
- unsigned int old, new, prev;
- volatile unsigned int *lock = &lock_data->hw_lock->lock;
-
- spin_lock_bh(&lock_data->spinlock);
- if (lock_data->kernel_waiters != 0) {
- drm_lock_transfer(lock_data, 0);
- lock_data->idle_has_lock = 1;
- spin_unlock_bh(&lock_data->spinlock);
- return 1;
- }
- spin_unlock_bh(&lock_data->spinlock);
-
- do {
- old = *lock;
- new = _DRM_LOCKING_CONTEXT(old);
- prev = cmpxchg(lock, old, new);
- } while (prev != old);
-
- if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
- DRM_ERROR("%d freed heavyweight lock held by %d\n",
- context, _DRM_LOCKING_CONTEXT(old));
- return 1;
- }
- wake_up_interruptible(&lock_data->lock_queue);
- return 0;
-}
-
-/**
* This function returns immediately and takes the hw lock
* with the kernel context if it is free, otherwise it gets the highest priority when and if
* it is eventually released.
@@ -330,11 +320,27 @@ void drm_legacy_idlelock_release(struct drm_lock_data *lock_data)
}
EXPORT_SYMBOL(drm_legacy_idlelock_release);
-int drm_legacy_i_have_hw_lock(struct drm_device *dev,
- struct drm_file *file_priv)
+static int drm_legacy_i_have_hw_lock(struct drm_device *dev,
+ struct drm_file *file_priv)
{
struct drm_master *master = file_priv->master;
return (file_priv->lock_count && master->lock.hw_lock &&
_DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) &&
master->lock.file_priv == file_priv);
}
+
+void drm_legacy_lock_release(struct drm_device *dev, struct file *filp)
+{
+ struct drm_file *file_priv = filp->private_data;
+
+ /* if the master has gone away we can't do anything with the lock */
+ if (!dev->master)
+ return;
+
+ if (drm_legacy_i_have_hw_lock(dev, file_priv)) {
+ DRM_DEBUG("File %p released, freeing lock for context %d\n",
+ filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+ drm_legacy_lock_free(&file_priv->master->lock,
+ _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+ }
+}
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index 29d5a548d07a..b2f8f1062d5f 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -144,50 +144,6 @@ int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
}
EXPORT_SYMBOL(drm_pci_set_busid);
-int drm_pci_set_unique(struct drm_device *dev,
- struct drm_master *master,
- struct drm_unique *u)
-{
- int domain, bus, slot, func, ret;
-
- master->unique_len = u->unique_len;
- master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
- if (!master->unique) {
- ret = -ENOMEM;
- goto err;
- }
-
- if (copy_from_user(master->unique, u->unique, master->unique_len)) {
- ret = -EFAULT;
- goto err;
- }
-
- master->unique[master->unique_len] = '\0';
-
- /* Return error if the busid submitted doesn't match the device's actual
- * busid.
- */
- ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
- if (ret != 3) {
- ret = -EINVAL;
- goto err;
- }
-
- domain = bus >> 8;
- bus &= 0xff;
-
- if ((domain != drm_get_pci_domain(dev)) ||
- (bus != dev->pdev->bus->number) ||
- (slot != PCI_SLOT(dev->pdev->devfn)) ||
- (func != PCI_FUNC(dev->pdev->devfn))) {
- ret = -EINVAL;
- goto err;
- }
- return 0;
-err:
- return ret;
-}
-
static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
{
if ((p->busnum >> 8) != drm_get_pci_domain(dev) ||
@@ -444,13 +400,6 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
{
return -EINVAL;
}
-
-int drm_pci_set_unique(struct drm_device *dev,
- struct drm_master *master,
- struct drm_unique *u)
-{
- return -EINVAL;
-}
#endif
EXPORT_SYMBOL(drm_pci_init);
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index fc51306fe365..16c4a7bd7465 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -115,6 +115,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
* @src: source coordinates in 16.16 fixed point
* @dest: integer destination coordinates
* @clip: integer clipping coordinates
+ * @rotation: plane rotation
* @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
* @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
* @can_position: is it legal to position the plane such that it
@@ -134,16 +135,17 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
* Zero if update appears valid, error code on failure
*/
int drm_plane_helper_check_update(struct drm_plane *plane,
- struct drm_crtc *crtc,
- struct drm_framebuffer *fb,
- struct drm_rect *src,
- struct drm_rect *dest,
- const struct drm_rect *clip,
- int min_scale,
- int max_scale,
- bool can_position,
- bool can_update_disabled,
- bool *visible)
+ struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_rect *src,
+ struct drm_rect *dest,
+ const struct drm_rect *clip,
+ unsigned int rotation,
+ int min_scale,
+ int max_scale,
+ bool can_position,
+ bool can_update_disabled,
+ bool *visible)
{
int hscale, vscale;
@@ -163,6 +165,8 @@ int drm_plane_helper_check_update(struct drm_plane *plane,
return -EINVAL;
}
+ drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation);
+
/* Check scaling */
hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale);
vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale);
@@ -174,6 +178,9 @@ int drm_plane_helper_check_update(struct drm_plane *plane,
}
*visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale);
+
+ drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation);
+
if (!*visible)
/*
* Plane isn't visible; some drivers can handle this
@@ -267,6 +274,7 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
ret = drm_plane_helper_check_update(plane, crtc, fb,
&src, &dest, &clip,
+ BIT(DRM_ROTATE_0),
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
false, false, &visible);
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index 644169e1a029..2c819ef90090 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -68,24 +68,6 @@ err_free:
return ret;
}
-int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master)
-{
- int id;
-
- id = dev->platformdev->id;
- if (id < 0)
- id = 0;
-
- master->unique = kasprintf(GFP_KERNEL, "platform:%s:%02d",
- dev->platformdev->name, id);
- if (!master->unique)
- return -ENOMEM;
-
- master->unique_len = strlen(master->unique);
- return 0;
-}
-EXPORT_SYMBOL(drm_platform_set_busid);
-
/**
* drm_platform_init - Register a platform device with the DRM subsystem
* @driver: DRM device driver
diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
index b2071d495ada..0db36d27e90b 100644
--- a/drivers/gpu/drm/drm_simple_kms_helper.c
+++ b/drivers/gpu/drm/drm_simple_kms_helper.c
@@ -105,6 +105,7 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
ret = drm_plane_helper_check_update(plane, &pipe->crtc,
plane_state->fb,
&src, &dest, &clip,
+ plane_state->rotation,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
false, true, &visible);
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index ac9f4b3ec615..43ff44a2b8e7 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -670,57 +670,3 @@ void drm_legacy_vma_flush(struct drm_device *dev)
kfree(vma);
}
}
-
-int drm_vma_info(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_vma_entry *pt;
- struct vm_area_struct *vma;
- unsigned long vma_count = 0;
-#if defined(__i386__)
- unsigned int pgprot;
-#endif
-
- mutex_lock(&dev->struct_mutex);
- list_for_each_entry(pt, &dev->vmalist, head)
- vma_count++;
-
- seq_printf(m, "vma use count: %lu, high_memory = %pK, 0x%pK\n",
- vma_count, high_memory,
- (void *)(unsigned long)virt_to_phys(high_memory));
-
- list_for_each_entry(pt, &dev->vmalist, head) {
- vma = pt->vma;
- if (!vma)
- continue;
- seq_printf(m,
- "\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000",
- pt->pid,
- (void *)vma->vm_start, (void *)vma->vm_end,
- vma->vm_flags & VM_READ ? 'r' : '-',
- vma->vm_flags & VM_WRITE ? 'w' : '-',
- vma->vm_flags & VM_EXEC ? 'x' : '-',
- vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
- vma->vm_flags & VM_LOCKED ? 'l' : '-',
- vma->vm_flags & VM_IO ? 'i' : '-',
- vma->vm_pgoff);
-
-#if defined(__i386__)
- pgprot = pgprot_val(vma->vm_page_prot);
- seq_printf(m, " %c%c%c%c%c%c%c%c%c",
- pgprot & _PAGE_PRESENT ? 'p' : '-',
- pgprot & _PAGE_RW ? 'w' : 'r',
- pgprot & _PAGE_USER ? 'u' : 's',
- pgprot & _PAGE_PWT ? 't' : 'b',
- pgprot & _PAGE_PCD ? 'u' : 'c',
- pgprot & _PAGE_ACCESSED ? 'a' : '-',
- pgprot & _PAGE_DIRTY ? 'd' : '-',
- pgprot & _PAGE_PSE ? 'm' : 'k',
- pgprot & _PAGE_GLOBAL ? 'g' : 'l');
-#endif
- seq_printf(m, "\n");
- }
- mutex_unlock(&dev->struct_mutex);
- return 0;
-}
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
index 3d4f56df8359..340d390306d8 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
@@ -496,7 +496,6 @@ static struct drm_driver etnaviv_drm_driver = {
DRIVER_RENDER,
.open = etnaviv_open,
.preclose = etnaviv_preclose,
- .set_busid = drm_platform_set_busid,
.gem_free_object_unlocked = etnaviv_gem_free_object,
.gem_vm_ops = &vm_ops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 4a679fb9bb02..13d28d4229e2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -407,7 +407,6 @@ static struct drm_driver exynos_drm_driver = {
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
.postclose = exynos_drm_postclose,
- .set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c
index c564ec612b59..a6e4cd591960 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c
@@ -37,23 +37,22 @@ int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev)
ret = fsl_dcu_drm_crtc_create(fsl_dev);
if (ret)
- return ret;
+ goto err;
ret = fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc);
if (ret)
- goto fail_encoder;
+ goto err;
ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder);
if (ret)
- goto fail_connector;
+ goto err;
drm_mode_config_reset(fsl_dev->drm);
drm_kms_helper_poll_init(fsl_dev->drm);
return 0;
-fail_encoder:
- fsl_dev->crtc.funcs->destroy(&fsl_dev->crtc);
-fail_connector:
- fsl_dev->encoder.funcs->destroy(&fsl_dev->encoder);
+
+err:
+ drm_mode_config_cleanup(fsl_dev->drm);
return ret;
}
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
index 193657259ee9..1edd9bc80294 100644
--- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
@@ -171,7 +171,6 @@ static struct drm_driver kirin_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
.fops = &kirin_drm_fops,
- .set_busid = drm_platform_set_busid,
.gem_free_object_unlocked = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
@@ -221,19 +220,12 @@ static int kirin_drm_bind(struct device *dev)
if (ret)
goto err_kms_cleanup;
- /* connectors should be registered after drm device register */
- ret = drm_connector_register_all(drm_dev);
- if (ret)
- goto err_drm_dev_unregister;
-
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel,
driver->date, drm_dev->primary->index);
return 0;
-err_drm_dev_unregister:
- drm_dev_unregister(drm_dev);
err_kms_cleanup:
kirin_drm_kms_cleanup(drm_dev);
err_drm_dev_unref:
@@ -246,7 +238,6 @@ static void kirin_drm_unbind(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
- drm_connector_unregister_all(drm_dev);
drm_dev_unregister(drm_dev);
kirin_drm_kms_cleanup(drm_dev);
drm_dev_unref(drm_dev);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b6c8ce484011..74d0a61de75a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -47,6 +47,7 @@
#include <drm/intel-gtt.h>
#include <drm/drm_legacy.h> /* for struct drm_dma_handle */
#include <drm/drm_gem.h>
+#include <drm/drm_auth.h>
#include "i915_params.h"
#include "i915_reg.h"
@@ -3728,7 +3729,7 @@ extern void intel_modeset_init_hw(struct drm_device *dev);
extern void intel_modeset_init(struct drm_device *dev);
extern void intel_modeset_gem_init(struct drm_device *dev);
extern void intel_modeset_cleanup(struct drm_device *dev);
-extern void intel_connector_unregister(struct intel_connector *);
+extern void intel_connector_unregister(struct drm_connector *);
extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
extern void intel_display_resume(struct drm_device *dev);
extern void i915_redisable_vga(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 8097698b9622..7941f1fe9cd2 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -1446,7 +1446,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
dispatch_flags = 0;
if (args->flags & I915_EXEC_SECURE) {
- if (!file->is_master || !capable(CAP_SYS_ADMIN))
+ if (!drm_is_current_master(file) || !capable(CAP_SYS_ADMIN))
return -EPERM;
dispatch_flags |= I915_DISPATCH_SECURE;
@@ -1553,7 +1553,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
batch_obj,
args->batch_start_offset,
args->batch_len,
- file->is_master);
+ drm_is_current_master(file));
if (IS_ERR(parsed_batch_obj)) {
ret = PTR_ERR(parsed_batch_obj);
goto err;
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 9465de4135aa..e115bcc6766f 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -743,6 +743,7 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.detect = intel_crt_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
+ .early_unregister = intel_connector_unregister,
.destroy = intel_crt_destroy,
.set_property = intel_crt_set_property,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -875,7 +876,6 @@ void intel_crt_init(struct drm_device *dev)
crt->base.get_hw_state = intel_crt_get_hw_state;
}
intel_connector->get_hw_state = intel_connector_get_hw_state;
- intel_connector->unregister = intel_connector_unregister;
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index b6bb43875a03..1141b8699dbe 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -14184,6 +14184,7 @@ intel_check_primary_plane(struct drm_plane *plane,
return drm_plane_helper_check_update(plane, crtc, fb, &state->src,
&state->dst, &state->clip,
+ state->base.rotation,
min_scale, max_scale,
can_position, true,
&state->visible);
@@ -14375,6 +14376,7 @@ intel_check_cursor_plane(struct drm_plane *plane,
ret = drm_plane_helper_check_update(plane, crtc, fb, &state->src,
&state->dst, &state->clip,
+ state->base.rotation,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
true, true, &state->visible);
@@ -16316,23 +16318,20 @@ void intel_modeset_gem_init(struct drm_device *dev)
intel_backlight_register(dev);
}
-void intel_connector_unregister(struct intel_connector *intel_connector)
+void intel_connector_unregister(struct drm_connector *connector)
{
- struct drm_connector *connector = &intel_connector->base;
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ intel_backlight_device_unregister(intel_connector);
intel_panel_destroy_backlight(connector);
- drm_connector_unregister(connector);
}
void intel_modeset_cleanup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_connector *connector;
intel_disable_gt_powersave(dev_priv);
- intel_backlight_unregister(dev);
-
/*
* Interrupts and polling as the first thing to avoid creating havoc.
* Too much stuff here (turning of connectors, ...) would
@@ -16353,9 +16352,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
/* flush any delayed tasks or pending work */
flush_scheduled_work();
- /* destroy the backlight and sysfs files before encoders/connectors */
- for_each_intel_connector(dev, connector)
- connector->unregister(connector);
+ drm_connector_unregister_all(dev);
drm_mode_config_cleanup(dev);
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 8bb20e7048ec..85f2801b16ed 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1246,7 +1246,6 @@ static void intel_aux_reg_init(struct intel_dp *intel_dp)
static void
intel_dp_aux_fini(struct intel_dp *intel_dp)
{
- drm_dp_aux_unregister(&intel_dp->aux);
kfree(intel_dp->aux.name);
}
@@ -1281,15 +1280,6 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
return 0;
}
-static void
-intel_dp_connector_unregister(struct intel_connector *intel_connector)
-{
- struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
-
- intel_dp_aux_fini(intel_dp);
- intel_connector_unregister(intel_connector);
-}
-
static int
intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates)
{
@@ -4531,6 +4521,13 @@ done:
}
static void
+intel_dp_connector_unregister(struct drm_connector *connector)
+{
+ drm_dp_aux_unregister(&intel_attached_dp(connector)->aux);
+ intel_connector_unregister(connector);
+}
+
+static void
intel_dp_connector_destroy(struct drm_connector *connector)
{
struct intel_connector *intel_connector = to_intel_connector(connector);
@@ -4570,6 +4567,9 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
intel_dp->edp_notifier.notifier_call = NULL;
}
}
+
+ intel_dp_aux_fini(intel_dp);
+
drm_encoder_cleanup(encoder);
kfree(intel_dig_port);
}
@@ -4648,6 +4648,7 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_dp_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
+ .early_unregister = intel_dp_connector_unregister,
.destroy = intel_dp_connector_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -5583,7 +5584,6 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
else
intel_connector->get_hw_state = intel_connector_get_hw_state;
- intel_connector->unregister = intel_dp_connector_unregister;
/* Set up the hotplug pin. */
switch (port) {
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
index f62ca9a126b3..9646816604be 100644
--- a/drivers/gpu/drm/i915/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -336,6 +336,7 @@ static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_dp_mst_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
+ .early_unregister = intel_connector_unregister,
.destroy = intel_dp_mst_connector_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -455,7 +456,6 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo
drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort);
drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
- intel_connector->unregister = intel_connector_unregister;
intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
intel_connector->mst_port = intel_dp;
intel_connector->port = port;
@@ -489,7 +489,7 @@ static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
struct intel_connector *intel_connector = to_intel_connector(connector);
struct drm_device *dev = connector->dev;
- intel_connector->unregister(intel_connector);
+ drm_connector_unregister(connector);
/* need to nuke the connector */
drm_modeset_lock_all(dev);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 59e1f56a9232..1f82dccfcd58 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -243,14 +243,6 @@ struct intel_connector {
* and active (i.e. dpms ON state). */
bool (*get_hw_state)(struct intel_connector *);
- /*
- * Removes all interfaces through which the connector is accessible
- * - like sysfs, debugfs entries -, so that no new operations can be
- * started on the connector. Also makes sure all currently pending
- * operations finish before returing.
- */
- void (*unregister)(struct intel_connector *);
-
/* Panel info for eDP and LVDS */
struct intel_panel panel;
@@ -1514,7 +1506,14 @@ extern struct drm_display_mode *intel_find_panel_downclock(
struct drm_display_mode *fixed_mode,
struct drm_connector *connector);
void intel_backlight_register(struct drm_device *dev);
-void intel_backlight_unregister(struct drm_device *dev);
+
+#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
+void intel_backlight_device_unregister(struct intel_connector *connector);
+#else /* CONFIG_BACKLIGHT_CLASS_DEVICE */
+static inline void intel_backlight_device_unregister(struct intel_connector *connector)
+{
+}
+#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
/* intel_psr.c */
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index 10e12829b13f..b444d0e35a98 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -1390,6 +1390,7 @@ static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs
static const struct drm_connector_funcs intel_dsi_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.detect = intel_dsi_detect,
+ .early_unregister = intel_connector_unregister,
.destroy = intel_dsi_connector_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_dsi_set_property,
@@ -1466,7 +1467,6 @@ void intel_dsi_init(struct drm_device *dev)
intel_encoder->get_config = intel_dsi_get_config;
intel_connector->get_hw_state = intel_connector_get_hw_state;
- intel_connector->unregister = intel_connector_unregister;
/*
* On BYT/CHV, pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index c86f88ed92fd..60e4ddf2ec6d 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -341,6 +341,7 @@ static void intel_dvo_destroy(struct drm_connector *connector)
static const struct drm_connector_funcs intel_dvo_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.detect = intel_dvo_detect,
+ .early_unregister = intel_connector_unregister,
.destroy = intel_dvo_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_get_property = intel_connector_atomic_get_property,
@@ -447,7 +448,6 @@ void intel_dvo_init(struct drm_device *dev)
intel_encoder->compute_config = intel_dvo_compute_config;
intel_encoder->pre_enable = intel_dvo_pre_enable;
intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
- intel_connector->unregister = intel_connector_unregister;
/* Now, try to find a controller */
for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index ad0c71b3e242..fb21626ada64 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1774,6 +1774,7 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_hdmi_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
+ .early_unregister = intel_connector_unregister,
.destroy = intel_hdmi_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -1909,7 +1910,6 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
else
intel_connector->get_hw_state = intel_connector_get_hw_state;
- intel_connector->unregister = intel_connector_unregister;
intel_hdmi_add_properties(intel_hdmi, connector);
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 72096fd28023..935b53642948 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -555,6 +555,7 @@ static const struct drm_connector_funcs intel_lvds_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_lvds_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
+ .early_unregister = intel_connector_unregister,
.destroy = intel_lvds_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -993,7 +994,6 @@ void intel_lvds_init(struct drm_device *dev)
intel_encoder->get_hw_state = intel_lvds_get_hw_state;
intel_encoder->get_config = intel_lvds_get_config;
intel_connector->get_hw_state = intel_connector_get_hw_state;
- intel_connector->unregister = intel_connector_unregister;
intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_LVDS;
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index f0b1602c3258..bf721781c259 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -1216,7 +1216,7 @@ static int intel_backlight_device_register(struct intel_connector *connector)
return 0;
}
-static void intel_backlight_device_unregister(struct intel_connector *connector)
+void intel_backlight_device_unregister(struct intel_connector *connector)
{
struct intel_panel *panel = &connector->panel;
@@ -1230,9 +1230,6 @@ static int intel_backlight_device_register(struct intel_connector *connector)
{
return 0;
}
-static void intel_backlight_device_unregister(struct intel_connector *connector)
-{
-}
#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
/*
@@ -1820,11 +1817,3 @@ void intel_backlight_register(struct drm_device *dev)
for_each_intel_connector(dev, connector)
intel_backlight_device_register(connector);
}
-
-void intel_backlight_unregister(struct drm_device *dev)
-{
- struct intel_connector *connector;
-
- for_each_intel_connector(dev, connector)
- intel_backlight_device_unregister(connector);
-}
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index ab2d0658abe6..02b4a6695528 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2177,12 +2177,23 @@ done:
#undef CHECK_PROPERTY
}
+static void
+intel_sdvo_connector_unregister(struct drm_connector *connector)
+{
+ struct intel_sdvo *sdvo = intel_attached_sdvo(connector);
+
+ sysfs_remove_link(&connector->kdev->kobj,
+ sdvo->ddc.dev.kobj.name);
+ intel_connector_unregister(connector);
+}
+
static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.detect = intel_sdvo_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_sdvo_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
+ .early_unregister = intel_sdvo_connector_unregister,
.destroy = intel_sdvo_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -2345,20 +2356,6 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, struct intel_sdvo *sdvo)
return 0x72;
}
-static void
-intel_sdvo_connector_unregister(struct intel_connector *intel_connector)
-{
- struct drm_connector *drm_connector;
- struct intel_sdvo *sdvo_encoder;
-
- drm_connector = &intel_connector->base;
- sdvo_encoder = intel_attached_sdvo(&intel_connector->base);
-
- sysfs_remove_link(&drm_connector->kdev->kobj,
- sdvo_encoder->ddc.dev.kobj.name);
- intel_connector_unregister(intel_connector);
-}
-
static int
intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
struct intel_sdvo *encoder)
@@ -2381,7 +2378,6 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
connector->base.base.doublescan_allowed = 0;
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
connector->base.get_hw_state = intel_sdvo_connector_get_hw_state;
- connector->base.unregister = intel_sdvo_connector_unregister;
intel_connector_attach_encoder(&connector->base, &encoder->base);
ret = drm_connector_register(drm_connector);
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 7ac9e9b0e2c3..4ce70a9f9df2 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1501,6 +1501,7 @@ out:
static const struct drm_connector_funcs intel_tv_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.detect = intel_tv_detect,
+ .early_unregister = intel_connector_unregister,
.destroy = intel_tv_destroy,
.set_property = intel_tv_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
@@ -1599,7 +1600,6 @@ intel_tv_init(struct drm_device *dev)
intel_encoder->disable = intel_disable_tv;
intel_encoder->get_hw_state = intel_tv_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
- intel_connector->unregister = intel_connector_unregister;
intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_TVOUT;
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
index 82656654fb21..7746418a4c08 100644
--- a/drivers/gpu/drm/imx/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -407,7 +407,6 @@ static struct drm_driver imx_drm_driver = {
.load = imx_drm_driver_load,
.unload = imx_drm_driver_unload,
.lastclose = imx_drm_driver_lastclose,
- .set_busid = drm_platform_set_busid,
.gem_free_object_unlocked = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = drm_gem_cma_dumb_create,
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index eeefc971801a..23ac8041c562 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -6,7 +6,6 @@ config DRM_MEDIATEK
select DRM_KMS_HELPER
select DRM_MIPI_DSI
select DRM_PANEL
- select IOMMU_DMA
select MEMORY
select MTK_SMI
help
@@ -14,3 +13,11 @@ config DRM_MEDIATEK
The module will be called mediatek-drm
This driver provides kernel mode setting and
buffer management to userspace.
+
+config DRM_MEDIATEK_HDMI
+ tristate "DRM HDMI Support for Mediatek SoCs"
+ depends on DRM_MEDIATEK
+ select SND_SOC_HDMI_CODEC if SND_SOC
+ select GENERIC_PHY
+ help
+ DRM/KMS HDMI driver for Mediatek SoCs
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 5fcf58e87786..bf2e5be1ab30 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -12,3 +12,10 @@ mediatek-drm-y := mtk_disp_ovl.o \
mtk_dpi.o
obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
+
+mediatek-drm-hdmi-objs := mtk_cec.o \
+ mtk_hdmi.o \
+ mtk_hdmi_ddc.o \
+ mtk_mt8173_hdmi_phy.o
+
+obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c
new file mode 100644
index 000000000000..7a3eb8c17ef9
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_cec.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include "mtk_cec.h"
+
+#define TR_CONFIG 0x00
+#define CLEAR_CEC_IRQ BIT(15)
+
+#define CEC_CKGEN 0x04
+#define CEC_32K_PDN BIT(19)
+#define PDN BIT(16)
+
+#define RX_EVENT 0x54
+#define HDMI_PORD BIT(25)
+#define HDMI_HTPLG BIT(24)
+#define HDMI_PORD_INT_EN BIT(9)
+#define HDMI_HTPLG_INT_EN BIT(8)
+
+#define RX_GEN_WD 0x58
+#define HDMI_PORD_INT_32K_STATUS BIT(26)
+#define RX_RISC_INT_32K_STATUS BIT(25)
+#define HDMI_HTPLG_INT_32K_STATUS BIT(24)
+#define HDMI_PORD_INT_32K_CLR BIT(18)
+#define RX_INT_32K_CLR BIT(17)
+#define HDMI_HTPLG_INT_32K_CLR BIT(16)
+#define HDMI_PORD_INT_32K_STA_MASK BIT(10)
+#define RX_RISC_INT_32K_STA_MASK BIT(9)
+#define HDMI_HTPLG_INT_32K_STA_MASK BIT(8)
+#define HDMI_PORD_INT_32K_EN BIT(2)
+#define RX_INT_32K_EN BIT(1)
+#define HDMI_HTPLG_INT_32K_EN BIT(0)
+
+#define NORMAL_INT_CTRL 0x5C
+#define HDMI_HTPLG_INT_STA BIT(0)
+#define HDMI_PORD_INT_STA BIT(1)
+#define HDMI_HTPLG_INT_CLR BIT(16)
+#define HDMI_PORD_INT_CLR BIT(17)
+#define HDMI_FULL_INT_CLR BIT(20)
+
+struct mtk_cec {
+ void __iomem *regs;
+ struct clk *clk;
+ int irq;
+ bool hpd;
+ void (*hpd_event)(bool hpd, struct device *dev);
+ struct device *hdmi_dev;
+ spinlock_t lock;
+};
+
+static void mtk_cec_clear_bits(struct mtk_cec *cec, unsigned int offset,
+ unsigned int bits)
+{
+ void __iomem *reg = cec->regs + offset;
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp &= ~bits;
+ writel(tmp, reg);
+}
+
+static void mtk_cec_set_bits(struct mtk_cec *cec, unsigned int offset,
+ unsigned int bits)
+{
+ void __iomem *reg = cec->regs + offset;
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp |= bits;
+ writel(tmp, reg);
+}
+
+static void mtk_cec_mask(struct mtk_cec *cec, unsigned int offset,
+ unsigned int val, unsigned int mask)
+{
+ u32 tmp = readl(cec->regs + offset) & ~mask;
+
+ tmp |= val & mask;
+ writel(val, cec->regs + offset);
+}
+
+void mtk_cec_set_hpd_event(struct device *dev,
+ void (*hpd_event)(bool hpd, struct device *dev),
+ struct device *hdmi_dev)
+{
+ struct mtk_cec *cec = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&cec->lock, flags);
+ cec->hdmi_dev = hdmi_dev;
+ cec->hpd_event = hpd_event;
+ spin_unlock_irqrestore(&cec->lock, flags);
+}
+
+bool mtk_cec_hpd_high(struct device *dev)
+{
+ struct mtk_cec *cec = dev_get_drvdata(dev);
+ unsigned int status;
+
+ status = readl(cec->regs + RX_EVENT);
+
+ return (status & (HDMI_PORD | HDMI_HTPLG)) == (HDMI_PORD | HDMI_HTPLG);
+}
+
+static void mtk_cec_htplg_irq_init(struct mtk_cec *cec)
+{
+ mtk_cec_mask(cec, CEC_CKGEN, 0 | CEC_32K_PDN, PDN | CEC_32K_PDN);
+ mtk_cec_set_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
+ RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
+ mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_CLR | RX_INT_32K_CLR |
+ HDMI_HTPLG_INT_32K_CLR | HDMI_PORD_INT_32K_EN |
+ RX_INT_32K_EN | HDMI_HTPLG_INT_32K_EN);
+}
+
+static void mtk_cec_htplg_irq_enable(struct mtk_cec *cec)
+{
+ mtk_cec_set_bits(cec, RX_EVENT, HDMI_PORD_INT_EN | HDMI_HTPLG_INT_EN);
+}
+
+static void mtk_cec_htplg_irq_disable(struct mtk_cec *cec)
+{
+ mtk_cec_clear_bits(cec, RX_EVENT, HDMI_PORD_INT_EN | HDMI_HTPLG_INT_EN);
+}
+
+static void mtk_cec_clear_htplg_irq(struct mtk_cec *cec)
+{
+ mtk_cec_set_bits(cec, TR_CONFIG, CLEAR_CEC_IRQ);
+ mtk_cec_set_bits(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR |
+ HDMI_PORD_INT_CLR | HDMI_FULL_INT_CLR);
+ mtk_cec_set_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
+ RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
+ usleep_range(5, 10);
+ mtk_cec_clear_bits(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR |
+ HDMI_PORD_INT_CLR | HDMI_FULL_INT_CLR);
+ mtk_cec_clear_bits(cec, TR_CONFIG, CLEAR_CEC_IRQ);
+ mtk_cec_clear_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
+ RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
+}
+
+static void mtk_cec_hpd_event(struct mtk_cec *cec, bool hpd)
+{
+ void (*hpd_event)(bool hpd, struct device *dev);
+ struct device *hdmi_dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cec->lock, flags);
+ hpd_event = cec->hpd_event;
+ hdmi_dev = cec->hdmi_dev;
+ spin_unlock_irqrestore(&cec->lock, flags);
+
+ if (hpd_event)
+ hpd_event(hpd, hdmi_dev);
+}
+
+static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg)
+{
+ struct device *dev = arg;
+ struct mtk_cec *cec = dev_get_drvdata(dev);
+ bool hpd;
+
+ mtk_cec_clear_htplg_irq(cec);
+ hpd = mtk_cec_hpd_high(dev);
+
+ if (cec->hpd != hpd) {
+ dev_dbg(dev, "hotplug event! cur hpd = %d, hpd = %d\n",
+ cec->hpd, hpd);
+ cec->hpd = hpd;
+ mtk_cec_hpd_event(cec, hpd);
+ }
+ return IRQ_HANDLED;
+}
+
+static int mtk_cec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_cec *cec;
+ struct resource *res;
+ int ret;
+
+ cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cec);
+ spin_lock_init(&cec->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cec->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cec->regs)) {
+ ret = PTR_ERR(cec->regs);
+ dev_err(dev, "Failed to ioremap cec: %d\n", ret);
+ return ret;
+ }
+
+ cec->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(cec->clk)) {
+ ret = PTR_ERR(cec->clk);
+ dev_err(dev, "Failed to get cec clock: %d\n", ret);
+ return ret;
+ }
+
+ cec->irq = platform_get_irq(pdev, 0);
+ if (cec->irq < 0) {
+ dev_err(dev, "Failed to get cec irq: %d\n", cec->irq);
+ return cec->irq;
+ }
+
+ ret = devm_request_threaded_irq(dev, cec->irq, NULL,
+ mtk_cec_htplg_isr_thread,
+ IRQF_SHARED | IRQF_TRIGGER_LOW |
+ IRQF_ONESHOT, "hdmi hpd", dev);
+ if (ret) {
+ dev_err(dev, "Failed to register cec irq: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(cec->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable cec clock: %d\n", ret);
+ return ret;
+ }
+
+ mtk_cec_htplg_irq_init(cec);
+ mtk_cec_htplg_irq_enable(cec);
+
+ return 0;
+}
+
+static int mtk_cec_remove(struct platform_device *pdev)
+{
+ struct mtk_cec *cec = platform_get_drvdata(pdev);
+
+ mtk_cec_htplg_irq_disable(cec);
+ clk_disable_unprepare(cec->clk);
+ return 0;
+}
+
+static const struct of_device_id mtk_cec_of_ids[] = {
+ { .compatible = "mediatek,mt8173-cec", },
+ {}
+};
+
+struct platform_driver mtk_cec_driver = {
+ .probe = mtk_cec_probe,
+ .remove = mtk_cec_remove,
+ .driver = {
+ .name = "mediatek-cec",
+ .of_match_table = mtk_cec_of_ids,
+ },
+};
diff --git a/drivers/gpu/drm/mediatek/mtk_cec.h b/drivers/gpu/drm/mediatek/mtk_cec.h
new file mode 100644
index 000000000000..10057b7eabec
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_cec.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MTK_CEC_H
+#define _MTK_CEC_H
+
+#include <linux/types.h>
+
+struct device;
+
+void mtk_cec_set_hpd_event(struct device *dev,
+ void (*hotplug_event)(bool hpd, struct device *dev),
+ struct device *hdmi_dev);
+bool mtk_cec_hpd_high(struct device *dev);
+
+#endif /* _MTK_CEC_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index c33bf98c5d5e..eebb7d881c2b 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -280,8 +280,6 @@ static int mtk_drm_bind(struct device *dev)
if (!drm)
return -ENOMEM;
- drm_dev_set_unique(drm, dev_name(dev));
-
drm->dev_private = private;
private->drm = drm;
@@ -293,14 +291,8 @@ static int mtk_drm_bind(struct device *dev)
if (ret < 0)
goto err_deinit;
- ret = drm_connector_register_all(drm);
- if (ret < 0)
- goto err_unregister;
-
return 0;
-err_unregister:
- drm_dev_unregister(drm);
err_deinit:
mtk_drm_kms_deinit(drm);
err_free:
@@ -455,7 +447,6 @@ static int mtk_drm_remove(struct platform_device *pdev)
struct drm_device *drm = private->drm;
int i;
- drm_connector_unregister_all(drm);
drm_dev_unregister(drm);
mtk_drm_kms_deinit(drm);
drm_dev_unref(drm);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
index 51bc8988fc26..3995765a90dc 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_plane.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
@@ -170,6 +170,7 @@ static int mtk_plane_atomic_check(struct drm_plane *plane,
return drm_plane_helper_check_update(plane, state->crtc, fb,
&src, &dest, &clip,
+ state->rotation,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
true, true, &visible);
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
new file mode 100644
index 000000000000..ba812ef2c9d1
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -0,0 +1,1828 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/hdmi-codec.h>
+#include "mtk_cec.h"
+#include "mtk_hdmi.h"
+#include "mtk_hdmi_regs.h"
+
+#define NCTS_BYTES 7
+
+enum mtk_hdmi_clk_id {
+ MTK_HDMI_CLK_HDMI_PIXEL,
+ MTK_HDMI_CLK_HDMI_PLL,
+ MTK_HDMI_CLK_AUD_BCLK,
+ MTK_HDMI_CLK_AUD_SPDIF,
+ MTK_HDMI_CLK_COUNT
+};
+
+enum hdmi_aud_input_type {
+ HDMI_AUD_INPUT_I2S = 0,
+ HDMI_AUD_INPUT_SPDIF,
+};
+
+enum hdmi_aud_i2s_fmt {
+ HDMI_I2S_MODE_RJT_24BIT = 0,
+ HDMI_I2S_MODE_RJT_16BIT,
+ HDMI_I2S_MODE_LJT_24BIT,
+ HDMI_I2S_MODE_LJT_16BIT,
+ HDMI_I2S_MODE_I2S_24BIT,
+ HDMI_I2S_MODE_I2S_16BIT
+};
+
+enum hdmi_aud_mclk {
+ HDMI_AUD_MCLK_128FS,
+ HDMI_AUD_MCLK_192FS,
+ HDMI_AUD_MCLK_256FS,
+ HDMI_AUD_MCLK_384FS,
+ HDMI_AUD_MCLK_512FS,
+ HDMI_AUD_MCLK_768FS,
+ HDMI_AUD_MCLK_1152FS,
+};
+
+enum hdmi_aud_channel_type {
+ HDMI_AUD_CHAN_TYPE_1_0 = 0,
+ HDMI_AUD_CHAN_TYPE_1_1,
+ HDMI_AUD_CHAN_TYPE_2_0,
+ HDMI_AUD_CHAN_TYPE_2_1,
+ HDMI_AUD_CHAN_TYPE_3_0,
+ HDMI_AUD_CHAN_TYPE_3_1,
+ HDMI_AUD_CHAN_TYPE_4_0,
+ HDMI_AUD_CHAN_TYPE_4_1,
+ HDMI_AUD_CHAN_TYPE_5_0,
+ HDMI_AUD_CHAN_TYPE_5_1,
+ HDMI_AUD_CHAN_TYPE_6_0,
+ HDMI_AUD_CHAN_TYPE_6_1,
+ HDMI_AUD_CHAN_TYPE_7_0,
+ HDMI_AUD_CHAN_TYPE_7_1,
+ HDMI_AUD_CHAN_TYPE_3_0_LRS,
+ HDMI_AUD_CHAN_TYPE_3_1_LRS,
+ HDMI_AUD_CHAN_TYPE_4_0_CLRS,
+ HDMI_AUD_CHAN_TYPE_4_1_CLRS,
+ HDMI_AUD_CHAN_TYPE_6_1_CS,
+ HDMI_AUD_CHAN_TYPE_6_1_CH,
+ HDMI_AUD_CHAN_TYPE_6_1_OH,
+ HDMI_AUD_CHAN_TYPE_6_1_CHR,
+ HDMI_AUD_CHAN_TYPE_7_1_LH_RH,
+ HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR,
+ HDMI_AUD_CHAN_TYPE_7_1_LC_RC,
+ HDMI_AUD_CHAN_TYPE_7_1_LW_RW,
+ HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD,
+ HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS,
+ HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS,
+ HDMI_AUD_CHAN_TYPE_7_1_CS_CH,
+ HDMI_AUD_CHAN_TYPE_7_1_CS_OH,
+ HDMI_AUD_CHAN_TYPE_7_1_CS_CHR,
+ HDMI_AUD_CHAN_TYPE_7_1_CH_OH,
+ HDMI_AUD_CHAN_TYPE_7_1_CH_CHR,
+ HDMI_AUD_CHAN_TYPE_7_1_OH_CHR,
+ HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR,
+ HDMI_AUD_CHAN_TYPE_6_0_CS,
+ HDMI_AUD_CHAN_TYPE_6_0_CH,
+ HDMI_AUD_CHAN_TYPE_6_0_OH,
+ HDMI_AUD_CHAN_TYPE_6_0_CHR,
+ HDMI_AUD_CHAN_TYPE_7_0_LH_RH,
+ HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR,
+ HDMI_AUD_CHAN_TYPE_7_0_LC_RC,
+ HDMI_AUD_CHAN_TYPE_7_0_LW_RW,
+ HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD,
+ HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS,
+ HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS,
+ HDMI_AUD_CHAN_TYPE_7_0_CS_CH,
+ HDMI_AUD_CHAN_TYPE_7_0_CS_OH,
+ HDMI_AUD_CHAN_TYPE_7_0_CS_CHR,
+ HDMI_AUD_CHAN_TYPE_7_0_CH_OH,
+ HDMI_AUD_CHAN_TYPE_7_0_CH_CHR,
+ HDMI_AUD_CHAN_TYPE_7_0_OH_CHR,
+ HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR,
+ HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS,
+ HDMI_AUD_CHAN_TYPE_UNKNOWN = 0xFF
+};
+
+enum hdmi_aud_channel_swap_type {
+ HDMI_AUD_SWAP_LR,
+ HDMI_AUD_SWAP_LFE_CC,
+ HDMI_AUD_SWAP_LSRS,
+ HDMI_AUD_SWAP_RLS_RRS,
+ HDMI_AUD_SWAP_LR_STATUS,
+};
+
+struct hdmi_audio_param {
+ enum hdmi_audio_coding_type aud_codec;
+ enum hdmi_audio_sample_size aud_sampe_size;
+ enum hdmi_aud_input_type aud_input_type;
+ enum hdmi_aud_i2s_fmt aud_i2s_fmt;
+ enum hdmi_aud_mclk aud_mclk;
+ enum hdmi_aud_channel_type aud_input_chan_type;
+ struct hdmi_codec_params codec_params;
+};
+
+struct mtk_hdmi {
+ struct drm_bridge bridge;
+ struct drm_connector conn;
+ struct device *dev;
+ struct phy *phy;
+ struct device *cec_dev;
+ struct i2c_adapter *ddc_adpt;
+ struct clk *clk[MTK_HDMI_CLK_COUNT];
+ struct drm_display_mode mode;
+ bool dvi_mode;
+ u32 min_clock;
+ u32 max_clock;
+ u32 max_hdisplay;
+ u32 max_vdisplay;
+ u32 ibias;
+ u32 ibias_up;
+ struct regmap *sys_regmap;
+ unsigned int sys_offset;
+ void __iomem *regs;
+ enum hdmi_colorspace csp;
+ struct hdmi_audio_param aud_param;
+ bool audio_enable;
+ bool powered;
+ bool enabled;
+};
+
+static inline struct mtk_hdmi *hdmi_ctx_from_bridge(struct drm_bridge *b)
+{
+ return container_of(b, struct mtk_hdmi, bridge);
+}
+
+static inline struct mtk_hdmi *hdmi_ctx_from_conn(struct drm_connector *c)
+{
+ return container_of(c, struct mtk_hdmi, conn);
+}
+
+static u32 mtk_hdmi_read(struct mtk_hdmi *hdmi, u32 offset)
+{
+ return readl(hdmi->regs + offset);
+}
+
+static void mtk_hdmi_write(struct mtk_hdmi *hdmi, u32 offset, u32 val)
+{
+ writel(val, hdmi->regs + offset);
+}
+
+static void mtk_hdmi_clear_bits(struct mtk_hdmi *hdmi, u32 offset, u32 bits)
+{
+ void __iomem *reg = hdmi->regs + offset;
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp &= ~bits;
+ writel(tmp, reg);
+}
+
+static void mtk_hdmi_set_bits(struct mtk_hdmi *hdmi, u32 offset, u32 bits)
+{
+ void __iomem *reg = hdmi->regs + offset;
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp |= bits;
+ writel(tmp, reg);
+}
+
+static void mtk_hdmi_mask(struct mtk_hdmi *hdmi, u32 offset, u32 val, u32 mask)
+{
+ void __iomem *reg = hdmi->regs + offset;
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp = (tmp & ~mask) | (val & mask);
+ writel(tmp, reg);
+}
+
+static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black)
+{
+ mtk_hdmi_mask(hdmi, VIDEO_CFG_4, black ? GEN_RGB : NORMAL_PATH,
+ VIDEO_SOURCE_SEL);
+}
+
+static void mtk_hdmi_hw_make_reg_writable(struct mtk_hdmi *hdmi, bool enable)
+{
+ struct arm_smccc_res res;
+
+ /*
+ * MT8173 HDMI hardware has an output control bit to enable/disable HDMI
+ * output. This bit can only be controlled in ARM supervisor mode.
+ * The ARM trusted firmware provides an API for the HDMI driver to set
+ * this control bit to enable HDMI output in supervisor mode.
+ */
+ arm_smccc_smc(MTK_SIP_SET_AUTHORIZED_SECURE_REG, 0x14000904, 0x80000000,
+ 0, 0, 0, 0, 0, &res);
+
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
+ HDMI_PCLK_FREE_RUN, enable ? HDMI_PCLK_FREE_RUN : 0);
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C,
+ HDMI_ON | ANLG_ON, enable ? (HDMI_ON | ANLG_ON) : 0);
+}
+
+static void mtk_hdmi_hw_1p4_version_enable(struct mtk_hdmi *hdmi, bool enable)
+{
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
+ HDMI2P0_EN, enable ? 0 : HDMI2P0_EN);
+}
+
+static void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi)
+{
+ mtk_hdmi_set_bits(hdmi, GRL_AUDIO_CFG, AUDIO_ZERO);
+}
+
+static void mtk_hdmi_hw_aud_unmute(struct mtk_hdmi *hdmi)
+{
+ mtk_hdmi_clear_bits(hdmi, GRL_AUDIO_CFG, AUDIO_ZERO);
+}
+
+static void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi)
+{
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C,
+ HDMI_RST, HDMI_RST);
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C,
+ HDMI_RST, 0);
+ mtk_hdmi_clear_bits(hdmi, GRL_CFG3, CFG3_CONTROL_PACKET_DELAY);
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C,
+ ANLG_ON, ANLG_ON);
+}
+
+static void mtk_hdmi_hw_enable_notice(struct mtk_hdmi *hdmi, bool enable_notice)
+{
+ mtk_hdmi_mask(hdmi, GRL_CFG2, enable_notice ? CFG2_NOTICE_EN : 0,
+ CFG2_NOTICE_EN);
+}
+
+static void mtk_hdmi_hw_write_int_mask(struct mtk_hdmi *hdmi, u32 int_mask)
+{
+ mtk_hdmi_write(hdmi, GRL_INT_MASK, int_mask);
+}
+
+static void mtk_hdmi_hw_enable_dvi_mode(struct mtk_hdmi *hdmi, bool enable)
+{
+ mtk_hdmi_mask(hdmi, GRL_CFG1, enable ? CFG1_DVI : 0, CFG1_DVI);
+}
+
+static void mtk_hdmi_hw_send_info_frame(struct mtk_hdmi *hdmi, u8 *buffer,
+ u8 len)
+{
+ u32 ctrl_reg = GRL_CTRL;
+ int i;
+ u8 *frame_data;
+ enum hdmi_infoframe_type frame_type;
+ u8 frame_ver;
+ u8 frame_len;
+ u8 checksum;
+ int ctrl_frame_en = 0;
+
+ frame_type = *buffer;
+ buffer += 1;
+ frame_ver = *buffer;
+ buffer += 1;
+ frame_len = *buffer;
+ buffer += 1;
+ checksum = *buffer;
+ buffer += 1;
+ frame_data = buffer;
+
+ dev_dbg(hdmi->dev,
+ "frame_type:0x%x,frame_ver:0x%x,frame_len:0x%x,checksum:0x%x\n",
+ frame_type, frame_ver, frame_len, checksum);
+
+ switch (frame_type) {
+ case HDMI_INFOFRAME_TYPE_AVI:
+ ctrl_frame_en = CTRL_AVI_EN;
+ ctrl_reg = GRL_CTRL;
+ break;
+ case HDMI_INFOFRAME_TYPE_SPD:
+ ctrl_frame_en = CTRL_SPD_EN;
+ ctrl_reg = GRL_CTRL;
+ break;
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ ctrl_frame_en = CTRL_AUDIO_EN;
+ ctrl_reg = GRL_CTRL;
+ break;
+ case HDMI_INFOFRAME_TYPE_VENDOR:
+ ctrl_frame_en = VS_EN;
+ ctrl_reg = GRL_ACP_ISRC_CTRL;
+ break;
+ }
+ mtk_hdmi_clear_bits(hdmi, ctrl_reg, ctrl_frame_en);
+ mtk_hdmi_write(hdmi, GRL_INFOFRM_TYPE, frame_type);
+ mtk_hdmi_write(hdmi, GRL_INFOFRM_VER, frame_ver);
+ mtk_hdmi_write(hdmi, GRL_INFOFRM_LNG, frame_len);
+
+ mtk_hdmi_write(hdmi, GRL_IFM_PORT, checksum);
+ for (i = 0; i < frame_len; i++)
+ mtk_hdmi_write(hdmi, GRL_IFM_PORT, frame_data[i]);
+
+ mtk_hdmi_set_bits(hdmi, ctrl_reg, ctrl_frame_en);
+}
+
+static void mtk_hdmi_hw_send_aud_packet(struct mtk_hdmi *hdmi, bool enable)
+{
+ mtk_hdmi_mask(hdmi, GRL_SHIFT_R2, enable ? 0 : AUDIO_PACKET_OFF,
+ AUDIO_PACKET_OFF);
+}
+
+static void mtk_hdmi_hw_config_sys(struct mtk_hdmi *hdmi)
+{
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
+ HDMI_OUT_FIFO_EN | MHL_MODE_ON, 0);
+ usleep_range(2000, 4000);
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
+ HDMI_OUT_FIFO_EN | MHL_MODE_ON, HDMI_OUT_FIFO_EN);
+}
+
+static void mtk_hdmi_hw_set_deep_color_mode(struct mtk_hdmi *hdmi)
+{
+ regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
+ DEEP_COLOR_MODE_MASK | DEEP_COLOR_EN,
+ COLOR_8BIT_MODE);
+}
+
+static void mtk_hdmi_hw_send_av_mute(struct mtk_hdmi *hdmi)
+{
+ mtk_hdmi_clear_bits(hdmi, GRL_CFG4, CTRL_AVMUTE);
+ usleep_range(2000, 4000);
+ mtk_hdmi_set_bits(hdmi, GRL_CFG4, CTRL_AVMUTE);
+}
+
+static void mtk_hdmi_hw_send_av_unmute(struct mtk_hdmi *hdmi)
+{
+ mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_EN,
+ CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET);
+ usleep_range(2000, 4000);
+ mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_SET,
+ CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET);
+}
+
+static void mtk_hdmi_hw_ncts_enable(struct mtk_hdmi *hdmi, bool on)
+{
+ mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, on ? 0 : CTS_CTRL_SOFT,
+ CTS_CTRL_SOFT);
+}
+
+static void mtk_hdmi_hw_ncts_auto_write_enable(struct mtk_hdmi *hdmi,
+ bool enable)
+{
+ mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, enable ? NCTS_WRI_ANYTIME : 0,
+ NCTS_WRI_ANYTIME);
+}
+
+static void mtk_hdmi_hw_msic_setting(struct mtk_hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ mtk_hdmi_clear_bits(hdmi, GRL_CFG4, CFG4_MHL_MODE);
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE &&
+ mode->clock == 74250 &&
+ mode->vdisplay == 1080)
+ mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_MHL_DE_SEL);
+ else
+ mtk_hdmi_set_bits(hdmi, GRL_CFG2, CFG2_MHL_DE_SEL);
+}
+
+static void mtk_hdmi_hw_aud_set_channel_swap(struct mtk_hdmi *hdmi,
+ enum hdmi_aud_channel_swap_type swap)
+{
+ u8 swap_bit;
+
+ switch (swap) {
+ case HDMI_AUD_SWAP_LR:
+ swap_bit = LR_SWAP;
+ break;
+ case HDMI_AUD_SWAP_LFE_CC:
+ swap_bit = LFE_CC_SWAP;
+ break;
+ case HDMI_AUD_SWAP_LSRS:
+ swap_bit = LSRS_SWAP;
+ break;
+ case HDMI_AUD_SWAP_RLS_RRS:
+ swap_bit = RLS_RRS_SWAP;
+ break;
+ case HDMI_AUD_SWAP_LR_STATUS:
+ swap_bit = LR_STATUS_SWAP;
+ break;
+ default:
+ swap_bit = LFE_CC_SWAP;
+ break;
+ }
+ mtk_hdmi_mask(hdmi, GRL_CH_SWAP, swap_bit, 0xff);
+}
+
+static void mtk_hdmi_hw_aud_set_bit_num(struct mtk_hdmi *hdmi,
+ enum hdmi_audio_sample_size bit_num)
+{
+ u32 val;
+
+ switch (bit_num) {
+ case HDMI_AUDIO_SAMPLE_SIZE_16:
+ val = AOUT_16BIT;
+ break;
+ case HDMI_AUDIO_SAMPLE_SIZE_20:
+ val = AOUT_20BIT;
+ break;
+ case HDMI_AUDIO_SAMPLE_SIZE_24:
+ case HDMI_AUDIO_SAMPLE_SIZE_STREAM:
+ val = AOUT_24BIT;
+ break;
+ }
+
+ mtk_hdmi_mask(hdmi, GRL_AOUT_CFG, val, AOUT_BNUM_SEL_MASK);
+}
+
+static void mtk_hdmi_hw_aud_set_i2s_fmt(struct mtk_hdmi *hdmi,
+ enum hdmi_aud_i2s_fmt i2s_fmt)
+{
+ u32 val;
+
+ val = mtk_hdmi_read(hdmi, GRL_CFG0);
+ val &= ~(CFG0_W_LENGTH_MASK | CFG0_I2S_MODE_MASK);
+
+ switch (i2s_fmt) {
+ case HDMI_I2S_MODE_RJT_24BIT:
+ val |= CFG0_I2S_MODE_RTJ | CFG0_W_LENGTH_24BIT;
+ break;
+ case HDMI_I2S_MODE_RJT_16BIT:
+ val |= CFG0_I2S_MODE_RTJ | CFG0_W_LENGTH_16BIT;
+ break;
+ case HDMI_I2S_MODE_LJT_24BIT:
+ default:
+ val |= CFG0_I2S_MODE_LTJ | CFG0_W_LENGTH_24BIT;
+ break;
+ case HDMI_I2S_MODE_LJT_16BIT:
+ val |= CFG0_I2S_MODE_LTJ | CFG0_W_LENGTH_16BIT;
+ break;
+ case HDMI_I2S_MODE_I2S_24BIT:
+ val |= CFG0_I2S_MODE_I2S | CFG0_W_LENGTH_24BIT;
+ break;
+ case HDMI_I2S_MODE_I2S_16BIT:
+ val |= CFG0_I2S_MODE_I2S | CFG0_W_LENGTH_16BIT;
+ break;
+ }
+ mtk_hdmi_write(hdmi, GRL_CFG0, val);
+}
+
+static void mtk_hdmi_hw_audio_config(struct mtk_hdmi *hdmi, bool dst)
+{
+ const u8 mask = HIGH_BIT_RATE | DST_NORMAL_DOUBLE | SACD_DST | DSD_SEL;
+ u8 val;
+
+ /* Disable high bitrate, set DST packet normal/double */
+ mtk_hdmi_clear_bits(hdmi, GRL_AOUT_CFG, HIGH_BIT_RATE_PACKET_ALIGN);
+
+ if (dst)
+ val = DST_NORMAL_DOUBLE | SACD_DST;
+ else
+ val = 0;
+
+ mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, val, mask);
+}
+
+static void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi,
+ enum hdmi_aud_channel_type channel_type,
+ u8 channel_count)
+{
+ unsigned int ch_switch;
+ u8 i2s_uv;
+
+ ch_switch = CH_SWITCH(7, 7) | CH_SWITCH(6, 6) |
+ CH_SWITCH(5, 5) | CH_SWITCH(4, 4) |
+ CH_SWITCH(3, 3) | CH_SWITCH(1, 2) |
+ CH_SWITCH(2, 1) | CH_SWITCH(0, 0);
+
+ if (channel_count == 2) {
+ i2s_uv = I2S_UV_CH_EN(0);
+ } else if (channel_count == 3 || channel_count == 4) {
+ if (channel_count == 4 &&
+ (channel_type == HDMI_AUD_CHAN_TYPE_3_0_LRS ||
+ channel_type == HDMI_AUD_CHAN_TYPE_4_0))
+ i2s_uv = I2S_UV_CH_EN(2) | I2S_UV_CH_EN(0);
+ else
+ i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2);
+ } else if (channel_count == 6 || channel_count == 5) {
+ if (channel_count == 6 &&
+ channel_type != HDMI_AUD_CHAN_TYPE_5_1 &&
+ channel_type != HDMI_AUD_CHAN_TYPE_4_1_CLRS) {
+ i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2) |
+ I2S_UV_CH_EN(1) | I2S_UV_CH_EN(0);
+ } else {
+ i2s_uv = I2S_UV_CH_EN(2) | I2S_UV_CH_EN(1) |
+ I2S_UV_CH_EN(0);
+ }
+ } else if (channel_count == 8 || channel_count == 7) {
+ i2s_uv = I2S_UV_CH_EN(3) | I2S_UV_CH_EN(2) |
+ I2S_UV_CH_EN(1) | I2S_UV_CH_EN(0);
+ } else {
+ i2s_uv = I2S_UV_CH_EN(0);
+ }
+
+ mtk_hdmi_write(hdmi, GRL_CH_SW0, ch_switch & 0xff);
+ mtk_hdmi_write(hdmi, GRL_CH_SW1, (ch_switch >> 8) & 0xff);
+ mtk_hdmi_write(hdmi, GRL_CH_SW2, (ch_switch >> 16) & 0xff);
+ mtk_hdmi_write(hdmi, GRL_I2S_UV, i2s_uv);
+}
+
+static void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi,
+ enum hdmi_aud_input_type input_type)
+{
+ u32 val;
+
+ val = mtk_hdmi_read(hdmi, GRL_CFG1);
+ if (input_type == HDMI_AUD_INPUT_I2S &&
+ (val & CFG1_SPDIF) == CFG1_SPDIF) {
+ val &= ~CFG1_SPDIF;
+ } else if (input_type == HDMI_AUD_INPUT_SPDIF &&
+ (val & CFG1_SPDIF) == 0) {
+ val |= CFG1_SPDIF;
+ }
+ mtk_hdmi_write(hdmi, GRL_CFG1, val);
+}
+
+static void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi,
+ u8 *channel_status)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ mtk_hdmi_write(hdmi, GRL_I2S_C_STA0 + i * 4, channel_status[i]);
+ mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, channel_status[i]);
+ mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, channel_status[i]);
+ }
+ for (; i < 24; i++) {
+ mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, 0);
+ mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, 0);
+ }
+}
+
+static void mtk_hdmi_hw_aud_src_reenable(struct mtk_hdmi *hdmi)
+{
+ u32 val;
+
+ val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL);
+ if (val & MIX_CTRL_SRC_EN) {
+ val &= ~MIX_CTRL_SRC_EN;
+ mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val);
+ usleep_range(255, 512);
+ val |= MIX_CTRL_SRC_EN;
+ mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val);
+ }
+}
+
+static void mtk_hdmi_hw_aud_src_disable(struct mtk_hdmi *hdmi)
+{
+ u32 val;
+
+ val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL);
+ val &= ~MIX_CTRL_SRC_EN;
+ mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val);
+ mtk_hdmi_write(hdmi, GRL_SHIFT_L1, 0x00);
+}
+
+static void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi,
+ enum hdmi_aud_mclk mclk)
+{
+ u32 val;
+
+ val = mtk_hdmi_read(hdmi, GRL_CFG5);
+ val &= CFG5_CD_RATIO_MASK;
+
+ switch (mclk) {
+ case HDMI_AUD_MCLK_128FS:
+ val |= CFG5_FS128;
+ break;
+ case HDMI_AUD_MCLK_256FS:
+ val |= CFG5_FS256;
+ break;
+ case HDMI_AUD_MCLK_384FS:
+ val |= CFG5_FS384;
+ break;
+ case HDMI_AUD_MCLK_512FS:
+ val |= CFG5_FS512;
+ break;
+ case HDMI_AUD_MCLK_768FS:
+ val |= CFG5_FS768;
+ break;
+ default:
+ val |= CFG5_FS256;
+ break;
+ }
+ mtk_hdmi_write(hdmi, GRL_CFG5, val);
+}
+
+struct hdmi_acr_n {
+ unsigned int clock;
+ unsigned int n[3];
+};
+
+/* Recommended N values from HDMI specification, tables 7-1 to 7-3 */
+static const struct hdmi_acr_n hdmi_rec_n_table[] = {
+ /* Clock, N: 32kHz 44.1kHz 48kHz */
+ { 25175, { 4576, 7007, 6864 } },
+ { 74176, { 11648, 17836, 11648 } },
+ { 148352, { 11648, 8918, 5824 } },
+ { 296703, { 5824, 4459, 5824 } },
+ { 297000, { 3072, 4704, 5120 } },
+ { 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */
+};
+
+/**
+ * hdmi_recommended_n() - Return N value recommended by HDMI specification
+ * @freq: audio sample rate in Hz
+ * @clock: rounded TMDS clock in kHz
+ */
+static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock)
+{
+ const struct hdmi_acr_n *recommended;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) {
+ if (clock == hdmi_rec_n_table[i].clock)
+ break;
+ }
+ recommended = hdmi_rec_n_table + i;
+
+ switch (freq) {
+ case 32000:
+ return recommended->n[0];
+ case 44100:
+ return recommended->n[1];
+ case 48000:
+ return recommended->n[2];
+ case 88200:
+ return recommended->n[1] * 2;
+ case 96000:
+ return recommended->n[2] * 2;
+ case 176400:
+ return recommended->n[1] * 4;
+ case 192000:
+ return recommended->n[2] * 4;
+ default:
+ return (128 * freq) / 1000;
+ }
+}
+
+static unsigned int hdmi_mode_clock_to_hz(unsigned int clock)
+{
+ switch (clock) {
+ case 25175:
+ return 25174825; /* 25.2/1.001 MHz */
+ case 74176:
+ return 74175824; /* 74.25/1.001 MHz */
+ case 148352:
+ return 148351648; /* 148.5/1.001 MHz */
+ case 296703:
+ return 296703297; /* 297/1.001 MHz */
+ default:
+ return clock * 1000;
+ }
+}
+
+static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate,
+ unsigned int tmds_clock, unsigned int n)
+{
+ return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n,
+ 128 * audio_sample_rate);
+}
+
+static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n,
+ unsigned int cts)
+{
+ unsigned char val[NCTS_BYTES];
+ int i;
+
+ mtk_hdmi_write(hdmi, GRL_NCTS, 0);
+ mtk_hdmi_write(hdmi, GRL_NCTS, 0);
+ mtk_hdmi_write(hdmi, GRL_NCTS, 0);
+ memset(val, 0, sizeof(val));
+
+ val[0] = (cts >> 24) & 0xff;
+ val[1] = (cts >> 16) & 0xff;
+ val[2] = (cts >> 8) & 0xff;
+ val[3] = cts & 0xff;
+
+ val[4] = (n >> 16) & 0xff;
+ val[5] = (n >> 8) & 0xff;
+ val[6] = n & 0xff;
+
+ for (i = 0; i < NCTS_BYTES; i++)
+ mtk_hdmi_write(hdmi, GRL_NCTS, val[i]);
+}
+
+static void mtk_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi,
+ unsigned int sample_rate,
+ unsigned int clock)
+{
+ unsigned int n, cts;
+
+ n = hdmi_recommended_n(sample_rate, clock);
+ cts = hdmi_expected_cts(sample_rate, clock, n);
+
+ dev_dbg(hdmi->dev, "%s: sample_rate=%u, clock=%d, cts=%u, n=%u\n",
+ __func__, sample_rate, clock, n, cts);
+
+ mtk_hdmi_mask(hdmi, DUMMY_304, AUDIO_I2S_NCTS_SEL_64,
+ AUDIO_I2S_NCTS_SEL);
+ do_hdmi_hw_aud_set_ncts(hdmi, n, cts);
+}
+
+static u8 mtk_hdmi_aud_get_chnl_count(enum hdmi_aud_channel_type channel_type)
+{
+ switch (channel_type) {
+ case HDMI_AUD_CHAN_TYPE_1_0:
+ case HDMI_AUD_CHAN_TYPE_1_1:
+ case HDMI_AUD_CHAN_TYPE_2_0:
+ return 2;
+ case HDMI_AUD_CHAN_TYPE_2_1:
+ case HDMI_AUD_CHAN_TYPE_3_0:
+ return 3;
+ case HDMI_AUD_CHAN_TYPE_3_1:
+ case HDMI_AUD_CHAN_TYPE_4_0:
+ case HDMI_AUD_CHAN_TYPE_3_0_LRS:
+ return 4;
+ case HDMI_AUD_CHAN_TYPE_4_1:
+ case HDMI_AUD_CHAN_TYPE_5_0:
+ case HDMI_AUD_CHAN_TYPE_3_1_LRS:
+ case HDMI_AUD_CHAN_TYPE_4_0_CLRS:
+ return 5;
+ case HDMI_AUD_CHAN_TYPE_5_1:
+ case HDMI_AUD_CHAN_TYPE_6_0:
+ case HDMI_AUD_CHAN_TYPE_4_1_CLRS:
+ case HDMI_AUD_CHAN_TYPE_6_0_CS:
+ case HDMI_AUD_CHAN_TYPE_6_0_CH:
+ case HDMI_AUD_CHAN_TYPE_6_0_OH:
+ case HDMI_AUD_CHAN_TYPE_6_0_CHR:
+ return 6;
+ case HDMI_AUD_CHAN_TYPE_6_1:
+ case HDMI_AUD_CHAN_TYPE_6_1_CS:
+ case HDMI_AUD_CHAN_TYPE_6_1_CH:
+ case HDMI_AUD_CHAN_TYPE_6_1_OH:
+ case HDMI_AUD_CHAN_TYPE_6_1_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_0:
+ case HDMI_AUD_CHAN_TYPE_7_0_LH_RH:
+ case HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR:
+ case HDMI_AUD_CHAN_TYPE_7_0_LC_RC:
+ case HDMI_AUD_CHAN_TYPE_7_0_LW_RW:
+ case HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD:
+ case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS:
+ case HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS:
+ case HDMI_AUD_CHAN_TYPE_7_0_CS_CH:
+ case HDMI_AUD_CHAN_TYPE_7_0_CS_OH:
+ case HDMI_AUD_CHAN_TYPE_7_0_CS_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_0_CH_OH:
+ case HDMI_AUD_CHAN_TYPE_7_0_CH_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_0_OH_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR:
+ case HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS:
+ return 7;
+ case HDMI_AUD_CHAN_TYPE_7_1:
+ case HDMI_AUD_CHAN_TYPE_7_1_LH_RH:
+ case HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR:
+ case HDMI_AUD_CHAN_TYPE_7_1_LC_RC:
+ case HDMI_AUD_CHAN_TYPE_7_1_LW_RW:
+ case HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD:
+ case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS:
+ case HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS:
+ case HDMI_AUD_CHAN_TYPE_7_1_CS_CH:
+ case HDMI_AUD_CHAN_TYPE_7_1_CS_OH:
+ case HDMI_AUD_CHAN_TYPE_7_1_CS_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_1_CH_OH:
+ case HDMI_AUD_CHAN_TYPE_7_1_CH_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_1_OH_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR:
+ return 8;
+ default:
+ return 2;
+ }
+}
+
+static int mtk_hdmi_video_change_vpll(struct mtk_hdmi *hdmi, u32 clock)
+{
+ unsigned long rate;
+ int ret;
+
+ /* The DPI driver already should have set TVDPLL to the correct rate */
+ ret = clk_set_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL], clock);
+ if (ret) {
+ dev_err(hdmi->dev, "Failed to set PLL to %u Hz: %d\n", clock,
+ ret);
+ return ret;
+ }
+
+ rate = clk_get_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);
+
+ if (DIV_ROUND_CLOSEST(rate, 1000) != DIV_ROUND_CLOSEST(clock, 1000))
+ dev_warn(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock,
+ rate);
+ else
+ dev_dbg(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock, rate);
+
+ mtk_hdmi_hw_config_sys(hdmi);
+ mtk_hdmi_hw_set_deep_color_mode(hdmi);
+ return 0;
+}
+
+static void mtk_hdmi_video_set_display_mode(struct mtk_hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ mtk_hdmi_hw_reset(hdmi);
+ mtk_hdmi_hw_enable_notice(hdmi, true);
+ mtk_hdmi_hw_write_int_mask(hdmi, 0xff);
+ mtk_hdmi_hw_enable_dvi_mode(hdmi, hdmi->dvi_mode);
+ mtk_hdmi_hw_ncts_auto_write_enable(hdmi, true);
+
+ mtk_hdmi_hw_msic_setting(hdmi, mode);
+}
+
+static int mtk_hdmi_aud_enable_packet(struct mtk_hdmi *hdmi, bool enable)
+{
+ mtk_hdmi_hw_send_aud_packet(hdmi, enable);
+ return 0;
+}
+
+static int mtk_hdmi_aud_on_off_hw_ncts(struct mtk_hdmi *hdmi, bool on)
+{
+ mtk_hdmi_hw_ncts_enable(hdmi, on);
+ return 0;
+}
+
+static int mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi)
+{
+ enum hdmi_aud_channel_type chan_type;
+ u8 chan_count;
+ bool dst;
+
+ mtk_hdmi_hw_aud_set_channel_swap(hdmi, HDMI_AUD_SWAP_LFE_CC);
+ mtk_hdmi_set_bits(hdmi, GRL_MIX_CTRL, MIX_CTRL_FLAT);
+
+ if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF &&
+ hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST) {
+ mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24);
+ } else if (hdmi->aud_param.aud_i2s_fmt == HDMI_I2S_MODE_LJT_24BIT) {
+ hdmi->aud_param.aud_i2s_fmt = HDMI_I2S_MODE_LJT_16BIT;
+ }
+
+ mtk_hdmi_hw_aud_set_i2s_fmt(hdmi, hdmi->aud_param.aud_i2s_fmt);
+ mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24);
+
+ dst = ((hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF) &&
+ (hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST));
+ mtk_hdmi_hw_audio_config(hdmi, dst);
+
+ if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF)
+ chan_type = HDMI_AUD_CHAN_TYPE_2_0;
+ else
+ chan_type = hdmi->aud_param.aud_input_chan_type;
+ chan_count = mtk_hdmi_aud_get_chnl_count(chan_type);
+ mtk_hdmi_hw_aud_set_i2s_chan_num(hdmi, chan_type, chan_count);
+ mtk_hdmi_hw_aud_set_input_type(hdmi, hdmi->aud_param.aud_input_type);
+
+ return 0;
+}
+
+static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi,
+ struct drm_display_mode *display_mode)
+{
+ unsigned int sample_rate = hdmi->aud_param.codec_params.sample_rate;
+
+ mtk_hdmi_aud_on_off_hw_ncts(hdmi, false);
+ mtk_hdmi_hw_aud_src_disable(hdmi);
+ mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_ACLK_INV);
+
+ if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) {
+ switch (sample_rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ break;
+ default:
+ return -EINVAL;
+ }
+ mtk_hdmi_hw_aud_set_mclk(hdmi, hdmi->aud_param.aud_mclk);
+ } else {
+ switch (sample_rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ break;
+ default:
+ return -EINVAL;
+ }
+ mtk_hdmi_hw_aud_set_mclk(hdmi, HDMI_AUD_MCLK_128FS);
+ }
+
+ mtk_hdmi_hw_aud_set_ncts(hdmi, sample_rate, display_mode->clock);
+
+ mtk_hdmi_hw_aud_src_reenable(hdmi);
+ return 0;
+}
+
+static int mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi,
+ struct drm_display_mode *display_mode)
+{
+ mtk_hdmi_hw_aud_mute(hdmi);
+ mtk_hdmi_aud_enable_packet(hdmi, false);
+
+ mtk_hdmi_aud_set_input(hdmi);
+ mtk_hdmi_aud_set_src(hdmi, display_mode);
+ mtk_hdmi_hw_aud_set_channel_status(hdmi,
+ hdmi->aud_param.codec_params.iec.status);
+
+ usleep_range(50, 100);
+
+ mtk_hdmi_aud_on_off_hw_ncts(hdmi, true);
+ mtk_hdmi_aud_enable_packet(hdmi, true);
+ mtk_hdmi_hw_aud_unmute(hdmi);
+ return 0;
+}
+
+static int mtk_hdmi_setup_avi_infoframe(struct mtk_hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ struct hdmi_avi_infoframe frame;
+ u8 buffer[17];
+ ssize_t err;
+
+ err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
+ if (err < 0) {
+ dev_err(hdmi->dev,
+ "Failed to get AVI infoframe from mode: %zd\n", err);
+ return err;
+ }
+
+ err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (err < 0) {
+ dev_err(hdmi->dev, "Failed to pack AVI infoframe: %zd\n", err);
+ return err;
+ }
+
+ mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer));
+ return 0;
+}
+
+static int mtk_hdmi_setup_spd_infoframe(struct mtk_hdmi *hdmi,
+ const char *vendor,
+ const char *product)
+{
+ struct hdmi_spd_infoframe frame;
+ u8 buffer[29];
+ ssize_t err;
+
+ err = hdmi_spd_infoframe_init(&frame, vendor, product);
+ if (err < 0) {
+ dev_err(hdmi->dev, "Failed to initialize SPD infoframe: %zd\n",
+ err);
+ return err;
+ }
+
+ err = hdmi_spd_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (err < 0) {
+ dev_err(hdmi->dev, "Failed to pack SDP infoframe: %zd\n", err);
+ return err;
+ }
+
+ mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer));
+ return 0;
+}
+
+static int mtk_hdmi_setup_audio_infoframe(struct mtk_hdmi *hdmi)
+{
+ struct hdmi_audio_infoframe frame;
+ u8 buffer[14];
+ ssize_t err;
+
+ err = hdmi_audio_infoframe_init(&frame);
+ if (err < 0) {
+ dev_err(hdmi->dev, "Failed to setup audio infoframe: %zd\n",
+ err);
+ return err;
+ }
+
+ frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+ frame.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+ frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+ frame.channels = mtk_hdmi_aud_get_chnl_count(
+ hdmi->aud_param.aud_input_chan_type);
+
+ err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (err < 0) {
+ dev_err(hdmi->dev, "Failed to pack audio infoframe: %zd\n",
+ err);
+ return err;
+ }
+
+ mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer));
+ return 0;
+}
+
+static int mtk_hdmi_setup_vendor_specific_infoframe(struct mtk_hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ struct hdmi_vendor_infoframe frame;
+ u8 buffer[10];
+ ssize_t err;
+
+ err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode);
+ if (err) {
+ dev_err(hdmi->dev,
+ "Failed to get vendor infoframe from mode: %zd\n", err);
+ return err;
+ }
+
+ err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (err) {
+ dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n",
+ err);
+ return err;
+ }
+
+ mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer));
+ return 0;
+}
+
+static int mtk_hdmi_output_init(struct mtk_hdmi *hdmi)
+{
+ struct hdmi_audio_param *aud_param = &hdmi->aud_param;
+
+ hdmi->csp = HDMI_COLORSPACE_RGB;
+ aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
+ aud_param->aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+ aud_param->aud_input_type = HDMI_AUD_INPUT_I2S;
+ aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
+ aud_param->aud_mclk = HDMI_AUD_MCLK_128FS;
+ aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
+
+ return 0;
+}
+
+void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi)
+{
+ mtk_hdmi_aud_enable_packet(hdmi, true);
+ hdmi->audio_enable = true;
+}
+
+void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi)
+{
+ mtk_hdmi_aud_enable_packet(hdmi, false);
+ hdmi->audio_enable = false;
+}
+
+int mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi,
+ struct hdmi_audio_param *param)
+{
+ if (!hdmi->audio_enable) {
+ dev_err(hdmi->dev, "hdmi audio is in disable state!\n");
+ return -EINVAL;
+ }
+ dev_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n",
+ param->aud_codec, param->aud_input_type,
+ param->aud_input_chan_type, param->codec_params.sample_rate);
+ memcpy(&hdmi->aud_param, param, sizeof(*param));
+ return mtk_hdmi_aud_output_config(hdmi, &hdmi->mode);
+}
+
+static int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ int ret;
+
+ mtk_hdmi_hw_vid_black(hdmi, true);
+ mtk_hdmi_hw_aud_mute(hdmi);
+ mtk_hdmi_hw_send_av_mute(hdmi);
+ phy_power_off(hdmi->phy);
+
+ ret = mtk_hdmi_video_change_vpll(hdmi,
+ mode->clock * 1000);
+ if (ret) {
+ dev_err(hdmi->dev, "Failed to set vpll: %d\n", ret);
+ return ret;
+ }
+ mtk_hdmi_video_set_display_mode(hdmi, mode);
+
+ phy_power_on(hdmi->phy);
+ mtk_hdmi_aud_output_config(hdmi, mode);
+
+ mtk_hdmi_setup_audio_infoframe(hdmi);
+ mtk_hdmi_setup_avi_infoframe(hdmi, mode);
+ mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI");
+ if (mode->flags & DRM_MODE_FLAG_3D_MASK)
+ mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode);
+
+ mtk_hdmi_hw_vid_black(hdmi, false);
+ mtk_hdmi_hw_aud_unmute(hdmi);
+ mtk_hdmi_hw_send_av_unmute(hdmi);
+
+ return 0;
+}
+
+static const char * const mtk_hdmi_clk_names[MTK_HDMI_CLK_COUNT] = {
+ [MTK_HDMI_CLK_HDMI_PIXEL] = "pixel",
+ [MTK_HDMI_CLK_HDMI_PLL] = "pll",
+ [MTK_HDMI_CLK_AUD_BCLK] = "bclk",
+ [MTK_HDMI_CLK_AUD_SPDIF] = "spdif",
+};
+
+static int mtk_hdmi_get_all_clk(struct mtk_hdmi *hdmi,
+ struct device_node *np)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_hdmi_clk_names); i++) {
+ hdmi->clk[i] = of_clk_get_by_name(np,
+ mtk_hdmi_clk_names[i]);
+ if (IS_ERR(hdmi->clk[i]))
+ return PTR_ERR(hdmi->clk[i]);
+ }
+ return 0;
+}
+
+static int mtk_hdmi_clk_enable_audio(struct mtk_hdmi *hdmi)
+{
+ int ret;
+
+ ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]);
+ return ret;
+}
+
+static void mtk_hdmi_clk_disable_audio(struct mtk_hdmi *hdmi)
+{
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]);
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]);
+}
+
+static enum drm_connector_status hdmi_conn_detect(struct drm_connector *conn,
+ bool force)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
+
+ return mtk_cec_hpd_high(hdmi->cec_dev) ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static void hdmi_conn_destroy(struct drm_connector *conn)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
+
+ mtk_cec_set_hpd_event(hdmi->cec_dev, NULL, NULL);
+
+ drm_connector_cleanup(conn);
+}
+
+static int mtk_hdmi_conn_get_modes(struct drm_connector *conn)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
+ struct edid *edid;
+ int ret;
+
+ if (!hdmi->ddc_adpt)
+ return -ENODEV;
+
+ edid = drm_get_edid(conn, hdmi->ddc_adpt);
+ if (!edid)
+ return -ENODEV;
+
+ hdmi->dvi_mode = !drm_detect_monitor_audio(edid);
+
+ drm_mode_connector_update_edid_property(conn, edid);
+
+ ret = drm_add_edid_modes(conn, edid);
+ drm_edid_to_eld(conn, edid);
+ kfree(edid);
+ return ret;
+}
+
+static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn,
+ struct drm_display_mode *mode)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
+
+ dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
+ mode->hdisplay, mode->vdisplay, mode->vrefresh,
+ !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
+
+ if (hdmi->bridge.next) {
+ struct drm_display_mode adjusted_mode;
+
+ drm_mode_copy(&adjusted_mode, mode);
+ if (!drm_bridge_mode_fixup(hdmi->bridge.next, mode,
+ &adjusted_mode))
+ return MODE_BAD;
+ }
+
+ if (mode->clock < 27000)
+ return MODE_CLOCK_LOW;
+ if (mode->clock > 297000)
+ return MODE_CLOCK_HIGH;
+
+ return drm_mode_validate_size(mode, 0x1fff, 0x1fff);
+}
+
+static struct drm_encoder *mtk_hdmi_conn_best_enc(struct drm_connector *conn)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
+
+ return hdmi->bridge.encoder;
+}
+
+static const struct drm_connector_funcs mtk_hdmi_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .detect = hdmi_conn_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = hdmi_conn_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_connector_helper_funcs
+ mtk_hdmi_connector_helper_funcs = {
+ .get_modes = mtk_hdmi_conn_get_modes,
+ .mode_valid = mtk_hdmi_conn_mode_valid,
+ .best_encoder = mtk_hdmi_conn_best_enc,
+};
+
+static void mtk_hdmi_hpd_event(bool hpd, struct device *dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ if (hdmi && hdmi->bridge.encoder && hdmi->bridge.encoder->dev)
+ drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev);
+}
+
+/*
+ * Bridge callbacks
+ */
+
+static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+ int ret;
+
+ ret = drm_connector_init(bridge->encoder->dev, &hdmi->conn,
+ &mtk_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret) {
+ dev_err(hdmi->dev, "Failed to initialize connector: %d\n", ret);
+ return ret;
+ }
+ drm_connector_helper_add(&hdmi->conn, &mtk_hdmi_connector_helper_funcs);
+
+ hdmi->conn.polled = DRM_CONNECTOR_POLL_HPD;
+ hdmi->conn.interlace_allowed = true;
+ hdmi->conn.doublescan_allowed = false;
+
+ ret = drm_mode_connector_attach_encoder(&hdmi->conn,
+ bridge->encoder);
+ if (ret) {
+ dev_err(hdmi->dev,
+ "Failed to attach connector to encoder: %d\n", ret);
+ return ret;
+ }
+
+ if (bridge->next) {
+ bridge->next->encoder = bridge->encoder;
+ ret = drm_bridge_attach(bridge->encoder->dev, bridge->next);
+ if (ret) {
+ dev_err(hdmi->dev,
+ "Failed to attach external bridge: %d\n", ret);
+ return ret;
+ }
+ }
+
+ mtk_cec_set_hpd_event(hdmi->cec_dev, mtk_hdmi_hpd_event, hdmi->dev);
+
+ return 0;
+}
+
+static bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ if (!hdmi->enabled)
+ return;
+
+ phy_power_off(hdmi->phy);
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]);
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);
+
+ hdmi->enabled = false;
+}
+
+static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ if (!hdmi->powered)
+ return;
+
+ mtk_hdmi_hw_1p4_version_enable(hdmi, true);
+ mtk_hdmi_hw_make_reg_writable(hdmi, false);
+
+ hdmi->powered = false;
+}
+
+static void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n",
+ adjusted_mode->name, adjusted_mode->hdisplay);
+ dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d",
+ adjusted_mode->hsync_start, adjusted_mode->hsync_end,
+ adjusted_mode->htotal);
+ dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n",
+ adjusted_mode->hskew, adjusted_mode->vdisplay);
+ dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d",
+ adjusted_mode->vsync_start, adjusted_mode->vsync_end,
+ adjusted_mode->vtotal);
+ dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n",
+ adjusted_mode->vscan, adjusted_mode->flags);
+
+ drm_mode_copy(&hdmi->mode, adjusted_mode);
+}
+
+static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ mtk_hdmi_hw_make_reg_writable(hdmi, true);
+ mtk_hdmi_hw_1p4_version_enable(hdmi, true);
+
+ hdmi->powered = true;
+}
+
+static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode);
+ clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);
+ clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]);
+ phy_power_on(hdmi->phy);
+
+ hdmi->enabled = true;
+}
+
+static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = {
+ .attach = mtk_hdmi_bridge_attach,
+ .mode_fixup = mtk_hdmi_bridge_mode_fixup,
+ .disable = mtk_hdmi_bridge_disable,
+ .post_disable = mtk_hdmi_bridge_post_disable,
+ .mode_set = mtk_hdmi_bridge_mode_set,
+ .pre_enable = mtk_hdmi_bridge_pre_enable,
+ .enable = mtk_hdmi_bridge_enable,
+};
+
+static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *cec_np, *port, *ep, *remote, *i2c_np;
+ struct platform_device *cec_pdev;
+ struct regmap *regmap;
+ struct resource *mem;
+ int ret;
+
+ ret = mtk_hdmi_get_all_clk(hdmi, np);
+ if (ret) {
+ dev_err(dev, "Failed to get clocks: %d\n", ret);
+ return ret;
+ }
+
+ /* The CEC module handles HDMI hotplug detection */
+ cec_np = of_find_compatible_node(np->parent, NULL,
+ "mediatek,mt8173-cec");
+ if (!cec_np) {
+ dev_err(dev, "Failed to find CEC node\n");
+ return -EINVAL;
+ }
+
+ cec_pdev = of_find_device_by_node(cec_np);
+ if (!cec_pdev) {
+ dev_err(hdmi->dev, "Waiting for CEC device %s\n",
+ cec_np->full_name);
+ return -EPROBE_DEFER;
+ }
+ hdmi->cec_dev = &cec_pdev->dev;
+
+ /*
+ * The mediatek,syscon-hdmi property contains a phandle link to the
+ * MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG
+ * registers it contains.
+ */
+ regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,syscon-hdmi");
+ ret = of_property_read_u32_index(np, "mediatek,syscon-hdmi", 1,
+ &hdmi->sys_offset);
+ if (IS_ERR(regmap))
+ ret = PTR_ERR(regmap);
+ if (ret) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev,
+ "Failed to get system configuration registers: %d\n",
+ ret);
+ return ret;
+ }
+ hdmi->sys_regmap = regmap;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hdmi->regs = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(hdmi->regs))
+ return PTR_ERR(hdmi->regs);
+
+ port = of_graph_get_port_by_id(np, 1);
+ if (!port) {
+ dev_err(dev, "Missing output port node\n");
+ return -EINVAL;
+ }
+
+ ep = of_get_child_by_name(port, "endpoint");
+ if (!ep) {
+ dev_err(dev, "Missing endpoint node in port %s\n",
+ port->full_name);
+ of_node_put(port);
+ return -EINVAL;
+ }
+ of_node_put(port);
+
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote) {
+ dev_err(dev, "Missing connector/bridge node for endpoint %s\n",
+ ep->full_name);
+ of_node_put(ep);
+ return -EINVAL;
+ }
+ of_node_put(ep);
+
+ if (!of_device_is_compatible(remote, "hdmi-connector")) {
+ hdmi->bridge.next = of_drm_find_bridge(remote);
+ if (!hdmi->bridge.next) {
+ dev_err(dev, "Waiting for external bridge\n");
+ of_node_put(remote);
+ return -EPROBE_DEFER;
+ }
+ }
+
+ i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0);
+ if (!i2c_np) {
+ dev_err(dev, "Failed to find ddc-i2c-bus node in %s\n",
+ remote->full_name);
+ of_node_put(remote);
+ return -EINVAL;
+ }
+ of_node_put(remote);
+
+ hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np);
+ if (!hdmi->ddc_adpt) {
+ dev_err(dev, "Failed to get ddc i2c adapter by node\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * HDMI audio codec callbacks
+ */
+
+static int mtk_hdmi_audio_hw_params(struct device *dev,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+ struct hdmi_audio_param hdmi_params;
+ unsigned int chan = params->cea.channels;
+
+ dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
+ params->sample_rate, params->sample_width, chan);
+
+ if (!hdmi->bridge.encoder)
+ return -ENODEV;
+
+ switch (chan) {
+ case 2:
+ hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
+ break;
+ case 4:
+ hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0;
+ break;
+ case 6:
+ hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1;
+ break;
+ case 8:
+ hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1;
+ break;
+ default:
+ dev_err(hdmi->dev, "channel[%d] not supported!\n", chan);
+ return -EINVAL;
+ }
+
+ switch (params->sample_rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ case 176400:
+ case 192000:
+ break;
+ default:
+ dev_err(hdmi->dev, "rate[%d] not supported!\n",
+ params->sample_rate);
+ return -EINVAL;
+ }
+
+ switch (daifmt->fmt) {
+ case HDMI_I2S:
+ hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
+ hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+ hdmi_params.aud_input_type = HDMI_AUD_INPUT_I2S;
+ hdmi_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
+ hdmi_params.aud_mclk = HDMI_AUD_MCLK_128FS;
+ break;
+ default:
+ dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__,
+ daifmt->fmt);
+ return -EINVAL;
+ }
+
+ memcpy(&hdmi_params.codec_params, params,
+ sizeof(hdmi_params.codec_params));
+
+ mtk_hdmi_audio_set_param(hdmi, &hdmi_params);
+
+ return 0;
+}
+
+static int mtk_hdmi_audio_startup(struct device *dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ mtk_hdmi_audio_enable(hdmi);
+
+ return 0;
+}
+
+static void mtk_hdmi_audio_shutdown(struct device *dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ mtk_hdmi_audio_disable(hdmi);
+}
+
+int mtk_hdmi_audio_digital_mute(struct device *dev, bool enable)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s(%d)\n", __func__, enable);
+
+ if (enable)
+ mtk_hdmi_hw_aud_mute(hdmi);
+ else
+ mtk_hdmi_hw_aud_unmute(hdmi);
+
+ return 0;
+}
+
+static int mtk_hdmi_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ memcpy(buf, hdmi->conn.eld, min(sizeof(hdmi->conn.eld), len));
+
+ return 0;
+}
+
+static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = {
+ .hw_params = mtk_hdmi_audio_hw_params,
+ .audio_startup = mtk_hdmi_audio_startup,
+ .audio_shutdown = mtk_hdmi_audio_shutdown,
+ .digital_mute = mtk_hdmi_audio_digital_mute,
+ .get_eld = mtk_hdmi_audio_get_eld,
+};
+
+static void mtk_hdmi_register_audio_driver(struct device *dev)
+{
+ struct hdmi_codec_pdata codec_data = {
+ .ops = &mtk_hdmi_audio_codec_ops,
+ .max_i2s_channels = 2,
+ .i2s = 1,
+ };
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+ PLATFORM_DEVID_AUTO, &codec_data,
+ sizeof(codec_data));
+ if (IS_ERR(pdev))
+ return;
+
+ DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME);
+}
+
+static int mtk_drm_hdmi_probe(struct platform_device *pdev)
+{
+ struct mtk_hdmi *hdmi;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return -ENOMEM;
+
+ hdmi->dev = dev;
+
+ ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev);
+ if (ret)
+ return ret;
+
+ hdmi->phy = devm_phy_get(dev, "hdmi");
+ if (IS_ERR(hdmi->phy)) {
+ ret = PTR_ERR(hdmi->phy);
+ dev_err(dev, "Failed to get HDMI PHY: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, hdmi);
+
+ ret = mtk_hdmi_output_init(hdmi);
+ if (ret) {
+ dev_err(dev, "Failed to initialize hdmi output\n");
+ return ret;
+ }
+
+ mtk_hdmi_register_audio_driver(dev);
+
+ hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs;
+ hdmi->bridge.of_node = pdev->dev.of_node;
+ ret = drm_bridge_add(&hdmi->bridge);
+ if (ret) {
+ dev_err(dev, "failed to add bridge, ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = mtk_hdmi_clk_enable_audio(hdmi);
+ if (ret) {
+ dev_err(dev, "Failed to enable audio clocks: %d\n", ret);
+ goto err_bridge_remove;
+ }
+
+ dev_dbg(dev, "mediatek hdmi probe success\n");
+ return 0;
+
+err_bridge_remove:
+ drm_bridge_remove(&hdmi->bridge);
+ return ret;
+}
+
+static int mtk_drm_hdmi_remove(struct platform_device *pdev)
+{
+ struct mtk_hdmi *hdmi = platform_get_drvdata(pdev);
+
+ drm_bridge_remove(&hdmi->bridge);
+ mtk_hdmi_clk_disable_audio(hdmi);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_hdmi_suspend(struct device *dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ mtk_hdmi_clk_disable_audio(hdmi);
+ dev_dbg(dev, "hdmi suspend success!\n");
+ return 0;
+}
+
+static int mtk_hdmi_resume(struct device *dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = mtk_hdmi_clk_enable_audio(hdmi);
+ if (ret) {
+ dev_err(dev, "hdmi resume failed!\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "hdmi resume success!\n");
+ return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(mtk_hdmi_pm_ops,
+ mtk_hdmi_suspend, mtk_hdmi_resume);
+
+static const struct of_device_id mtk_drm_hdmi_of_ids[] = {
+ { .compatible = "mediatek,mt8173-hdmi", },
+ {}
+};
+
+static struct platform_driver mtk_hdmi_driver = {
+ .probe = mtk_drm_hdmi_probe,
+ .remove = mtk_drm_hdmi_remove,
+ .driver = {
+ .name = "mediatek-drm-hdmi",
+ .of_match_table = mtk_drm_hdmi_of_ids,
+ .pm = &mtk_hdmi_pm_ops,
+ },
+};
+
+static struct platform_driver * const mtk_hdmi_drivers[] = {
+ &mtk_hdmi_phy_driver,
+ &mtk_hdmi_ddc_driver,
+ &mtk_cec_driver,
+ &mtk_hdmi_driver,
+};
+
+static int __init mtk_hdmitx_init(void)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_hdmi_drivers); i++) {
+ ret = platform_driver_register(mtk_hdmi_drivers[i]);
+ if (ret < 0) {
+ pr_err("Failed to register %s driver: %d\n",
+ mtk_hdmi_drivers[i]->driver.name, ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ while (--i >= 0)
+ platform_driver_unregister(mtk_hdmi_drivers[i]);
+
+ return ret;
+}
+
+static void __exit mtk_hdmitx_exit(void)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(mtk_hdmi_drivers) - 1; i >= 0; i--)
+ platform_driver_unregister(mtk_hdmi_drivers[i]);
+}
+
+module_init(mtk_hdmitx_init);
+module_exit(mtk_hdmitx_exit);
+
+MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek HDMI Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.h b/drivers/gpu/drm/mediatek/mtk_hdmi.h
new file mode 100644
index 000000000000..6371b3de1ff6
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MTK_HDMI_CTRL_H
+#define _MTK_HDMI_CTRL_H
+
+struct platform_driver;
+
+extern struct platform_driver mtk_cec_driver;
+extern struct platform_driver mtk_hdmi_ddc_driver;
+extern struct platform_driver mtk_hdmi_phy_driver;
+
+#endif /* _MTK_HDMI_CTRL_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
new file mode 100644
index 000000000000..33c9e1bdb114
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#define SIF1_CLOK (288)
+#define DDC_DDCMCTL0 (0x0)
+#define DDCM_ODRAIN BIT(31)
+#define DDCM_CLK_DIV_OFFSET (16)
+#define DDCM_CLK_DIV_MASK (0xfff << 16)
+#define DDCM_CS_STATUS BIT(4)
+#define DDCM_SCL_STATE BIT(3)
+#define DDCM_SDA_STATE BIT(2)
+#define DDCM_SM0EN BIT(1)
+#define DDCM_SCL_STRECH BIT(0)
+#define DDC_DDCMCTL1 (0x4)
+#define DDCM_ACK_OFFSET (16)
+#define DDCM_ACK_MASK (0xff << 16)
+#define DDCM_PGLEN_OFFSET (8)
+#define DDCM_PGLEN_MASK (0x7 << 8)
+#define DDCM_SIF_MODE_OFFSET (4)
+#define DDCM_SIF_MODE_MASK (0x7 << 4)
+#define DDCM_START (0x1)
+#define DDCM_WRITE_DATA (0x2)
+#define DDCM_STOP (0x3)
+#define DDCM_READ_DATA_NO_ACK (0x4)
+#define DDCM_READ_DATA_ACK (0x5)
+#define DDCM_TRI BIT(0)
+#define DDC_DDCMD0 (0x8)
+#define DDCM_DATA3 (0xff << 24)
+#define DDCM_DATA2 (0xff << 16)
+#define DDCM_DATA1 (0xff << 8)
+#define DDCM_DATA0 (0xff << 0)
+#define DDC_DDCMD1 (0xc)
+#define DDCM_DATA7 (0xff << 24)
+#define DDCM_DATA6 (0xff << 16)
+#define DDCM_DATA5 (0xff << 8)
+#define DDCM_DATA4 (0xff << 0)
+
+struct mtk_hdmi_ddc {
+ struct i2c_adapter adap;
+ struct clk *clk;
+ void __iomem *regs;
+};
+
+static inline void sif_set_bit(struct mtk_hdmi_ddc *ddc, unsigned int offset,
+ unsigned int val)
+{
+ writel(readl(ddc->regs + offset) | val, ddc->regs + offset);
+}
+
+static inline void sif_clr_bit(struct mtk_hdmi_ddc *ddc, unsigned int offset,
+ unsigned int val)
+{
+ writel(readl(ddc->regs + offset) & ~val, ddc->regs + offset);
+}
+
+static inline bool sif_bit_is_set(struct mtk_hdmi_ddc *ddc, unsigned int offset,
+ unsigned int val)
+{
+ return (readl(ddc->regs + offset) & val) == val;
+}
+
+static inline void sif_write_mask(struct mtk_hdmi_ddc *ddc, unsigned int offset,
+ unsigned int mask, unsigned int shift,
+ unsigned int val)
+{
+ unsigned int tmp;
+
+ tmp = readl(ddc->regs + offset);
+ tmp &= ~mask;
+ tmp |= (val << shift) & mask;
+ writel(tmp, ddc->regs + offset);
+}
+
+static inline unsigned int sif_read_mask(struct mtk_hdmi_ddc *ddc,
+ unsigned int offset, unsigned int mask,
+ unsigned int shift)
+{
+ return (readl(ddc->regs + offset) & mask) >> shift;
+}
+
+static void ddcm_trigger_mode(struct mtk_hdmi_ddc *ddc, int mode)
+{
+ u32 val;
+
+ sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_SIF_MODE_MASK,
+ DDCM_SIF_MODE_OFFSET, mode);
+ sif_set_bit(ddc, DDC_DDCMCTL1, DDCM_TRI);
+ readl_poll_timeout(ddc->regs + DDC_DDCMCTL1, val,
+ (val & DDCM_TRI) != DDCM_TRI, 4, 20000);
+}
+
+static int mtk_hdmi_ddc_read_msg(struct mtk_hdmi_ddc *ddc, struct i2c_msg *msg)
+{
+ struct device *dev = ddc->adap.dev.parent;
+ u32 remain_count, ack_count, ack_final, read_count, temp_count;
+ u32 index = 0;
+ u32 ack;
+ int i;
+
+ ddcm_trigger_mode(ddc, DDCM_START);
+ sif_write_mask(ddc, DDC_DDCMD0, 0xff, 0, (msg->addr << 1) | 0x01);
+ sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET,
+ 0x00);
+ ddcm_trigger_mode(ddc, DDCM_WRITE_DATA);
+ ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET);
+ dev_dbg(dev, "ack = 0x%x\n", ack);
+ if (ack != 0x01) {
+ dev_err(dev, "i2c ack err!\n");
+ return -ENXIO;
+ }
+
+ remain_count = msg->len;
+ ack_count = (msg->len - 1) / 8;
+ ack_final = 0;
+
+ while (remain_count > 0) {
+ if (ack_count > 0) {
+ read_count = 8;
+ ack_final = 0;
+ ack_count--;
+ } else {
+ read_count = remain_count;
+ ack_final = 1;
+ }
+
+ sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK,
+ DDCM_PGLEN_OFFSET, read_count - 1);
+ ddcm_trigger_mode(ddc, (ack_final == 1) ?
+ DDCM_READ_DATA_NO_ACK :
+ DDCM_READ_DATA_ACK);
+
+ ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK,
+ DDCM_ACK_OFFSET);
+ temp_count = 0;
+ while (((ack & (1 << temp_count)) != 0) && (temp_count < 8))
+ temp_count++;
+ if (((ack_final == 1) && (temp_count != (read_count - 1))) ||
+ ((ack_final == 0) && (temp_count != read_count))) {
+ dev_err(dev, "Address NACK! ACK(0x%x)\n", ack);
+ break;
+ }
+
+ for (i = read_count; i >= 1; i--) {
+ int shift;
+ int offset;
+
+ if (i > 4) {
+ offset = DDC_DDCMD1;
+ shift = (i - 5) * 8;
+ } else {
+ offset = DDC_DDCMD0;
+ shift = (i - 1) * 8;
+ }
+
+ msg->buf[index + i - 1] = sif_read_mask(ddc, offset,
+ 0xff << shift,
+ shift);
+ }
+
+ remain_count -= read_count;
+ index += read_count;
+ }
+
+ return 0;
+}
+
+static int mtk_hdmi_ddc_write_msg(struct mtk_hdmi_ddc *ddc, struct i2c_msg *msg)
+{
+ struct device *dev = ddc->adap.dev.parent;
+ u32 ack;
+
+ ddcm_trigger_mode(ddc, DDCM_START);
+ sif_write_mask(ddc, DDC_DDCMD0, DDCM_DATA0, 0, msg->addr << 1);
+ sif_write_mask(ddc, DDC_DDCMD0, DDCM_DATA1, 8, msg->buf[0]);
+ sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET,
+ 0x1);
+ ddcm_trigger_mode(ddc, DDCM_WRITE_DATA);
+
+ ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET);
+ dev_dbg(dev, "ack = %d\n", ack);
+
+ if (ack != 0x03) {
+ dev_err(dev, "i2c ack err!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mtk_hdmi_ddc_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct mtk_hdmi_ddc *ddc = adapter->algo_data;
+ struct device *dev = adapter->dev.parent;
+ int ret;
+ int i;
+
+ if (!ddc) {
+ dev_err(dev, "invalid arguments\n");
+ return -EINVAL;
+ }
+
+ sif_set_bit(ddc, DDC_DDCMCTL0, DDCM_SCL_STRECH);
+ sif_set_bit(ddc, DDC_DDCMCTL0, DDCM_SM0EN);
+ sif_clr_bit(ddc, DDC_DDCMCTL0, DDCM_ODRAIN);
+
+ if (sif_bit_is_set(ddc, DDC_DDCMCTL1, DDCM_TRI)) {
+ dev_err(dev, "ddc line is busy!\n");
+ return -EBUSY;
+ }
+
+ sif_write_mask(ddc, DDC_DDCMCTL0, DDCM_CLK_DIV_MASK,
+ DDCM_CLK_DIV_OFFSET, SIF1_CLOK);
+
+ for (i = 0; i < num; i++) {
+ struct i2c_msg *msg = &msgs[i];
+
+ dev_dbg(dev, "i2c msg, adr:0x%x, flags:%d, len :0x%x\n",
+ msg->addr, msg->flags, msg->len);
+
+ if (msg->flags & I2C_M_RD)
+ ret = mtk_hdmi_ddc_read_msg(ddc, msg);
+ else
+ ret = mtk_hdmi_ddc_write_msg(ddc, msg);
+ if (ret < 0)
+ goto xfer_end;
+ }
+
+ ddcm_trigger_mode(ddc, DDCM_STOP);
+
+ return i;
+
+xfer_end:
+ ddcm_trigger_mode(ddc, DDCM_STOP);
+ dev_err(dev, "ddc failed!\n");
+ return ret;
+}
+
+static u32 mtk_hdmi_ddc_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm mtk_hdmi_ddc_algorithm = {
+ .master_xfer = mtk_hdmi_ddc_xfer,
+ .functionality = mtk_hdmi_ddc_func,
+};
+
+static int mtk_hdmi_ddc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_hdmi_ddc *ddc;
+ struct resource *mem;
+ int ret;
+
+ ddc = devm_kzalloc(dev, sizeof(struct mtk_hdmi_ddc), GFP_KERNEL);
+ if (!ddc)
+ return -ENOMEM;
+
+ ddc->clk = devm_clk_get(dev, "ddc-i2c");
+ if (IS_ERR(ddc->clk)) {
+ dev_err(dev, "get ddc_clk failed: %p ,\n", ddc->clk);
+ return PTR_ERR(ddc->clk);
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ddc->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(ddc->regs))
+ return PTR_ERR(ddc->regs);
+
+ ret = clk_prepare_enable(ddc->clk);
+ if (ret) {
+ dev_err(dev, "enable ddc clk failed!\n");
+ return ret;
+ }
+
+ strlcpy(ddc->adap.name, "mediatek-hdmi-ddc", sizeof(ddc->adap.name));
+ ddc->adap.owner = THIS_MODULE;
+ ddc->adap.class = I2C_CLASS_DDC;
+ ddc->adap.algo = &mtk_hdmi_ddc_algorithm;
+ ddc->adap.retries = 3;
+ ddc->adap.dev.of_node = dev->of_node;
+ ddc->adap.algo_data = ddc;
+ ddc->adap.dev.parent = &pdev->dev;
+
+ ret = i2c_add_adapter(&ddc->adap);
+ if (ret < 0) {
+ dev_err(dev, "failed to add bus to i2c core\n");
+ goto err_clk_disable;
+ }
+
+ platform_set_drvdata(pdev, ddc);
+
+ dev_dbg(dev, "ddc->adap: %p\n", &ddc->adap);
+ dev_dbg(dev, "ddc->clk: %p\n", ddc->clk);
+ dev_dbg(dev, "physical adr: %pa, end: %pa\n", &mem->start,
+ &mem->end);
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(ddc->clk);
+ return ret;
+}
+
+static int mtk_hdmi_ddc_remove(struct platform_device *pdev)
+{
+ struct mtk_hdmi_ddc *ddc = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&ddc->adap);
+ clk_disable_unprepare(ddc->clk);
+
+ return 0;
+}
+
+static const struct of_device_id mtk_hdmi_ddc_match[] = {
+ { .compatible = "mediatek,mt8173-hdmi-ddc", },
+ {},
+};
+
+struct platform_driver mtk_hdmi_ddc_driver = {
+ .probe = mtk_hdmi_ddc_probe,
+ .remove = mtk_hdmi_ddc_remove,
+ .driver = {
+ .name = "mediatek-hdmi-ddc",
+ .of_match_table = mtk_hdmi_ddc_match,
+ },
+};
+
+MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek HDMI DDC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_regs.h b/drivers/gpu/drm/mediatek/mtk_hdmi_regs.h
new file mode 100644
index 000000000000..a5cb07d12c9c
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_regs.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MTK_HDMI_REGS_H
+#define _MTK_HDMI_REGS_H
+
+#define GRL_INT_MASK 0x18
+#define GRL_IFM_PORT 0x188
+#define GRL_CH_SWAP 0x198
+#define LR_SWAP BIT(0)
+#define LFE_CC_SWAP BIT(1)
+#define LSRS_SWAP BIT(2)
+#define RLS_RRS_SWAP BIT(3)
+#define LR_STATUS_SWAP BIT(4)
+#define GRL_I2S_C_STA0 0x140
+#define GRL_I2S_C_STA1 0x144
+#define GRL_I2S_C_STA2 0x148
+#define GRL_I2S_C_STA3 0x14C
+#define GRL_I2S_C_STA4 0x150
+#define GRL_I2S_UV 0x154
+#define I2S_UV_V BIT(0)
+#define I2S_UV_U BIT(1)
+#define I2S_UV_CH_EN_MASK 0x3c
+#define I2S_UV_CH_EN(x) BIT((x) + 2)
+#define I2S_UV_TMDS_DEBUG BIT(6)
+#define I2S_UV_NORMAL_INFO_INV BIT(7)
+#define GRL_ACP_ISRC_CTRL 0x158
+#define VS_EN BIT(0)
+#define ACP_EN BIT(1)
+#define ISRC1_EN BIT(2)
+#define ISRC2_EN BIT(3)
+#define GAMUT_EN BIT(4)
+#define GRL_CTS_CTRL 0x160
+#define CTS_CTRL_SOFT BIT(0)
+#define GRL_INT 0x14
+#define INT_MDI BIT(0)
+#define INT_HDCP BIT(1)
+#define INT_FIFO_O BIT(2)
+#define INT_FIFO_U BIT(3)
+#define INT_IFM_ERR BIT(4)
+#define INT_INF_DONE BIT(5)
+#define INT_NCTS_DONE BIT(6)
+#define INT_CTRL_PKT_DONE BIT(7)
+#define GRL_INT_MASK 0x18
+#define GRL_CTRL 0x1C
+#define CTRL_GEN_EN BIT(2)
+#define CTRL_SPD_EN BIT(3)
+#define CTRL_MPEG_EN BIT(4)
+#define CTRL_AUDIO_EN BIT(5)
+#define CTRL_AVI_EN BIT(6)
+#define CTRL_AVMUTE BIT(7)
+#define GRL_STATUS 0x20
+#define STATUS_HTPLG BIT(0)
+#define STATUS_PORD BIT(1)
+#define GRL_DIVN 0x170
+#define NCTS_WRI_ANYTIME BIT(6)
+#define GRL_AUDIO_CFG 0x17C
+#define AUDIO_ZERO BIT(0)
+#define HIGH_BIT_RATE BIT(1)
+#define SACD_DST BIT(2)
+#define DST_NORMAL_DOUBLE BIT(3)
+#define DSD_INV BIT(4)
+#define LR_INV BIT(5)
+#define LR_MIX BIT(6)
+#define DSD_SEL BIT(7)
+#define GRL_NCTS 0x184
+#define GRL_CH_SW0 0x18C
+#define GRL_CH_SW1 0x190
+#define GRL_CH_SW2 0x194
+#define CH_SWITCH(from, to) ((from) << ((to) * 3))
+#define GRL_INFOFRM_VER 0x19C
+#define GRL_INFOFRM_TYPE 0x1A0
+#define GRL_INFOFRM_LNG 0x1A4
+#define GRL_MIX_CTRL 0x1B4
+#define MIX_CTRL_SRC_EN BIT(0)
+#define BYPASS_VOLUME BIT(1)
+#define MIX_CTRL_FLAT BIT(7)
+#define GRL_AOUT_CFG 0x1C4
+#define AOUT_BNUM_SEL_MASK 0x03
+#define AOUT_24BIT 0x00
+#define AOUT_20BIT 0x02
+#define AOUT_16BIT 0x03
+#define AOUT_FIFO_ADAP_CTRL BIT(6)
+#define AOUT_BURST_PREAMBLE_EN BIT(7)
+#define HIGH_BIT_RATE_PACKET_ALIGN (AOUT_BURST_PREAMBLE_EN | \
+ AOUT_FIFO_ADAP_CTRL)
+#define GRL_SHIFT_L1 0x1C0
+#define GRL_SHIFT_R2 0x1B0
+#define AUDIO_PACKET_OFF BIT(6)
+#define GRL_CFG0 0x24
+#define CFG0_I2S_MODE_MASK 0x3
+#define CFG0_I2S_MODE_RTJ 0x1
+#define CFG0_I2S_MODE_LTJ 0x0
+#define CFG0_I2S_MODE_I2S 0x2
+#define CFG0_W_LENGTH_MASK 0x30
+#define CFG0_W_LENGTH_24BIT 0x00
+#define CFG0_W_LENGTH_16BIT 0x10
+#define GRL_CFG1 0x28
+#define CFG1_EDG_SEL BIT(0)
+#define CFG1_SPDIF BIT(1)
+#define CFG1_DVI BIT(2)
+#define CFG1_HDCP_DEBUG BIT(3)
+#define GRL_CFG2 0x2c
+#define CFG2_MHL_DE_SEL BIT(3)
+#define CFG2_MHL_FAKE_DE_SEL BIT(4)
+#define CFG2_MHL_DATA_REMAP BIT(5)
+#define CFG2_NOTICE_EN BIT(6)
+#define CFG2_ACLK_INV BIT(7)
+#define GRL_CFG3 0x30
+#define CFG3_AES_KEY_INDEX_MASK 0x3f
+#define CFG3_CONTROL_PACKET_DELAY BIT(6)
+#define CFG3_KSV_LOAD_START BIT(7)
+#define GRL_CFG4 0x34
+#define CFG4_AES_KEY_LOAD BIT(4)
+#define CFG4_AV_UNMUTE_EN BIT(5)
+#define CFG4_AV_UNMUTE_SET BIT(6)
+#define CFG4_MHL_MODE BIT(7)
+#define GRL_CFG5 0x38
+#define CFG5_CD_RATIO_MASK 0x8F
+#define CFG5_FS128 (0x1 << 4)
+#define CFG5_FS256 (0x2 << 4)
+#define CFG5_FS384 (0x3 << 4)
+#define CFG5_FS512 (0x4 << 4)
+#define CFG5_FS768 (0x6 << 4)
+#define DUMMY_304 0x304
+#define CHMO_SEL (0x3 << 2)
+#define CHM1_SEL (0x3 << 4)
+#define CHM2_SEL (0x3 << 6)
+#define AUDIO_I2S_NCTS_SEL BIT(1)
+#define AUDIO_I2S_NCTS_SEL_64 (1 << 1)
+#define AUDIO_I2S_NCTS_SEL_128 (0 << 1)
+#define NEW_GCP_CTRL BIT(0)
+#define NEW_GCP_CTRL_MERGE BIT(0)
+#define GRL_L_STATUS_0 0x200
+#define GRL_L_STATUS_1 0x204
+#define GRL_L_STATUS_2 0x208
+#define GRL_L_STATUS_3 0x20c
+#define GRL_L_STATUS_4 0x210
+#define GRL_L_STATUS_5 0x214
+#define GRL_L_STATUS_6 0x218
+#define GRL_L_STATUS_7 0x21c
+#define GRL_L_STATUS_8 0x220
+#define GRL_L_STATUS_9 0x224
+#define GRL_L_STATUS_10 0x228
+#define GRL_L_STATUS_11 0x22c
+#define GRL_L_STATUS_12 0x230
+#define GRL_L_STATUS_13 0x234
+#define GRL_L_STATUS_14 0x238
+#define GRL_L_STATUS_15 0x23c
+#define GRL_L_STATUS_16 0x240
+#define GRL_L_STATUS_17 0x244
+#define GRL_L_STATUS_18 0x248
+#define GRL_L_STATUS_19 0x24c
+#define GRL_L_STATUS_20 0x250
+#define GRL_L_STATUS_21 0x254
+#define GRL_L_STATUS_22 0x258
+#define GRL_L_STATUS_23 0x25c
+#define GRL_R_STATUS_0 0x260
+#define GRL_R_STATUS_1 0x264
+#define GRL_R_STATUS_2 0x268
+#define GRL_R_STATUS_3 0x26c
+#define GRL_R_STATUS_4 0x270
+#define GRL_R_STATUS_5 0x274
+#define GRL_R_STATUS_6 0x278
+#define GRL_R_STATUS_7 0x27c
+#define GRL_R_STATUS_8 0x280
+#define GRL_R_STATUS_9 0x284
+#define GRL_R_STATUS_10 0x288
+#define GRL_R_STATUS_11 0x28c
+#define GRL_R_STATUS_12 0x290
+#define GRL_R_STATUS_13 0x294
+#define GRL_R_STATUS_14 0x298
+#define GRL_R_STATUS_15 0x29c
+#define GRL_R_STATUS_16 0x2a0
+#define GRL_R_STATUS_17 0x2a4
+#define GRL_R_STATUS_18 0x2a8
+#define GRL_R_STATUS_19 0x2ac
+#define GRL_R_STATUS_20 0x2b0
+#define GRL_R_STATUS_21 0x2b4
+#define GRL_R_STATUS_22 0x2b8
+#define GRL_R_STATUS_23 0x2bc
+#define GRL_ABIST_CTRL0 0x2D4
+#define GRL_ABIST_CTRL1 0x2D8
+#define ABIST_EN BIT(7)
+#define ABIST_DATA_FMT (0x7 << 0)
+#define VIDEO_CFG_0 0x380
+#define VIDEO_CFG_1 0x384
+#define VIDEO_CFG_2 0x388
+#define VIDEO_CFG_3 0x38c
+#define VIDEO_CFG_4 0x390
+#define VIDEO_SOURCE_SEL BIT(7)
+#define NORMAL_PATH (1 << 7)
+#define GEN_RGB (0 << 7)
+
+#define HDMI_SYS_CFG1C 0x000
+#define HDMI_ON BIT(0)
+#define HDMI_RST BIT(1)
+#define ANLG_ON BIT(2)
+#define CFG10_DVI BIT(3)
+#define HDMI_TST BIT(3)
+#define SYS_KEYMASK1 (0xff << 8)
+#define SYS_KEYMASK2 (0xff << 16)
+#define AUD_OUTSYNC_EN BIT(24)
+#define AUD_OUTSYNC_PRE_EN BIT(25)
+#define I2CM_ON BIT(26)
+#define E2PROM_TYPE_8BIT BIT(27)
+#define MCM_E2PROM_ON BIT(28)
+#define EXT_E2PROM_ON BIT(29)
+#define HTPLG_PIN_SEL_OFF BIT(30)
+#define AES_EFUSE_ENABLE BIT(31)
+#define HDMI_SYS_CFG20 0x004
+#define DEEP_COLOR_MODE_MASK (3 << 1)
+#define COLOR_8BIT_MODE (0 << 1)
+#define COLOR_10BIT_MODE (1 << 1)
+#define COLOR_12BIT_MODE (2 << 1)
+#define COLOR_16BIT_MODE (3 << 1)
+#define DEEP_COLOR_EN BIT(0)
+#define HDMI_AUDIO_TEST_SEL BIT(8)
+#define HDMI2P0_EN BIT(11)
+#define HDMI_OUT_FIFO_EN BIT(16)
+#define HDMI_OUT_FIFO_CLK_INV BIT(17)
+#define MHL_MODE_ON BIT(28)
+#define MHL_PP_MODE BIT(29)
+#define MHL_SYNC_AUTO_EN BIT(30)
+#define HDMI_PCLK_FREE_RUN BIT(31)
+
+#define MTK_SIP_SET_AUTHORIZED_SECURE_REG 0x82000001
+#endif
diff --git a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
new file mode 100644
index 000000000000..8a24754b440f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#define HDMI_CON0 0x00
+#define RG_HDMITX_PLL_EN BIT(31)
+#define RG_HDMITX_PLL_FBKDIV (0x7f << 24)
+#define PLL_FBKDIV_SHIFT 24
+#define RG_HDMITX_PLL_FBKSEL (0x3 << 22)
+#define PLL_FBKSEL_SHIFT 22
+#define RG_HDMITX_PLL_PREDIV (0x3 << 20)
+#define PREDIV_SHIFT 20
+#define RG_HDMITX_PLL_POSDIV (0x3 << 18)
+#define POSDIV_SHIFT 18
+#define RG_HDMITX_PLL_RST_DLY (0x3 << 16)
+#define RG_HDMITX_PLL_IR (0xf << 12)
+#define PLL_IR_SHIFT 12
+#define RG_HDMITX_PLL_IC (0xf << 8)
+#define PLL_IC_SHIFT 8
+#define RG_HDMITX_PLL_BP (0xf << 4)
+#define PLL_BP_SHIFT 4
+#define RG_HDMITX_PLL_BR (0x3 << 2)
+#define PLL_BR_SHIFT 2
+#define RG_HDMITX_PLL_BC (0x3 << 0)
+#define PLL_BC_SHIFT 0
+#define HDMI_CON1 0x04
+#define RG_HDMITX_PLL_DIVEN (0x7 << 29)
+#define PLL_DIVEN_SHIFT 29
+#define RG_HDMITX_PLL_AUTOK_EN BIT(28)
+#define RG_HDMITX_PLL_AUTOK_KF (0x3 << 26)
+#define RG_HDMITX_PLL_AUTOK_KS (0x3 << 24)
+#define RG_HDMITX_PLL_AUTOK_LOAD BIT(23)
+#define RG_HDMITX_PLL_BAND (0x3f << 16)
+#define RG_HDMITX_PLL_REF_SEL BIT(15)
+#define RG_HDMITX_PLL_BIAS_EN BIT(14)
+#define RG_HDMITX_PLL_BIAS_LPF_EN BIT(13)
+#define RG_HDMITX_PLL_TXDIV_EN BIT(12)
+#define RG_HDMITX_PLL_TXDIV (0x3 << 10)
+#define PLL_TXDIV_SHIFT 10
+#define RG_HDMITX_PLL_LVROD_EN BIT(9)
+#define RG_HDMITX_PLL_MONVC_EN BIT(8)
+#define RG_HDMITX_PLL_MONCK_EN BIT(7)
+#define RG_HDMITX_PLL_MONREF_EN BIT(6)
+#define RG_HDMITX_PLL_TST_EN BIT(5)
+#define RG_HDMITX_PLL_TST_CK_EN BIT(4)
+#define RG_HDMITX_PLL_TST_SEL (0xf << 0)
+#define HDMI_CON2 0x08
+#define RGS_HDMITX_PLL_AUTOK_BAND (0x7f << 8)
+#define RGS_HDMITX_PLL_AUTOK_FAIL BIT(1)
+#define RG_HDMITX_EN_TX_CKLDO BIT(0)
+#define HDMI_CON3 0x0c
+#define RG_HDMITX_SER_EN (0xf << 28)
+#define RG_HDMITX_PRD_EN (0xf << 24)
+#define RG_HDMITX_PRD_IMP_EN (0xf << 20)
+#define RG_HDMITX_DRV_EN (0xf << 16)
+#define RG_HDMITX_DRV_IMP_EN (0xf << 12)
+#define DRV_IMP_EN_SHIFT 12
+#define RG_HDMITX_MHLCK_FORCE BIT(10)
+#define RG_HDMITX_MHLCK_PPIX_EN BIT(9)
+#define RG_HDMITX_MHLCK_EN BIT(8)
+#define RG_HDMITX_SER_DIN_SEL (0xf << 4)
+#define RG_HDMITX_SER_5T1_BIST_EN BIT(3)
+#define RG_HDMITX_SER_BIST_TOG BIT(2)
+#define RG_HDMITX_SER_DIN_TOG BIT(1)
+#define RG_HDMITX_SER_CLKDIG_INV BIT(0)
+#define HDMI_CON4 0x10
+#define RG_HDMITX_PRD_IBIAS_CLK (0xf << 24)
+#define RG_HDMITX_PRD_IBIAS_D2 (0xf << 16)
+#define RG_HDMITX_PRD_IBIAS_D1 (0xf << 8)
+#define RG_HDMITX_PRD_IBIAS_D0 (0xf << 0)
+#define PRD_IBIAS_CLK_SHIFT 24
+#define PRD_IBIAS_D2_SHIFT 16
+#define PRD_IBIAS_D1_SHIFT 8
+#define PRD_IBIAS_D0_SHIFT 0
+#define HDMI_CON5 0x14
+#define RG_HDMITX_DRV_IBIAS_CLK (0x3f << 24)
+#define RG_HDMITX_DRV_IBIAS_D2 (0x3f << 16)
+#define RG_HDMITX_DRV_IBIAS_D1 (0x3f << 8)
+#define RG_HDMITX_DRV_IBIAS_D0 (0x3f << 0)
+#define DRV_IBIAS_CLK_SHIFT 24
+#define DRV_IBIAS_D2_SHIFT 16
+#define DRV_IBIAS_D1_SHIFT 8
+#define DRV_IBIAS_D0_SHIFT 0
+#define HDMI_CON6 0x18
+#define RG_HDMITX_DRV_IMP_CLK (0x3f << 24)
+#define RG_HDMITX_DRV_IMP_D2 (0x3f << 16)
+#define RG_HDMITX_DRV_IMP_D1 (0x3f << 8)
+#define RG_HDMITX_DRV_IMP_D0 (0x3f << 0)
+#define DRV_IMP_CLK_SHIFT 24
+#define DRV_IMP_D2_SHIFT 16
+#define DRV_IMP_D1_SHIFT 8
+#define DRV_IMP_D0_SHIFT 0
+#define HDMI_CON7 0x1c
+#define RG_HDMITX_MHLCK_DRV_IBIAS (0x1f << 27)
+#define RG_HDMITX_SER_DIN (0x3ff << 16)
+#define RG_HDMITX_CHLDC_TST (0xf << 12)
+#define RG_HDMITX_CHLCK_TST (0xf << 8)
+#define RG_HDMITX_RESERVE (0xff << 0)
+#define HDMI_CON8 0x20
+#define RGS_HDMITX_2T1_LEV (0xf << 16)
+#define RGS_HDMITX_2T1_EDG (0xf << 12)
+#define RGS_HDMITX_5T1_LEV (0xf << 8)
+#define RGS_HDMITX_5T1_EDG (0xf << 4)
+#define RGS_HDMITX_PLUG_TST BIT(0)
+
+struct mtk_hdmi_phy {
+ void __iomem *regs;
+ struct device *dev;
+ struct clk *pll;
+ struct clk_hw pll_hw;
+ unsigned long pll_rate;
+ u8 drv_imp_clk;
+ u8 drv_imp_d2;
+ u8 drv_imp_d1;
+ u8 drv_imp_d0;
+ u32 ibias;
+ u32 ibias_up;
+};
+
+static const u8 PREDIV[3][4] = {
+ {0x0, 0x0, 0x0, 0x0}, /* 27Mhz */
+ {0x1, 0x1, 0x1, 0x1}, /* 74Mhz */
+ {0x1, 0x1, 0x1, 0x1} /* 148Mhz */
+};
+
+static const u8 TXDIV[3][4] = {
+ {0x3, 0x3, 0x3, 0x2}, /* 27Mhz */
+ {0x2, 0x1, 0x1, 0x1}, /* 74Mhz */
+ {0x1, 0x0, 0x0, 0x0} /* 148Mhz */
+};
+
+static const u8 FBKSEL[3][4] = {
+ {0x1, 0x1, 0x1, 0x1}, /* 27Mhz */
+ {0x1, 0x0, 0x1, 0x1}, /* 74Mhz */
+ {0x1, 0x0, 0x1, 0x1} /* 148Mhz */
+};
+
+static const u8 FBKDIV[3][4] = {
+ {19, 24, 29, 19}, /* 27Mhz */
+ {19, 24, 14, 19}, /* 74Mhz */
+ {19, 24, 14, 19} /* 148Mhz */
+};
+
+static const u8 DIVEN[3][4] = {
+ {0x2, 0x1, 0x1, 0x2}, /* 27Mhz */
+ {0x2, 0x2, 0x2, 0x2}, /* 74Mhz */
+ {0x2, 0x2, 0x2, 0x2} /* 148Mhz */
+};
+
+static const u8 HTPLLBP[3][4] = {
+ {0xc, 0xc, 0x8, 0xc}, /* 27Mhz */
+ {0xc, 0xf, 0xf, 0xc}, /* 74Mhz */
+ {0xc, 0xf, 0xf, 0xc} /* 148Mhz */
+};
+
+static const u8 HTPLLBC[3][4] = {
+ {0x2, 0x3, 0x3, 0x2}, /* 27Mhz */
+ {0x2, 0x3, 0x3, 0x2}, /* 74Mhz */
+ {0x2, 0x3, 0x3, 0x2} /* 148Mhz */
+};
+
+static const u8 HTPLLBR[3][4] = {
+ {0x1, 0x1, 0x0, 0x1}, /* 27Mhz */
+ {0x1, 0x2, 0x2, 0x1}, /* 74Mhz */
+ {0x1, 0x2, 0x2, 0x1} /* 148Mhz */
+};
+
+static void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset,
+ u32 bits)
+{
+ void __iomem *reg = hdmi_phy->regs + offset;
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp &= ~bits;
+ writel(tmp, reg);
+}
+
+static void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset,
+ u32 bits)
+{
+ void __iomem *reg = hdmi_phy->regs + offset;
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp |= bits;
+ writel(tmp, reg);
+}
+
+static void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset,
+ u32 val, u32 mask)
+{
+ void __iomem *reg = hdmi_phy->regs + offset;
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp = (tmp & ~mask) | (val & mask);
+ writel(tmp, reg);
+}
+
+static inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw)
+{
+ return container_of(hw, struct mtk_hdmi_phy, pll_hw);
+}
+
+static int mtk_hdmi_pll_prepare(struct clk_hw *hw)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+
+ dev_dbg(hdmi_phy->dev, "%s\n", __func__);
+
+ mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN);
+ mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV);
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, RG_HDMITX_MHLCK_EN);
+ mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_EN);
+ usleep_range(100, 150);
+ mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_EN);
+ usleep_range(100, 150);
+ mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN);
+ mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN);
+
+ return 0;
+}
+
+static void mtk_hdmi_pll_unprepare(struct clk_hw *hw)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+
+ dev_dbg(hdmi_phy->dev, "%s\n", __func__);
+
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN);
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN);
+ usleep_range(100, 150);
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_EN);
+ usleep_range(100, 150);
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_EN);
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV);
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN);
+ usleep_range(100, 150);
+}
+
+static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+ unsigned int pre_div;
+ unsigned int div;
+
+ dev_dbg(hdmi_phy->dev, "%s: %lu Hz, parent: %lu Hz\n", __func__,
+ rate, parent_rate);
+
+ if (rate <= 27000000) {
+ pre_div = 0;
+ div = 3;
+ } else if (rate <= 74250000) {
+ pre_div = 1;
+ div = 2;
+ } else {
+ pre_div = 1;
+ div = 1;
+ }
+
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0,
+ (pre_div << PREDIV_SHIFT), RG_HDMITX_PLL_PREDIV);
+ mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0,
+ (0x1 << PLL_IC_SHIFT) | (0x1 << PLL_IR_SHIFT),
+ RG_HDMITX_PLL_IC | RG_HDMITX_PLL_IR);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1,
+ (div << PLL_TXDIV_SHIFT), RG_HDMITX_PLL_TXDIV);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0,
+ (0x1 << PLL_FBKSEL_SHIFT) | (19 << PLL_FBKDIV_SHIFT),
+ RG_HDMITX_PLL_FBKSEL | RG_HDMITX_PLL_FBKDIV);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1,
+ (0x2 << PLL_DIVEN_SHIFT), RG_HDMITX_PLL_DIVEN);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0,
+ (0xc << PLL_BP_SHIFT) | (0x2 << PLL_BC_SHIFT) |
+ (0x1 << PLL_BR_SHIFT),
+ RG_HDMITX_PLL_BP | RG_HDMITX_PLL_BC |
+ RG_HDMITX_PLL_BR);
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, RG_HDMITX_PRD_IMP_EN);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON4,
+ (0x3 << PRD_IBIAS_CLK_SHIFT) |
+ (0x3 << PRD_IBIAS_D2_SHIFT) |
+ (0x3 << PRD_IBIAS_D1_SHIFT) |
+ (0x3 << PRD_IBIAS_D0_SHIFT),
+ RG_HDMITX_PRD_IBIAS_CLK |
+ RG_HDMITX_PRD_IBIAS_D2 |
+ RG_HDMITX_PRD_IBIAS_D1 |
+ RG_HDMITX_PRD_IBIAS_D0);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3,
+ (0x0 << DRV_IMP_EN_SHIFT), RG_HDMITX_DRV_IMP_EN);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6,
+ (hdmi_phy->drv_imp_clk << DRV_IMP_CLK_SHIFT) |
+ (hdmi_phy->drv_imp_d2 << DRV_IMP_D2_SHIFT) |
+ (hdmi_phy->drv_imp_d1 << DRV_IMP_D1_SHIFT) |
+ (hdmi_phy->drv_imp_d0 << DRV_IMP_D0_SHIFT),
+ RG_HDMITX_DRV_IMP_CLK | RG_HDMITX_DRV_IMP_D2 |
+ RG_HDMITX_DRV_IMP_D1 | RG_HDMITX_DRV_IMP_D0);
+ mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON5,
+ (hdmi_phy->ibias << DRV_IBIAS_CLK_SHIFT) |
+ (hdmi_phy->ibias << DRV_IBIAS_D2_SHIFT) |
+ (hdmi_phy->ibias << DRV_IBIAS_D1_SHIFT) |
+ (hdmi_phy->ibias << DRV_IBIAS_D0_SHIFT),
+ RG_HDMITX_DRV_IBIAS_CLK | RG_HDMITX_DRV_IBIAS_D2 |
+ RG_HDMITX_DRV_IBIAS_D1 | RG_HDMITX_DRV_IBIAS_D0);
+ return 0;
+}
+
+static long mtk_hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+
+ hdmi_phy->pll_rate = rate;
+ if (rate <= 74250000)
+ *parent_rate = rate;
+ else
+ *parent_rate = rate / 2;
+
+ return rate;
+}
+
+static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+
+ return hdmi_phy->pll_rate;
+}
+
+static const struct clk_ops mtk_hdmi_pll_ops = {
+ .prepare = mtk_hdmi_pll_prepare,
+ .unprepare = mtk_hdmi_pll_unprepare,
+ .set_rate = mtk_hdmi_pll_set_rate,
+ .round_rate = mtk_hdmi_pll_round_rate,
+ .recalc_rate = mtk_hdmi_pll_recalc_rate,
+};
+
+static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy)
+{
+ mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON3,
+ RG_HDMITX_SER_EN | RG_HDMITX_PRD_EN |
+ RG_HDMITX_DRV_EN);
+ usleep_range(100, 150);
+}
+
+static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy)
+{
+ mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3,
+ RG_HDMITX_DRV_EN | RG_HDMITX_PRD_EN |
+ RG_HDMITX_SER_EN);
+}
+
+static int mtk_hdmi_phy_power_on(struct phy *phy)
+{
+ struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_prepare_enable(hdmi_phy->pll);
+ if (ret < 0)
+ return ret;
+
+ mtk_hdmi_phy_enable_tmds(hdmi_phy);
+
+ return 0;
+}
+
+static int mtk_hdmi_phy_power_off(struct phy *phy)
+{
+ struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
+
+ mtk_hdmi_phy_disable_tmds(hdmi_phy);
+ clk_disable_unprepare(hdmi_phy->pll);
+
+ return 0;
+}
+
+static const struct phy_ops mtk_hdmi_phy_ops = {
+ .power_on = mtk_hdmi_phy_power_on,
+ .power_off = mtk_hdmi_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int mtk_hdmi_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_hdmi_phy *hdmi_phy;
+ struct resource *mem;
+ struct clk *ref_clk;
+ const char *ref_clk_name;
+ struct clk_init_data clk_init = {
+ .ops = &mtk_hdmi_pll_ops,
+ .num_parents = 1,
+ .parent_names = (const char * const *)&ref_clk_name,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
+ };
+ struct phy *phy;
+ struct phy_provider *phy_provider;
+ int ret;
+
+ hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL);
+ if (!hdmi_phy)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hdmi_phy->regs = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(hdmi_phy->regs)) {
+ ret = PTR_ERR(hdmi_phy->regs);
+ dev_err(dev, "Failed to get memory resource: %d\n", ret);
+ return ret;
+ }
+
+ ref_clk = devm_clk_get(dev, "pll_ref");
+ if (IS_ERR(ref_clk)) {
+ ret = PTR_ERR(ref_clk);
+ dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n",
+ ret);
+ return ret;
+ }
+ ref_clk_name = __clk_get_name(ref_clk);
+
+ ret = of_property_read_string(dev->of_node, "clock-output-names",
+ &clk_init.name);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read clock-output-names: %d\n", ret);
+ return ret;
+ }
+
+ hdmi_phy->pll_hw.init = &clk_init;
+ hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw);
+ if (IS_ERR(hdmi_phy->pll)) {
+ ret = PTR_ERR(hdmi_phy->pll);
+ dev_err(dev, "Failed to register PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "mediatek,ibias",
+ &hdmi_phy->ibias);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret);
+ return ret;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up",
+ &hdmi_phy->ibias_up);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n");
+ hdmi_phy->drv_imp_clk = 0x30;
+ hdmi_phy->drv_imp_d2 = 0x30;
+ hdmi_phy->drv_imp_d1 = 0x30;
+ hdmi_phy->drv_imp_d0 = 0x30;
+
+ phy = devm_phy_create(dev, NULL, &mtk_hdmi_phy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "Failed to create HDMI PHY\n");
+ return PTR_ERR(phy);
+ }
+ phy_set_drvdata(phy, hdmi_phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ hdmi_phy->dev = dev;
+ return of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+ hdmi_phy->pll);
+}
+
+static int mtk_hdmi_phy_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id mtk_hdmi_phy_match[] = {
+ { .compatible = "mediatek,mt8173-hdmi-phy", },
+ {},
+};
+
+struct platform_driver mtk_hdmi_phy_driver = {
+ .probe = mtk_hdmi_phy_probe,
+ .remove = mtk_hdmi_phy_remove,
+ .driver = {
+ .name = "mediatek-hdmi-phy",
+ .of_match_table = mtk_hdmi_phy_match,
+ },
+};
+
+MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek MT8173 HDMI PHY Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 9c654092ef78..a02dc2b27739 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -197,8 +197,6 @@ static int msm_drm_uninit(struct device *dev)
drm_kms_helper_poll_fini(ddev);
- drm_connector_unregister_all(ddev);
-
drm_dev_unregister(ddev);
#ifdef CONFIG_DRM_FBDEV_EMULATION
@@ -431,12 +429,6 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
if (ret)
goto fail;
- ret = drm_connector_register_all(ddev);
- if (ret) {
- dev_err(dev, "failed to register connectors\n");
- goto fail;
- }
-
drm_mode_config_reset(ddev);
#ifdef CONFIG_DRM_FBDEV_EMULATION
@@ -730,7 +722,6 @@ static struct drm_driver msm_driver = {
.open = msm_open,
.preclose = msm_preclose,
.lastclose = msm_lastclose,
- .set_busid = drm_platform_set_busid,
.irq_handler = msm_irq,
.irq_preinstall = msm_irq_preinstall,
.irq_postinstall = msm_irq_postinstall,
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index c00ff6e786a3..295e7621cc68 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -1070,7 +1070,6 @@ nouveau_drm_init(void)
driver_pci = driver_stub;
driver_pci.set_busid = drm_pci_set_busid;
driver_platform = driver_stub;
- driver_platform.set_busid = drm_platform_set_busid;
nouveau_display_options();
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 6b97011154bf..26c6134eb744 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -801,7 +801,6 @@ static struct drm_driver omap_drm_driver = {
.unload = dev_unload,
.open = dev_open,
.lastclose = dev_lastclose,
- .set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = omap_irq_enable_vblank,
.disable_vblank = omap_irq_disable_vblank,
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 31dfa0893416..adb10fbe918d 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -279,9 +279,6 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
if (ret)
goto fini;
- /* disable all the possible outputs/crtcs before entering KMS mode */
- drm_helper_disable_unused_functions(dev);
-
ret = drm_fb_helper_initial_config(helper, 32);
if (ret)
goto fini;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 48ec4b6e8b26..899ef7a2a7b4 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -278,7 +278,6 @@ static int rcar_du_remove(struct platform_device *pdev)
struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
struct drm_device *ddev = rcdu->ddev;
- drm_connector_unregister_all(ddev);
drm_dev_unregister(ddev);
if (rcdu->fbdev)
@@ -320,8 +319,6 @@ static int rcar_du_probe(struct platform_device *pdev)
if (!ddev)
return -ENOMEM;
- drm_dev_set_unique(ddev, dev_name(&pdev->dev));
-
rcdu->ddev = ddev;
ddev->dev_private = rcdu;
@@ -339,15 +336,15 @@ static int rcar_du_probe(struct platform_device *pdev)
* disabled for all CRTCs.
*/
ret = drm_vblank_init(ddev, (1 << rcdu->info->num_crtcs) - 1);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to initialize vblank\n");
+ if (ret < 0)
goto error;
- }
/* DRM/KMS objects */
ret = rcar_du_modeset_init(rcdu);
if (ret < 0) {
- dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "failed to initialize DRM/KMS (%d)\n", ret);
goto error;
}
@@ -360,10 +357,6 @@ static int rcar_du_probe(struct platform_device *pdev)
if (ret)
goto error;
- ret = drm_connector_register_all(ddev);
- if (ret < 0)
- goto error;
-
DRM_INFO("Device %s probed\n", dev_name(&pdev->dev));
return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
index d2f66068e52c..fedb0161e234 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -195,9 +195,10 @@
#define DEFR6_ODPM12_DISP (2 << 8)
#define DEFR6_ODPM12_CDE (3 << 8)
#define DEFR6_ODPM12_MASK (3 << 8)
-#define DEFR6_TCNE2 (1 << 6)
+#define DEFR6_TCNE1 (1 << 6)
+#define DEFR6_TCNE0 (1 << 4)
#define DEFR6_MLOS1 (1 << 2)
-#define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE2)
+#define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1)
/* -----------------------------------------------------------------------------
* R8A7790-only Control Registers
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index d30bdc38a760..e48611e83c03 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -2,6 +2,7 @@ config DRM_ROCKCHIP
tristate "DRM Support for Rockchip"
depends on DRM && ROCKCHIP_IOMMU
depends on RESET_CONTROLLER
+ select DRM_GEM_CMA_HELPER
select DRM_KMS_HELPER
select DRM_KMS_FB_HELPER
select DRM_PANEL
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index c2bcc5ea1abe..d665fb04d264 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -146,16 +146,12 @@ static int rockchip_drm_bind(struct device *dev)
if (!drm_dev)
return -ENOMEM;
- ret = drm_dev_register(drm_dev, 0);
- if (ret)
- goto err_free;
-
dev_set_drvdata(dev, drm_dev);
private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
if (!private) {
ret = -ENOMEM;
- goto err_unregister;
+ goto err_free;
}
drm_dev->dev_private = private;
@@ -197,12 +193,6 @@ static int rockchip_drm_bind(struct device *dev)
if (ret)
goto err_detach_device;
- ret = drm_connector_register_all(drm_dev);
- if (ret) {
- dev_err(dev, "failed to register connectors\n");
- goto err_unbind;
- }
-
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(drm_dev);
@@ -222,14 +212,19 @@ static int rockchip_drm_bind(struct device *dev)
if (ret)
goto err_vblank_cleanup;
+ ret = drm_dev_register(drm_dev, 0);
+ if (ret)
+ goto err_fbdev_fini;
+
if (is_support_iommu)
arm_iommu_release_mapping(mapping);
return 0;
+err_fbdev_fini:
+ rockchip_drm_fbdev_fini(drm_dev);
err_vblank_cleanup:
drm_vblank_cleanup(drm_dev);
err_kms_helper_poll_fini:
drm_kms_helper_poll_fini(drm_dev);
-err_unbind:
component_unbind_all(dev, drm_dev);
err_detach_device:
if (is_support_iommu)
@@ -240,8 +235,6 @@ err_release_mapping:
err_config_cleanup:
drm_mode_config_cleanup(drm_dev);
drm_dev->dev_private = NULL;
-err_unregister:
- drm_dev_unregister(drm_dev);
err_free:
drm_dev_unref(drm_dev);
return ret;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 8cd840f602b7..6255e5bcd954 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -626,6 +626,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
ret = drm_plane_helper_check_update(plane, crtc, state->fb,
src, dest, &clip,
+ state->rotation,
min_scale,
max_scale,
true, true, &visible);
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
index ee79264b5b6a..f0492603ea88 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
@@ -259,7 +259,6 @@ static struct drm_driver shmob_drm_driver = {
| DRIVER_PRIME,
.load = shmob_drm_load,
.unload = shmob_drm_unload,
- .set_busid = drm_platform_set_busid,
.irq_handler = shmob_drm_irq,
.get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = shmob_drm_enable_vblank,
diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c
index 93ad8a5704d1..03defda77766 100644
--- a/drivers/gpu/drm/sis/sis_mm.c
+++ b/drivers/gpu/drm/sis/sis_mm.c
@@ -316,7 +316,7 @@ void sis_reclaim_buffers_locked(struct drm_device *dev,
struct sis_file_private *file_priv = file->driver_priv;
struct sis_memblock *entry, *next;
- if (!(file->minor->master && file->master->lock.hw_lock))
+ if (!(dev->master && file->master->lock.hw_lock))
return;
drm_legacy_idlelock_take(&file->master->lock);
diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c
index 3d2fa3ab33df..794148ff0e57 100644
--- a/drivers/gpu/drm/sti/sti_compositor.c
+++ b/drivers/gpu/drm/sti/sti_compositor.c
@@ -55,6 +55,26 @@ struct sti_compositor_data stih416_compositor_data = {
},
};
+int sti_compositor_debufs_init(struct sti_compositor *compo,
+ struct drm_minor *minor)
+{
+ int ret = 0, i;
+
+ for (i = 0; compo->vid[i]; i++) {
+ ret = vid_debugfs_init(compo->vid[i], minor);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; compo->mixer[i]; i++) {
+ ret = sti_mixer_debugfs_init(compo->mixer[i], minor);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int sti_compositor_bind(struct device *dev,
struct device *master,
void *data)
diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h
index 1a4a73dab11e..24444ef42a98 100644
--- a/drivers/gpu/drm/sti/sti_compositor.h
+++ b/drivers/gpu/drm/sti/sti_compositor.h
@@ -81,4 +81,7 @@ struct sti_compositor {
struct notifier_block vtg_vblank_nb;
};
+int sti_compositor_debufs_init(struct sti_compositor *compo,
+ struct drm_minor *minor);
+
#endif
diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c
index e04deedabd4a..7fab3af7473b 100644
--- a/drivers/gpu/drm/sti/sti_crtc.c
+++ b/drivers/gpu/drm/sti/sti_crtc.c
@@ -331,6 +331,17 @@ void sti_crtc_disable_vblank(struct drm_device *drm_dev, unsigned int pipe)
}
}
+static int sti_crtc_late_register(struct drm_crtc *crtc)
+{
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+ struct sti_compositor *compo = dev_get_drvdata(mixer->dev);
+
+ if (drm_crtc_index(crtc) == 0)
+ return sti_compositor_debufs_init(compo, crtc->dev->primary);
+
+ return 0;
+}
+
static const struct drm_crtc_funcs sti_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
@@ -339,6 +350,7 @@ static const struct drm_crtc_funcs sti_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .late_register = sti_crtc_late_register,
};
bool sti_crtc_is_main(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c
index 53aa0029295b..a263bbba4119 100644
--- a/drivers/gpu/drm/sti/sti_cursor.c
+++ b/drivers/gpu/drm/sti/sti_cursor.c
@@ -329,6 +329,33 @@ static const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = {
.atomic_disable = sti_cursor_atomic_disable,
};
+static void sti_cursor_destroy(struct drm_plane *drm_plane)
+{
+ DRM_DEBUG_DRIVER("\n");
+
+ drm_plane_helper_disable(drm_plane);
+ drm_plane_cleanup(drm_plane);
+}
+
+static int sti_cursor_late_register(struct drm_plane *drm_plane)
+{
+ struct sti_plane *plane = to_sti_plane(drm_plane);
+ struct sti_cursor *cursor = to_sti_cursor(plane);
+
+ return cursor_debugfs_init(cursor, drm_plane->dev->primary);
+}
+
+struct drm_plane_funcs sti_cursor_plane_helpers_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = sti_cursor_destroy,
+ .set_property = sti_plane_set_property,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .late_register = sti_cursor_late_register,
+};
+
struct drm_plane *sti_cursor_create(struct drm_device *drm_dev,
struct device *dev, int desc,
void __iomem *baseaddr,
@@ -363,7 +390,7 @@ struct drm_plane *sti_cursor_create(struct drm_device *drm_dev,
res = drm_universal_plane_init(drm_dev, &cursor->plane.drm_plane,
possible_crtcs,
- &sti_plane_helpers_funcs,
+ &sti_cursor_plane_helpers_funcs,
cursor_supported_formats,
ARRAY_SIZE(cursor_supported_formats),
DRM_PLANE_TYPE_CURSOR, NULL);
@@ -377,9 +404,6 @@ struct drm_plane *sti_cursor_create(struct drm_device *drm_dev,
sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR);
- if (cursor_debugfs_init(cursor, drm_dev->primary))
- DRM_ERROR("CURSOR debugfs setup failed\n");
-
return &cursor->plane.drm_plane;
err_plane:
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
index dd2c400c4a46..96bd3d08b2d4 100644
--- a/drivers/gpu/drm/sti/sti_drv.c
+++ b/drivers/gpu/drm/sti/sti_drv.c
@@ -226,8 +226,28 @@ static int sti_atomic_commit(struct drm_device *drm,
return 0;
}
+static void sti_output_poll_changed(struct drm_device *ddev)
+{
+ struct sti_private *private = ddev->dev_private;
+
+ if (!ddev->mode_config.num_connector)
+ return;
+
+ if (private->fbdev) {
+ drm_fbdev_cma_hotplug_event(private->fbdev);
+ return;
+ }
+
+ private->fbdev = drm_fbdev_cma_init(ddev, 32,
+ ddev->mode_config.num_crtc,
+ ddev->mode_config.num_connector);
+ if (IS_ERR(private->fbdev))
+ private->fbdev = NULL;
+}
+
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
.fb_create = drm_fb_cma_create,
+ .output_poll_changed = sti_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = sti_atomic_commit,
};
@@ -248,45 +268,6 @@ static void sti_mode_config_init(struct drm_device *dev)
dev->mode_config.funcs = &sti_mode_config_funcs;
}
-static int sti_load(struct drm_device *dev, unsigned long flags)
-{
- struct sti_private *private;
- int ret;
-
- private = kzalloc(sizeof(*private), GFP_KERNEL);
- if (!private) {
- DRM_ERROR("Failed to allocate private\n");
- return -ENOMEM;
- }
- dev->dev_private = (void *)private;
- private->drm_dev = dev;
-
- mutex_init(&private->commit.lock);
- INIT_WORK(&private->commit.work, sti_atomic_work);
-
- drm_mode_config_init(dev);
- drm_kms_helper_poll_init(dev);
-
- sti_mode_config_init(dev);
-
- ret = component_bind_all(dev->dev, dev);
- if (ret) {
- drm_kms_helper_poll_fini(dev);
- drm_mode_config_cleanup(dev);
- kfree(private);
- return ret;
- }
-
- drm_mode_config_reset(dev);
-
- drm_helper_disable_unused_functions(dev);
- drm_fbdev_cma_init(dev, 32,
- dev->mode_config.num_crtc,
- dev->mode_config.num_connector);
-
- return 0;
-}
-
static const struct file_operations sti_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
@@ -303,7 +284,6 @@ static const struct file_operations sti_driver_fops = {
static struct drm_driver sti_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
- .load = sti_load,
.gem_free_object_unlocked = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = drm_gem_cma_dumb_create,
@@ -340,14 +320,88 @@ static int compare_of(struct device *dev, void *data)
return dev->of_node == data;
}
+static int sti_init(struct drm_device *ddev)
+{
+ struct sti_private *private;
+
+ private = kzalloc(sizeof(*private), GFP_KERNEL);
+ if (!private)
+ return -ENOMEM;
+
+ ddev->dev_private = (void *)private;
+ dev_set_drvdata(ddev->dev, ddev);
+ private->drm_dev = ddev;
+
+ mutex_init(&private->commit.lock);
+ INIT_WORK(&private->commit.work, sti_atomic_work);
+
+ drm_mode_config_init(ddev);
+
+ sti_mode_config_init(ddev);
+
+ drm_kms_helper_poll_init(ddev);
+
+ return 0;
+}
+
+static void sti_cleanup(struct drm_device *ddev)
+{
+ struct sti_private *private = ddev->dev_private;
+
+ if (private->fbdev) {
+ drm_fbdev_cma_fini(private->fbdev);
+ private->fbdev = NULL;
+ }
+
+ drm_kms_helper_poll_fini(ddev);
+ drm_vblank_cleanup(ddev);
+ kfree(private);
+ ddev->dev_private = NULL;
+}
+
static int sti_bind(struct device *dev)
{
- return drm_platform_init(&sti_driver, to_platform_device(dev));
+ struct drm_device *ddev;
+ int ret;
+
+ ddev = drm_dev_alloc(&sti_driver, dev);
+ if (!ddev)
+ return -ENOMEM;
+
+ ddev->platformdev = to_platform_device(dev);
+
+ ret = sti_init(ddev);
+ if (ret)
+ goto err_drm_dev_unref;
+
+ ret = component_bind_all(ddev->dev, ddev);
+ if (ret)
+ goto err_cleanup;
+
+ ret = drm_dev_register(ddev, 0);
+ if (ret)
+ goto err_register;
+
+ drm_mode_config_reset(ddev);
+
+ return 0;
+
+err_register:
+ drm_mode_config_cleanup(ddev);
+err_cleanup:
+ sti_cleanup(ddev);
+err_drm_dev_unref:
+ drm_dev_unref(ddev);
+ return ret;
}
static void sti_unbind(struct device *dev)
{
- drm_put_dev(dev_get_drvdata(dev));
+ struct drm_device *ddev = dev_get_drvdata(dev);
+
+ drm_dev_unregister(ddev);
+ sti_cleanup(ddev);
+ drm_dev_unref(ddev);
}
static const struct component_master_ops sti_ops = {
diff --git a/drivers/gpu/drm/sti/sti_drv.h b/drivers/gpu/drm/sti/sti_drv.h
index 30ddc20841c3..78ebe5e30f53 100644
--- a/drivers/gpu/drm/sti/sti_drv.h
+++ b/drivers/gpu/drm/sti/sti_drv.h
@@ -24,6 +24,7 @@ struct sti_private {
struct sti_compositor *compo;
struct drm_property *plane_zorder_property;
struct drm_device *drm_dev;
+ struct drm_fbdev_cma *fbdev;
struct {
struct drm_atomic_state *state;
diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
index e2901667eceb..ec3108074350 100644
--- a/drivers/gpu/drm/sti/sti_dvo.c
+++ b/drivers/gpu/drm/sti/sti_dvo.c
@@ -404,24 +404,29 @@ sti_dvo_connector_detect(struct drm_connector *connector, bool force)
return connector_status_disconnected;
}
-static void sti_dvo_connector_destroy(struct drm_connector *connector)
+static int sti_dvo_late_register(struct drm_connector *connector)
{
struct sti_dvo_connector *dvo_connector
= to_sti_dvo_connector(connector);
+ struct sti_dvo *dvo = dvo_connector->dvo;
+
+ if (dvo_debugfs_init(dvo, dvo->drm_dev->primary)) {
+ DRM_ERROR("DVO debugfs setup failed\n");
+ return -EINVAL;
+ }
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
- kfree(dvo_connector);
+ return 0;
}
static const struct drm_connector_funcs sti_dvo_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = sti_dvo_connector_detect,
- .destroy = sti_dvo_connector_destroy,
+ .destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .late_register = sti_dvo_late_register,
};
static struct drm_encoder *sti_dvo_find_encoder(struct drm_device *dev)
@@ -492,26 +497,16 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
drm_connector_helper_add(drm_connector,
&sti_dvo_connector_helper_funcs);
- err = drm_connector_register(drm_connector);
- if (err)
- goto err_connector;
-
err = drm_mode_connector_attach_encoder(drm_connector, encoder);
if (err) {
DRM_ERROR("Failed to attach a connector to a encoder\n");
goto err_sysfs;
}
- if (dvo_debugfs_init(dvo, drm_dev->primary))
- DRM_ERROR("DVO debugfs setup failed\n");
-
return 0;
err_sysfs:
- drm_connector_unregister(drm_connector);
-err_connector:
drm_bridge_remove(bridge);
- drm_connector_cleanup(drm_connector);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
index fdf69b5a041b..bf63086a3dc8 100644
--- a/drivers/gpu/drm/sti/sti_gdp.c
+++ b/drivers/gpu/drm/sti/sti_gdp.c
@@ -866,6 +866,33 @@ static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = {
.atomic_disable = sti_gdp_atomic_disable,
};
+static void sti_gdp_destroy(struct drm_plane *drm_plane)
+{
+ DRM_DEBUG_DRIVER("\n");
+
+ drm_plane_helper_disable(drm_plane);
+ drm_plane_cleanup(drm_plane);
+}
+
+static int sti_gdp_late_register(struct drm_plane *drm_plane)
+{
+ struct sti_plane *plane = to_sti_plane(drm_plane);
+ struct sti_gdp *gdp = to_sti_gdp(plane);
+
+ return gdp_debugfs_init(gdp, drm_plane->dev->primary);
+}
+
+struct drm_plane_funcs sti_gdp_plane_helpers_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = sti_gdp_destroy,
+ .set_property = sti_plane_set_property,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .late_register = sti_gdp_late_register,
+};
+
struct drm_plane *sti_gdp_create(struct drm_device *drm_dev,
struct device *dev, int desc,
void __iomem *baseaddr,
@@ -892,7 +919,7 @@ struct drm_plane *sti_gdp_create(struct drm_device *drm_dev,
res = drm_universal_plane_init(drm_dev, &gdp->plane.drm_plane,
possible_crtcs,
- &sti_plane_helpers_funcs,
+ &sti_gdp_plane_helpers_funcs,
gdp_supported_formats,
ARRAY_SIZE(gdp_supported_formats),
type, NULL);
@@ -905,9 +932,6 @@ struct drm_plane *sti_gdp_create(struct drm_device *drm_dev,
sti_plane_init_property(&gdp->plane, type);
- if (gdp_debugfs_init(gdp, drm_dev->primary))
- DRM_ERROR("GDP debugfs setup failed\n");
-
return &gdp->plane.drm_plane;
err:
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
index dcec5a8eda59..8505569f75de 100644
--- a/drivers/gpu/drm/sti/sti_hda.c
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -681,24 +681,29 @@ sti_hda_connector_detect(struct drm_connector *connector, bool force)
return connector_status_connected;
}
-static void sti_hda_connector_destroy(struct drm_connector *connector)
+static int sti_hda_late_register(struct drm_connector *connector)
{
struct sti_hda_connector *hda_connector
= to_sti_hda_connector(connector);
+ struct sti_hda *hda = hda_connector->hda;
+
+ if (hda_debugfs_init(hda, hda->drm_dev->primary)) {
+ DRM_ERROR("HDA debugfs setup failed\n");
+ return -EINVAL;
+ }
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
- kfree(hda_connector);
+ return 0;
}
static const struct drm_connector_funcs sti_hda_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = sti_hda_connector_detect,
- .destroy = sti_hda_connector_destroy,
+ .destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .late_register = sti_hda_late_register,
};
static struct drm_encoder *sti_hda_find_encoder(struct drm_device *dev)
@@ -756,10 +761,6 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
drm_connector_helper_add(drm_connector,
&sti_hda_connector_helper_funcs);
- err = drm_connector_register(drm_connector);
- if (err)
- goto err_connector;
-
err = drm_mode_connector_attach_encoder(drm_connector, encoder);
if (err) {
DRM_ERROR("Failed to attach a connector to a encoder\n");
@@ -769,15 +770,10 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
/* force to disable hd dacs at startup */
hda_enable_hd_dacs(hda, false);
- if (hda_debugfs_init(hda, drm_dev->primary))
- DRM_ERROR("HDA debugfs setup failed\n");
-
return 0;
err_sysfs:
- drm_connector_unregister(drm_connector);
-err_connector:
- drm_connector_cleanup(drm_connector);
+ drm_bridge_remove(bridge);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index 36d9d6635784..8d1402b245bf 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -915,16 +915,6 @@ sti_hdmi_connector_detect(struct drm_connector *connector, bool force)
return connector_status_disconnected;
}
-static void sti_hdmi_connector_destroy(struct drm_connector *connector)
-{
- struct sti_hdmi_connector *hdmi_connector
- = to_sti_hdmi_connector(connector);
-
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
- kfree(hdmi_connector);
-}
-
static void sti_hdmi_connector_init_property(struct drm_device *drm_dev,
struct drm_connector *connector)
{
@@ -1007,17 +997,31 @@ sti_hdmi_connector_get_property(struct drm_connector *connector,
return -EINVAL;
}
+static int sti_hdmi_late_register(struct drm_connector *connector)
+{
+ struct sti_hdmi_connector *hdmi_connector
+ = to_sti_hdmi_connector(connector);
+ struct sti_hdmi *hdmi = hdmi_connector->hdmi;
+
+ if (hdmi_debugfs_init(hdmi, hdmi->drm_dev->primary)) {
+ DRM_ERROR("HDMI debugfs setup failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct drm_connector_funcs sti_hdmi_connector_funcs = {
- .dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = sti_hdmi_connector_detect,
- .destroy = sti_hdmi_connector_destroy,
+ .destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.set_property = drm_atomic_helper_connector_set_property,
.atomic_set_property = sti_hdmi_connector_set_property,
.atomic_get_property = sti_hdmi_connector_get_property,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .late_register = sti_hdmi_late_register,
};
static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev)
@@ -1078,10 +1082,6 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
/* initialise property */
sti_hdmi_connector_init_property(drm_dev, drm_connector);
- err = drm_connector_register(drm_connector);
- if (err)
- goto err_connector;
-
err = drm_mode_connector_attach_encoder(drm_connector, encoder);
if (err) {
DRM_ERROR("Failed to attach a connector to a encoder\n");
@@ -1091,16 +1091,10 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
/* Enable default interrupts */
hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN);
- if (hdmi_debugfs_init(hdmi, drm_dev->primary))
- DRM_ERROR("HDMI debugfs setup failed\n");
-
return 0;
err_sysfs:
- drm_connector_unregister(drm_connector);
-err_connector:
- drm_connector_cleanup(drm_connector);
-
+ drm_bridge_remove(bridge);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c
index 1c06a50fddca..33d2f42550cc 100644
--- a/drivers/gpu/drm/sti/sti_hqvdp.c
+++ b/drivers/gpu/drm/sti/sti_hqvdp.c
@@ -1234,6 +1234,33 @@ static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs = {
.atomic_disable = sti_hqvdp_atomic_disable,
};
+static void sti_hqvdp_destroy(struct drm_plane *drm_plane)
+{
+ DRM_DEBUG_DRIVER("\n");
+
+ drm_plane_helper_disable(drm_plane);
+ drm_plane_cleanup(drm_plane);
+}
+
+static int sti_hqvdp_late_register(struct drm_plane *drm_plane)
+{
+ struct sti_plane *plane = to_sti_plane(drm_plane);
+ struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane);
+
+ return hqvdp_debugfs_init(hqvdp, drm_plane->dev->primary);
+}
+
+struct drm_plane_funcs sti_hqvdp_plane_helpers_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = sti_hqvdp_destroy,
+ .set_property = sti_plane_set_property,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .late_register = sti_hqvdp_late_register,
+};
+
static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev,
struct device *dev, int desc)
{
@@ -1246,7 +1273,7 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev,
sti_hqvdp_init(hqvdp);
res = drm_universal_plane_init(drm_dev, &hqvdp->plane.drm_plane, 1,
- &sti_plane_helpers_funcs,
+ &sti_hqvdp_plane_helpers_funcs,
hqvdp_supported_formats,
ARRAY_SIZE(hqvdp_supported_formats),
DRM_PLANE_TYPE_OVERLAY, NULL);
@@ -1259,9 +1286,6 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev,
sti_plane_init_property(&hqvdp->plane, DRM_PLANE_TYPE_OVERLAY);
- if (hqvdp_debugfs_init(hqvdp, drm_dev->primary))
- DRM_ERROR("HQVDP debugfs setup failed\n");
-
return &hqvdp->plane.drm_plane;
}
diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c
index 6f86f2b2b6a5..1885c7ab5a8b 100644
--- a/drivers/gpu/drm/sti/sti_mixer.c
+++ b/drivers/gpu/drm/sti/sti_mixer.c
@@ -181,7 +181,7 @@ static struct drm_info_list mixer1_debugfs_files[] = {
{ "mixer_aux", mixer_dbg_show, 0, NULL },
};
-static int mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor)
+int sti_mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor)
{
unsigned int i;
struct drm_info_list *mixer_debugfs_files;
@@ -393,8 +393,5 @@ struct sti_mixer *sti_mixer_create(struct device *dev,
DRM_DEBUG_DRIVER("%s created. Regs=%p\n",
sti_mixer_to_str(mixer), mixer->regs);
- if (mixer_debugfs_init(mixer, drm_dev->primary))
- DRM_ERROR("MIXER debugfs setup failed\n");
-
return mixer;
}
diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h
index 6f35fc086873..830a3c42d886 100644
--- a/drivers/gpu/drm/sti/sti_mixer.h
+++ b/drivers/gpu/drm/sti/sti_mixer.h
@@ -55,6 +55,8 @@ int sti_mixer_active_video_area(struct sti_mixer *mixer,
void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable);
+int sti_mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor);
+
/* depth in Cross-bar control = z order */
#define GAM_MIXER_NB_DEPTH_LEVEL 6
diff --git a/drivers/gpu/drm/sti/sti_plane.c b/drivers/gpu/drm/sti/sti_plane.c
index f10c98d3f012..85cee9098439 100644
--- a/drivers/gpu/drm/sti/sti_plane.c
+++ b/drivers/gpu/drm/sti/sti_plane.c
@@ -106,17 +106,9 @@ void sti_plane_update_fps(struct sti_plane *plane,
plane->fps_info.fips_str);
}
-static void sti_plane_destroy(struct drm_plane *drm_plane)
-{
- DRM_DEBUG_DRIVER("\n");
-
- drm_plane_helper_disable(drm_plane);
- drm_plane_cleanup(drm_plane);
-}
-
-static int sti_plane_set_property(struct drm_plane *drm_plane,
- struct drm_property *property,
- uint64_t val)
+int sti_plane_set_property(struct drm_plane *drm_plane,
+ struct drm_property *property,
+ uint64_t val)
{
struct drm_device *dev = drm_plane->dev;
struct sti_private *private = dev->dev_private;
@@ -170,13 +162,3 @@ void sti_plane_init_property(struct sti_plane *plane,
plane->drm_plane.base.id,
sti_plane_to_str(plane), plane->zorder);
}
-
-struct drm_plane_funcs sti_plane_helpers_funcs = {
- .update_plane = drm_atomic_helper_update_plane,
- .disable_plane = drm_atomic_helper_disable_plane,
- .destroy = sti_plane_destroy,
- .set_property = sti_plane_set_property,
- .reset = drm_atomic_helper_plane_reset,
- .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
-};
diff --git a/drivers/gpu/drm/sti/sti_plane.h b/drivers/gpu/drm/sti/sti_plane.h
index c50a3b9f5d37..39d39f5b7dd9 100644
--- a/drivers/gpu/drm/sti/sti_plane.h
+++ b/drivers/gpu/drm/sti/sti_plane.h
@@ -11,8 +11,6 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_plane_helper.h>
-extern struct drm_plane_funcs sti_plane_helpers_funcs;
-
#define to_sti_plane(x) container_of(x, struct sti_plane, drm_plane)
#define STI_PLANE_TYPE_SHIFT 8
@@ -83,6 +81,11 @@ const char *sti_plane_to_str(struct sti_plane *plane);
void sti_plane_update_fps(struct sti_plane *plane,
bool new_frame,
bool new_field);
+
+int sti_plane_set_property(struct drm_plane *drm_plane,
+ struct drm_property *property,
+ uint64_t val);
+
void sti_plane_init_property(struct sti_plane *plane,
enum drm_plane_type type);
#endif
diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c
index 60fe0afa5644..e25995b35715 100644
--- a/drivers/gpu/drm/sti/sti_tvout.c
+++ b/drivers/gpu/drm/sti/sti_tvout.c
@@ -112,6 +112,7 @@ struct sti_tvout {
struct drm_encoder *hdmi;
struct drm_encoder *hda;
struct drm_encoder *dvo;
+ bool debugfs_registered;
};
struct sti_tvout_encoder {
@@ -625,8 +626,37 @@ static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)
kfree(sti_encoder);
}
+static int sti_tvout_late_register(struct drm_encoder *encoder)
+{
+ struct sti_tvout *tvout = to_sti_tvout(encoder);
+ int ret;
+
+ if (tvout->debugfs_registered)
+ return 0;
+
+ ret = tvout_debugfs_init(tvout, encoder->dev->primary);
+ if (ret)
+ return ret;
+
+ tvout->debugfs_registered = true;
+ return 0;
+}
+
+static void sti_tvout_early_unregister(struct drm_encoder *encoder)
+{
+ struct sti_tvout *tvout = to_sti_tvout(encoder);
+
+ if (!tvout->debugfs_registered)
+ return;
+
+ tvout_debugfs_exit(tvout, encoder->dev->primary);
+ tvout->debugfs_registered = false;
+}
+
static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {
.destroy = sti_tvout_encoder_destroy,
+ .late_register = sti_tvout_late_register,
+ .early_unregister = sti_tvout_early_unregister,
};
static void sti_dvo_encoder_enable(struct drm_encoder *encoder)
@@ -813,9 +843,6 @@ static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
sti_tvout_create_encoders(drm_dev, tvout);
- if (tvout_debugfs_init(tvout, drm_dev->primary))
- DRM_ERROR("TVOUT debugfs setup failed\n");
-
return 0;
}
@@ -823,11 +850,8 @@ static void sti_tvout_unbind(struct device *dev, struct device *master,
void *data)
{
struct sti_tvout *tvout = dev_get_drvdata(dev);
- struct drm_device *drm_dev = data;
sti_tvout_destroy_encoders(tvout);
-
- tvout_debugfs_exit(tvout, drm_dev->primary);
}
static const struct component_ops sti_tvout_ops = {
diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c
index 0132aaebe598..47634a0251fc 100644
--- a/drivers/gpu/drm/sti/sti_vid.c
+++ b/drivers/gpu/drm/sti/sti_vid.c
@@ -123,7 +123,7 @@ static struct drm_info_list vid_debugfs_files[] = {
{ "vid", vid_dbg_show, 0, NULL },
};
-static int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor)
+int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor)
{
unsigned int i;
@@ -220,8 +220,5 @@ struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev,
sti_vid_init(vid);
- if (vid_debugfs_init(vid, drm_dev->primary))
- DRM_ERROR("VID debugfs setup failed\n");
-
return vid;
}
diff --git a/drivers/gpu/drm/sti/sti_vid.h b/drivers/gpu/drm/sti/sti_vid.h
index 6c842344f3d8..fdc90f922a05 100644
--- a/drivers/gpu/drm/sti/sti_vid.h
+++ b/drivers/gpu/drm/sti/sti_vid.h
@@ -26,4 +26,6 @@ void sti_vid_disable(struct sti_vid *vid);
struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev,
int id, void __iomem *baseaddr);
+int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor);
+
#endif
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 68e9d85085fb..9a67f927a53e 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -24,34 +24,6 @@
#include "sun4i_layer.h"
#include "sun4i_tcon.h"
-static int sun4i_drv_connector_plug_all(struct drm_device *drm)
-{
- struct drm_connector *connector, *failed;
- int ret;
-
- mutex_lock(&drm->mode_config.mutex);
- list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
- ret = drm_connector_register(connector);
- if (ret) {
- failed = connector;
- goto err;
- }
- }
- mutex_unlock(&drm->mode_config.mutex);
- return 0;
-
-err:
- list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
- if (failed == connector)
- break;
-
- drm_connector_unregister(connector);
- }
- mutex_unlock(&drm->mode_config.mutex);
-
- return ret;
-}
-
static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
{
struct sun4i_drv *drv = drm->dev_private;
@@ -135,10 +107,6 @@ static int sun4i_drv_bind(struct device *dev)
if (!drm)
return -ENOMEM;
- ret = drm_dev_set_unique(drm, dev_name(drm->dev));
- if (ret)
- goto free_drm;
-
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
if (!drv) {
ret = -ENOMEM;
@@ -187,14 +155,8 @@ static int sun4i_drv_bind(struct device *dev)
if (ret)
goto free_drm;
- ret = sun4i_drv_connector_plug_all(drm);
- if (ret)
- goto unregister_drm;
-
return 0;
-unregister_drm:
- drm_dev_unregister(drm);
free_drm:
drm_dev_unref(drm);
return ret;
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
index 308e197908fc..d27809372d54 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -541,7 +541,6 @@ static struct drm_driver tilcdc_driver = {
.load = tilcdc_load,
.unload = tilcdc_unload,
.lastclose = tilcdc_lastclose,
- .set_busid = drm_platform_set_busid,
.irq_handler = tilcdc_irq,
.irq_preinstall = tilcdc_irq_preinstall,
.irq_postinstall = tilcdc_irq_postinstall,
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 58b8fc036332..9e88231b8906 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -176,7 +176,6 @@ static int vc4_drm_bind(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm;
- struct drm_connector *connector;
struct vc4_dev *vc4;
int ret = 0;
@@ -211,22 +210,10 @@ static int vc4_drm_bind(struct device *dev)
if (ret < 0)
goto unbind_all;
- /* Connector registration has to occur after DRM device
- * registration, because it creates sysfs entries based on the
- * DRM device.
- */
- list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
- ret = drm_connector_register(connector);
- if (ret)
- goto unregister;
- }
-
vc4_kms_load(drm);
return 0;
-unregister:
- drm_dev_unregister(drm);
unbind_all:
component_unbind_all(dev, drm);
gem_destroy:
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c
index 1b4cc8b27080..35ea5d02a827 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.c
+++ b/drivers/gpu/drm/vgem/vgem_drv.c
@@ -260,8 +260,6 @@ static int __init vgem_init(void)
goto out;
}
- drm_dev_set_unique(vgem_device, "vgem");
-
ret = drm_dev_register(vgem_device, 0);
if (ret)
diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c
index 4f20742e7788..a04ef1c992d9 100644
--- a/drivers/gpu/drm/via/via_mm.c
+++ b/drivers/gpu/drm/via/via_mm.c
@@ -208,7 +208,7 @@ void via_reclaim_buffers_locked(struct drm_device *dev,
struct via_file_private *file_priv = file->driver_priv;
struct via_memblock *entry, *next;
- if (!(file->minor->master && file->master->lock.hw_lock))
+ if (!(dev->master && file->master->lock.hw_lock))
return;
drm_legacy_idlelock_take(&file->master->lock);
diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
index 88a39165edd5..7f0e93f87a55 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
@@ -27,16 +27,6 @@
#include "virtgpu_drv.h"
-int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master)
-{
- struct pci_dev *pdev = dev->pdev;
-
- if (pdev) {
- return drm_pci_set_busid(dev, master);
- }
- return 0;
-}
-
static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev)
{
struct apertures_struct *ap;
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 5820b7020ae5..c13f70cfc461 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -117,7 +117,6 @@ static const struct file_operations virtio_gpu_driver_fops = {
static struct drm_driver driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER | DRIVER_ATOMIC,
- .set_busid = drm_virtio_set_busid,
.load = virtio_gpu_driver_load,
.unload = virtio_gpu_driver_unload,
.open = virtio_gpu_driver_open,
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index acf556a35cb2..b18ef3111f0c 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -49,7 +49,6 @@
#define DRIVER_PATCHLEVEL 1
/* virtgpu_drm_bus.c */
-int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master);
int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev);
struct virtio_gpu_object {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 9fcd8200d485..60646644bef3 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1049,7 +1049,7 @@ static struct vmw_master *vmw_master_check(struct drm_device *dev,
if (unlikely(ret != 0))
return ERR_PTR(-ERESTARTSYS);
- if (file_priv->is_master) {
+ if (drm_is_current_master(file_priv)) {
mutex_unlock(&dev->master_mutex);
return NULL;
}
@@ -1228,8 +1228,7 @@ static int vmw_master_set(struct drm_device *dev,
}
static void vmw_master_drop(struct drm_device *dev,
- struct drm_file *file_priv,
- bool from_release)
+ struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index 1980e2a28265..9a90f824814e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -32,6 +32,7 @@
#include <drm/drmP.h>
#include <drm/vmwgfx_drm.h>
#include <drm/drm_hashtab.h>
+#include <drm/drm_auth.h>
#include <linux/suspend.h>
#include <drm/ttm/ttm_bo_driver.h>
#include <drm/ttm/ttm_object.h>
OpenPOWER on IntegriCloud