From 490cda5a3c82c4a6a0bbdffe783eb48aec25511a Mon Sep 17 00:00:00 2001 From: Giulio Benetti Date: Wed, 18 Jul 2018 16:23:57 +0200 Subject: [PATCH 001/138] drm/sun4i: Handle DRM_BUS_FLAG_PIXDATA_*EDGE checking if panel is used. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle both positive and negative dclk polarity, according to bus_flags, taking care of this: On A20 and similar SoCs, the only way to achieve Positive Edge (Rising Edge), is setting dclk clock phase to 2/3(240°). By default TCON works in Negative Edge(Falling Edge), this is why phase is set to 0 in that case. Unfortunately there's no way to logically invert dclk through IO_POL register. The only acceptable way to work, triple checked with scope, is using clock phase set to 0° for Negative Edge and set to 240° for Positive Edge. On A33 and similar SoCs there would be a 90° phase option, but it divides also dclk by 2. This patch is a way to avoid quirks all around TCON and DOTCLOCK drivers for using A33 90° phase divided by 2 and consequently increase code complexity. Check if panel is used. TCON can also handle VGA DAC, then panel could be empty. Signed-off-by: Giulio Benetti Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20180718142357.120998-1-giulio.benetti@micronovasrl.com --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 3fb084f802e2..9bfb5b967d38 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -474,6 +475,33 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, if (mode->flags & DRM_MODE_FLAG_PVSYNC) val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; + /* + * On A20 and similar SoCs, the only way to achieve Positive Edge + * (Rising Edge), is setting dclk clock phase to 2/3(240°). + * By default TCON works in Negative Edge(Falling Edge), + * this is why phase is set to 0 in that case. + * Unfortunately there's no way to logically invert dclk through + * IO_POL register. + * The only acceptable way to work, triple checked with scope, + * is using clock phase set to 0° for Negative Edge and set to 240° + * for Positive Edge. + * On A33 and similar SoCs there would be a 90° phase option, + * but it divides also dclk by 2. + * Following code is a way to avoid quirks all around TCON + * and DOTCLOCK drivers. + */ + if (!IS_ERR(tcon->panel)) { + struct drm_panel *panel = tcon->panel; + struct drm_connector *connector = panel->connector; + struct drm_display_info display_info = connector->display_info; + + if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) + clk_set_phase(tcon->dclk, 240); + + if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) + clk_set_phase(tcon->dclk, 0); + } + regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, val); From e527cd9e48e33e8caffc0b00a4f5bd1add0b3d09 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 19 Jul 2018 10:08:37 +0200 Subject: [PATCH 002/138] drm/sun4i: sun4i: Register quirks with the backend structure In prevision for introducing a new quirk that will be used at atomic plane check time, register the quirks structure with the backend structure. This way, it can easily be grabbed where needed. Signed-off-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20180719080838.31598-1-paul.kocialkowski@bootlin.com --- drivers/gpu/drm/sun4i/sun4i_backend.c | 2 ++ drivers/gpu/drm/sun4i/sun4i_backend.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index d7950b52a1fd..e2f0d8fc3dde 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -876,6 +876,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, : SUN4I_BACKEND_MODCTL_OUT_LCD0)); } + backend->quirks = quirks; + return 0; err_disable_ram_clk: diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 4caee0392fa4..ad6609323f0e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -187,6 +187,8 @@ struct sun4i_backend { /* Protects against races in the frontend teardown */ spinlock_t frontend_lock; bool frontend_teardown; + + const struct sun4i_backend_quirks *quirks; }; static inline struct sun4i_backend * From dcf496a6a608733ef18a2f757b55111df9eadca6 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 19 Jul 2018 10:08:38 +0200 Subject: [PATCH 003/138] drm/sun4i: sun4i: Introduce a quirk for lowest plane alpha support Not all sunxi platforms with the first version of the Display Engine support an alpha component on the plane with the lowest z position (as in: lowest z-pos), that gets blended with the background color. In particular, the A13 is known to have this limitation. However, it was recently discovered that the A20 and A33 are capable of having alpha on their lowest plane. Thus, this introduces a specific quirk to indicate such support, per-platform. Since this was not tested on sun4i and sun6i platforms, a conservative approach is kept and this feature is not supported. Signed-off-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20180719080838.31598-2-paul.kocialkowski@bootlin.com --- drivers/gpu/drm/sun4i/sun4i_backend.c | 42 ++++++++++++++++++--------- drivers/gpu/drm/sun4i/sun4i_backend.h | 1 - 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index e2f0d8fc3dde..f6d8d0d4d5bf 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -34,6 +34,9 @@ struct sun4i_backend_quirks { /* backend <-> TCON muxing selection done in backend */ bool needs_output_muxing; + + /* alpha at the lowest z position is not always supported */ + bool supports_lowest_plane_alpha; }; static const u32 sunxi_rgb2yuv_coef[12] = { @@ -457,12 +460,14 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, struct drm_crtc_state *crtc_state) { struct drm_plane_state *plane_states[SUN4I_BACKEND_NUM_LAYERS] = { 0 }; + struct sun4i_backend *backend = engine_to_sun4i_backend(engine); struct drm_atomic_state *state = crtc_state->state; struct drm_device *drm = state->dev; struct drm_plane *plane; unsigned int num_planes = 0; unsigned int num_alpha_planes = 0; unsigned int num_frontend_planes = 0; + unsigned int num_alpha_planes_max = 1; unsigned int num_yuv_planes = 0; unsigned int current_pipe = 0; unsigned int i; @@ -526,33 +531,40 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, * the layer with the highest priority. * * The second step is the actual alpha blending, that takes - * the two pipes as input, and uses the eventual alpha + * the two pipes as input, and uses the potential alpha * component to do the transparency between the two. * - * This two steps scenario makes us unable to guarantee a + * This two-step scenario makes us unable to guarantee a * robust alpha blending between the 4 layers in all * situations, since this means that we need to have one layer * with alpha at the lowest position of our two pipes. * - * However, we cannot even do that, since the hardware has a - * bug where the lowest plane of the lowest pipe (pipe 0, - * priority 0), if it has any alpha, will discard the pixel - * entirely and just display the pixels in the background - * color (black by default). + * However, we cannot even do that on every platform, since + * the hardware has a bug where the lowest plane of the lowest + * pipe (pipe 0, priority 0), if it has any alpha, will + * discard the pixel data entirely and just display the pixels + * in the background color (black by default). * - * This means that we effectively have only three valid - * configurations with alpha, all of them with the alpha being - * on pipe1 with the lowest position, which can be 1, 2 or 3 - * depending on the number of planes and their zpos. + * This means that on the affected platforms, we effectively + * have only three valid configurations with alpha, all of + * them with the alpha being on pipe1 with the lowest + * position, which can be 1, 2 or 3 depending on the number of + * planes and their zpos. */ - if (num_alpha_planes > SUN4I_BACKEND_NUM_ALPHA_LAYERS) { + + /* For platforms that are not affected by the issue described above. */ + if (backend->quirks->supports_lowest_plane_alpha) + num_alpha_planes_max++; + + if (num_alpha_planes > num_alpha_planes_max) { DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n"); return -EINVAL; } /* We can't have an alpha plane at the lowest position */ - if (plane_states[0]->fb->format->has_alpha || - (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE)) + if (!backend->quirks->supports_lowest_plane_alpha && + (plane_states[0]->fb->format->has_alpha || + (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE))) return -EINVAL; for (i = 1; i < num_planes; i++) { @@ -937,9 +949,11 @@ static const struct sun4i_backend_quirks sun6i_backend_quirks = { static const struct sun4i_backend_quirks sun7i_backend_quirks = { .needs_output_muxing = true, + .supports_lowest_plane_alpha = true, }; static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { + .supports_lowest_plane_alpha = true, }; static const struct sun4i_backend_quirks sun9i_backend_quirks = { diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index ad6609323f0e..e3d4c6035eb2 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -167,7 +167,6 @@ #define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p))) #define SUN4I_BACKEND_NUM_LAYERS 4 -#define SUN4I_BACKEND_NUM_ALPHA_LAYERS 1 #define SUN4I_BACKEND_NUM_FRONTEND_LAYERS 1 #define SUN4I_BACKEND_NUM_YUV_PLANES 1 From 106b6c39c870c1f7628ba41693e571f419b1981d Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Wed, 18 Jul 2018 17:57:16 -0400 Subject: [PATCH 004/138] drm/print: Fix DRM_DEBUG_DP macro This isn't supposed to take dev as an argument, I guess no one noticed! Signed-off-by: Lyude Paul Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20180718215716.5784-1-lyude@redhat.com --- include/drm/drm_print.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index 767c90b654c5..fc08584a5101 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -310,7 +310,7 @@ void drm_err(const char *format, ...); #define DRM_DEV_DEBUG_DP(dev, fmt, ...) \ drm_dev_dbg(dev, DRM_UT_DP, fmt, ## __VA_ARGS__) -#define DRM_DEBUG_DP(dev, fmt, ...) \ +#define DRM_DEBUG_DP(fmt, ...) \ drm_dbg(DRM_UT_DP, fmt, ## __VA_ARGS__) #define _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, category, fmt, ...) \ From a31ac0b23cb8ae832d49162bd217b2727154889b Mon Sep 17 00:00:00 2001 From: Oleksandr Andrushchenko Date: Thu, 19 Jul 2018 12:37:13 +0300 Subject: [PATCH 005/138] drm: Replace NULL with error value in drm_prime_pages_to_sg Dan Carpenter has reported that there is the following static checker warning: drivers/gpu/drm/drm_prime.c:317 drm_gem_map_dma_buf() warn: 'sgt' can also be NULL 314 sgt = obj->dev->driver->gem_prime_get_sg_table(obj); 315 316 if (!IS_ERR(sgt)) { 317 if (!dma_map_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir, Problematic functions are xen_drm_front_gem_get_sg_table and drm_gem_cma_prime_get_sg_table. Fix those by replacing NULL with error value. Signed-off-by: Oleksandr Andrushchenko Reported-by: Dan Carpenter Reviewed-by: Dan Carpenter Link: https://patchwork.freedesktop.org/patch/msgid/20180719093713.3643-1-andr2000@gmail.com --- drivers/gpu/drm/drm_gem_cma_helper.c | 4 ++-- drivers/gpu/drm/xen/xen_drm_front_gem.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 80a5115c3846..1d2ced882b66 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -436,7 +436,7 @@ struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj) sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) - return NULL; + return ERR_PTR(-ENOMEM); ret = dma_get_sgtable(obj->dev->dev, sgt, cma_obj->vaddr, cma_obj->paddr, obj->size); @@ -447,7 +447,7 @@ struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj) out: kfree(sgt); - return NULL; + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table); diff --git a/drivers/gpu/drm/xen/xen_drm_front_gem.c b/drivers/gpu/drm/xen/xen_drm_front_gem.c index c85bfe7571cb..47ff019d3aef 100644 --- a/drivers/gpu/drm/xen/xen_drm_front_gem.c +++ b/drivers/gpu/drm/xen/xen_drm_front_gem.c @@ -179,7 +179,7 @@ struct sg_table *xen_drm_front_gem_get_sg_table(struct drm_gem_object *gem_obj) struct xen_gem_object *xen_obj = to_xen_gem_obj(gem_obj); if (!xen_obj->pages) - return NULL; + return ERR_PTR(-ENOMEM); return drm_prime_pages_to_sg(xen_obj->pages, xen_obj->num_pages); } From 574e0fbfc95e7fccc3b8321f25e1e4366ce72ad2 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 17 Jul 2018 13:09:27 +0200 Subject: [PATCH 006/138] drm/rockchip: Replace drm_dev_unref with drm_dev_put This patch unifies the naming of DRM functions for reference counting of struct drm_device. The resulting code is more aligned with the rest of the Linux kernel interfaces. Signed-off-by: Thomas Zimmermann Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20180717110927.30776-1-tzimmermann@suse.de --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index f814d37b1db2..9c846be8fc64 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -184,7 +184,7 @@ err_mode_config_cleanup: err_free: drm_dev->dev_private = NULL; dev_set_drvdata(dev, NULL); - drm_dev_unref(drm_dev); + drm_dev_put(drm_dev); return ret; } @@ -204,7 +204,7 @@ static void rockchip_drm_unbind(struct device *dev) drm_dev->dev_private = NULL; dev_set_drvdata(dev, NULL); - drm_dev_unref(drm_dev); + drm_dev_put(drm_dev); } static const struct file_operations rockchip_drm_driver_fops = { From 2aafafab5a9ad922ae13d04f5abca7942a499a5e Mon Sep 17 00:00:00 2001 From: Ayan Kumar Halder Date: Mon, 23 Jul 2018 09:57:00 +0100 Subject: [PATCH 007/138] drm/sun4i: Use (struct drm_format_info) fields to determine if a format is yuv and multi planar or not. We do not need sun4i_backend_format_is_packed_yuv422() / sun4i_backend_format_is_planar_yuv() to determine if the format is yuv multi planar or not. (struct drm_format_info *)->is_yuv tells if the format is yuv or not. And (struct drm_format_info *)->num_planes denotes the number of planes. This issue was identified during a review on a previous patch:- https://lists.freedesktop.org/archives/dri-devel/2018-July/183840.html Signed-off-by: Ayan Kumar halder Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/1532336220-3791-1-git-send-email-ayan.halder@arm.com --- drivers/gpu/drm/sun4i/sun4i_backend.c | 37 +++++---------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index f6d8d0d4d5bf..bf49c55b0f2c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -63,32 +63,6 @@ static const u32 sunxi_bt601_yuv2rgb_coef[12] = { 0x000004a7, 0x00000812, 0x00000000, 0x00002eb1, }; -static inline bool sun4i_backend_format_is_planar_yuv(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YUV444: - return true; - default: - return false; - } -} - -static inline bool sun4i_backend_format_is_packed_yuv422(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - return true; - - default: - return false; - } -} - static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine) { int i; @@ -218,7 +192,8 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, { struct drm_plane_state *state = plane->state; struct drm_framebuffer *fb = state->fb; - uint32_t format = fb->format->format; + const struct drm_format_info *format = fb->format; + const uint32_t fmt = format->format; u32 val = SUN4I_BACKEND_IYUVCTL_EN; int i; @@ -236,16 +211,16 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN); /* TODO: Add support for the multi-planar YUV formats */ - if (sun4i_backend_format_is_packed_yuv422(format)) + if (format->num_planes == 1) val |= SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422; else - DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", format); + DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", fmt); /* * Allwinner seems to list the pixel sequence from right to left, while * DRM lists it from left to right. */ - switch (format) { + switch (fmt) { case DRM_FORMAT_YUYV: val |= SUN4I_BACKEND_IYUVCTL_FBPS_VYUY; break; @@ -260,7 +235,7 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, break; default: DRM_DEBUG_DRIVER("Unsupported YUV pixel sequence (0x%x)\n", - format); + fmt); } regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVCTL_REG, val); From cf77d79b4e296d3c65aefa094232694a5977ea9a Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Tue, 10 Jul 2018 22:35:05 +0200 Subject: [PATCH 008/138] drm/sun4i: tcon: Add another way for matching mixers with tcon Till now, new way of matching engines with TCONs was reading their respective ids and match them by those ids. However, with introduction of TCON TOP, that might not be so straightforward anymore. - there might be more TCONs that engines (mixers) - TCON ids might have non-consecutive ids Workaround that by matching mixer id with TCON index from TCON list. For example, R40 has 2 mixers and 4 TCONs. Board designer can choose 2 outputs, which are connected to any of those 4 TCONs. As long as there are only 2 TCONs enabled in DT, using index in list as alternative id, will allow to match them with mixer 0 and 1. Reviewed-by: Chen-Yu Tsai Signed-off-by: Jernej Skrabec Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20180710203511.18454-13-jernej.skrabec@siol.net --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 51 ++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 9bfb5b967d38..8b3ca1e83f26 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -36,6 +36,7 @@ #include "sun4i_rgb.h" #include "sun4i_tcon.h" #include "sun6i_mipi_dsi.h" +#include "sun8i_tcon_top.h" #include "sunxi_engine.h" static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder) @@ -908,6 +909,36 @@ static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv, return ERR_PTR(-EINVAL); } +static bool sun4i_tcon_connected_to_tcon_top(struct device_node *node) +{ + struct device_node *remote; + bool ret = false; + + remote = of_graph_get_remote_node(node, 0, -1); + if (remote) { + ret = !!of_match_node(sun8i_tcon_top_of_table, remote); + of_node_put(remote); + } + + return ret; +} + +static int sun4i_tcon_get_index(struct sun4i_drv *drv) +{ + struct list_head *pos; + int size = 0; + + /* + * Because TCON is added to the list at the end of the probe + * (after this function is called), index of the current TCON + * will be same as current TCON list size. + */ + list_for_each(pos, &drv->tcon_list) + ++size; + + return size; +} + /* * On SoCs with the old display pipeline design (Display Engine 1.0), * we assumed the TCON was always tied to just one backend. However @@ -956,8 +987,24 @@ static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, * connections between the backend and TCON? */ if (of_get_child_count(port) > 1) { - /* Get our ID directly from an upstream endpoint */ - int id = sun4i_tcon_of_get_id_from_port(port); + int id; + + /* + * When pipeline has the same number of TCONs and engines which + * are represented by frontends/backends (DE1) or mixers (DE2), + * we match them by their respective IDs. However, if pipeline + * contains TCON TOP, chances are that there are either more + * TCONs than engines (R40) or TCONs with non-consecutive ids. + * (H6). In that case it's easier just use TCON index in list + * as an id. That means that on R40, any 2 TCONs can be enabled + * in DT out of 4 (there are 2 mixers). Due to the design of + * TCON TOP, remaining 2 TCONs can't be connected to anything + * anyway. + */ + if (sun4i_tcon_connected_to_tcon_top(node)) + id = sun4i_tcon_get_index(drv); + else + id = sun4i_tcon_of_get_id_from_port(port); /* Get our engine by matching our ID */ engine = sun4i_tcon_get_engine_by_id(drv, id); From 0305189afb323c1bad2be7fe713d556a9b708003 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Tue, 10 Jul 2018 22:35:06 +0200 Subject: [PATCH 009/138] drm/sun4i: tcon: Add support for R40 TCON R40 TV TCON is basically the same as on A83T. However, it needs special handling, because it has to set up TCON TOP muxes at runtime. Reviewed-by: Chen-Yu Tsai Signed-off-by: Jernej Skrabec Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20180710203511.18454-14-jernej.skrabec@siol.net --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 8b3ca1e83f26..0cebb2db5b99 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -1319,6 +1319,40 @@ static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon, return 0; } +static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon, + const struct drm_encoder *encoder) +{ + struct device_node *port, *remote; + struct platform_device *pdev; + int id, ret; + + /* find TCON TOP platform device and TCON id */ + + port = of_graph_get_port_by_id(tcon->dev->of_node, 0); + if (!port) + return -EINVAL; + + id = sun4i_tcon_of_get_id_from_port(port); + of_node_put(port); + + remote = of_graph_get_remote_node(tcon->dev->of_node, 0, -1); + if (!remote) + return -EINVAL; + + pdev = of_find_device_by_node(remote); + of_node_put(remote); + if (!pdev) + return -EINVAL; + + if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) { + ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id); + if (ret) + return ret; + } + + return sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id); +} + static const struct sun4i_tcon_quirks sun4i_a10_quirks = { .has_channel_0 = true, .has_channel_1 = true, @@ -1366,6 +1400,11 @@ static const struct sun4i_tcon_quirks sun8i_a83t_tv_quirks = { .has_channel_1 = true, }; +static const struct sun4i_tcon_quirks sun8i_r40_tv_quirks = { + .has_channel_1 = true, + .set_mux = sun8i_r40_tcon_tv_set_mux, +}; + static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { .has_channel_0 = true, }; @@ -1390,6 +1429,7 @@ const struct of_device_id sun4i_tcon_of_table[] = { { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, { .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks }, { .compatible = "allwinner,sun8i-a83t-tcon-tv", .data = &sun8i_a83t_tv_quirks }, + { .compatible = "allwinner,sun8i-r40-tcon-tv", .data = &sun8i_r40_tv_quirks }, { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, { .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = &sun9i_a80_tcon_lcd_quirks }, { .compatible = "allwinner,sun9i-a80-tcon-tv", .data = &sun9i_a80_tcon_tv_quirks }, From 9b32f8951f0f3f8e07c0682d853f5aaf77603aef Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 9 Jul 2018 14:37:50 +0530 Subject: [PATCH 010/138] dt-bindings: mipi-dsi: Add info about peripherals with non-DSI control bus Add a section that describes dt-bindings for peripherals that support MIPI DSI, but have a different bus as the primary control bus, or no control bus at all. Add an example for a peripheral with a non-DSI control bus. Reviewed-by: Rob Herring Reviewed-by: Boris Brezillon Reviewed-by: Philippe Cornu Reviewed-by: Sean Paul Signed-off-by: Archit Taneja Link: https://patchwork.freedesktop.org/patch/msgid/20180709090751.10221-2-architt@codeaurora.org --- .../bindings/display/mipi-dsi-bus.txt | 71 +++++++++++++++++-- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt b/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt index 973c27273772..b7c5bf47403c 100644 --- a/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt +++ b/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt @@ -16,7 +16,7 @@ The following assumes that only a single peripheral is connected to a DSI host. Experience shows that this is true for the large majority of setups. DSI host --------- +======== In addition to the standard properties and those defined by the parent bus of a DSI host, the following properties apply to a node representing a DSI host. @@ -30,11 +30,16 @@ Required properties: different value here. See below. DSI peripheral --------------- +============== -Peripherals are represented as child nodes of the DSI host's node. Properties -described here apply to all DSI peripherals, but individual bindings may want -to define additional, device-specific properties. +Peripherals with DSI as control bus, or no control bus +------------------------------------------------------ + +Peripherals with the DSI bus as the primary control bus, or peripherals with +no control bus but use the DSI bus to transmit pixel data are represented +as child nodes of the DSI host's node. Properties described here apply to all +DSI peripherals, but individual bindings may want to define additional, +device-specific properties. Required properties: - reg: The virtual channel number of a DSI peripheral. Must be in the range @@ -49,9 +54,25 @@ case two alternative representations can be chosen: property is the number of the first virtual channel and the second cell is the number of consecutive virtual channels. -Example -------- +Peripherals with a different control bus +---------------------------------------- +There are peripherals that have I2C/SPI (or some other non-DSI bus) as the +primary control bus, but are also connected to a DSI bus (mostly for the data +path). Connections between such peripherals and a DSI host can be represented +using the graph bindings [1], [2]. + +[1] Documentation/devicetree/bindings/graph.txt +[2] Documentation/devicetree/bindings/media/video-interfaces.txt + +Examples +======== +- (1), (2) and (3) are examples of a DSI host and peripheral on the DSI bus + with different virtual channel configurations. +- (4) is an example of a peripheral on a I2C control bus connected to a + DSI host using of-graph bindings. + +1) dsi-host { ... @@ -67,6 +88,7 @@ Example ... }; +2) dsi-host { ... @@ -82,6 +104,7 @@ Example ... }; +3) dsi-host { ... @@ -96,3 +119,37 @@ Example ... }; + +4) + i2c-host { + ... + + dsi-bridge@35 { + compatible = "..."; + reg = <0x35>; + + ports { + ... + + port { + bridge_mipi_in: endpoint { + remote-endpoint = <&host_mipi_out>; + }; + }; + }; + }; + }; + + dsi-host { + ... + + ports { + ... + + port { + host_mipi_out: endpoint { + remote-endpoint = <&bridge_mipi_in>; + }; + }; + }; + }; From 5e03f02cb58d1cee5d98f323da9abe2e7073b2b4 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 9 Jul 2018 14:37:51 +0530 Subject: [PATCH 011/138] dt-bindings: mipi-dsi: Add dual-channel DSI related info Add binding info for peripherals that support dual-channel DSI. Add corresponding optional bindings for DSI host controllers that may be configured in this mode. Add an example of an I2C controlled device operating in dual-channel DSI mode. Reviewed-by: Rob Herring Reviewed-by: Philippe Cornu Reviewed-by: Sean Paul Reviewed-by: Heiko Stuebner Signed-off-by: Archit Taneja Link: https://patchwork.freedesktop.org/patch/msgid/20180709090751.10221-3-architt@codeaurora.org --- .../bindings/display/mipi-dsi-bus.txt | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt b/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt index b7c5bf47403c..a336599f6c03 100644 --- a/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt +++ b/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt @@ -29,6 +29,13 @@ Required properties: - #size-cells: Should be 0. There are cases where it makes sense to use a different value here. See below. +Optional properties: +- clock-master: boolean. Should be enabled if the host is being used in + conjunction with another DSI host to drive the same peripheral. Hardware + supporting such a configuration generally requires the data on both the busses + to be driven by the same clock. Only the DSI host instance controlling this + clock should contain this property. + DSI peripheral ============== @@ -62,6 +69,16 @@ primary control bus, but are also connected to a DSI bus (mostly for the data path). Connections between such peripherals and a DSI host can be represented using the graph bindings [1], [2]. +Peripherals that support dual channel DSI +----------------------------------------- + +Peripherals with higher bandwidth requirements can be connected to 2 DSI +busses. Each DSI bus/channel drives some portion of the pixel data (generally +left/right half of each line of the display, or even/odd lines of the display). +The graph bindings should be used to represent the multiple DSI busses that are +connected to this peripheral. Each DSI host's output endpoint can be linked to +an input endpoint of the DSI peripheral. + [1] Documentation/devicetree/bindings/graph.txt [2] Documentation/devicetree/bindings/media/video-interfaces.txt @@ -71,6 +88,8 @@ Examples with different virtual channel configurations. - (4) is an example of a peripheral on a I2C control bus connected to a DSI host using of-graph bindings. +- (5) is an example of 2 DSI hosts driving a dual-channel DSI peripheral, + which uses I2C as its primary control bus. 1) dsi-host { @@ -153,3 +172,64 @@ Examples }; }; }; + +5) + i2c-host { + dsi-bridge@35 { + compatible = "..."; + reg = <0x35>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi0_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + dsi1_in: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; + }; + }; + + dsi0-host { + ... + + /* + * this DSI instance drives the clock for both the host + * controllers + */ + clock-master; + + ports { + ... + + port { + dsi0_out: endpoint { + remote-endpoint = <&dsi0_in>; + }; + }; + }; + }; + + dsi1-host { + ... + + ports { + ... + + port { + dsi1_out: endpoint { + remote-endpoint = <&dsi1_in>; + }; + }; + }; + }; From e3896e6dddf0b821dc3214b623a922aa7ee2c2d5 Mon Sep 17 00:00:00 2001 From: Sandeep Panda Date: Mon, 16 Jul 2018 14:13:30 +0530 Subject: [PATCH 012/138] dt-bindings: drm/bridge: Document sn65dsi86 bridge bindings Document the bindings used for the sn65dsi86 DSI to eDP bridge. Changes in v1: - Rephrase the dt-binding descriptions to be more inline with existing bindings (Andrzej Hajda). - Add missing dt-binding that are parsed by corresponding driver (Andrzej Hajda). Changes in v2: - Remove edp panel specific dt-binding entries. Only keep bridge specific entries (Sean Paul). - Remove custom-modes dt entry since its usage is removed from driver also (Sean Paul). - Remove is-pluggable dt entry since this will not be needed anymore (Sean Paul). Changes in v3: - Remove irq-gpio dt entry and instead populate is an interrupt property (Rob Herring). Changes in v4: - Add link to bridge chip datasheet (Stephen Boyd) - Add vpll and vcc regulator supply bindings (Stephen Boyd) - Add ref clk optional dt binding (Stephen Boyd) - Add gpio-controller optional dt binding (Stephen Boyd) Changes in v5: - Use clock property to specify the input refclk (Stephen Boyd). - Update gpio cell and pwm cell numbers (Stephen Boyd). Changes in v6: - Add property to mention the lane mapping scheme and polarity inversion (Stephen Boyd). Changes in v7: - Detail description of lane mapping scheme dt property (Andrzej Hajda/ Rob Herring). - Removed HDP gpio binding, since the bridge uses IRQ signal to determine HPD, and IRQ property is already documented in binding. Changes in v8: - Removed unnecessary explanation of lane mapping and polarity dt property, since these are already explained in media/video-interface dt binidng (Rob Herring). Changes in v9: - Avoid putting re-definition of lane mapping and polarity dt binding (Rob Herring). Changes in v10: - Use interrupts-extended property instead of interrupts to specify interrupt line (Andrzej Hajda). - Move data-lanes and lane-polarity property example to proper place (Andrzej Hajda). Changes in v11: - Add a property for suspend gpio function of GPIO1 pin on bridge chip (Stephen Boyd). Changes in v12: - Remove binding for dedicated DDC line (Andrzej Hajda). Signed-off-by: Sandeep Panda Reviewed-by: Stephen Boyd Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/20180716084330.26698-3-spanda@codeaurora.org --- .../bindings/display/bridge/ti,sn65dsi86.txt | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.txt diff --git a/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.txt b/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.txt new file mode 100644 index 000000000000..0a3fbb53a16e --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.txt @@ -0,0 +1,87 @@ +SN65DSI86 DSI to eDP bridge chip +-------------------------------- + +This is the binding for Texas Instruments SN65DSI86 bridge. +http://www.ti.com/general/docs/lit/getliterature.tsp?genericPartNumber=sn65dsi86&fileType=pdf + +Required properties: +- compatible: Must be "ti,sn65dsi86" +- reg: i2c address of the chip, 0x2d as per datasheet +- enable-gpios: gpio specification for bridge_en pin (active high) + +- vccio-supply: A 1.8V supply that powers up the digital IOs. +- vpll-supply: A 1.8V supply that powers up the displayport PLL. +- vcca-supply: A 1.2V supply that powers up the analog circuits. +- vcc-supply: A 1.2V supply that powers up the digital core. + +Optional properties: +- interrupts-extended: Specifier for the SN65DSI86 interrupt line. + +- gpio-controller: Marks the device has a GPIO controller. +- #gpio-cells : Should be two. The first cell is the pin number and + the second cell is used to specify flags. + See ../../gpio/gpio.txt for more information. +- #pwm-cells : Should be one. See ../../pwm/pwm.txt for description of + the cell formats. + +- clock-names: should be "refclk" +- clocks: Specification for input reference clock. The reference + clock rate must be 12 MHz, 19.2 MHz, 26 MHz, 27 MHz or 38.4 MHz. + +- data-lanes: See ../../media/video-interface.txt +- lane-polarities: See ../../media/video-interface.txt + +- suspend-gpios: specification for GPIO1 pin on bridge (active low) + +Required nodes: +This device has two video ports. Their connections are modelled using the +OF graph bindings specified in Documentation/devicetree/bindings/graph.txt. + +- Video port 0 for DSI input +- Video port 1 for eDP output + +Example +------- + +edp-bridge@2d { + compatible = "ti,sn65dsi86"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x2d>; + + enable-gpios = <&msmgpio 33 GPIO_ACTIVE_HIGH>; + suspend-gpios = <&msmgpio 34 GPIO_ACTIVE_LOW>; + + interrupts-extended = <&gpio3 4 IRQ_TYPE_EDGE_FALLING>; + + vccio-supply = <&pm8916_l17>; + vcca-supply = <&pm8916_l6>; + vpll-supply = <&pm8916_l17>; + vcc-supply = <&pm8916_l6>; + + clock-names = "refclk"; + clocks = <&input_refclk>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + edp_bridge_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + + port@1 { + reg = <1>; + + edp_bridge_out: endpoint { + data-lanes = <2 1 3 0>; + lane-polarities = <0 1 0 1>; + remote-endpoint = <&edp_panel_in>; + }; + }; + }; +} From a095f15c00e2788bd83f4e9aa64ef303cae8e649 Mon Sep 17 00:00:00 2001 From: Sandeep Panda Date: Fri, 20 Jul 2018 12:24:02 +0530 Subject: [PATCH 013/138] drm/bridge: add support for sn65dsi86 bridge driver Add support for TI's sn65dsi86 dsi2edp bridge chip. The chip converts DSI transmitted signal to eDP signal, which is fed to the connected eDP panel. This chip can be controlled via either i2c interface or dsi interface. Currently in driver all the control registers are being accessed through i2c interface only. Also as of now HPD support has not been added to bridge chip driver. Changes in v1: - Split the dt-bindings and the driver support into separate patches (Andrzej Hajda). - Use of gpiod APIs to parse and configure gpios instead of obsolete ones (Andrzej Hajda). - Use macros to define the register offsets (Andrzej Hajda). Changes in v2: - Separate out edp panel specific HW resource handling from bridge driver and create a separate edp panel drivers to handle panel specific mode information and HW resources (Sean Paul). - Replace pr_* APIs to DRM_* APIs to log error or debug information (Sean Paul). - Remove some of the unnecessary structure/variable from driver (Sean Paul). - Rename the function and structure prefix "sn65dsi86" to "ti_sn_bridge" (Sean Paul / Rob Herring). - Remove most of the hard-coding and modified the bridge init sequence based on current mode (Sean Paul). - Remove the existing function to retrieve the EDID data and implemented this as an i2c_adapter and use drm_get_edid() (Sean Paul). - Remove the dummy irq handler implementation, will add back the proper irq handling later (Sean Paul). - Capture the required enable gpios in a single array based on dt entry instead of having individual descriptor for each gpio (Sean Paul). Changes in v3: - Remove usage of irq_gpio and replace it as "interrupts" property (Rob Herring). - Remove the unnecessary header file inclusions (Sean Paul). - Rearrange the header files in alphabetical order (Sean Paul). - Use regmap interface to perform i2c transactions. - Update Copyright/License field and address other review comments (Jordan Crouse). Changes in v4: - Update License/Copyright (Sean Paul). - Add Kconfig and Makefile changes (Sean Paul). - Drop i2c gpio handling from this bridge driver, since i2c sda/scl gpios will be handled by i2c master. - Update required supplies names. - Remove unnecessary goto statements (Sean Paul). - Add mutex lock to power_ctrl API to avoid race conditions (Sean Paul). - Add support to parse reference clk frequency from dt(optional). - Update the bridge chip enable/disable sequence. Changes in v5: - Fixed Kbuild test service reported warnings. Changes in v6: - Use PM runtime based ref-counting instead of local ref_count mechanism (Stephen Boyd). - Clean up some debug logs and indentations (Sean Paul). - Simplify dp rate calculation (Sean Paul). - Add support to configure refclk based on input REFCLK pin or DACP/N pin (Stephen Boyd). Changes in v7: - Use static supply entries instead of dynamic allocation (Andrzej Hajda). - Defer bridge driver probe if panel is not probed (Andrzej Hajda). - Update of_graph APIs for correct node reference management. (Andrzej Hajda). - Remove local display_mode object (Andrzej Hajda). - Remove version id check function from driver. Changes in v8: - Move dsi register/attach function to bridge driver probe (Andrzej Hajda). - Introduce a new helper function to write 16bit words into consecutive registers (Andrzej Hajda). - Remove unnecessary macros (Andrzej Hajda). Changes in v9: - Remove dsi register/attach from bridge probe, since dsi dev register completion also waits for any panel or bridge to get added. This creates deadlock situation when bridge driver calls dsi dev register and attach before bridge add, in its probe function. - Fix issues faced during testing of bridge driver on actual HW. - Remove unnecessary initializations (Stephen Boyd). - Use local refclk lut size instead of global macro (Sean Paul). Changes in v10: - Use refclk to determine if continuous dsi clock is needed or not. Changes in v11: - Read DPPLL_SRC register to determine continuous clock instead of using refclk handle (Stephen Boyd). Changes in v12: - Explain in comment as in why dsi dev registration is done in bridge_attach (Andrzej Hajda). - Move HPD disable to bridge_pre_enable (Andrzej Hajda). - Make panel/DDC exclusive until HPD support is added (Andrzej Hajda). Changes in v13: - eDP panels report EDID via DP-AUX channel, so remove support for dedicated DDC line (Andrzej Hajda). Changes in v14: - Remove unnecessary drm_panel checks (Andrzej Hajda). Signed-off-by: Sandeep Panda Reviewed-by: Andrzej Hajda Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1532069642-21392-1-git-send-email-spanda@codeaurora.org --- drivers/gpu/drm/bridge/Kconfig | 9 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ti-sn65dsi86.c | 664 ++++++++++++++++++++++++++ 3 files changed, 674 insertions(+) create mode 100644 drivers/gpu/drm/bridge/ti-sn65dsi86.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index bf6cad6c9178..eeb9c489a817 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -128,6 +128,15 @@ config DRM_TI_TFP410 ---help--- Texas Instruments TFP410 DVI/HDMI Transmitter driver +config DRM_TI_SN65DSI86 + tristate "TI SN65DSI86 DSI to eDP bridge" + depends on OF + select DRM_KMS_HELPER + select REGMAP_I2C + select DRM_PANEL + help + Texas Instruments SN65DSI86 DSI to eDP Bridge driver + source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/adv7511/Kconfig" diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 35f88d48ec20..fd90ac059ceb 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ +obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o obj-y += synopsys/ diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c new file mode 100644 index 000000000000..5b93ef518861 --- /dev/null +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -0,0 +1,664 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Link Training specific registers */ +#define SN_DEVICE_REV_REG 0x08 +#define SN_HPD_DISABLE_REG 0x5C +#define SN_DPPLL_SRC_REG 0x0A +#define SN_DSI_LANES_REG 0x10 +#define SN_DSIA_CLK_FREQ_REG 0x12 +#define SN_ENH_FRAME_REG 0x5A +#define SN_SSC_CONFIG_REG 0x93 +#define SN_DATARATE_CONFIG_REG 0x94 +#define SN_PLL_ENABLE_REG 0x0D +#define SN_SCRAMBLE_CONFIG_REG 0x95 +#define SN_AUX_WDATA0_REG 0x64 +#define SN_AUX_ADDR_19_16_REG 0x74 +#define SN_AUX_ADDR_15_8_REG 0x75 +#define SN_AUX_ADDR_7_0_REG 0x76 +#define SN_AUX_LENGTH_REG 0x77 +#define SN_AUX_CMD_REG 0x78 +#define SN_ML_TX_MODE_REG 0x96 +/* video config specific registers */ +#define SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG 0x20 +#define SN_CHA_VERTICAL_DISPLAY_SIZE_LOW_REG 0x24 +#define SN_CHA_HSYNC_PULSE_WIDTH_LOW_REG 0x2C +#define SN_CHA_HSYNC_PULSE_WIDTH_HIGH_REG 0x2D +#define SN_CHA_VSYNC_PULSE_WIDTH_LOW_REG 0x30 +#define SN_CHA_VSYNC_PULSE_WIDTH_HIGH_REG 0x31 +#define SN_CHA_HORIZONTAL_BACK_PORCH_REG 0x34 +#define SN_CHA_VERTICAL_BACK_PORCH_REG 0x36 +#define SN_CHA_HORIZONTAL_FRONT_PORCH_REG 0x38 +#define SN_CHA_VERTICAL_FRONT_PORCH_REG 0x3A +#define SN_DATA_FORMAT_REG 0x5B + +#define MIN_DSI_CLK_FREQ_MHZ 40 + +/* fudge factor required to account for 8b/10b encoding */ +#define DP_CLK_FUDGE_NUM 10 +#define DP_CLK_FUDGE_DEN 8 + +#define DPPLL_CLK_SRC_REFCLK 0 +#define DPPLL_CLK_SRC_DSICLK 1 + +#define SN_REFCLK_FREQ_OFFSET 1 +#define SN_DSIA_LANE_OFFSET 3 +#define SN_DP_LANE_OFFSET 4 +#define SN_DP_DATA_RATE_OFFSET 5 +#define SN_SYNC_POLARITY_OFFSET 7 + +#define SN_ENABLE_VID_STREAM_BIT BIT(3) +#define SN_REFCLK_FREQ_BITS GENMASK(3, 1) +#define SN_DSIA_NUM_LANES_BITS GENMASK(4, 3) +#define SN_DP_NUM_LANES_BITS GENMASK(5, 4) +#define SN_DP_DATA_RATE_BITS GENMASK(7, 5) +#define SN_HPD_DISABLE_BIT BIT(0) + +#define SN_REGULATOR_SUPPLY_NUM 4 + +struct ti_sn_bridge { + struct device *dev; + struct regmap *regmap; + struct drm_bridge bridge; + struct drm_connector connector; + struct device_node *host_node; + struct mipi_dsi_device *dsi; + struct clk *refclk; + struct drm_panel *panel; + struct gpio_desc *enable_gpio; + struct regulator_bulk_data supplies[SN_REGULATOR_SUPPLY_NUM]; +}; + +static const struct regmap_range ti_sn_bridge_volatile_ranges[] = { + { .range_min = 0, .range_max = 0xFF }, +}; + +static const struct regmap_access_table ti_sn_bridge_volatile_table = { + .yes_ranges = ti_sn_bridge_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(ti_sn_bridge_volatile_ranges), +}; + +static const struct regmap_config ti_sn_bridge_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &ti_sn_bridge_volatile_table, + .cache_type = REGCACHE_NONE, +}; + +static void ti_sn_bridge_write_u16(struct ti_sn_bridge *pdata, + unsigned int reg, u16 val) +{ + regmap_write(pdata->regmap, reg, val & 0xFF); + regmap_write(pdata->regmap, reg + 1, val >> 8); +} + +static int __maybe_unused ti_sn_bridge_resume(struct device *dev) +{ + struct ti_sn_bridge *pdata = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(SN_REGULATOR_SUPPLY_NUM, pdata->supplies); + if (ret) { + DRM_ERROR("failed to enable supplies %d\n", ret); + return ret; + } + + gpiod_set_value(pdata->enable_gpio, 1); + + return ret; +} + +static int __maybe_unused ti_sn_bridge_suspend(struct device *dev) +{ + struct ti_sn_bridge *pdata = dev_get_drvdata(dev); + int ret; + + gpiod_set_value(pdata->enable_gpio, 0); + + ret = regulator_bulk_disable(SN_REGULATOR_SUPPLY_NUM, pdata->supplies); + if (ret) + DRM_ERROR("failed to disable supplies %d\n", ret); + + return ret; +} + +static const struct dev_pm_ops ti_sn_bridge_pm_ops = { + SET_RUNTIME_PM_OPS(ti_sn_bridge_suspend, ti_sn_bridge_resume, NULL) +}; + +/* Connector funcs */ +static struct ti_sn_bridge * +connector_to_ti_sn_bridge(struct drm_connector *connector) +{ + return container_of(connector, struct ti_sn_bridge, connector); +} + +static int ti_sn_bridge_connector_get_modes(struct drm_connector *connector) +{ + struct ti_sn_bridge *pdata = connector_to_ti_sn_bridge(connector); + + return drm_panel_get_modes(pdata->panel); +} + +static enum drm_mode_status +ti_sn_bridge_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* maximum supported resolution is 4K at 60 fps */ + if (mode->clock > 594000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static struct drm_connector_helper_funcs ti_sn_bridge_connector_helper_funcs = { + .get_modes = ti_sn_bridge_connector_get_modes, + .mode_valid = ti_sn_bridge_connector_mode_valid, +}; + +static enum drm_connector_status +ti_sn_bridge_connector_detect(struct drm_connector *connector, bool force) +{ + /** + * TODO: Currently if drm_panel is present, then always + * return the status as connected. Need to add support to detect + * device state for hot pluggable scenarios. + */ + return connector_status_connected; +} + +static const struct drm_connector_funcs ti_sn_bridge_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = ti_sn_bridge_connector_detect, + .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 struct ti_sn_bridge *bridge_to_ti_sn_bridge(struct drm_bridge *bridge) +{ + return container_of(bridge, struct ti_sn_bridge, bridge); +} + +static int ti_sn_bridge_parse_regulators(struct ti_sn_bridge *pdata) +{ + unsigned int i; + const char * const ti_sn_bridge_supply_names[] = { + "vcca", "vcc", "vccio", "vpll", + }; + + for (i = 0; i < SN_REGULATOR_SUPPLY_NUM; i++) + pdata->supplies[i].supply = ti_sn_bridge_supply_names[i]; + + return devm_regulator_bulk_get(pdata->dev, SN_REGULATOR_SUPPLY_NUM, + pdata->supplies); +} + +static int ti_sn_bridge_attach(struct drm_bridge *bridge) +{ + int ret, val; + struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); + struct mipi_dsi_host *host; + struct mipi_dsi_device *dsi; + const struct mipi_dsi_device_info info = { .type = "ti_sn_bridge", + .channel = 0, + .node = NULL, + }; + + ret = drm_connector_init(bridge->dev, &pdata->connector, + &ti_sn_bridge_connector_funcs, + DRM_MODE_CONNECTOR_eDP); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + + drm_connector_helper_add(&pdata->connector, + &ti_sn_bridge_connector_helper_funcs); + drm_mode_connector_attach_encoder(&pdata->connector, bridge->encoder); + + /* + * TODO: ideally finding host resource and dsi dev registration needs + * to be done in bridge probe. But some existing DSI host drivers will + * wait for any of the drm_bridge/drm_panel to get added to the global + * bridge/panel list, before completing their probe. So if we do the + * dsi dev registration part in bridge probe, before populating in + * the global bridge list, then it will cause deadlock as dsi host probe + * will never complete, neither our bridge probe. So keeping it here + * will satisfy most of the existing host drivers. Once the host driver + * is fixed we can move the below code to bridge probe safely. + */ + host = of_find_mipi_dsi_host_by_node(pdata->host_node); + if (!host) { + DRM_ERROR("failed to find dsi host\n"); + ret = -ENODEV; + goto err_dsi_host; + } + + dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dsi)) { + DRM_ERROR("failed to create dsi device\n"); + ret = PTR_ERR(dsi); + goto err_dsi_host; + } + + /* TODO: setting to 4 lanes always for now */ + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE; + + /* check if continuous dsi clock is required or not */ + pm_runtime_get_sync(pdata->dev); + regmap_read(pdata->regmap, SN_DPPLL_SRC_REG, &val); + pm_runtime_put(pdata->dev); + if (!(val & DPPLL_CLK_SRC_DSICLK)) + dsi->mode_flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + DRM_ERROR("failed to attach dsi to host\n"); + goto err_dsi_attach; + } + pdata->dsi = dsi; + + /* attach panel to bridge */ + drm_panel_attach(pdata->panel, &pdata->connector); + + return 0; + +err_dsi_attach: + mipi_dsi_device_unregister(dsi); +err_dsi_host: + drm_connector_cleanup(&pdata->connector); + return ret; +} + +static void ti_sn_bridge_disable(struct drm_bridge *bridge) +{ + struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); + + drm_panel_disable(pdata->panel); + + /* disable video stream */ + regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, + SN_ENABLE_VID_STREAM_BIT, 0); + /* semi auto link training mode OFF */ + regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0); + /* disable DP PLL */ + regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 0); + + drm_panel_unprepare(pdata->panel); +} + +static u32 ti_sn_bridge_get_dsi_freq(struct ti_sn_bridge *pdata) +{ + u32 bit_rate_khz, clk_freq_khz; + struct drm_display_mode *mode = + &pdata->bridge.encoder->crtc->state->adjusted_mode; + + bit_rate_khz = mode->clock * + mipi_dsi_pixel_format_to_bpp(pdata->dsi->format); + clk_freq_khz = bit_rate_khz / (pdata->dsi->lanes * 2); + + return clk_freq_khz; +} + +/* clk frequencies supported by bridge in Hz in case derived from REFCLK pin */ +static const u32 ti_sn_bridge_refclk_lut[] = { + 12000000, + 19200000, + 26000000, + 27000000, + 38400000, +}; + +/* clk frequencies supported by bridge in Hz in case derived from DACP/N pin */ +static const u32 ti_sn_bridge_dsiclk_lut[] = { + 468000000, + 384000000, + 416000000, + 486000000, + 460800000, +}; + +static void ti_sn_bridge_set_refclk_freq(struct ti_sn_bridge *pdata) +{ + int i; + u32 refclk_rate; + const u32 *refclk_lut; + size_t refclk_lut_size; + + if (pdata->refclk) { + refclk_rate = clk_get_rate(pdata->refclk); + refclk_lut = ti_sn_bridge_refclk_lut; + refclk_lut_size = ARRAY_SIZE(ti_sn_bridge_refclk_lut); + clk_prepare_enable(pdata->refclk); + } else { + refclk_rate = ti_sn_bridge_get_dsi_freq(pdata) * 1000; + refclk_lut = ti_sn_bridge_dsiclk_lut; + refclk_lut_size = ARRAY_SIZE(ti_sn_bridge_dsiclk_lut); + } + + /* for i equals to refclk_lut_size means default frequency */ + for (i = 0; i < refclk_lut_size; i++) + if (refclk_lut[i] == refclk_rate) + break; + + regmap_update_bits(pdata->regmap, SN_DPPLL_SRC_REG, + SN_REFCLK_FREQ_BITS, i << SN_REFCLK_FREQ_OFFSET); +} + +/** + * LUT index corresponds to register value and + * LUT values corresponds to dp data rate supported + * by the bridge in Mbps unit. + */ +static const unsigned int ti_sn_bridge_dp_rate_lut[] = { + 0, 1620, 2160, 2430, 2700, 3240, 4320, 5400 +}; + +static void ti_sn_bridge_set_dsi_dp_rate(struct ti_sn_bridge *pdata) +{ + unsigned int bit_rate_mhz, clk_freq_mhz, dp_rate_mhz; + unsigned int val, i; + struct drm_display_mode *mode = + &pdata->bridge.encoder->crtc->state->adjusted_mode; + + /* set DSIA clk frequency */ + bit_rate_mhz = (mode->clock / 1000) * + mipi_dsi_pixel_format_to_bpp(pdata->dsi->format); + clk_freq_mhz = bit_rate_mhz / (pdata->dsi->lanes * 2); + + /* for each increment in val, frequency increases by 5MHz */ + val = (MIN_DSI_CLK_FREQ_MHZ / 5) + + (((clk_freq_mhz - MIN_DSI_CLK_FREQ_MHZ) / 5) & 0xFF); + regmap_write(pdata->regmap, SN_DSIA_CLK_FREQ_REG, val); + + /* set DP data rate */ + dp_rate_mhz = ((bit_rate_mhz / pdata->dsi->lanes) * DP_CLK_FUDGE_NUM) / + DP_CLK_FUDGE_DEN; + for (i = 0; i < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1; i++) + if (ti_sn_bridge_dp_rate_lut[i] > dp_rate_mhz) + break; + + regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG, + SN_DP_DATA_RATE_BITS, i << SN_DP_DATA_RATE_OFFSET); +} + +static void ti_sn_bridge_set_video_timings(struct ti_sn_bridge *pdata) +{ + struct drm_display_mode *mode = + &pdata->bridge.encoder->crtc->state->adjusted_mode; + u8 hsync_polarity = 0, vsync_polarity = 0; + + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + hsync_polarity = BIT(SN_SYNC_POLARITY_OFFSET); + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + vsync_polarity = BIT(SN_SYNC_POLARITY_OFFSET); + + ti_sn_bridge_write_u16(pdata, SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG, + mode->hdisplay); + ti_sn_bridge_write_u16(pdata, SN_CHA_VERTICAL_DISPLAY_SIZE_LOW_REG, + mode->vdisplay); + regmap_write(pdata->regmap, SN_CHA_HSYNC_PULSE_WIDTH_LOW_REG, + (mode->hsync_end - mode->hsync_start) & 0xFF); + regmap_write(pdata->regmap, SN_CHA_HSYNC_PULSE_WIDTH_HIGH_REG, + (((mode->hsync_end - mode->hsync_start) >> 8) & 0x7F) | + hsync_polarity); + regmap_write(pdata->regmap, SN_CHA_VSYNC_PULSE_WIDTH_LOW_REG, + (mode->vsync_end - mode->vsync_start) & 0xFF); + regmap_write(pdata->regmap, SN_CHA_VSYNC_PULSE_WIDTH_HIGH_REG, + (((mode->vsync_end - mode->vsync_start) >> 8) & 0x7F) | + vsync_polarity); + + regmap_write(pdata->regmap, SN_CHA_HORIZONTAL_BACK_PORCH_REG, + (mode->htotal - mode->hsync_end) & 0xFF); + regmap_write(pdata->regmap, SN_CHA_VERTICAL_BACK_PORCH_REG, + (mode->vtotal - mode->vsync_end) & 0xFF); + + regmap_write(pdata->regmap, SN_CHA_HORIZONTAL_FRONT_PORCH_REG, + (mode->hsync_start - mode->hdisplay) & 0xFF); + regmap_write(pdata->regmap, SN_CHA_VERTICAL_FRONT_PORCH_REG, + (mode->vsync_start - mode->vdisplay) & 0xFF); + + usleep_range(10000, 10500); /* 10ms delay recommended by spec */ +} + +static void ti_sn_bridge_enable(struct drm_bridge *bridge) +{ + struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); + unsigned int val; + + drm_panel_prepare(pdata->panel); + + /* DSI_A lane config */ + val = (4 - pdata->dsi->lanes) << SN_DSIA_LANE_OFFSET; + regmap_update_bits(pdata->regmap, SN_DSI_LANES_REG, + SN_DSIA_NUM_LANES_BITS, val); + + /* DP lane config */ + val = (pdata->dsi->lanes - 1) << SN_DP_LANE_OFFSET; + regmap_update_bits(pdata->regmap, SN_SSC_CONFIG_REG, + SN_DP_NUM_LANES_BITS, val); + + /* set dsi/dp clk frequency value */ + ti_sn_bridge_set_dsi_dp_rate(pdata); + + /* enable DP PLL */ + regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 1); + usleep_range(10000, 10500); /* 10ms delay recommended by spec */ + + /** + * The SN65DSI86 only supports ASSR Display Authentication method and + * this method is enabled by default. An eDP panel must support this + * authentication method. We need to enable this method in the eDP panel + * at DisplayPort address 0x0010A prior to link training. + */ + regmap_write(pdata->regmap, SN_AUX_WDATA0_REG, 0x01); + regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, 0x00); + regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG, 0x01); + regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, 0x0A); + regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, 0x01); + regmap_write(pdata->regmap, SN_AUX_CMD_REG, 0x81); + usleep_range(10000, 10500); /* 10ms delay recommended by spec */ + + /* Semi auto link training mode */ + regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0x0A); + msleep(20); /* 20ms delay recommended by spec */ + + /* config video parameters */ + ti_sn_bridge_set_video_timings(pdata); + + /* enable video stream */ + regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, + SN_ENABLE_VID_STREAM_BIT, SN_ENABLE_VID_STREAM_BIT); + + drm_panel_enable(pdata->panel); +} + +static void ti_sn_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); + + pm_runtime_get_sync(pdata->dev); + + /* configure bridge ref_clk */ + ti_sn_bridge_set_refclk_freq(pdata); + + /* in case drm_panel is connected then HPD is not supported */ + regmap_update_bits(pdata->regmap, SN_HPD_DISABLE_REG, + SN_HPD_DISABLE_BIT, SN_HPD_DISABLE_BIT); +} + +static void ti_sn_bridge_post_disable(struct drm_bridge *bridge) +{ + struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); + + if (pdata->refclk) + clk_disable_unprepare(pdata->refclk); + + pm_runtime_put_sync(pdata->dev); +} + +static const struct drm_bridge_funcs ti_sn_bridge_funcs = { + .attach = ti_sn_bridge_attach, + .pre_enable = ti_sn_bridge_pre_enable, + .enable = ti_sn_bridge_enable, + .disable = ti_sn_bridge_disable, + .post_disable = ti_sn_bridge_post_disable, +}; + +static int ti_sn_bridge_parse_dsi_host(struct ti_sn_bridge *pdata) +{ + struct device_node *np = pdata->dev->of_node; + + pdata->host_node = of_graph_get_remote_node(np, 0, 0); + + if (!pdata->host_node) { + DRM_ERROR("remote dsi host node not found\n"); + return -ENODEV; + } + + return 0; +} + +static int ti_sn_bridge_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ti_sn_bridge *pdata; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + DRM_ERROR("device doesn't support I2C\n"); + return -ENODEV; + } + + pdata = devm_kzalloc(&client->dev, sizeof(struct ti_sn_bridge), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->regmap = devm_regmap_init_i2c(client, + &ti_sn_bridge_regmap_config); + if (IS_ERR(pdata->regmap)) { + DRM_ERROR("regmap i2c init failed\n"); + return PTR_ERR(pdata->regmap); + } + + pdata->dev = &client->dev; + + ret = drm_of_find_panel_or_bridge(pdata->dev->of_node, 1, 0, + &pdata->panel, NULL); + if (ret) { + DRM_ERROR("could not find any panel node\n"); + return ret; + } + + dev_set_drvdata(&client->dev, pdata); + + pdata->enable_gpio = devm_gpiod_get(pdata->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(pdata->enable_gpio)) { + DRM_ERROR("failed to get enable gpio from DT\n"); + ret = PTR_ERR(pdata->enable_gpio); + return ret; + } + + ret = ti_sn_bridge_parse_regulators(pdata); + if (ret) { + DRM_ERROR("failed to parse regulators\n"); + return ret; + } + + pdata->refclk = devm_clk_get(pdata->dev, "refclk"); + if (IS_ERR(pdata->refclk)) { + ret = PTR_ERR(pdata->refclk); + if (ret == -EPROBE_DEFER) + return ret; + DRM_DEBUG_KMS("refclk not found\n"); + pdata->refclk = NULL; + } + + ret = ti_sn_bridge_parse_dsi_host(pdata); + if (ret) + return ret; + + pm_runtime_enable(pdata->dev); + + i2c_set_clientdata(client, pdata); + + pdata->bridge.funcs = &ti_sn_bridge_funcs; + pdata->bridge.of_node = client->dev.of_node; + + drm_bridge_add(&pdata->bridge); + + return 0; +} + +static int ti_sn_bridge_remove(struct i2c_client *client) +{ + struct ti_sn_bridge *pdata = i2c_get_clientdata(client); + + if (!pdata) + return -EINVAL; + + of_node_put(pdata->host_node); + + pm_runtime_disable(pdata->dev); + + if (pdata->dsi) { + mipi_dsi_detach(pdata->dsi); + mipi_dsi_device_unregister(pdata->dsi); + } + + drm_bridge_remove(&pdata->bridge); + + return 0; +} + +static struct i2c_device_id ti_sn_bridge_id[] = { + { "ti,sn65dsi86", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ti_sn_bridge_id); + +static const struct of_device_id ti_sn_bridge_match_table[] = { + {.compatible = "ti,sn65dsi86"}, + {}, +}; +MODULE_DEVICE_TABLE(of, ti_sn_bridge_match_table); + +static struct i2c_driver ti_sn_bridge_driver = { + .driver = { + .name = "ti_sn65dsi86", + .of_match_table = ti_sn_bridge_match_table, + .pm = &ti_sn_bridge_pm_ops, + }, + .probe = ti_sn_bridge_probe, + .remove = ti_sn_bridge_remove, + .id_table = ti_sn_bridge_id, +}; +module_i2c_driver(ti_sn_bridge_driver); + +MODULE_AUTHOR("Sandeep Panda "); +MODULE_DESCRIPTION("sn65dsi86 DSI to eDP bridge driver"); +MODULE_LICENSE("GPL v2"); From eb91cde094f523c06adf463aad0e41ae35765d71 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 25 Jul 2018 17:46:39 +0200 Subject: [PATCH 014/138] dt-bindings: tc358754: add DT bindings The patch adds bindings to Toshiba DSI/LVDS bridge TC358764. Bindings describe power supplies, reset gpio and video interfaces. Signed-off-by: Andrzej Hajda Signed-off-by: Maciej Purski Reviewed-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20180725154644.25412-5-a.hajda@samsung.com --- .../display/bridge/toshiba,tc358764.txt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt new file mode 100644 index 000000000000..8f9abf28a8fa --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt @@ -0,0 +1,35 @@ +TC358764 MIPI-DSI to LVDS panel bridge + +Required properties: + - compatible: "toshiba,tc358764" + - reg: the virtual channel number of a DSI peripheral + - vddc-supply: core voltage supply, 1.2V + - vddio-supply: I/O voltage supply, 1.8V or 3.3V + - vddlvds-supply: LVDS1/2 voltage supply, 3.3V + - reset-gpios: a GPIO spec for the reset pin + +The device node can contain following 'port' child nodes, +according to the OF graph bindings defined in [1]: + 0: DSI Input, not required, if the bridge is DSI controlled + 1: LVDS Output, mandatory + +[1]: Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + + bridge@0 { + reg = <0>; + compatible = "toshiba,tc358764"; + vddc-supply = <&vcc_1v2_reg>; + vddio-supply = <&vcc_1v8_reg>; + vddlvds-supply = <&vcc_3v3_reg>; + reset-gpios = <&gpd1 6 GPIO_ACTIVE_LOW>; + #address-cells = <1>; + #size-cells = <0>; + port@1 { + reg = <1>; + lvds_ep: endpoint { + remote-endpoint = <&panel_ep>; + }; + }; + }; From f38b7cca6d0eafef278703ddadb369322a510ca6 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 25 Jul 2018 17:46:40 +0200 Subject: [PATCH 015/138] drm/bridge: tc358764: Add DSI to LVDS bridge driver Add a drm_bridge driver for the Toshiba TC358764 DSI to LVDS bridge. Changes in v4: - removed license blob, - ordered includes, - added error handling, - fixed reset GPIO handling, - added missing calls to the panel, - custom OF graph code replaced with helpers, - removed tc358764_poweroff from remove callback. v5: - fixed supply names, - fixed broken console - added connector to fb_helper, - added detach callback - unbinding works, - fixed typo in error checking code, - removed sparse bridge->encoder check - core does it already. Signed-off-by: Andrzej Hajda Signed-off-by: Maciej Purski [ a.hajda@samsung.com: v4, v5 ] Signed-off-by: Andrzej Hajda Reviewed-by: Archit Taneja Link: https://patchwork.freedesktop.org/patch/msgid/20180725154644.25412-6-a.hajda@samsung.com --- drivers/gpu/drm/bridge/Kconfig | 8 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/tc358764.c | 499 ++++++++++++++++++++++++++++++ 3 files changed, 508 insertions(+) create mode 100644 drivers/gpu/drm/bridge/tc358764.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index eeb9c489a817..9a455c5f24f0 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -112,6 +112,14 @@ config DRM_THINE_THC63LVD1024 ---help--- Thine THC63LVD1024 LVDS/parallel converter driver. +config DRM_TOSHIBA_TC358764 + tristate "TC358764 DSI/LVDS bridge" + depends on DRM && DRM_PANEL + depends on OF + select DRM_MIPI_DSI + help + Toshiba TC358764 DSI/LVDS bridge driver. + config DRM_TOSHIBA_TC358767 tristate "Toshiba TC358767 eDP bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index fd90ac059ceb..4934fcf5a6f8 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o obj-$(CONFIG_DRM_SII902X) += sii902x.o obj-$(CONFIG_DRM_SII9234) += sii9234.o obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o +obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c new file mode 100644 index 000000000000..779bc5fce22a --- /dev/null +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd + * + * Authors: + * Andrzej Hajda + * Maciej Purski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include