diff options
author | Dave Airlie <airlied@redhat.com> | 2017-08-29 10:37:36 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2017-08-29 10:37:36 +1000 |
commit | 7ebdb0dd52404907b8eac2bab476b43a8b8aa9f1 (patch) | |
tree | d54244823f1c17fc60c018d0aee0ccd53f49e1ba | |
parent | 095e2d04f9fa88a7c6923734ab4162833ff4f230 (diff) | |
parent | 5b7b1b7fa10145c014750b09ff4cf31ac4e1843a (diff) | |
download | linux-7ebdb0dd52404907b8eac2bab476b43a8b8aa9f1.tar.gz linux-7ebdb0dd52404907b8eac2bab476b43a8b8aa9f1.tar.bz2 linux-7ebdb0dd52404907b8eac2bab476b43a8b8aa9f1.zip |
Merge tag 'exynos-drm-next-for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next
Summary:
- Provide NV12MT pixel format support of Mixer driver in generic way.
- Refactor Exynos KMS drivers
. Refactoring to panel detection way
. Refactoring to setting up possible_crtcs
. Refactoring to video and command mode support
- Some cleanups
* tag 'exynos-drm-next-for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
drm/exynos: simplify set_pixfmt() in DECON and FIMD drivers
drm/exynos: consistent use of cpp
drm/exynos: mixer: remove src offset from mixer_graph_buffer()
drm/exynos: mixer: simplify mixer_graph_buffer()
drm/exynos: mixer: simplify vp_video_buffer()
drm/exynos: mixer: enable NV12MT support for the video plane
drm/exynos: mixer: fix chroma comment in vp_video_buffer()
arm64: dts: exynos: remove i80-if-timings nodes
dt-bindings: exynos5433-decon: remove i80-if-timings property
drm/exynos/decon5433: use mode info stored in CRTC to detect i80 mode
drm/exynos: add mode_valid callback to exynos_drm
drm/exynos/decon5433: refactor irq requesting code
drm/exynos/mic: use mode info stored in CRTC to detect i80 mode
drm/exynos/dsi: propagate info about command mode from panel
drm/exynos/dsi: refactor panel detection logic
drm/exynos: use helper to set possible crtcs
drm/exynos/decon5433: use readl_poll_timeout helpers
-rw-r--r-- | Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt | 12 | ||||
-rw-r--r-- | arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 124 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos7_drm_decon.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_dp.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_core.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.c | 33 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.h | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_dpi.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_dsi.c | 218 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_mic.c | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.c | 27 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_vidi.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 25 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_mixer.c | 48 |
18 files changed, 299 insertions, 327 deletions
diff --git a/Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt b/Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt index 549c538b38a5..fc2588292a68 100644 --- a/Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt +++ b/Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt @@ -25,12 +25,6 @@ Required properties: size-cells must 1 and 0, respectively. - port: contains an endpoint node which is connected to the endpoint in the mic node. The reg value muset be 0. -- i80-if-timings: specify whether the panel which is connected to decon uses - i80 lcd interface or mipi video interface. This node contains - no timing information as that of fimd does. Because there is - no register in decon to specify i80 interface timing value, - it is not needed, but make it remain to use same kind of node - in fimd and exynos7 decon. Example: SoC specific DT entry: @@ -59,9 +53,3 @@ decon: decon@13800000 { }; }; }; - -Board specific DT entry: -&decon { - i80-if-timings { - }; -}; diff --git a/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi b/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi index e2b0da2c0bc7..105b2938082f 100644 --- a/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi @@ -280,9 +280,6 @@ &decon { status = "okay"; - - i80-if-timings { - }; }; &decon_tv { @@ -1116,9 +1113,6 @@ &mic { status = "okay"; - - i80-if-timings { - }; }; &pmu_system_controller { diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 5792ca88ab7a..730b8d9db187 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -13,6 +13,7 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/component.h> +#include <linux/iopoll.h> #include <linux/mfd/syscon.h> #include <linux/of_device.h> #include <linux/of_gpio.h> @@ -33,9 +34,8 @@ #define WINDOWS_NR 3 #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 -#define IFTYPE_I80 (1 << 0) -#define I80_HW_TRG (1 << 1) -#define IFTYPE_HDMI (1 << 2) +#define I80_HW_TRG (1 << 0) +#define IFTYPE_HDMI (1 << 1) static const char * const decon_clks_name[] = { "pclk", @@ -57,6 +57,8 @@ struct decon_context { struct regmap *sysreg; struct clk *clks[ARRAY_SIZE(decon_clks_name)]; unsigned int irq; + unsigned int irq_vsync; + unsigned int irq_lcd_sys; unsigned int te_irq; unsigned long out_type; int first_win; @@ -90,7 +92,7 @@ static int decon_enable_vblank(struct exynos_drm_crtc *crtc) u32 val; val = VIDINTCON0_INTEN; - if (ctx->out_type & IFTYPE_I80) + if (crtc->i80_mode) val |= VIDINTCON0_FRAMEDONE; else val |= VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP; @@ -139,7 +141,7 @@ static u32 decon_get_frame_count(struct decon_context *ctx, bool end) switch (status & (VIDCON1_VSTATUS_MASK | VIDCON1_I80_ACTIVE)) { case VIDCON1_VSTATUS_VS: - if (!(ctx->out_type & IFTYPE_I80)) + if (!(ctx->crtc->i80_mode)) --frm; break; case VIDCON1_VSTATUS_BP: @@ -166,7 +168,7 @@ static u32 decon_get_vblank_counter(struct exynos_drm_crtc *crtc) static void decon_setup_trigger(struct decon_context *ctx) { - if (!(ctx->out_type & (IFTYPE_I80 | I80_HW_TRG))) + if (!ctx->crtc->i80_mode && !(ctx->out_type & I80_HW_TRG)) return; if (!(ctx->out_type & I80_HW_TRG)) { @@ -206,7 +208,7 @@ static void decon_commit(struct exynos_drm_crtc *crtc) val = VIDOUT_LCD_ON; if (interlaced) val |= VIDOUT_INTERLACE_EN_F; - if (ctx->out_type & IFTYPE_I80) { + if (crtc->i80_mode) { val |= VIDOUT_COMMAND_IF; } else { val |= VIDOUT_RGB_IF; @@ -222,7 +224,7 @@ static void decon_commit(struct exynos_drm_crtc *crtc) VIDTCON2_HOZVAL(m->hdisplay - 1); writel(val, ctx->addr + DECON_VIDTCON2); - if (!(ctx->out_type & IFTYPE_I80)) { + if (!crtc->i80_mode) { int vbp = m->crtc_vtotal - m->crtc_vsync_end; int vfp = m->crtc_vsync_start - m->crtc_vdisplay; @@ -277,16 +279,14 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, val |= WINCONx_BURSTLEN_16WORD; break; case DRM_FORMAT_ARGB8888: + default: val |= WINCONx_BPPMODE_32BPP_A8888; val |= WINCONx_WSWP_F | WINCONx_BLD_PIX_F | WINCONx_ALPHA_SEL_F; val |= WINCONx_BURSTLEN_16WORD; break; - default: - DRM_ERROR("Proper pixel format is not set\n"); - return; } - DRM_DEBUG_KMS("bpp = %u\n", fb->format->cpp[0] * 8); + DRM_DEBUG_KMS("cpp = %u\n", fb->format->cpp[0]); /* * In case of exynos, setting dma-burst to 16Word causes permanent @@ -329,7 +329,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, struct decon_context *ctx = crtc->ctx; struct drm_framebuffer *fb = state->base.fb; unsigned int win = plane->index; - unsigned int bpp = fb->format->cpp[0]; + unsigned int cpp = fb->format->cpp[0]; unsigned int pitch = fb->pitches[0]; dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0); u32 val; @@ -365,11 +365,11 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, writel(val, ctx->addr + DECON_VIDW0xADD1B0(win)); if (!(ctx->out_type & IFTYPE_HDMI)) - val = BIT_VAL(pitch - state->crtc.w * bpp, 27, 14) - | BIT_VAL(state->crtc.w * bpp, 13, 0); + val = BIT_VAL(pitch - state->crtc.w * cpp, 27, 14) + | BIT_VAL(state->crtc.w * cpp, 13, 0); else - val = BIT_VAL(pitch - state->crtc.w * bpp, 29, 15) - | BIT_VAL(state->crtc.w * bpp, 14, 0); + val = BIT_VAL(pitch - state->crtc.w * cpp, 29, 15) + | BIT_VAL(state->crtc.w * cpp, 14, 0); writel(val, ctx->addr + DECON_VIDW0xADD2(win)); decon_win_set_pixfmt(ctx, win, fb); @@ -407,24 +407,19 @@ static void decon_atomic_flush(struct exynos_drm_crtc *crtc) static void decon_swreset(struct decon_context *ctx) { - unsigned int tries; unsigned long flags; + u32 val; + int ret; writel(0, ctx->addr + DECON_VIDCON0); - for (tries = 2000; tries; --tries) { - if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_STOP_STATUS) - break; - udelay(10); - } + readl_poll_timeout(ctx->addr + DECON_VIDCON0, val, + ~val & VIDCON0_STOP_STATUS, 12, 20000); writel(VIDCON0_SWRESET, ctx->addr + DECON_VIDCON0); - for (tries = 2000; tries; --tries) { - if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_SWRESET) - break; - udelay(10); - } + ret = readl_poll_timeout(ctx->addr + DECON_VIDCON0, val, + ~val & VIDCON0_SWRESET, 12, 20000); - WARN(tries == 0, "failed to software reset DECON\n"); + WARN(ret < 0, "failed to software reset DECON\n"); spin_lock_irqsave(&ctx->vblank_lock, flags); ctx->frame_id = 0; @@ -515,6 +510,22 @@ err: clk_disable_unprepare(ctx->clks[i]); } +static enum drm_mode_status decon_mode_valid(struct exynos_drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct decon_context *ctx = crtc->ctx; + + ctx->irq = crtc->i80_mode ? ctx->irq_lcd_sys : ctx->irq_vsync; + + if (ctx->irq) + return MODE_OK; + + dev_info(ctx->dev, "Sink requires %s mode, but appropriate interrupt is not provided.\n", + crtc->i80_mode ? "command" : "video"); + + return MODE_BAD; +} + static const struct exynos_drm_crtc_ops decon_crtc_ops = { .enable = decon_enable, .disable = decon_disable, @@ -524,6 +535,7 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = { .atomic_begin = decon_atomic_begin, .update_plane = decon_update_plane, .disable_plane = decon_disable_plane, + .mode_valid = decon_mode_valid, .atomic_flush = decon_atomic_flush, }; @@ -674,19 +686,22 @@ static const struct of_device_id exynos5433_decon_driver_dt_match[] = { MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match); static int decon_conf_irq(struct decon_context *ctx, const char *name, - irq_handler_t handler, unsigned long int flags, bool required) + irq_handler_t handler, unsigned long int flags) { struct platform_device *pdev = to_platform_device(ctx->dev); int ret, irq = platform_get_irq_byname(pdev, name); if (irq < 0) { - if (irq == -EPROBE_DEFER) + switch (irq) { + case -EPROBE_DEFER: return irq; - if (required) - dev_err(ctx->dev, "cannot get %s IRQ\n", name); - else - irq = 0; - return irq; + case -ENODATA: + case -ENXIO: + return 0; + default: + dev_err(ctx->dev, "IRQ %s get failed, %d\n", name, irq); + return irq; + } } irq_set_status_flags(irq, IRQ_NOAUTOEN); ret = devm_request_irq(ctx->dev, irq, handler, flags, "drm_decon", ctx); @@ -714,11 +729,8 @@ static int exynos5433_decon_probe(struct platform_device *pdev) ctx->out_type = (unsigned long)of_device_get_match_data(dev); spin_lock_init(&ctx->vblank_lock); - if (ctx->out_type & IFTYPE_HDMI) { + if (ctx->out_type & IFTYPE_HDMI) ctx->first_win = 1; - } else if (of_get_child_by_name(dev->of_node, "i80-if-timings")) { - ctx->out_type |= IFTYPE_I80; - } for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { struct clk *clk; @@ -742,25 +754,23 @@ static int exynos5433_decon_probe(struct platform_device *pdev) return PTR_ERR(ctx->addr); } - if (ctx->out_type & IFTYPE_I80) { - ret = decon_conf_irq(ctx, "lcd_sys", decon_irq_handler, 0, true); - if (ret < 0) - return ret; - ctx->irq = ret; + ret = decon_conf_irq(ctx, "vsync", decon_irq_handler, 0); + if (ret < 0) + return ret; + ctx->irq_vsync = ret; - ret = decon_conf_irq(ctx, "te", decon_te_irq_handler, - IRQF_TRIGGER_RISING, false); - if (ret < 0) - return ret; - if (ret) { - ctx->te_irq = ret; - ctx->out_type &= ~I80_HW_TRG; - } - } else { - ret = decon_conf_irq(ctx, "vsync", decon_irq_handler, 0, true); - if (ret < 0) + ret = decon_conf_irq(ctx, "lcd_sys", decon_irq_handler, 0); + if (ret < 0) + return ret; + ctx->irq_lcd_sys = ret; + + ret = decon_conf_irq(ctx, "te", decon_te_irq_handler, + IRQF_TRIGGER_RISING); + if (ret < 0) return ret; - ctx->irq = ret; + if (ret) { + ctx->te_irq = ret; + ctx->out_type &= ~I80_HW_TRG; } if (ctx->out_type & I80_HW_TRG) { diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 3e88269fdc2e..615efcf7782a 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -309,19 +309,14 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, val |= WINCONx_BURSTLEN_16WORD; break; case DRM_FORMAT_BGRA8888: + default: val |= WINCONx_BPPMODE_32BPP_BGRA | WINCONx_BLD_PIX | WINCONx_ALPHA_SEL; val |= WINCONx_BURSTLEN_16WORD; break; - default: - DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); - - val |= WINCONx_BPPMODE_24BPP_xRGB; - val |= WINCONx_BURSTLEN_16WORD; - break; } - DRM_DEBUG_KMS("bpp = %d\n", fb->format->cpp[0] * 8); + DRM_DEBUG_KMS("cpp = %d\n", fb->format->cpp[0]); /* * In case of exynos, setting dma-burst to 16Word causes permanent @@ -398,7 +393,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, unsigned int last_x; unsigned int last_y; unsigned int win = plane->index; - unsigned int bpp = fb->format->cpp[0]; + unsigned int cpp = fb->format->cpp[0]; unsigned int pitch = fb->pitches[0]; if (ctx->suspended) @@ -418,7 +413,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, val = (unsigned long)exynos_drm_fb_dma_addr(fb, 0); writel(val, ctx->regs + VIDW_BUF_START(win)); - padding = (pitch / bpp) - fb->width; + padding = (pitch / cpp) - fb->width; /* buffer size */ writel(fb->width + padding, ctx->regs + VIDW_WHOLE_X(win)); diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 385537b726a6..39629e7a80b9 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -155,7 +155,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) struct exynos_dp_device *dp = dev_get_drvdata(dev); struct drm_encoder *encoder = &dp->encoder; struct drm_device *drm_dev = data; - int pipe, ret; + int ret; /* * Just like the probe function said, we don't need the @@ -179,20 +179,15 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) return ret; } - pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev, - EXYNOS_DISPLAY_TYPE_LCD); - if (pipe < 0) - return pipe; - - encoder->possible_crtcs = 1 << pipe; - - DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); - drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs); + ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); + if (ret < 0) + return ret; + dp->plat_data.encoder = encoder; return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data); diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index edbd98ff293e..b0c0621fcdf7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -13,6 +13,7 @@ */ #include <drm/drmP.h> + #include "exynos_drm_drv.h" #include "exynos_drm_crtc.h" diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index c37078fbe0ea..6ce0821590df 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -16,6 +16,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_encoder.h> #include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" @@ -83,7 +84,19 @@ static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, exynos_crtc->ops->atomic_flush(exynos_crtc); } +static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + if (exynos_crtc->ops->mode_valid) + return exynos_crtc->ops->mode_valid(exynos_crtc, mode); + + return MODE_OK; +} + static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { + .mode_valid = exynos_crtc_mode_valid, .atomic_check = exynos_crtc_atomic_check, .atomic_begin = exynos_crtc_atomic_begin, .atomic_flush = exynos_crtc_atomic_flush, @@ -191,16 +204,30 @@ err_crtc: return ERR_PTR(ret); } -int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, +struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev, enum exynos_drm_output_type out_type) { struct drm_crtc *crtc; drm_for_each_crtc(crtc, drm_dev) if (to_exynos_crtc(crtc)->type == out_type) - return drm_crtc_index(crtc); + return to_exynos_crtc(crtc); - return -EPERM; + return ERR_PTR(-EPERM); +} + +int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder, + enum exynos_drm_output_type out_type) +{ + struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev, + out_type); + + if (IS_ERR(crtc)) + return PTR_ERR(crtc); + + encoder->possible_crtcs = drm_crtc_mask(&crtc->base); + + return 0; } void exynos_drm_crtc_te_handler(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index ef58b64e3d2d..dec446109e6c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -15,21 +15,25 @@ #ifndef _EXYNOS_DRM_CRTC_H_ #define _EXYNOS_DRM_CRTC_H_ + #include "exynos_drm_drv.h" struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, struct drm_plane *plane, - enum exynos_drm_output_type type, + enum exynos_drm_output_type out_type, const struct exynos_drm_crtc_ops *ops, void *context); void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc); void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, struct exynos_drm_plane *exynos_plane); -/* This function gets pipe value to crtc device matched with out_type. */ -int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, +/* This function gets crtc device matched with out_type. */ +struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev, enum exynos_drm_output_type out_type); +int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder, + enum exynos_drm_output_type out_type); + /* * This function calls the crtc device(manager)'s te_handler() callback * to trigger to transfer video image at the tearing effect synchronization diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 76d80e5de521..66945e0dc57f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -202,19 +202,15 @@ int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder) { int ret; - ret = exynos_drm_crtc_get_pipe_from_type(dev, EXYNOS_DISPLAY_TYPE_LCD); - if (ret < 0) - return ret; - - encoder->possible_crtcs = 1 << ret; - - DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); - drm_encoder_init(dev, encoder, &exynos_dpi_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs); + ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); + if (ret < 0) + return ret; + ret = exynos_dpi_create_connector(encoder); if (ret) { DRM_ERROR("failed to create connector ret = %d\n", ret); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index a93de321706b..cf131c2aa23e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -91,6 +91,7 @@ struct exynos_drm_plane { #define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0) #define EXYNOS_DRM_PLANE_CAP_SCALE (1 << 1) #define EXYNOS_DRM_PLANE_CAP_ZPOS (1 << 2) +#define EXYNOS_DRM_PLANE_CAP_TILE (1 << 3) /* * Exynos DRM plane configuration structure. @@ -117,6 +118,7 @@ struct exynos_drm_plane_config { * @disable: disable the device * @enable_vblank: specific driver callback for enabling vblank interrupt. * @disable_vblank: specific driver callback for disabling vblank interrupt. + * @mode_valid: specific driver callback for mode validation * @atomic_check: validate state * @atomic_begin: prepare device to receive an update * @atomic_flush: mark the end of device update @@ -132,6 +134,8 @@ struct exynos_drm_crtc_ops { int (*enable_vblank)(struct exynos_drm_crtc *crtc); void (*disable_vblank)(struct exynos_drm_crtc *crtc); u32 (*get_vblank_counter)(struct exynos_drm_crtc *crtc); + enum drm_mode_status (*mode_valid)(struct exynos_drm_crtc *crtc, + const struct drm_display_mode *mode); int (*atomic_check)(struct exynos_drm_crtc *crtc, struct drm_crtc_state *state); void (*atomic_begin)(struct exynos_drm_crtc *crtc); @@ -162,6 +166,7 @@ struct exynos_drm_crtc { const struct exynos_drm_crtc_ops *ops; void *ctx; struct exynos_drm_clk *pipe_clk; + bool i80_mode : 1; }; static inline void exynos_drm_pipe_clk_enable(struct exynos_drm_crtc *crtc, diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index c399dc9b325f..7904ffa9abfb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -254,7 +254,6 @@ struct exynos_dsi { struct drm_encoder encoder; struct mipi_dsi_host dsi_host; struct drm_connector connector; - struct device_node *panel_node; struct drm_panel *panel; struct device *dev; @@ -1329,12 +1328,13 @@ static int exynos_dsi_init(struct exynos_dsi *dsi) return 0; } -static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi) +static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi, + struct device *panel) { int ret; int te_gpio_irq; - dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0); + dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0); if (dsi->te_gpio == -ENOENT) return 0; @@ -1374,85 +1374,6 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi) } } -static int exynos_dsi_host_attach(struct mipi_dsi_host *host, - struct mipi_dsi_device *device) -{ - struct exynos_dsi *dsi = host_to_dsi(host); - - dsi->lanes = device->lanes; - dsi->format = device->format; - dsi->mode_flags = device->mode_flags; - dsi->panel_node = device->dev.of_node; - - /* - * This is a temporary solution and should be made by more generic way. - * - * If attached panel device is for command mode one, dsi should register - * TE interrupt handler. - */ - if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) { - int ret = exynos_dsi_register_te_irq(dsi); - - if (ret) - return ret; - } - - if (dsi->connector.dev) - drm_helper_hpd_irq_event(dsi->connector.dev); - - return 0; -} - -static int exynos_dsi_host_detach(struct mipi_dsi_host *host, - struct mipi_dsi_device *device) -{ - struct exynos_dsi *dsi = host_to_dsi(host); - - exynos_dsi_unregister_te_irq(dsi); - - dsi->panel_node = NULL; - - if (dsi->connector.dev) - drm_helper_hpd_irq_event(dsi->connector.dev); - - return 0; -} - -static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, - const struct mipi_dsi_msg *msg) -{ - struct exynos_dsi *dsi = host_to_dsi(host); - struct exynos_dsi_transfer xfer; - int ret; - - if (!(dsi->state & DSIM_STATE_ENABLED)) - return -EINVAL; - - if (!(dsi->state & DSIM_STATE_INITIALIZED)) { - ret = exynos_dsi_init(dsi); - if (ret) - return ret; - dsi->state |= DSIM_STATE_INITIALIZED; - } - - ret = mipi_dsi_create_packet(&xfer.packet, msg); - if (ret < 0) - return ret; - - xfer.rx_len = msg->rx_len; - xfer.rx_payload = msg->rx_buf; - xfer.flags = msg->flags; - - ret = exynos_dsi_transfer(dsi, &xfer); - return (ret < 0) ? ret : xfer.rx_done; -} - -static const struct mipi_dsi_host_ops exynos_dsi_ops = { - .attach = exynos_dsi_host_attach, - .detach = exynos_dsi_host_detach, - .transfer = exynos_dsi_host_transfer, -}; - static void exynos_dsi_enable(struct drm_encoder *encoder) { struct exynos_dsi *dsi = encoder_to_dsi(encoder); @@ -1508,25 +1429,7 @@ static void exynos_dsi_disable(struct drm_encoder *encoder) static enum drm_connector_status exynos_dsi_detect(struct drm_connector *connector, bool force) { - struct exynos_dsi *dsi = connector_to_dsi(connector); - - if (!dsi->panel) { - dsi->panel = of_drm_find_panel(dsi->panel_node); - if (dsi->panel) - drm_panel_attach(dsi->panel, &dsi->connector); - } else if (!dsi->panel_node) { - struct drm_encoder *encoder; - - encoder = platform_get_drvdata(to_platform_device(dsi->dev)); - exynos_dsi_disable(encoder); - drm_panel_detach(dsi->panel); - dsi->panel = NULL; - } - - if (dsi->panel) - return connector_status_connected; - - return connector_status_disconnected; + return connector->status; } static void exynos_dsi_connector_destroy(struct drm_connector *connector) @@ -1575,6 +1478,7 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder) return ret; } + connector->status = connector_status_disconnected; drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs); drm_mode_connector_attach_encoder(connector, encoder); @@ -1611,6 +1515,105 @@ static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = { MODULE_DEVICE_TABLE(of, exynos_dsi_of_match); +static int exynos_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct exynos_dsi *dsi = host_to_dsi(host); + struct drm_device *drm = dsi->connector.dev; + + /* + * This is a temporary solution and should be made by more generic way. + * + * If attached panel device is for command mode one, dsi should register + * TE interrupt handler. + */ + if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) { + int ret = exynos_dsi_register_te_irq(dsi, &device->dev); + if (ret) + return ret; + } + + mutex_lock(&drm->mode_config.mutex); + + dsi->lanes = device->lanes; + dsi->format = device->format; + dsi->mode_flags = device->mode_flags; + dsi->panel = of_drm_find_panel(device->dev.of_node); + if (dsi->panel) { + drm_panel_attach(dsi->panel, &dsi->connector); + dsi->connector.status = connector_status_connected; + } + exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode = + !(dsi->mode_flags & MIPI_DSI_MODE_VIDEO); + + mutex_unlock(&drm->mode_config.mutex); + + if (drm->mode_config.poll_enabled) + drm_kms_helper_hotplug_event(drm); + + return 0; +} + +static int exynos_dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct exynos_dsi *dsi = host_to_dsi(host); + struct drm_device *drm = dsi->connector.dev; + + mutex_lock(&drm->mode_config.mutex); + + if (dsi->panel) { + exynos_dsi_disable(&dsi->encoder); + drm_panel_detach(dsi->panel); + dsi->panel = NULL; + dsi->connector.status = connector_status_disconnected; + } + + mutex_unlock(&drm->mode_config.mutex); + + if (drm->mode_config.poll_enabled) + drm_kms_helper_hotplug_event(drm); + + exynos_dsi_unregister_te_irq(dsi); + + return 0; +} + +static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct exynos_dsi *dsi = host_to_dsi(host); + struct exynos_dsi_transfer xfer; + int ret; + + if (!(dsi->state & DSIM_STATE_ENABLED)) + return -EINVAL; + + if (!(dsi->state & DSIM_STATE_INITIALIZED)) { + ret = exynos_dsi_init(dsi); + if (ret) + return ret; + dsi->state |= DSIM_STATE_INITIALIZED; + } + + ret = mipi_dsi_create_packet(&xfer.packet, msg); + if (ret < 0) + return ret; + + xfer.rx_len = msg->rx_len; + xfer.rx_payload = msg->rx_buf; + xfer.flags = msg->flags; + + ret = exynos_dsi_transfer(dsi, &xfer); + return (ret < 0) ? ret : xfer.rx_done; +} + +static const struct mipi_dsi_host_ops exynos_dsi_ops = { + .attach = exynos_dsi_host_attach, + .detach = exynos_dsi_host_detach, + .transfer = exynos_dsi_host_transfer, |