diff options
Diffstat (limited to 'drivers/gpu/drm/rcar-du')
21 files changed, 513 insertions, 377 deletions
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 225141656e19..7c36e2777a15 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -4,6 +4,7 @@ config DRM_RCAR_DU depends on DRM && OF depends on ARM || ARM64 depends on ARCH_RENESAS || COMPILE_TEST + imply DRM_RCAR_LVDS select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 17741843cf51..4cdea14d552f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -9,23 +9,26 @@ #include <linux/clk.h> #include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/sys_soc.h> -#include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_device.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_vblank.h> #include "rcar_du_crtc.h" #include "rcar_du_drv.h" +#include "rcar_du_encoder.h" #include "rcar_du_kms.h" #include "rcar_du_plane.h" #include "rcar_du_regs.h" #include "rcar_du_vsp.h" +#include "rcar_lvds.h" static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) { @@ -226,9 +229,6 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) * system clock, and have no internal clock divider. */ - if (WARN_ON(!rcrtc->extclock)) - return; - /* * The H3 ES1.x exhibits dot clock duty cycle stability issues. * We can work around them by configuring the DPLL to twice the @@ -319,26 +319,6 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); } -void rcar_du_crtc_route_output(struct drm_crtc *crtc, - enum rcar_du_output output) -{ - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - struct rcar_du_device *rcdu = rcrtc->group->dev; - - /* - * Store the route from the CRTC output to the DU output. The DU will be - * configured when starting the CRTC. - */ - rcrtc->outputs |= BIT(output); - - /* - * Store RGB routing to DPAD0, the hardware will be configured when - * starting the CRTC. - */ - if (output == RCAR_DU_OUTPUT_DPAD0) - rcdu->dpad0_source = rcrtc->index; -} - static unsigned int plane_zpos(struct rcar_du_plane *plane) { return plane->plane.state->normalized_zpos; @@ -658,12 +638,49 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) * CRTC Functions */ +static int rcar_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(state); + struct drm_encoder *encoder; + + /* Store the routes from the CRTC output to the DU outputs. */ + rstate->outputs = 0; + + drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask) { + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + rstate->outputs |= BIT(renc->output); + } + + return 0; +} + static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state); + struct rcar_du_device *rcdu = rcrtc->group->dev; rcar_du_crtc_get(rcrtc); + + /* + * On D3/E3 the dot clock is provided by the LVDS encoder attached to + * the DU channel. We need to enable its clock output explicitly if + * the LVDS output is disabled. + */ + if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) && + rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { + struct rcar_du_encoder *encoder = + rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index]; + const struct drm_display_mode *mode = + &crtc->state->adjusted_mode; + + rcar_lvds_clk_enable(encoder->base.bridge, + mode->clock * 1000); + } + rcar_du_crtc_start(rcrtc); } @@ -671,18 +688,30 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(old_state); + struct rcar_du_device *rcdu = rcrtc->group->dev; rcar_du_crtc_stop(rcrtc); rcar_du_crtc_put(rcrtc); + if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) && + rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { + struct rcar_du_encoder *encoder = + rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index]; + + /* + * Disable the LVDS clock output, see + * rcar_du_crtc_atomic_enable(). + */ + rcar_lvds_clk_disable(encoder->base.bridge); + } + spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { drm_crtc_send_vblank_event(crtc, crtc->state->event); crtc->state->event = NULL; } spin_unlock_irq(&crtc->dev->event_lock); - - rcrtc->outputs = 0; } static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc, @@ -701,7 +730,7 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc, * CRTC will be put later in .atomic_disable(). * * If a mode set is not in progress the CRTC is enabled, and the - * following get call will be a no-op. There is thus no need to belance + * following get call will be a no-op. There is thus no need to balance * it in .atomic_flush() either. */ rcar_du_crtc_get(rcrtc); @@ -738,14 +767,27 @@ enum drm_mode_status rcar_du_crtc_mode_valid(struct drm_crtc *crtc, struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_device *rcdu = rcrtc->group->dev; bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; + unsigned int vbp; if (interlaced && !rcar_du_has(rcdu, RCAR_DU_FEATURE_INTERLACED)) return MODE_NO_INTERLACE; + /* + * The hardware requires a minimum combined horizontal sync and back + * porch of 20 pixels and a minimum vertical back porch of 3 lines. + */ + if (mode->htotal - mode->hsync_start < 20) + return MODE_HBLANK_NARROW; + + vbp = (mode->vtotal - mode->vsync_end) / (interlaced ? 2 : 1); + if (vbp < 3) + return MODE_VBLANK_NARROW; + return MODE_OK; } static const struct drm_crtc_helper_funcs crtc_helper_funcs = { + .atomic_check = rcar_du_crtc_atomic_check, .atomic_begin = rcar_du_crtc_atomic_begin, .atomic_flush = rcar_du_crtc_atomic_flush, .atomic_enable = rcar_du_crtc_atomic_enable, @@ -1002,7 +1044,7 @@ unlock: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - return 0; + return ret; } static const struct drm_crtc_funcs crtc_funcs_gen2 = { @@ -1113,9 +1155,16 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex, clk = devm_clk_get(rcdu->dev, clk_name); if (!IS_ERR(clk)) { rcrtc->extclock = clk; - } else if (PTR_ERR(rcrtc->clock) == -EPROBE_DEFER) { - dev_info(rcdu->dev, "can't get external clock %u\n", hwindex); + } else if (PTR_ERR(clk) == -EPROBE_DEFER) { return -EPROBE_DEFER; + } else if (rcdu->info->dpll_mask & BIT(hwindex)) { + /* + * DU channels that have a display PLL can't use the internal + * system clock and thus require an external clock. + */ + ret = PTR_ERR(clk); + dev_err(rcdu->dev, "can't get dclkin.%u: %d\n", hwindex, ret); + return ret; } init_waitqueue_head(&rcrtc->flip_wait); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 59ac6e7d22c9..bcb35b0b7612 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -14,7 +14,6 @@ #include <linux/spinlock.h> #include <linux/wait.h> -#include <drm/drmP.h> #include <drm/drm_crtc.h> #include <media/vsp1.h> @@ -37,7 +36,6 @@ struct rcar_du_vsp; * @vblank_lock: protects vblank_wait and vblank_count * @vblank_wait: wait queue used to signal vertical blanking * @vblank_count: number of vertical blanking interrupts to wait for - * @outputs: bitmask of the outputs (enum rcar_du_output) driven by this CRTC * @group: CRTC group this CRTC belongs to * @vsp: VSP feeding video to this CRTC * @vsp_pipe: index of the VSP pipeline feeding video to this CRTC @@ -61,8 +59,6 @@ struct rcar_du_crtc { wait_queue_head_t vblank_wait; unsigned int vblank_count; - unsigned int outputs; - struct rcar_du_group *group; struct rcar_du_vsp *vsp; unsigned int vsp_pipe; @@ -77,11 +73,13 @@ struct rcar_du_crtc { * struct rcar_du_crtc_state - Driver-specific CRTC state * @state: base DRM CRTC state * @crc: CRC computation configuration + * @outputs: bitmask of the outputs (enum rcar_du_output) driven by this CRTC */ struct rcar_du_crtc_state { struct drm_crtc_state state; struct vsp1_du_crc_config crc; + unsigned int outputs; }; #define to_rcar_crtc_state(s) container_of(s, struct rcar_du_crtc_state, state) @@ -102,8 +100,6 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex, void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); -void rcar_du_crtc_route_output(struct drm_crtc *crtc, - enum rcar_du_output output); void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 084f58df4a8c..75ab17af13a9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -17,11 +17,12 @@ #include <linux/slab.h> #include <linux/wait.h> -#include <drm/drmP.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_drv.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_probe_helper.h> #include "rcar_du_drv.h" #include "rcar_du_kms.h" @@ -35,13 +36,12 @@ static const struct rcar_du_device_info rzg1_du_r8a7743_info = { .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, .channels_mask = BIT(1) | BIT(0), .routes = { /* - * R8A7743 has one RGB output and one LVDS output + * R8A774[34] has one RGB output and one LVDS output */ [RCAR_DU_OUTPUT_DPAD0] = { .possible_crtcs = BIT(1) | BIT(0), @@ -58,7 +58,6 @@ static const struct rcar_du_device_info rzg1_du_r8a7743_info = { static const struct rcar_du_device_info rzg1_du_r8a7745_info = { .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, .channels_mask = BIT(1) | BIT(0), @@ -77,8 +76,60 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = { }, }; -static const struct rcar_du_device_info rcar_du_r8a7779_info = { +static const struct rcar_du_device_info rzg1_du_r8a77470_info = { .gen = 2, + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK + | RCAR_DU_FEATURE_INTERLACED + | RCAR_DU_FEATURE_TVM_SYNC, + .channels_mask = BIT(1) | BIT(0), + .routes = { + /* + * R8A77470 has two RGB outputs, one LVDS output, and + * one (currently unsupported) analog video output + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(0), + .port = 0, + }, + [RCAR_DU_OUTPUT_DPAD1] = { + .possible_crtcs = BIT(1), + .port = 1, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0) | BIT(1), + .port = 2, + }, + }, +}; + +static const struct rcar_du_device_info rcar_du_r8a774c0_info = { + .gen = 3, + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK + | RCAR_DU_FEATURE_VSP1_SOURCE, + .channels_mask = BIT(1) | BIT(0), + .routes = { + /* + * R8A774C0 has one RGB output and two LVDS outputs + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(0) | BIT(1), + .port = 0, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0), + .port = 1, + }, + [RCAR_DU_OUTPUT_LVDS1] = { + .possible_crtcs = BIT(1), + .port = 2, + }, + }, + .num_lvds = 2, + .lvds_clk_mask = BIT(1) | BIT(0), +}; + +static const struct rcar_du_device_info rcar_du_r8a7779_info = { + .gen = 1, .features = RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, .channels_mask = BIT(1) | BIT(0), @@ -101,7 +152,6 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { static const struct rcar_du_device_info rcar_du_r8a7790_info = { .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, .quirks = RCAR_DU_QUIRK_ALIGN_128B, @@ -131,7 +181,6 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = { static const struct rcar_du_device_info rcar_du_r8a7791_info = { .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, .channels_mask = BIT(1) | BIT(0), @@ -155,7 +204,6 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = { static const struct rcar_du_device_info rcar_du_r8a7792_info = { .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, .channels_mask = BIT(1) | BIT(0), @@ -175,7 +223,6 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = { static const struct rcar_du_device_info rcar_du_r8a7794_info = { .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, .channels_mask = BIT(1) | BIT(0), @@ -198,7 +245,6 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = { static const struct rcar_du_device_info rcar_du_r8a7795_info = { .gen = 3, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_VSP1_SOURCE | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, @@ -232,7 +278,6 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = { static const struct rcar_du_device_info rcar_du_r8a7796_info = { .gen = 3, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_VSP1_SOURCE | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, @@ -262,7 +307,6 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = { static const struct rcar_du_device_info rcar_du_r8a77965_info = { .gen = 3, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_VSP1_SOURCE | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, @@ -292,7 +336,6 @@ static const struct rcar_du_device_info rcar_du_r8a77965_info = { static const struct rcar_du_device_info rcar_du_r8a77970_info = { .gen = 3, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_VSP1_SOURCE | RCAR_DU_FEATURE_INTERLACED | RCAR_DU_FEATURE_TVM_SYNC, @@ -314,7 +357,6 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = { static const struct rcar_du_device_info rcar_du_r8a7799x_info = { .gen = 3, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK - | RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_VSP1_SOURCE, .channels_mask = BIT(1) | BIT(0), .routes = { @@ -341,7 +383,10 @@ static const struct rcar_du_device_info rcar_du_r8a7799x_info = { static const struct of_device_id rcar_du_of_table[] = { { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info }, + { .compatible = "renesas,du-r8a7744", .data = &rzg1_du_r8a7743_info }, { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info }, + { .compatible = "renesas,du-r8a77470", .data = &rzg1_du_r8a77470_info }, + { .compatible = "renesas,du-r8a774c0", .data = &rcar_du_r8a774c0_info }, { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info }, { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info }, { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info }, @@ -363,19 +408,11 @@ MODULE_DEVICE_TABLE(of, rcar_du_of_table); * DRM operations */ -static void rcar_du_lastclose(struct drm_device *dev) -{ - struct rcar_du_device *rcdu = dev->dev_private; - - drm_fbdev_cma_restore_mode(rcdu->fbdev); -} - DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops); static struct drm_driver rcar_du_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = rcar_du_lastclose, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, @@ -404,32 +441,15 @@ static struct drm_driver rcar_du_driver = { static int rcar_du_pm_suspend(struct device *dev) { struct rcar_du_device *rcdu = dev_get_drvdata(dev); - struct drm_atomic_state *state; - drm_kms_helper_poll_disable(rcdu->ddev); - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, true); - - state = drm_atomic_helper_suspend(rcdu->ddev); - if (IS_ERR(state)) { - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, false); - drm_kms_helper_poll_enable(rcdu->ddev); - return PTR_ERR(state); - } - - rcdu->suspend_state = state; - - return 0; + return drm_mode_config_helper_suspend(rcdu->ddev); } static int rcar_du_pm_resume(struct device *dev) { struct rcar_du_device *rcdu = dev_get_drvdata(dev); - drm_atomic_helper_resume(rcdu->ddev, rcdu->suspend_state); - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, false); - drm_kms_helper_poll_enable(rcdu->ddev); - - return 0; + return drm_mode_config_helper_resume(rcdu->ddev); } #endif @@ -448,13 +468,10 @@ static int rcar_du_remove(struct platform_device *pdev) drm_dev_unregister(ddev); - if (rcdu->fbdev) - drm_fbdev_cma_fini(rcdu->fbdev); - drm_kms_helper_poll_fini(ddev); drm_mode_config_cleanup(ddev); - drm_dev_unref(ddev); + drm_dev_put(ddev); return 0; } @@ -510,6 +527,8 @@ static int rcar_du_probe(struct platform_device *pdev) DRM_INFO("Device %s probed\n", dev_name(&pdev->dev)); + drm_fbdev_generic_setup(ddev, 32); + return 0; error: diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 143c037e2c0f..1327cd0df90a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -20,14 +20,14 @@ struct clk; struct device; struct drm_device; -struct drm_fbdev_cma; +struct drm_property; struct rcar_du_device; +struct rcar_du_encoder; #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK BIT(0) /* Per-CRTC IRQ and clock */ -#define RCAR_DU_FEATURE_EXT_CTRL_REGS BIT(1) /* Has extended control registers */ -#define RCAR_DU_FEATURE_VSP1_SOURCE BIT(2) /* Has inputs from VSP1 */ -#define RCAR_DU_FEATURE_INTERLACED BIT(3) /* HW supports interlaced */ -#define RCAR_DU_FEATURE_TVM_SYNC BIT(4) /* Has TV switch/sync modes */ +#define RCAR_DU_FEATURE_VSP1_SOURCE BIT(1) /* Has inputs from VSP1 */ +#define RCAR_DU_FEATURE_INTERLACED BIT(2) /* HW supports interlaced */ +#define RCAR_DU_FEATURE_TVM_SYNC BIT(3) /* Has TV switch/sync modes */ #define RCAR_DU_QUIRK_ALIGN_128B BIT(0) /* Align pitches to 128 bytes */ @@ -78,12 +78,12 @@ struct rcar_du_device { void __iomem *mmio; struct drm_device *ddev; - struct drm_fbdev_cma *fbdev; - struct drm_atomic_state *suspend_state; struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS]; unsigned int num_crtcs; + struct rcar_du_encoder *encoders[RCAR_DU_OUTPUT_MAX]; + struct rcar_du_group groups[RCAR_DU_MAX_GROUPS]; struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS]; @@ -92,6 +92,7 @@ struct rcar_du_device { } props; unsigned int dpad0_source; + unsigned int dpad1_source; unsigned int vspd1_sink; }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 1877764bd6d9..8ee4e762f4e5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -9,9 +9,8 @@ #include <linux/export.h> -#include <drm/drmP.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_panel.h> #include "rcar_du_drv.h" @@ -22,17 +21,7 @@ * Encoder */ -static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - struct rcar_du_encoder *renc = to_rcar_encoder(encoder); - - rcar_du_crtc_route_output(crtc_state->crtc, renc->output); -} - static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .atomic_mode_set = rcar_du_encoder_mode_set, }; static const struct drm_encoder_funcs encoder_funcs = { @@ -41,8 +30,7 @@ static const struct drm_encoder_funcs encoder_funcs = { int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_output output, - struct device_node *enc_node, - struct device_node *con_node) + struct device_node *enc_node) { struct rcar_du_encoder *renc; struct drm_encoder *encoder; @@ -53,6 +41,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, if (renc == NULL) return -ENOMEM; + rcdu->encoders[output] = renc; renc->output = output; encoder = rcar_encoder_to_drm_encoder(renc); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index ce3cbc85695e..df9be4524301 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -10,10 +10,8 @@ #ifndef __RCAR_DU_ENCODER_H__ #define __RCAR_DU_ENCODER_H__ -#include <drm/drm_crtc.h> #include <drm/drm_encoder.h> -struct drm_panel; struct rcar_du_device; struct rcar_du_encoder { @@ -28,7 +26,6 @@ struct rcar_du_encoder { int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_output output, - struct device_node *enc_node, - struct device_node *con_node); + struct device_node *enc_node); #endif /* __RCAR_DU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index cebf313c6e1f..9eee47969e77 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -147,7 +147,7 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp) rcar_du_group_setup_pins(rgrp); - if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) { + if (rcdu->info->gen >= 2) { rcar_du_group_setup_defr8(rgrp); rcar_du_group_setup_didsr(rgrp); } @@ -262,7 +262,7 @@ int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu) unsigned int index; int ret; - if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_EXT_CTRL_REGS)) + if (rcdu->info->gen < 2) return 0; /* @@ -287,9 +287,50 @@ int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu) return 0; } +static void rcar_du_group_set_dpad_levels(struct rcar_du_group *rgrp) +{ + static const u32 doflr_values[2] = { + DOFLR_HSYCFL0 | DOFLR_VSYCFL0 | DOFLR_ODDFL0 | + DOFLR_DISPFL0 | DOFLR_CDEFL0 | DOFLR_RGBFL0, + DOFLR_HSYCFL1 | DOFLR_VSYCFL1 | DOFLR_ODDFL1 | + DOFLR_DISPFL1 | DOFLR_CDEFL1 | DOFLR_RGBFL1, + }; + static const u32 dpad_mask = BIT(RCAR_DU_OUTPUT_DPAD1) + | BIT(RCAR_DU_OUTPUT_DPAD0); + struct rcar_du_device *rcdu = rgrp->dev; + u32 doflr = DOFLR_CODE; + unsigned int i; + + if (rcdu->info->gen < 2) + return; + + /* + * The DPAD outputs can't be controlled directly. However, the parallel + * output of the DU channels routed to DPAD can be set to fixed levels + * through the DOFLR group register. Use this to turn the DPAD on or off + * by driving fixed low-level signals at the output of any DU channel + * not routed to a DPAD output. This doesn't affect the DU output + * signals going to other outputs, such as the internal LVDS and HDMI + * encoders. + */ + + for (i = 0; i < rgrp->num_crtcs; ++i) { + struct rcar_du_crtc_state *rstate; + struct rcar_du_crtc *rcrtc; + + rcrtc = &rcdu->crtcs[rgrp->index * 2 + i]; + rstate = to_rcar_crtc_state(rcrtc->crtc.state); + + if (!(rstate->outputs & dpad_mask)) + doflr |= doflr_values[i]; + } + + rcar_du_group_write(rgrp, DOFLR, doflr); +} + int rcar_du_group_set_routing(struct rcar_du_group *rgrp) { - struct rcar_du_crtc *crtc0 = &rgrp->dev->crtcs[rgrp->index * 2]; + struct rcar_du_device *rcdu = rgrp->dev; u32 dorcr = rcar_du_group_read(rgrp, DORCR); dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); @@ -299,12 +340,14 @@ int rcar_du_group_set_routing(struct rcar_du_group *rgrp) * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1 * by default. */ - if (crtc0->outputs & BIT(RCAR_DU_OUTPUT_DPAD1)) + if (rcdu->dpad1_source == rgrp->index * 2) dorcr |= DORCR_PG2D_DS1; else dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; rcar_du_group_write(rgrp, DORCR, dorcr); + rcar_du_group_set_dpad_levels(rgrp); + return rcar_du_set_dpad0_vsp1_routing(rgrp->dev); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 4ebd61ecbee1..3b7d50a8fb9b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -7,14 +7,15 @@ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ -#include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_device.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> #include <linux/of_graph.h> #include <linux/wait.h> @@ -255,13 +256,6 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return drm_gem_fb_create(dev, file_priv, mode_cmd); } -static void rcar_du_output_poll_changed(struct drm_device *dev) -{ - struct rcar_du_device *rcdu = dev->dev_private; - - drm_fbdev_cma_hotplug_event(rcdu->fbdev); -} - /* ----------------------------------------------------------------------------- * Atomic Check and Update */ @@ -285,6 +279,28 @@ static int rcar_du_atomic_check(struct drm_device *dev, static void rcar_du_atomic_commit_tail(struct drm_atomic_state *old_state) { struct drm_device *dev = old_state->dev; + struct rcar_du_device *rcdu = dev->dev_private; + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + unsigned int i; + + /* + * Store RGB routing to DPAD0 and DPAD1, the hardware will be configured + * when starting the CRTCs. + */ + rcdu->dpad1_source = -1; + + for_each_new_crtc_in_state(old_state, crtc, crtc_state, i) { + struct rcar_du_crtc_state *rcrtc_state = + to_rcar_crtc_state(crtc_state); + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + if (rcrtc_state->outputs & BIT(RCAR_DU_OUTPUT_DPAD0)) + rcdu->dpad0_source = rcrtc->index; + + if (rcrtc_state->outputs & BIT(RCAR_DU_OUTPUT_DPAD1)) + rcdu->dpad1_source = rcrtc->index; + } /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); @@ -308,7 +324,6 @@ static const struct drm_mode_config_helper_funcs rcar_du_mode_config_helper = { static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { .fb_create = rcar_du_fb_create, - .output_poll_changed = rcar_du_output_poll_changed, .atomic_check = rcar_du_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -317,17 +332,10 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, enum rcar_du_output output, struct of_endpoint *ep) { - struct device_node *connector = NULL; - struct device_node *encoder = NULL; - struct device_node *ep_node = NULL; - struct device_node *entity_ep_node; struct device_node *entity; int ret; - /* - * Locate the connected entity and infer its type from the number of - * endpoints. - */ + /* Locate the connected entity and initialize the encoder. */ entity = of_graph_get_remote_port_parent(ep->local_node); if (!entity) { dev_dbg(rcdu->dev, "unconnected endpoint %pOF, skipping\n", @@ -339,52 +347,17 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, dev_dbg(rcdu->dev, "connected entity %pOF is disabled, skipping\n", entity); + of_node_put(entity); return -ENODEV; } - entity_ep_node = of_graph_get_remote_endpoint(ep->local_node); - - for_each_endpoint_of_node(entity, ep_node) { - if (ep_node == entity_ep_node) - continue; - - /* - * We've found one endpoint other than the input, this must - * be an encoder. Locate the connector. - */ - encoder = entity; - connector = of_graph_get_remote_port_parent(ep_node); - of_node_put(ep_node); - - if (!connector) { - dev_warn(rcdu->dev, - "no connector for encoder %pOF, skipping\n", - encoder); - of_node_put(entity_ep_node); - of_node_put(encoder); - return -ENODEV; - } - - break; - } - - of_node_put(entity_ep_node); - - if (!encoder) { - dev_warn(rcdu->dev, - "no encoder found for endpoint %pOF, skipping\n", - ep->local_node); - return -ENODEV; - } - - ret = rcar_du_encoder_init(rcdu, output, encoder, connector); + ret = rcar_du_encoder_init(rcdu, output, entity); if (ret && ret != -EPROBE_DEFER) dev_warn(rcdu->dev, "failed to initialize encoder %pOF on output %u (%d), skipping\n", - encoder, output, ret); + entity, output, ret); - of_node_put(encoder); - of_node_put(connector); + of_node_put(entity); return ret; } @@ -543,7 +516,6 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) struct drm_device *dev = rcdu->ddev; struct drm_encoder *encoder; - struct drm_fbdev_cma *fbdev; unsigned int dpad0_sources; unsigned int num_encoders; unsigned int num_groups; @@ -582,7 +554,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) * Initialize vertical blanking interrupts handling. Start with vblank * disabled for all CRTCs. */ - ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); + ret = drm_vblank_init(dev, rcdu->num_crtcs); if (ret < 0) return ret; @@ -682,17 +654,5 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) drm_kms_helper_poll_init(dev); - if (dev->mode_config.num_connector) { - fbdev = drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_connector); - if (IS_ERR(fbdev)) - return PTR_ERR(fbdev); - - rcdu->fbdev = fbdev; - } else { - dev_info(rcdu->dev, - "no connector found, disabling fbdev emulation\n"); - } - return 0; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7790.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7790.dts index 579753e04f3b..8bee4e787a0a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7790.dts +++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7790.dts @@ -7,70 +7,63 @@ /dts-v1/; /plugin/; -/ { - fragment@0 { - target-path = "/"; - __overlay__ { - #address-cells = <2>; - #size-cells = <2>; - lvds@feb90000 { - compatible = "renesas,r8a7790-lvds"; - reg = <0 0xfeb90000 0 0x1c>; +&{/} { + #address-cells = <2>; + #size-cells = <2>; - ports { - #address-cells = <1>; - #size-cells = <0>; + lvds@feb90000 { + compatible = "renesas,r8a7790-lvds"; + reg = <0 0xfeb90000 0 0x1c>; - port@0 { - reg = <0>; - lvds0_input: endpoint { - }; - }; - port@1 { - reg = <1>; - lvds0_out: endpoint { - }; - }; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_input: endpoint { }; }; - - lvds@feb94000 { - compatible = "renesas,r8a7790-lvds"; - reg = <0 0xfeb94000 0 0x1c>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - lvds1_input: endpoint { - }; - }; - port@1 { - reg = <1>; - lvds1_out: endpoint { - }; - }; + port@1 { + reg = <1>; + lvds0_out: endpoint { }; }; }; }; - fragment@1 { - target-path = "/display@feb00000/ports"; - __overlay__ { - port@1 { - endpoint { - remote-endpoint = <&lvds0_input>; + lvds@feb94000 { + compatible = "renesas,r8a7790-lvds"; + reg = <0 0xfeb94000 0 0x1c>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds1_input: endpoint { }; }; - port@2 { - endpoint { - remote-endpoint = <&lvds1_input>; + port@1 { + reg = <1>; + lvds1_out: endpoint { }; }; }; }; }; + +&{/display@feb00000/ports} { + port@1 { + endpoint { + remote-endpoint = <&lvds0_input>; + }; + }; + port@2 { + endpoint { + remote-endpoint = <&lvds1_input>; + }; + }; +}; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7791.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7791.dts index cb9da1f3942b..92c0509971ec 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7791.dts +++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7791.dts @@ -7,44 +7,37 @@ /dts-v1/; /plugin/; -/ { - fragment@0 { - target-path = "/"; - __overlay__ { - #address-cells = <2>; - #size-cells = <2>; - lvds@feb90000 { - compatible = "renesas,r8a7791-lvds"; - reg = <0 0xfeb90000 0 0x1c>; +&{/} { + #address-cells = <2>; + #size-cells = <2>; - ports { - #address-cells = <1>; - #size-cells = <0>; + lvds@feb90000 { + compatible = "renesas,r8a7791-lvds"; + reg = <0 0xfeb90000 0 0x1c>; - port@0 { - reg = <0>; - lvds0_input: endpoint { - }; - }; - port@1 { - reg = <1>; - lvds0_out: endpoint { - }; - }; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_input: endpoint { }; }; - }; - }; - - fragment@1 { - target-path = "/display@feb00000/ports"; - __overlay__ { port@1 { - endpoint { - remote-endpoint = <&lvds0_input>; + reg = <1>; + lvds0_out: endpoint { }; }; }; }; }; + +&{/display@feb00000/ports} { + port@1 { + endpoint { + remote-endpoint = <&lvds0_input>; + }; + }; +}; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7793.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7793.dts index e7b8804dc3c1..c8b93f21de0f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7793.dts +++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7793.dts @@ -7,44 +7,37 @@ /dts-v1/; /plugin/; -/ { - fragment@0 { - target-path = "/"; - __overlay__ { - #address-cells = <2>; - #size-cells = <2>; - lvds@feb90000 { - compatible = "renesas,r8a7793-lvds"; - reg = <0 0xfeb90000 0 0x1c>; +&{/} { + #address-cells = <2>; + #size-cells = <2>; - ports { - #address-cells = <1>; - #size-cells = <0>; + lvds@feb90000 { + compatible = "renesas,r8a7793-lvds"; + reg = <0 0xfeb90000 0 0x1c>; - port@0 { - reg = <0>; - lvds0_input: endpoint { - }; - }; - port@1 { - reg = <1>; - lvds0_out: endpoint { - }; - }; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_input: endpoint { }; }; - }; - }; - - fragment@1 { - target-path = "/display@feb00000/ports"; - __overlay__ { port@1 { - endpoint { - remote-endpoint = <&lvds0_input>; + reg = <1>; + lvds0_out: endpoint { }; }; }; }; }; + +&{/display@feb00000/ports} { + port@1 { + endpoint { + remote-endpoint = <&lvds0_input>; + }; + }; +}; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7795.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7795.dts index a1327443e6fa..16c2d03cb016 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7795.dts +++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7795.dts @@ -7,44 +7,37 @@ /dts-v1/; /plugin/; -/ { - fragment@0 { - target-path = "/soc"; - __overlay__ { - #address-cells = <2>; - #size-cells = <2>; - lvds@feb90000 { - compatible = "renesas,r8a7795-lvds"; - reg = <0 0xfeb90000 0 0x14>; +&{/soc} { + #address-cells = <2>; + #size-cells = <2>; - ports { - #address-cells = <1>; - #size-cells = <0>; + lvds@feb90000 { + compatible = "renesas,r8a7795-lvds"; + reg = <0 0xfeb90000 0 0x14>; - port@0 { - reg = <0>; - lvds0_input: endpoint { - }; - }; - port@1 { - reg = <1>; - lvds0_out: endpoint { - }; - }; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_input: endpoint { + }; + }; + port@1 { + reg = <1>; + lvds0_out: endpoint { }; }; }; }; +}; - fragment@1 { - target-path = "/soc/display@feb00000/ports"; - __overlay__ { - port@3 { - endpoint { - remote-endpoint = <&lvds0_input>; - }; - }; +&{/soc/display@feb00000/ports} { + port@3 { + endpoint { + remote-endpoint = <&lvds0_input>; }; }; }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7796.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7796.dts index b23d6466c415..680e923ac036 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7796.dts +++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7796.dts @@ -7,44 +7,37 @@ /dts-v1/; /plugin/; -/ { - fragment@0 { - target-path = "/soc"; - __overlay__ { - #address-cells = <2>; - #size-cells = <2>; - lvds@feb90000 { - compatible = "renesas,r8a7796-lvds"; - reg = <0 0xfeb90000 0 0x14>; +&{/soc} { + #address-cells = <2>; + #size-cells = <2>; - ports { - #address-cells = <1>; - #size-cells = <0>; + lvds@feb90000 { + compatible = "renesas,r8a7796-lvds"; + reg = <0 0xfeb90000 0 0x14>; - port@0 { - reg = <0>; - lvds0_input: endpoint { - }; - }; - port@1 { - reg = <1>; - lvds0_out: endpoint { - }; - }; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_input: endpoint { + }; + }; + port@1 { + reg = <1>; + lvds0_out: endpoint { }; }; }; }; +}; - fragment@1 { - target-path = "/soc/display@feb00000/ports"; - __overlay__ { - port@3 { - endpoint { - remote-endpoint = <&lvds0_input>; - }; - }; +&{/soc/display@feb00000/ports} { + port@3 { + endpoint { + remote-endpoint = <&lvds0_input>; }; }; }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 9e07758a755c..c6430027169f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -7,12 +7,12 @@ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ -#include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_device.h> #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_plane_helper.h> @@ -783,13 +783,14 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) drm_plane_helper_add(&plane->plane, &rcar_du_plane_helper_funcs); + drm_plane_create_alpha_property(&plane->plane); + if (type == DRM_PLANE_TYPE_PRIMARY) continue; drm_object_attach_property(&plane->plane.base, rcdu->props.colorkey, RCAR_DU_COLORKEY_NONE); - drm_plane_create_alpha_property(&plane->plane); drm_plane_create_zpos_property(&plane->plane, 1, 1, 7); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 2f223a4c1d33..81bbf207ad0e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -10,8 +10,7 @@ #ifndef __RCAR_DU_PLANE_H__ #define __RCAR_DU_PLANE_H__ -#include <drm/drmP.h> -#include <drm/drm_crtc.h> +#include <drm/drm_plane.h> struct rcar_du_format_info; struct rcar_du_group; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index 4576119e7777..0878accbd134 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c @@ -7,14 +7,13 @@ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ -#include <drm/drmP.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_vblank.h> #include <linux/bitops.h> #include <linux/dma-mapping.h> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h index e8c14dc5cb93..db232037f24a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h @@ -10,8 +10,7 @@ #ifndef __RCAR_DU_VSP_H__ #define __RCAR_DU_VSP_H__ -#include <drm/drmP.h> -#include <drm/drm_crtc.h> +#include <drm/drm_plane.h> struct rcar_du_format_info; struct rcar_du_vsp; diff --git a/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c index 75490a3e0a2a..452461dc96f2 100644 --- a/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c +++ b/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c @@ -7,10 +7,12 @@ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <drm/bridge/dw_hdmi.h> +#include <drm/drm_modes.h> #define RCAR_HDMI_PHY_OPMODE_PLLCFG 0x06 /* Mode of operation and PLL dividers */ #define RCAR_HDMI_PHY_PLLCURRGMPCTRL 0x10 /* PLL current and Gmp (conductance) */ @@ -35,6 +37,20 @@ static const struct rcar_hdmi_phy_params rcar_hdmi_phy_params[] = { { ~0UL, 0x0000, 0x0000, 0x0000 }, }; +static enum drm_mode_status +rcar_hdmi_mode_valid(struct drm_connector *connector, + const struct drm_display_mode *mode) +{ + /* + * The maximum supported clock frequency is 297 MHz, as shown in the PHY + * parameters table. + */ + if (mode->clock > 297000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi, const struct dw_hdmi_plat_data *pdata, unsigned long mpixelclock) @@ -59,6 +75,7 @@ static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi, } static const struct dw_hdmi_plat_data rcar_dw_hdmi_plat_data = { + .mode_valid = rcar_hdmi_mode_valid, .configure_phy = rcar_hdmi_phy_configure, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 173d7ad0b991..7ef97b2a6eda 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_graph.h> @@ -19,9 +20,10 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> +#include "rcar_lvds.h" #include "rcar_lvds_regs.h" struct rcar_lvds; @@ -182,8 +184,9 @@ struct pll_info { static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, unsigned long target, struct pll_info *pll, - u32 clksel) + u32 clksel, bool dot_clock_only) { + unsigned int div7 = dot_clock_only ? 1 : 7; unsigned long output; unsigned long fin; unsigned int m_min; @@ -217,9 +220,9 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, * `------------> | | * |/ * - * The /7 divider is optional when the LVDS PLL is used to generate a - * dot clock for the DU RGB output, without using the LVDS encoder. We - * don't support this configuration yet. + * The /7 divider is optional, it is enabled when the LVDS PLL is used + * to drive the LVDS encoder, and disabled when used to generate a dot + * clock for the DU RGB output, without using the LVDS encoder. * * The PLL allowed input frequency range is 12 MHz to 192 MHz. */ @@ -279,7 +282,7 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, * the PLL, followed by a an optional fixed /7 * divider. */ - fout = fvco / (1 << e) / 7; + fout = fvco / (1 << e) / div7; div = DIV_ROUND_CLOSEST(fout, target); diff = abs(fout / div - target); @@ -300,7 +303,7 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, done: output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e) - / 7 / pll->div; + / div7 / pll->div; error = (long)(output - target) * 10000 / (long)target; dev_dbg(lvds->dev, @@ -310,17 +313,18 @@ done: pll->pll_m, pll->pll_n, pll->pll_e, pll->div); } -static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) +static void __rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, + unsigned int freq, bool dot_clock_only) { struct pll_info pll = { .diff = (unsigned long)-1 }; u32 lvdpllcr; rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll, - LVDPLLCR_CKSEL_DU_DOTCLKIN(0)); + LVDPLLCR_CKSEL_DU_DOTCLKIN(0), dot_clock_only); rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll, - LVDPLLCR_CKSEL_DU_DOTCLKIN(1)); + LVDPLLCR_CKSEL_DU_DOTCLKIN(1), dot_clock_only); rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll, - LVDPLLCR_CKSEL_EXTAL); + LVDPLLCR_CKSEL_EXTAL, dot_clock_only); lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1); @@ -329,6 +333,9 @@ static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL | LVDPLLCR_PLLE(pll.pll_e - 1); + if (dot_clock_only) + lvdpllcr |= LVDPLLCR_OCKSEL; + rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); if (pll.div > 1) @@ -342,6 +349,57 @@ static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) rcar_lvds_write(lvds, LVDDIV, 0); } +static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) +{ + __rcar_lvds_pll_setup_d3_e3(lvds, freq, false); +} + +/* ----------------------------------------------------------------------------- + * Clock - D3/E3 only + */ + +int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq) +{ + struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); + int ret; + + if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) + return -ENODEV; + + dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq); + + WARN_ON(lvds->enabled); + + ret = clk_prepare_enable(lvds->clocks.mod); + if (ret < 0) + return ret; + + __rcar_lvds_pll_setup_d3_e3(lvds, freq, true); + + lvds->enabled = true; + return 0; +} +EXPORT_SYMBOL_GPL(rcar_lvds_clk_enable); + +void rcar_lvds_clk_disable(struct drm_bridge *bridge) +{ + struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); + + if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) + return; + + dev_dbg(lvds->dev, "disabling LVDS PLL\n"); + + WARN_ON(!lvds->enabled); + + rcar_lvds_write(lvds, LVDPLLCR, 0); + + clk_disable_unprepare(lvds->clocks.mod); + + lvds->enabled = false; +} +EXPORT_SYMBOL_GPL(rcar_lvds_clk_disable); + /* ----------------------------------------------------------------------------- * Bridge */ @@ -520,8 +578,8 @@ static void rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds) } static void rcar_lvds_mode_set(struct drm_bridge *bridge, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) { struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); @@ -544,7 +602,10 @@ static int rcar_lvds_attach(struct drm_bridge *bridge) return drm_bridge_attach(bridge->encoder, lvds->next_bridge, bridge); - /* Otherwise we have a panel, create a connector. */ + /* Otherwise if we have a panel, create a connector. */ + if (!lvds->panel) + return 0; + ret = drm_connector_init(bridge->dev, connector, &rcar_lvds_conn_funcs, DRM_MODE_CONNECTOR_LVDS); if (ret < 0) @@ -592,7 +653,8 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds) local_output = of_graph_get_endpoint_by_regs(lvds->dev->of_node, 1, 0); if (!local_output) { dev_dbg(lvds->dev, "unconnected port@1\n"); - return -ENODEV; + ret = -ENODEV; + goto done; } /* @@ -642,6 +704,15 @@ done: of_node_put(remote_input); of_node_put(remote); + /* + * On D3/E3 the LVDS encoder provides a clock to the DU, which can be + * used for the DPAD output even when the LVDS output is not connected. + * Don't fail probe in that case as the DU will need the bridge to + * control the clock. + */ + if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL) + return ret == -ENODEV ? 0 : ret; + return ret; } @@ -785,11 +856,14 @@ static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = { static const struct of_device_id rcar_lvds_of_table[] = { { .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info }, + { .compatible = "renesas,r8a7744-lvds", .data = &rcar_lvds_gen2_info }, + { .compatible = "renesas,r8a774c0-lvds", .data = &rcar_lvds_r8a77990_info }, { .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_r8a7790_info }, { .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info }, { .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info }, { .compatible = "renesas,r8a7795-lvds", .data = &rcar_lvds_gen3_info }, { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info }, + { .compatible = "renesas,r8a77965-lvds", .data = &rcar_lvds_gen3_info }, { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info }, { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info }, { .compatible = "renesas,r8a77990-lvds", .data = &rcar_lvds_r8a77990_info }, diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.h b/drivers/gpu/drm/rcar-du/rcar_lvds.h new file mode 100644 index 000000000000..a709cae1bc32 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rcar_lvds.h -- R-Car LVDS Encoder + * + * Copyright (C) 2013-2018 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ + +#ifndef __RCAR_LVDS_H__ +#define __RCAR_LVDS_H__ + +struct drm_bridge; + +#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) +int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq); +void rcar_lvds_clk_disable(struct drm_bridge *bridge); +#else +static inline int rcar_lvds_clk_enable(struct drm_bridge *bridge, + unsigned long freq) +{ + return -ENOSYS; +} +static inline void rcar_lvds_clk_disable(struct drm_bridge *bridge) { } +#endif /* CONFIG_DRM_RCAR_LVDS */ + +#endif /* __RCAR_LVDS_H__ */ |